[
  {
    "path": ".gitattributes",
    "content": "*.js linguist-language=Pyhton\n*.css linguist-language=Python\n*.html linguist-language=Python\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n\n# migrations\nmigrations/\nUser Guide/\nlogs/\njobs/\nusers/\nworkspace/\n.idea/\n.beats/\n\n\n# 忽略列表\n.DS_Store/"
  },
  {
    "path": "AutoLink.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\nimport os\nimport sys\n\nfrom flask_script import Manager\n\nfrom auto.www.app import create_app, load_all_task\nfrom auto.settings import HEADER\nfrom utils.help import check_version\n\nif sys.platform.startswith(\"linux\") or sys.platform.startswith(\"darwin\"):\n    os.environ[\"PATH\"] = os.environ[\"PATH\"] + \":\" + os.getcwd() + \"/driver\"\nelse:\n    os.environ[\"PATH\"] = os.environ[\"PATH\"] + \";\" + os.getcwd() + \"/driver\"\n\nprint(HEADER)\n\napp = create_app('default')\nmanager = Manager(app)\n\n\nif __name__ == '__main__':\n\n    check_version()\n\n    load_all_task(app)\n\n    manager.run()\n"
  },
  {
    "path": "CodeStats.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\n# coding=utf-8\nimport os\nimport time\nbasedir = os.getcwd()\nfilelists = []\n\n# 指定想要统计的文件类型\nwhitelist = ['py', 'js']\n\nfilelists = []\n\n\n# 遍历文件, 递归遍历文件夹中的所有\ndef get_file(base_dir):\n    for parent, dirnames, filenames in os.walk(basedir):\n        # for dirname in dirnames:\n        # getFile(os.path.join(parent,dirname)) #递归\n\n        for filename in filenames:\n            if filename in (\"AutoStats.py\", \"commonLibrary.py\"):\n                continue\n\n            ext = filename.split('.')[-1]\n            if ext == \"js\" and filename != \"auto.js\":\n                continue\n\n            # 只统计指定的文件类型，略过一些log和cache文件\n            if ext in whitelist:\n                filelists.append(os.path.join(parent, filename))\n\n\n# 统计一个文件的行数\ndef count_line(fname):\n    count = 0\n    for file_line in open(fname).readlines():\n        # 过滤掉空行\n        if file_line != '' and file_line != '\\n':\n            count += 1\n    print('%s ---- %s' % (fname, count))\n    return count\n\n\nif __name__ == '__main__' :\n    startTime = time.clock()\n    get_file(basedir)\n    totalline = 0\n    for filelist in filelists:\n        totalline = totalline + count_line(filelist)\n\n    print('total lines: %s' % totalline)\n    print('Done! Cost Time: %0.2f second' % (time.clock() - startTime))"
  },
  {
    "path": "INSTALL.md",
    "content": "## 安装与启动\n\n1. 安装Python3版本，确保加入环境变量，pip命令可用\n\n2. 从[AutoLink Github项目](https://github.com/small99/AutoLink)下载源码\n\n3. 执行以下命令安装AutoLink依赖\n\n> pip install -r requirements.txt\n\n4.1 执行以下命令启动AutoLink服务\n\n> python AutoLink.py runserver\n\n4.1.1 访问以下网址，即可\n\nhttp://127.0.0.1:5000\n\n4.2 执行以下命令可外网访问\n\n> python AutoLink.py runserver -h 0.0.0.0 -p 8000\n通过\n\n4.2.1 即可通过你的IP地址来访问\n\nhttp://ip:8000\n\n注： \n- -h选项指定为0.0.0.0即为绑定本机ip启动，网络其他用户通过你的ip和-p指定的端口即可访问AutoLink\n\n- -p指定AutoLink服务启动时的端口\n\n默认账号: AutoLink  \n默认密码: 123456\n\n5. 下载selenium webdriver对应的浏览器驱动放在driver目录即可"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "## 介绍\n\nAutoLink开源自动化测试集成解决方案.\n\n- AutoLink是RobotFramework的web集成开发环境.\n- AutoLink支持RobotFramework语法高亮，自动提示等功能.\n- AutoLink可以帮助你轻易的构建web自动化测试脚本、HTTP接口自动化测试脚本以及移动自动化测试脚本.\n- AutoLink完美的支持RobotFramework所有的关键字.\n- AutoLink可以直接应用到你的企业实践中，节省框架开发成本.\n- AutoLink是很简单的，但也很容易使用.\n- AutoLink支持项目级、套件级、用例级运行\n\n## 用户指南\n\n- [简介](./docs/README.md)\n- [安装与启动](./docs/安装与启动.md)\n- [如何创建测试项目](./docs/如何创建测试项目.md)\n- [如何运行测试项目](./docs/如何运行测试项目.md)\n- [如何管理用例顺序](./docs/如何管理测试项目中用例顺序.md)\n- [使用关键字快捷键](./docs/如何使用自动提示快捷输入关键字.md)\n- [关键字概要说明](./docs/关键字概要说明.md)\n- [如何使用调度管理](./docs/如何使用调度管理.md)\n- [上传和下载RobotFramework用例](./docs/上传和下载RobotFramework用例.md)\n- [接口测试示例](./docs/如何创建HTTP接口测试用例.md)\n- [Python自定义关键字](./docs/如何调用Python自定义库.md)\n- [如何查看关键字详细文档](./docs/如何查看关键字详细文档.md)\n- [查看测试报告](./docs/查看测试报告.md)\n- [配置SMTP及邮件通知](./docs/配置SMTP服务及邮件通知.md)\n\n## 苦叶子发起\n\n- 可以关注我的[公众号]，不定期分享开源测试技术\n\n- 加入免费的[读书会]和上千人沟通交流开源测试技术\n\n- 加入付费的[知识星球]与更多的牛人一起交流\n\n- 觉得AutoLink还不错的话，可以打赏一杯咖啡，谢谢\n\n公众号\n\n![公众号](./auto/www/static/img/公众号.jpg)\n\n## 截图\n\nAutoLink Web IDE编辑模式截图欣赏\n\n![index](./auto/www/static/img/index.png)\n\n![dashboard](./docs/img/dashboard_new.png)\n\n![help](./docs/img/keyword_help.png)\n\n![keyword](./docs/img/double_keyword.png)\n\n邮件通知截图\n\n![email](./docs/img/mail_report.png)"
  },
  {
    "path": "UPDATEING.md",
    "content": "2018-09-10 v1.0.12\n1. 新增icon图标资源\n2. 新增css定义\n3. 新增步骤级前端js处理\n4. 新增后端api步骤详细信息\n5. 修订主面板截图\n6. 修复用户反馈的缺陷\n\n2018-09-06 v1.0.11\n1. 修复用户反馈的缺陷\n2. 注释flask mail相关代码\n3. 新增smtp配置及邮件通知能力\n4. 新增smtp配置及邮件通知配置文档\n\n2018-08-27 v1.0.10\n1. 优化dashboard静态导航\n2. 优化项目读取接口\n3. 优化项目树控件刷新机制\n4. 优化文档首页截图信息\n5. 修复用户反馈缺陷\n\n2018-08-26 v1.0.9\n1. 新增导航条\n2. 新增优化调度管理\n3. 新增调度管理、用户管理、用户指南、和更新清单导航\n4. 新增查看报告使用文档\n5. 新增调度管理页面查看任务及最有一次任务状态信息功能\n6. 新增mac系统判断，以添加合适的环境变量\n\n2018-08-24 v1.0.8\n1. 新增关键字帮助文档信息\n2. 新增Python自定义库编写示例项目\n3. 新增用户指南相关文档教程\n4. 标识为v1.0.8版本，强制升级版本\n\n2018-08-23 v1.0.7\n1. 修复用户反馈的缺陷\n2. 对用户工作区的前端树操作进行了优化\n3. 对后端项目管理api进行了优化\n5. 新增图片类型资源\n6. 本版本为强制更新版本\n\n2018-08-21 v1.0.6\n1. 优化报告：显示详细的关键字步骤\n2. 修复自动补全功能缺陷\n3. 新增debug信息输出\n4. 新增github忽略列表，以解决可能出现的冲突问题\n5. 修订截图信心\n6. 新增接口测试项目demo及对应的文档\n\n2018-08-20 v1.0.5\n1. 新增关键字高亮和Ctrl键提示自动生成功能\n\n2018-08-17 v1.0.4\n1. 新增用户指南说明\n2. 新增ssh和database库关键字支持\n3. 在dashboard页面新增注销按钮\n4. 登录页面新增回车响应登录\n\n2018-08-16 v1.0.3\n1. 根据操作系统类型设置环境变量信息,强制更新版本\n\n2018-08-15 v1.0.2\n1. 新增根据文件类型来设置编辑器默认内容\n2. 修复用户反馈的缺陷\n3. 新增用户指南相关文档\n\n2018-08-13 v1.0.1\n1. 续订说明文档及图片细节\n2. 修订用户反馈的缺陷\n\n2018-08-13 v1.0.0\n1. 发布整个项目源码至github \n"
  },
  {
    "path": "auto/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n"
  },
  {
    "path": "auto/configuration.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\nimport logging\nimport os\nimport json\nimport codecs\nfrom apscheduler.executors.pool import ProcessPoolExecutor\n\nfrom utils.file import exists_path\n\n\nclass Config:\n    # conf_path = os.getcwd() + \"/.beats/auto.json\"\n\n    #if exists_path(conf_path):\n    #    config = json.load(codecs.open(conf_path, 'r', 'utf-8'))\n\n    #    MAIL_SERVER = config[\"smtp\"][\"server\"]\n    #    MAIL_PORT = config[\"smtp\"][\"port\"]\n    #    MAIL_USE_TLS = True\n    #    MAIL_USERNAME = config[\"smtp\"][\"username\"]\n    #    MAIL_PASSWORD = config[\"smtp\"][\"password\"]\n    #    DEFAULT_MAIL_SENDER = \"lymking@foxmail.com\"\n    #    FLASKY_ADMIN = config[\"smtp\"][\"username\"]\n    #    MAIL_USE_SSL = config[\"smtp\"][\"ssl\"]\n\n    # MAIL_DEBUG = True\n    SSL_REDIRECT = False\n\n    SECRET_KEY = 'QWERTYUIOPASDFGHJ'\n    # logging level\n    LOGGING_LEVEL = logging.INFO\n    AUTO_HOME = os.getcwd().replace('\\\\', '/') + '/.beats'\n\n    AUTO_ROBOT = []\n\n    executors = {\n        'default': {'type': 'threadpool', 'max_workers': 20},\n        'processpool': ProcessPoolExecutor(max_workers=5)\n    }\n\n    job_defaults = {\n        'coalesce': False,\n        'max_instances': 10\n    }\n\n    @staticmethod\n    def init_app(app):\n        pass\n\n\nclass DevelopmentConfig(Config):\n    DEBUG = True\n\n\nclass ProductionConfig(Config):\n    DEBUG = False\n\n    @classmethod\n    def init_app(cls, app):\n        Config.init_app(app)\n\n        # 发送服务启动初始化时错误信息给管理员\n        import logging\n        from logging.handlers import SMTPHandler\n        credentials = None\n        secure = None\n        if getattr(cls, 'MAIL_USERNAME', None) is not None:\n            credentials = (cls.MAIL_USERNAME, cls.MAIL_PASSWORD)\n            if getattr(cls, 'MAIL_USE_TLS', None):\n                secure = ()\n\n        mail_handler = SMTPHandler(\n            mailhost=(cls.MAIL_SERVER, cls.MAIL_PORT),\n            fromaddr=cls.FLASKY_MAIL_SENDER,\n            toaddrs=[cls.FLASKY_ADMIN],\n            subject=cls.FLASKY_MAIL_SUBJECT_PREFIX + ' AutoLine Startup Error',\n            credentials=credentials,\n            secure=secure)\n\n        mail_handler.setLevel(logging.ERROR)\n        app.logger.addHandler(mail_handler)\n\n\nconfig = {\n    \"development\": DevelopmentConfig,\n    \"production\": ProductionConfig,\n\n    \"default\": DevelopmentConfig\n}\n"
  },
  {
    "path": "auto/exceptions.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\n\nclass AutoBeatException(Exception):\n    pass\n\n\nclass AutoBeatConfigException(AutoBeatException):\n    pass\n\n\nclass AutoBeatExecutorTimeout(AutoBeatException):\n    pass\n\n\nclass AutoBeatTaskTimeout(AutoBeatException):\n    pass\n\n\nclass AutoBeatWebServerTimeout(AutoBeatException):\n    pass\n\n\nclass AutoBeatSkipException(AutoBeatException):\n    pass\n"
  },
  {
    "path": "auto/settings.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\n\nHEADER = \"\"\"\\\n   ___       __       __   _      __  \n  / _ |__ __/ /____  / /  (_)__  / /__\n / __ / // / __/ _ \\/ /__/ / _ \\/  '_/\n/_/ |_\\_,_/\\__/\\___/____/_/_//_/_/\\_\\ \n\"\"\"\n\n\n\n\n"
  },
  {
    "path": "auto/version.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\nversion = '1.0.0.0'\n"
  },
  {
    "path": "auto/www/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n"
  },
  {
    "path": "auto/www/api/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\nfrom flask import Blueprint\nfrom flask_restful import Api\n\n\napi_bp = Blueprint('api', __name__)\napi = Api(api_bp)\n\n\nfrom .auth import Auth\napi.add_resource(Auth, \"/auth/\")\n\nfrom .project import Project, ProjectList\napi.add_resource(Project, \"/project/\")\napi.add_resource(ProjectList, \"/project_list/\")\n\nfrom .suite import Suite\napi.add_resource(Suite, \"/suite/\")\n\nfrom .case import Case, ManageFile\napi.add_resource(Case, \"/case/\")\napi.add_resource(ManageFile, \"/manage_file/\")\n\nfrom .keyword import Keyword\napi.add_resource(Keyword, \"/keyword/\")\n\nfrom .task import Task, TaskList\napi.add_resource(Task, \"/task/\")\napi.add_resource(TaskList, \"/task_list/\")\n\n\nfrom .user import User\napi.add_resource(User, \"/user/\")\n\n\nfrom .settings import Settings\napi.add_resource(Settings, \"/settings/\")\n"
  },
  {
    "path": "auto/www/api/auth.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\nfrom flask import current_app, url_for, redirect, session\nfrom flask_restful import Resource, reqparse\nfrom werkzeug.security import generate_password_hash, check_password_hash\nimport json\nimport os\nimport codecs\n\n\nclass Auth(Resource):\n    def __init__(self):\n        self.parser = reqparse.RequestParser()\n        self.parser.add_argument('username', type=str)\n        self.parser.add_argument('password', type=str)\n\n    def get(self):\n        args = self.parser.parse_args()\n        username = args[\"username\"]\n\n        if username in session:\n            session.pop(username, None)\n\n        return {\"status\": \"success\", \"msg\": \"logout success\", \"url\": url_for('routes.index')}, 201\n\n    def post(self):\n        args = self.parser.parse_args()\n        username = args[\"username\"]\n        password = args[\"password\"]\n        app = current_app._get_current_object()\n        user_path = app.config[\"AUTO_HOME\"] + \"/users/\" + username\n        if os.path.exists(user_path):\n            user = json.load(codecs.open(user_path + '/config.json', 'r', 'utf-8'))\n            if check_password_hash(user['passwordHash'], password):\n                session['username'] = username\n                return {\"status\": \"success\", \"msg\": \"login success\", \"url\": url_for('routes.dashboard')}, 201\n\n        return {\"status\": \"fail\", \"msg\": \"login fail\", \"url\": url_for('routes.index')}, 201\n"
  },
  {
    "path": "auto/www/api/case.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\nimport os\nfrom urllib.parse import quote\nfrom flask import current_app, session, request, send_file, make_response\nfrom flask_restful import Resource, reqparse\nimport werkzeug\n\nfrom utils.file import exists_path, rename_file, make_nod, remove_file, write_file, read_file, get_splitext\n\n\nclass Case(Resource):\n    def __init__(self):\n        self.parser = reqparse.RequestParser()\n        self.parser.add_argument('method', type=str)\n        self.parser.add_argument('name', type=str)\n        self.parser.add_argument('new_name', type=str)\n        self.parser.add_argument('project_name', type=str)\n        self.parser.add_argument('suite_name', type=str)\n        self.parser.add_argument('category', type=str)\n        self.parser.add_argument('new_category', type=str)\n        self.parser.add_argument('path', type=str)\n        self.parser.add_argument('data', type=str)\n        self.app = current_app._get_current_object()\n\n    def get(self):\n        args = self.parser.parse_args()\n        result = {\"status\": \"success\", \"msg\": \"读取文件成功\"}\n\n        ext = get_splitext(args[\"path\"])\n        result[\"ext\"] = ext[1]\n\n        path = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s%s\" % (session[\"username\"], args[\"path\"])\n        data = read_file(path)\n        if not data[\"status\"]:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"读取文件失败\"\n\n        result[\"data\"] = data[\"data\"]\n\n        return result, 201\n\n    def post(self):\n        args = self.parser.parse_args()\n\n        method = args[\"method\"].lower()\n        if method == \"create\":\n            result = self.__create(args)\n        elif method == \"edit\":\n            result = self.__edit(args)\n        elif method == \"delete\":\n            result = self.__delete(args)\n        elif method == \"save\":\n            result = self.__save(args)\n        else:\n            print(request.files[\"files\"])\n\n        return result, 201\n\n    def __create(self, args):\n        result = {\"status\": \"success\", \"msg\": \"创建文件成功\"}\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s/%s/%s%s\" % (session[\"username\"],\n                                                                                 args[\"project_name\"],\n                                                                                 args[\"suite_name\"],\n                                                                                 args[\"name\"],\n                                                                                 args[\"category\"])\n        if not exists_path(user_path):\n            make_nod(user_path)\n        else:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"文件名称重复，创建失败\"\n\n        return result\n\n    def __edit(self, args):\n        result = {\"status\": \"success\", \"msg\": \"文件重命名成功\"}\n        old_name = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s/%s/%s%s\" % (session[\"username\"],\n                                                                                args[\"project_name\"],\n                                                                                args[\"suite_name\"],\n                                                                                args[\"name\"],\n                                                                                args[\"category\"])\n\n        new_name = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s/%s/%s%s\" % (session[\"username\"],\n                                                                                args[\"project_name\"],\n                                                                                args[\"suite_name\"],\n                                                                                args[\"new_name\"],\n                                                                                args[\"new_category\"])\n\n        if not rename_file(old_name, new_name):\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"文件重命名失败，名称重复\"\n\n        return result\n\n    def __delete(self, args):\n        result = {\"status\": \"success\", \"msg\": \"目录删除成功\"}\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s/%s/%s%s\" % (session[\"username\"],\n                                                                                 args[\"project_name\"],\n                                                                                 args[\"suite_name\"],\n                                                                                 args[\"name\"],\n                                                                                 args[\"category\"])\n        if exists_path(user_path):\n            remove_file(user_path)\n\n        else:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"删除失败，不存在的文件\"\n\n        return result\n\n    def __save(self, args):\n        result = {\"status\": \"success\", \"msg\": \"保存成功\"}\n\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s%s\" % (session[\"username\"], args[\"path\"])\n\n        if not write_file(user_path, args[\"data\"]):\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"保存失败\"\n\n        return result\n\n\nclass ManageFile(Resource):\n    def __init__(self):\n        self.parser = reqparse.RequestParser()\n        self.parser.add_argument('data', type=str)\n        self.parser.add_argument('file', type=werkzeug.datastructures.FileStorage, location='files', action='append')\n        self.app = current_app._get_current_object()\n\n    def post(self):\n        args = request.form.to_dict()\n        print(args)\n        method = args[\"method\"].lower()\n        if method == \"upload\":\n            file = request.files.to_dict()['files']\n            return self.__upload(file, args['path']), 201\n        elif method == \"download\":\n            return self.__download(args)\n\n    def __upload(self, file, path):\n        result = {\"status\": \"success\", \"msg\": \"上传成功\"}\n\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s\" % session['username'] + path + file.filename\n        if not exists_path(user_path):\n            file.save(user_path)\n        else:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"上传失败\"\n\n        return result\n\n    def __download(self, args):\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s\" % session['username'] + args[\"path\"]\n\n        response = make_response(send_file(user_path))\n        basename = os.path.basename(user_path)\n        response.headers[\"Content-Disposition\"] = \\\n            \"attachment;\" \\\n            \"filename*=UTF-8''{utf_filename}\".format(\n                utf_filename=quote(basename.encode('utf-8'))\n            )\n        return response\n\n        #return send_file(user_path, mimetype='application/octet-stream', as_attachment=True)\n"
  },
  {
    "path": "auto/www/api/editor.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n"
  },
  {
    "path": "auto/www/api/keyword.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\n\nfrom flask_restful import Resource, reqparse\n\nfrom utils.parsing import parser_robot_keyword_list\n\n\nclass Keyword(Resource):\n    def __init__(self):\n        self.parser = reqparse.RequestParser()\n        self.parser.add_argument('category', type=str)\n\n    def get(self):\n        args = self.parser.parse_args()\n        if args[\"category\"] == \"robot\":\n            return parser_robot_keyword_list()\n"
  },
  {
    "path": "auto/www/api/project.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\nfrom flask import current_app, session\nfrom flask_restful import Resource, reqparse\nimport json\nimport os\nimport codecs\n\nfrom robot.api import TestSuiteBuilder\nfrom robot.api import TestData, ResourceFile, TestCaseFile\n\nfrom utils.file import list_dir, mk_dirs, exists_path, rename_file, remove_dir, get_splitext\nfrom utils.resource import ICONS\n\n\nclass Project(Resource):\n    def __init__(self):\n        self.parser = reqparse.RequestParser()\n        self.parser.add_argument('method', type=str)\n        self.parser.add_argument('name', type=str)\n        self.parser.add_argument('new_name', type=str)\n        self.parser.add_argument('description', type=str)\n        self.parser.add_argument('enable', type=str, default=\"否\")\n        self.parser.add_argument('cron', type=str, default=\"* * * * * *\")\n        self.parser.add_argument('boolean', type=str, default=\"启用\")\n        self.app = current_app._get_current_object()\n\n    def get(self):\n        args = self.parser.parse_args()\n\n    def post(self):\n        args = self.parser.parse_args()\n\n        method = args[\"method\"].lower()\n        if method == \"create\":\n            result = self.__create(args)\n        elif method == \"edit\":\n            result = self.__edit(args)\n        elif method == \"delete\":\n            result = self.__delete(args)\n\n        return result, 201\n\n    def __create(self, args):\n        result = {\"status\": \"success\", \"msg\": \"创建项目成功\"}\n\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s\" % (session[\"username\"], args[\"name\"])\n        if not exists_path(user_path):\n            mk_dirs(user_path)\n\n            create_project(\n                self.app,\n                session[\"username\"],\n                {\n                    \"name\": args[\"name\"],\n                    \"description\": args[\"description\"],\n                    \"boolean\": args[\"boolean\"],\n                    \"enable\": args[\"enable\"],\n                    \"cron\": args[\"cron\"]\n                }\n            )\n        else:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"项目名称重复，创建失败\"\n\n        return result\n\n    def __edit(self, args):\n        result = {\"status\": \"success\", \"msg\": \"项目重命名成功\"}\n        old_name = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s\" % (session[\"username\"], args[\"name\"])\n        new_name = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s\" % (session[\"username\"], args[\"new_name\"])\n\n        if rename_file(old_name, new_name):\n            edit_project(\n                self.app,\n                session[\"username\"],\n                args[\"name\"],\n                {\n                    \"name\": args[\"new_name\"],\n                    \"description\": args[\"description\"],\n                    \"boolean\": args[\"boolean\"],\n                    \"enable\": args[\"enable\"],\n                    \"cron\": args[\"cron\"]\n                })\n        else:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"项目重命名失败，名称重复\"\n\n        return result\n\n    def __delete(self, args):\n        result = {\"status\": \"success\", \"msg\": \"项目删除成功\"}\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s\" % (session[\"username\"], args[\"name\"])\n        if exists_path(user_path):\n            remove_dir(user_path)\n\n            remove_project(self.app, session['username'], args['name'])\n        else:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"删除失败，不存在的项目\"\n\n        return result\n\n\nclass ProjectList(Resource):\n    def __init__(self):\n        self.parser = reqparse.RequestParser()\n        self.parser.add_argument('name', type=str)\n        self.parser.add_argument('category', type=str, default=\"root\")\n        self.parser.add_argument('project', type=str)\n        self.parser.add_argument('suite', type=str)\n        self.parser.add_argument('splitext', type=str)\n        self.app = current_app._get_current_object()\n\n    def get(self):\n        args = self.parser.parse_args()\n\n        if args[\"category\"] == \"root\":\n            return get_projects(self.app, session[\"username\"])\n        elif args[\"category\"] == \"project\":\n            return get_suite_by_project(self.app, session[\"username\"], args)\n        elif args[\"category\"] == \"suite\":\n            return get_case_by_suite(self.app, session[\"username\"], args)\n        elif args[\"category\"] == \"case\":\n            return get_step_by_case(self.app, session[\"username\"], args)\n\n        \"\"\"\n        projects = get_project_list(self.app, session['username'])\n        children = []\n        for p in projects:\n            detail = get_project_detail(self.app, session['username'], p[\"name\"])\n            children.append({\n                \"text\": p[\"name\"],\n                \"iconCls\": \"icon-project\",\n                \"state\": \"closed\",\n                \"attributes\": {\n                    \"name\": p[\"name\"],\n                    \"description\": p[\"description\"],\n                    \"category\": \"project\",\n                    \"boolean\": p[\"boolean\"]\n                },\n                \"children\": detail\n            })\n        \n        return [{\n            \"text\": session['username'],\n            \"iconCls\": \"icon-workspace\",\n            \"attributes\": {\n                \"category\": \"root\"\n            },\n            \"children\": children}]\n        \"\"\"\n\n\ndef create_project(app, username, project):\n    user_path = app.config[\"AUTO_HOME\"] + \"/users/\" + username\n    if os.path.exists(user_path):\n        config = json.load(codecs.open(user_path + '/config.json', 'r', 'utf-8'))\n        config[\"data\"].append(project)\n        json.dump(config, codecs.open(user_path + '/config.json', 'w', 'utf-8'))\n\n\ndef edit_project(app, username, old_name, new_project):\n    user_path = app.config[\"AUTO_HOME\"] + \"/users/\" + username\n    if os.path.exists(user_path):\n        config = json.load(codecs.open(user_path + '/config.json', 'r', 'utf-8'))\n        index = 0\n        for p in config[\"data\"]:\n            if p[\"name\"] == old_name:\n                config[\"data\"][index][\"name\"] = new_project[\"name\"]\n                config[\"data\"][index][\"description\"] = new_project[\"description\"]\n                config[\"data\"][index][\"boolean\"] = new_project[\"boolean\"]\n                break\n            index += 1\n\n    json.dump(config, codecs.open(user_path + '/config.json', 'w', 'utf-8'))\n\n\ndef remove_project(app, username, name):\n    user_path = app.config[\"AUTO_HOME\"] + \"/users/\" + username\n    if os.path.exists(user_path):\n        config = json.load(codecs.open(user_path + '/config.json', 'r', 'utf-8'))\n        index = 0\n        for p in config[\"data\"]:\n            if p[\"name\"] == name:\n                del config[\"data\"][index]\n                break\n            index += 1\n\n    json.dump(config, codecs.open(user_path + '/config.json', 'w', 'utf-8'))\n\n\ndef get_project_list(app, username):\n    work_path = app.config[\"AUTO_HOME\"] + \"/workspace/\" + username\n    if os.path.exists(work_path):\n        projects = list_dir(work_path)\n        if len(projects) > 1:\n            projects.sort()\n\n        return projects\n\n    return []\n\n\ndef get_project_detail(app, username, p_name):\n    path = app.config[\"AUTO_HOME\"] + \"/workspace/\" + username + \"/\" + p_name\n\n    projects = []\n    # raw_suites = list_dir(path)\n    # suites = sorted(raw_suites, key=lambda x: os.stat(path + \"/\" + x).st_ctime)\n    suites = list_dir(path)\n    if len(suites) > 1:\n        suites.sort()\n    for d in suites:\n        children = []\n        # cases = sorted(list_dir(path + \"/\" + d), key=lambda x: os.stat(path + \"/\" + d + \"/\" + x).st_ctime)\n        cases = list_dir(path + \"/\" + d)\n        if len(cases) > 1:\n            cases.sort()\n        for t in cases:\n            text = get_splitext(t)\n            if text[1] == \".robot\":\n                icons = \"icon-robot\"\n            elif text[1] == \".txt\":\n                icons = \"icon-resource\"\n            else:\n                icons = \"icon-file-default\"\n\n            children.append({\n                \"text\": t, \"iconCls\": icons,\n                \"attributes\": {\n                    \"name\": text[0], \"category\": \"case\", \"splitext\": text[1]\n                }\n            })\n        if len(children) == 0:\n            icons = \"icon-suite\"\n        else:\n            icons = \"icon-suite-open\"\n        projects.append({\n            \"text\": d, \"iconCls\": icons,\n            \"attributes\": {\n                \"name\": d, \"category\": \"suite\"\n            },\n            \"children\": children\n        })\n\n    return projects\n\n\ndef get_projects(app, username):\n    projects = get_project_list(app, username)\n    children = []\n    for p in projects:\n        children.append({\n            \"text\": p, \"iconCls\": \"icon-project\", \"state\": \"closed\",\n            \"attributes\": {\n                \"name\": p,  # \"description\": p[\"description\"],\n                \"category\": \"project\",  # \"boolean\": p[\"boolean\"]\n            },\n            \"children\": []\n        })\n\n    return [{\n        \"text\": session['username'], \"iconCls\": \"icon-workspace\",\n        \"attributes\": {\n            \"category\": \"root\"\n        },\n        \"children\": children}]\n\n\ndef get_suite_by_project(app, username, args):\n    path = app.config[\"AUTO_HOME\"] + \"/workspace/\" + username + \"/\" + args[\"name\"]\n\n    suites = list_dir(path)\n    children = []\n    if len(suites) > 1:\n        suites.sort()\n    for d in suites:\n        cases = list_dir(path + \"/\" + d)\n        icons = \"icon-suite\"\n        if len(cases) > 1:\n            icons = \"icon-suite-open\"\n\n        children.append({\n            \"text\": d, \"iconCls\": icons, \"state\": \"closed\",\n            \"attributes\": {\n                \"name\": d, \"category\": \"suite\"\n            },\n            \"children\": []\n        })\n\n    return children\n\n\ndef get_case_by_suite(app, username, args):\n    path = app.config[\"AUTO_HOME\"] + \"/workspace/\" + username + \"/%s/%s\" % (args[\"project\"], args[\"name\"] )\n\n    cases = list_dir(path)\n    if len(cases) > 1:\n        cases.sort()\n    children = []\n    for t in cases:\n        text = get_splitext(t)\n        if text[1] in ICONS:\n            icons = ICONS[text[1]]\n        else:\n            icons = \"icon-file-default\"\n\n        if text[1] in (\".robot\"):\n            children.append({\n                \"text\": t, \"iconCls\": icons, \"state\": \"closed\",\n                \"attributes\": {\n                    \"name\": text[0], \"category\": \"case\", \"splitext\": text[1]\n                },\n                \"children\": []\n            })\n        else:\n            children.append({\n                \"text\": t, \"iconCls\": icons, \"state\": \"open\",\n                \"attributes\": {\n                    \"name\": text[0], \"category\": \"case\", \"splitext\": text[1]\n                }\n            })\n\n    return children\n\n\ndef get_step_by_case(app, username, args):\n    print(args)\n    path = app.config[\"AUTO_HOME\"] + \"/workspace/\" + username + \"/%s/%s/%s%s\" % (args[\"project\"], args[\"suite\"], args[\"name\"], args[\"splitext\"])\n\n    data = []\n    if args[\"splitext\"] == \".robot\":\n        data = get_case_data(path)\n\n    return data\n\n\ndef get_case_data(path):\n    suite = TestSuiteBuilder().build(path)\n    children = []\n    if suite:\n        # add library\n        for i in suite.resource.imports:\n            children.append({\n                \"text\": i.name, \"iconCls\": \"icon-library\", \"state\": \"open\",\n                \"attributes\": {\n                    \"name\": i.name, \"category\": \"library\"\n                }\n            })\n\n        for v in suite.resource.variables:\n            children.append({\n                \"text\": v.name, \"iconCls\": \"icon-variable\", \"state\": \"open\",\n                \"attributes\": {\n                    \"name\": v.name, \"category\": \"variable\"\n                }\n            })\n\n        for t in suite.tests:\n            keys = []\n            for k in t.keywords:\n                keys.append({\n                    \"text\": k.name, \"iconCls\": \"icon-keyword\", \"state\": \"open\",\n                    \"attributes\": {\n                        \"name\": k.name, \"category\": \"keyword\"\n                    }\n                })\n\n            children.append({\n                \"text\": t.name, \"iconCls\": \"icon-step\", \"state\": \"closed\",\n                \"attributes\": {\n                    \"name\": t.name, \"category\": \"step\"\n                },\n                \"children\": keys\n            })\n\n        for v in suite.resource.keywords:\n            children.append({\n                \"text\": v.name, \"iconCls\": \"icon-user-keyword\", \"state\": \"open\",\n                \"attributes\": {\n                    \"name\": v.name, \"category\": \"user_keyword\"\n                }\n            })\n\n    return children\n"
  },
  {
    "path": "auto/www/api/settings.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\nimport json\nimport codecs\nfrom flask import current_app, session, request, send_file\nfrom flask_restful import Resource, reqparse\nfrom utils.file import exists_path, make_nod\n\n\nclass Settings(Resource):\n    def __init__(self):\n        self.parser = reqparse.RequestParser()\n        self.parser.add_argument('method', type=str)\n        self.parser.add_argument('ssl', type=bool, default=False)\n        self.parser.add_argument('server', type=str)\n        self.parser.add_argument('port', type=str)\n        self.parser.add_argument('username', type=str)\n        self.parser.add_argument('password', type=str)\n        self.parser.add_argument('project', type=str)\n        self.parser.add_argument('success_list', type=str)\n        self.parser.add_argument('fail_list', type=str)\n        self.app = current_app._get_current_object()\n\n    def get(self):\n        args = self.parser.parse_args()\n        method = args[\"method\"]\n\n        conf_path = self.app.config[\"AUTO_HOME\"] + \"/auto.json\"\n        if method == \"smtp\":\n            config = json.load(codecs.open(conf_path, 'r', 'utf-8'))\n            result = {\n                \"ssl\": config[\"smtp\"][\"ssl\"],\n                \"server\": config[\"smtp\"][\"server\"],\n                \"port\": config[\"smtp\"][\"port\"],\n                \"username\": config[\"smtp\"][\"username\"],\n                \"password\": config[\"smtp\"][\"password\"]\n            }\n        elif method == \"email\":\n            conf_path = self.app.config[\"AUTO_HOME\"] + \"/users/%s/config.json\" % session[\"username\"]\n            config = json.load(codecs.open(conf_path, 'r', 'utf-8'))\n            for p in config[\"data\"]:\n                if p[\"name\"] == args[\"project\"]:\n                    result = {\n                        \"success_list\": p[\"success_list\"],\n                        \"fail_list\": p[\"fail_list\"]\n                    }\n                    break\n\n        return result\n\n    def post(self):\n        args = self.parser.parse_args()\n        method = args[\"method\"]\n\n        if method == \"smtp\":\n            result = self.__smtp(args)\n        elif method == \"email\":\n            result = self.__email(args)\n\n        return result, 201\n\n    # 配置smtp\n    def __smtp(self, args):\n        result = {\"status\": \"success\", \"msg\": \"配置smtp服务成功\"}\n        conf_path = self.app.config[\"AUTO_HOME\"] + \"/auto.json\"\n        if not exists_path(conf_path):\n            make_nod(conf_path)\n        try:\n            config = json.load(codecs.open(conf_path, 'r', 'utf-8'))\n            config[\"smtp\"][\"ssl\"] = args[\"ssl\"]\n            config[\"smtp\"][\"server\"] = args[\"server\"]\n            config[\"smtp\"][\"port\"] = args[\"port\"]\n            config[\"smtp\"][\"username\"] = args[\"username\"]\n            config[\"smtp\"][\"password\"] = args[\"password\"]\n            json.dump(config, codecs.open(conf_path, 'w', 'utf-8'))\n\n            self.app.config[\"MAIL_SERVER\"] = args[\"server\"]\n            self.app.config[\"MAIL_PORT\"] = args[\"port\"]\n            self.app.config[\"MAIL_USERNAME\"] = args[\"username\"]\n            self.app.config[\"MAIL_PASSWORD\"] = args[\"password\"]\n            self.app.config[\"MAIL_USE_SSL\"] = args[\"ssl\"]\n        except Exception as e:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = str(e)\n\n        return result\n\n    # 设置email通知列表\n    def __email(self, args):\n        result = {\"status\": \"success\", \"msg\": \"配置smtp服务成功\"}\n\n        conf_path = self.app.config[\"AUTO_HOME\"] + \"/users/%s/config.json\" % (session[\"username\"])\n        try:\n            config = json.load(codecs.open(conf_path, 'r', 'utf-8'))\n            index = 0\n            for p in config[\"data\"]:\n                if p[\"name\"] == args[\"project\"]:\n                    config[\"data\"][index][\"success_list\"] = args[\"success_list\"]\n                    config[\"data\"][index][\"fail_list\"] = args[\"fail_list\"]\n                    break\n                else:\n                    index = index + 1\n                    continue\n\n            json.dump(config, codecs.open(conf_path, 'w', 'utf-8'))\n        except Exception as e:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = str(e)\n\n        return result\n"
  },
  {
    "path": "auto/www/api/suite.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\nfrom flask import current_app, session\nfrom flask_restful import Resource, reqparse\n\nfrom utils.file import mk_dirs, exists_path, rename_file, remove_dir\n\n\nclass Suite(Resource):\n    def __init__(self):\n        self.parser = reqparse.RequestParser()\n        self.parser.add_argument('method', type=str)\n        self.parser.add_argument('name', type=str)\n        self.parser.add_argument('new_name', type=str)\n        self.parser.add_argument('project_name', type=str)\n        self.app = current_app._get_current_object()\n\n    def post(self):\n        args = self.parser.parse_args()\n\n        method = args[\"method\"].lower()\n        if method == \"create\":\n            result = self.__create(args)\n        elif method == \"edit\":\n            result = self.__edit(args)\n        elif method == \"delete\":\n            result = self.__delete(args)\n\n        return result, 201\n\n    def __create(self, args):\n        result = {\"status\": \"success\", \"msg\": \"创建目录成功\"}\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s/%s\" % (session[\"username\"], args[\"project_name\"], args[\"name\"])\n        if not exists_path(user_path):\n            mk_dirs(user_path)\n        else:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"目录名称重复，创建失败\"\n\n        return result\n\n    def __edit(self, args):\n        result = {\"status\": \"success\", \"msg\": \"目录重命名成功\"}\n        old_name = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s/%s\" % (session[\"username\"], args[\"project_name\"], args[\"name\"])\n        new_name = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s/%s\" % (session[\"username\"], args[\"project_name\"], args[\"new_name\"])\n\n        if not rename_file(old_name, new_name):\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"目录重命名失败，名称重复\"\n\n        return result\n\n    def __delete(self, args):\n        result = {\"status\": \"success\", \"msg\": \"目录删除成功\"}\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s/%s\" % (session[\"username\"], args[\"project_name\"], args[\"name\"])\n        if exists_path(user_path):\n            remove_dir(user_path)\n\n        else:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"删除失败，不存在的目录\"\n\n        return result\n"
  },
  {
    "path": "auto/www/api/task.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\nfrom flask import current_app, session, url_for\nfrom flask_restful import Resource, reqparse\nimport json\nimport os\nimport codecs\nimport threading\nimport multiprocessing\nfrom dateutil import tz\n\nfrom robot.api import ExecutionResult\n\nfrom utils.file import exists_path, read_file, remove_dir\nfrom utils.run import robot_run, is_run, remove_robot, stop_robot, robot_job\nfrom ..app import scheduler\n\n\nclass Task(Resource):\n    def __init__(self):\n        self.parser = reqparse.RequestParser()\n        self.parser.add_argument('method', type=str)\n        self.parser.add_argument('category', type=str)\n        self.parser.add_argument('project', type=str)\n        self.parser.add_argument('suite', type=str)\n        self.parser.add_argument('case', type=str)\n        self.parser.add_argument('task_no', type=str)\n        self.app = current_app._get_current_object()\n\n    def post(self):\n        args = self.parser.parse_args()\n        category = args[\"category\"].lower()\n        if args[\"method\"] == \"run\":\n            project = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s\" % (session['username'], args[\"project\"])\n            output = self.app.config[\"AUTO_HOME\"] + \"/jobs/%s/%s\" % (session['username'], args[\"project\"])\n            if category == \"project\":\n                if not is_run(self.app, args[\"project\"]):\n                    p = multiprocessing.Process(target=robot_run, args=(session[\"username\"], args[\"project\"], project, output))\n                    p.start()\n                    self.app.config[\"AUTO_ROBOT\"].append({\"name\": args[\"project\"], \"process\": p})\n                else:\n                    return {\"status\": \"fail\", \"msg\": \"请等待上一个任务完成\"}\n            elif category == \"suite\":\n                case_path = project + \"/%s\" % args[\"suite\"]\n                if not is_run(self.app, args[\"project\"]):\n                    p = multiprocessing.Process(target=robot_run, args=(session[\"username\"], args[\"project\"], case_path, output))\n                    p.start()\n                    self.app.config[\"AUTO_ROBOT\"].append({\"name\": args[\"project\"], \"process\": p})\n                else:\n                    return {\"status\": \"fail\", \"msg\": \"请等待上一个任务完成\"}\n            elif category == \"case\":\n                case_path = project + \"/%s/%s\" % (args[\"suite\"], args[\"case\"])\n                if not is_run(self.app, args[\"project\"]):\n                    p = multiprocessing.Process(target=robot_run,\n                                                args=(session[\"username\"], args[\"project\"], case_path, output))\n                    p.start()\n                    self.app.config[\"AUTO_ROBOT\"].append(\n                        {\"name\": \"%s\" % args[\"project\"], \"process\": p})\n                else:\n                    return {\"status\": \"fail\", \"msg\": \"请等待上一个任务完成\"}\n\n            return {\"status\": \"success\", \"msg\": \"已启动运行\"}\n        elif args[\"method\"] == \"stop\":\n            stop_robot(self.app, args[\"project\"])\n\n            return {\"status\": \"success\", \"msg\": \"已停止运行\"}\n        elif args[\"method\"] == \"delete\":\n            delete_task_record(self.app, args[\"project\"], args[\"task_no\"])\n\n            return {\"status\": \"success\", \"msg\": \"已经删除记录\"}\n\n\nclass TaskList(Resource):\n    def __init__(self):\n        self.parser = reqparse.RequestParser()\n        self.parser.add_argument('method', type=str)\n        self.parser.add_argument('name', type=str)\n        self.parser.add_argument('cron', type=str)\n        self.app = current_app._get_current_object()\n\n    def get(self):\n        args = self.parser.parse_args()\n        project = args[\"name\"]\n\n        return get_task_list(self.app, session['username'], project)\n\n    def post(self):\n        args = self.parser.parse_args()\n        job_id = \"%s_%s\" % (session[\"username\"], args[\"name\"])\n        if args[\"method\"] == \"query\":\n            return get_all_task(self.app)\n        elif args[\"method\"] == \"start\":\n            result = {\"status\": \"success\", \"msg\": \"调度启动成功\"}\n            lock = threading.Lock()\n            lock.acquire()\n            job = scheduler.get_job(job_id)\n            if job:\n                scheduler.remove_job(job_id)\n            cron = args[\"cron\"].replace(\"\\n\", \"\").strip().split(\" \")\n            if args[\"cron\"] != \"* * * * * *\" and len(cron) == 6:\n                scheduler.add_job(id=job_id,\n                                  name=args[\"name\"],\n                                  func=robot_job,\n                                  args=(self.app, args[\"name\"], session[\"username\"]),\n                                  trigger=\"cron\",\n                                  second=cron[0],\n                                  minute=cron[1],\n                                  hour=cron[2],\n                                  day=cron[3],\n                                  month=cron[4],\n                                  day_of_week=cron[5])\n            else:\n                result[\"msg\"] = \"cron表达式为默认* * * * * *, <br><br>无法启动调度，请修改cron表达式\"\n            lock.release()\n            return result\n\n        elif args[\"method\"] == \"stop\":\n            lock = threading.Lock()\n            lock.acquire()\n            job = scheduler.get_job(job_id)\n            if job:\n                scheduler.remove_job(id=job_id)\n            lock.release()\n            return {\"status\": \"success\", \"msg\": \"停止调度成功\"}\n        elif args[\"method\"] == \"edit\":\n\n            result = edit_cron(self.app, args[\"name\"], args[\"cron\"])\n            if result:\n                # job_id = \"%s_%s\" % (session[\"username\"], args[\"name\"])\n                lock = threading.Lock()\n                lock.acquire()\n                job = scheduler.get_job(job_id)\n                if job:\n                    scheduler.remove_job(job_id)\n\n                cron = args[\"cron\"].replace(\"\\n\", \"\").strip().split(\" \")\n                if args[\"cron\"] != \"* * * * * *\" and len(cron) == 6:\n                    scheduler.add_job(id=job_id,\n                                      name=args[\"name\"],\n                                      func=robot_job,\n                                      args=(self.app, args[\"name\"], session[\"username\"]),\n                                      trigger=\"cron\",\n                                      second=cron[0],\n                                      minute=cron[1],\n                                      hour=cron[2],\n                                      day=cron[3],\n                                      month=cron[4],\n                                      day_of_week=cron[5])\n                lock.release()\n\n            return {\"status\": \"success\", \"msg\": \"更新调度成功\"}\n\n\ndef get_task_list(app, username, project):\n    job_path = app.config[\"AUTO_HOME\"] + \"/jobs/%s/%s\" % (username, project)\n    next_build = 0\n    task = []\n    if exists_path(job_path):\n        next_build = get_next_build_number(job_path)\n        if next_build != 0:\n            # 遍历所有任务结果\n            # 判断最近一个任务状态\n            icons = {\n                \"running\": url_for('static', filename='img/running.gif'),\n                \"success\": url_for('static', filename='img/success.png'),\n                \"fail\": url_for('static', filename='img/fail.png'),\n                \"exception\": url_for('static', filename='img/exception.png')}\n\n            #if exists_path(job_path + \"/%s\" % (next_build - 1)):\n            running = False\n            lock = threading.Lock()\n            lock.acquire()\n            remove_robot(app)\n            for p in app.config[\"AUTO_ROBOT\"]:\n                if p[\"name\"] == project:\n                    running = True\n                    break\n            lock.release()\n            if running:\n                task.append(\n                   {\n                       \"status\": icons[\"running\"],\n                       \"name\": \"%s_#%s\" % (project, next_build-1),\n                       \"success\": \"\",\n                       \"fail\": \"\"\n                   }\n                )\n            last = 1\n            if running:\n                last = 2\n            for i in range(next_build-last, -1, -1):\n                if exists_path(job_path + \"/%s\" % i):\n                    try:\n                        suite = ExecutionResult(job_path + \"/%s/output.xml\" % i).suite\n                        stat = suite.statistics.critical\n                        if stat.failed != 0:\n                            status = icons[\"fail\"]\n                        else:\n                            status = icons['success']\n                        task.append({\n                            \"task_no\": i,\n                            \"status\": status,\n                            \"name\": \"<a href='/view_report/%s/%s' target='_blank'>%s_#%s</a>\" % (project, i, project, i),\n                            \"success\": stat.passed,\n                            \"fail\": stat.failed,\n                            \"starttime\": suite.starttime,\n                            \"endtime\": suite.endtime,\n                            \"elapsedtime\": suite.elapsedtime,\n                            \"note\": \"\"\n                        })\n                    except:\n                        status = icons[\"exception\"]\n                        if i == next_build-last:\n                            status = icons[\"running\"]\n                        task.append({\n                            \"task_no\": i,\n                            \"status\": status,\n                            \"name\": \"%s_#%s\" % (project, i),\n                            \"success\": \"-\",\n                            \"fail\": \"-\",\n                            \"starttime\": \"-\",\n                            \"endtime\": \"-\",\n                            \"elapsedtime\": \"-\",\n                            \"note\": \"异常\"\n                        })\n\n    return {\"total\": next_build-1, \"rows\": task}\n\n\ndef get_last_task(app, username, project):\n    icons = {\n        \"running\": url_for('static', filename='img/running.gif'),\n        \"success\": url_for('static', filename='img/success.png'),\n        \"fail\": url_for('static', filename='img/fail.png'),\n        \"exception\": url_for('static', filename='img/exception.png')}\n    job_path = app.config[\"AUTO_HOME\"] + \"/jobs/%s/%s\" % (username, project)\n    status = icons[\"running\"]\n    if exists_path(job_path):\n        next_build = get_next_build_number(job_path)\n        last_job = next_build-1\n        if exists_path(job_path + \"/%s\" % last_job):\n            try:\n                suite = ExecutionResult(job_path + \"/%s/output.xml\" % last_job).suite\n                stat = suite.statistics.critical\n                if stat.failed != 0:\n                    status = icons[\"fail\"]\n                else:\n                    status = icons['success']\n            except:\n                status = icons[\"running\"]\n        else:\n            status = icons[\"exception\"]\n    else:\n        status = icons['success']\n\n    return status\n\n\ndef get_all_task(app):\n    user_path = app.config[\"AUTO_HOME\"] + \"/users/\" + session[\"username\"]\n    if exists_path(user_path):\n        config = json.load(codecs.open(user_path + '/config.json', 'r', 'utf-8'))\n        projects = config['data']\n        task_list = {\"total\": len(projects), \"rows\": []}\n        for p in projects:\n            # job_path = app.config[\"AUTO_HOME\"] + \"/jobs/%s/%s\" % (session[\"username\"], p[\"name\"])\n            # running = False\n            # lock = threading.Lock()\n            # lock.acquire()\n            # remove_robot(app)\n            # next_build = get_next_build_number(job_path)\n            # if next_build != 0:\n            #     for pp in app.config[\"AUTO_ROBOT\"]:\n            #         if pp[\"name\"] == p[\"name\"]:\n            #             running = True\n            #             status = icons[\"running\"]\n            #             break\n            #     if running is False:\n            #         if exists_path(job_path + \"/%s\" % (next_build-1)):\n            #             try:\n            #                 suite = ExecutionResult(job_path + \"/%s/output.xml\" % (next_build-1)).suite\n            #                 stat = suite.statistics.critical\n            #                 if stat.failed != 0:\n            #                     status = icons[\"fail\"]\n            #                 else:\n            #                     status = icons['success']\n            #             except:\n            #                 status = icons[\"running\"]\n            # else:\n            #     status = icons['success']\n\n            # lock.release()\n            task = {\n                #\"status\": status,\n                \"name\": p[\"name\"],\n                #\"last_success\": get_last_pass(job_path + \"/lastPassed\"),\n                #\"last_fail\": get_last_fail(job_path + \"/lastFail\"),\n                \"enable\": p[\"enable\"],\n                \"next_time\": get_next_time(app, p[\"name\"]),\n                \"cron\": p[\"cron\"],\n                \"status\": get_last_task(app, session[\"username\"], p[\"name\"])\n            }\n\n            task_list[\"rows\"].append(task)\n\n    return task_list\n\n\ndef get_last_pass(job_path):\n    passed = \"无\"\n    passed_path = job_path + \"lastPassed\"\n    if exists_path(passed_path):\n        f = codecs.open(passed_path, \"r\", \"utf-8\")\n\n        passed = f.read()\n\n        f.close()\n\n    return passed\n\n\ndef get_last_fail(job_path):\n    fail = \"无\"\n    fail_path = job_path + \"lastFail\"\n    if exists_path(fail_path):\n        f = codecs.open(fail_path, \"r\", \"utf-8\")\n\n        fail = f.read()\n\n        f.close()\n\n    return fail\n\n\ndef get_next_build_number(job_path):\n    next_build_number = 1\n    next_path = job_path + \"/nextBuildNumber\"\n    if exists_path(next_path):\n        f = codecs.open(next_path, \"r\", \"utf-8\")\n\n        next_build_number = int(f.read())\n\n        f.close()\n\n    return next_build_number\n\n\ndef get_next_time(app, name):\n    job = scheduler.get_job(\"%s_%s\" % (session[\"username\"], name))\n    if job:\n        to_zone = tz.gettz(\"CST\")\n        return job.next_run_time.astimezone(to_zone).strftime(\"%Y-%m-%d %H:%M:%S\")\n    else:\n        return \"-\"\n\n\ndef edit_cron(app, name, cron):\n    user_path = app.config[\"AUTO_HOME\"] + \"/users/\" + session[\"username\"]\n    if os.path.exists(user_path):\n        config = json.load(codecs.open(user_path + '/config.json', 'r', 'utf-8'))\n        index = 0\n        for p in config[\"data\"]:\n            if p[\"name\"] == name:\n                config[\"data\"][index][\"cron\"] = cron\n                break\n            index += 1\n\n        json.dump(config, codecs.open(user_path + '/config.json', 'w', 'utf-8'))\n\n        return True\n\n    return False\n\n\ndef delete_task_record(app, name, task_no):\n    task_path = app.config[\"AUTO_HOME\"] + \"/jobs/\" + session[\"username\"] + \"/%s/%s\" % (name, task_no)\n    if os.path.exists(task_path):\n        remove_dir(task_path)\n"
  },
  {
    "path": "auto/www/api/user.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\nimport json\nimport codecs\nfrom flask import current_app, session, request, send_file\nfrom flask_restful import Resource, reqparse\nfrom werkzeug.security import generate_password_hash, check_password_hash\n\nfrom utils.file import list_dir, exists_path, rename_file, make_nod, remove_dir, write_file, read_file, mk_dirs\n\n\nclass User(Resource):\n    def __init__(self):\n        self.parser = reqparse.RequestParser()\n        self.parser.add_argument('method', type=str)\n        self.parser.add_argument('username', type=str)\n        self.parser.add_argument('password', type=str)\n        self.parser.add_argument('new_password', type=str, default=\"\")\n        self.parser.add_argument('email', type=str)\n        self.parser.add_argument('fullname', type=str)\n        self.app = current_app._get_current_object()\n\n    def get(self):\n        user_list = {\"total\": 0, \"rows\": []}\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/users\"\n        if exists_path(user_path):\n            users = list_dir(user_path)\n\n            user_list[\"total\"] = len(users)\n            for user in users:\n                if user == \"AutoLink\":\n                    category = \"管理员\"\n                else:\n                    category = \"普通用户\"\n                config = json.load(codecs.open(user_path + \"/\" + user + '/config.json', 'r', 'utf-8'))\n                user_list[\"rows\"].append({ \"name\": user, \"fullname\": config[\"fullname\"], \"email\": config[\"email\"], \"category\": category })\n\n        return user_list\n\n    def post(self):\n        args = self.parser.parse_args()\n\n        method = args[\"method\"].lower()\n        if method == \"create\":\n            result = self.__create(args)\n        elif method == \"edit\":\n            result = self.__edit(args)\n        elif method == \"delete\":\n            result = self.__delete(args)\n        elif method == \"save\":\n            result = self.__save(args)\n        else:\n            print(request.files[\"files\"])\n\n        return result, 201\n\n    def __create(self, args):\n        result = {\"status\": \"success\", \"msg\": \"创建用户成功\"}\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/users/%s\" % (args[\"username\"])\n        if not exists_path(user_path):\n            mk_dirs(user_path)\n\n            make_nod(user_path + \"/config.json\")\n\n            user = {\"fullname\": args[\"fullname\"],\n                    \"email\": args[\"email\"],\n                    \"passwordHash\": generate_password_hash(args[\"password\"]),\n                    \"data\": []}\n            json.dump(user, codecs.open(user_path + '/config.json', 'w', 'utf-8'))\n        else:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"用户名称重复，创建失败\"\n\n        return result\n\n    def __edit(self, args):\n\n        result = {\"status\": \"success\", \"msg\": \"用户信息修改成功\"}\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/users/\" + args[\"username\"]\n        if exists_path(user_path):\n            config = json.load(codecs.open(user_path + '/config.json', 'r', 'utf-8'))\n            if check_password_hash(config[\"passwordHash\"], args[\"password\"]):\n                config[\"passwordHash\"] = generate_password_hash(args[\"new_password\"])\n                config[\"fullname\"] = args[\"fullname\"]\n                config[\"email\"] = args[\"email\"]\n                json.dump(config, codecs.open(user_path + '/config.json', 'w', 'utf-8'))\n            else:\n                result[\"status\"] = \"fail\"\n                result[\"msg\"] = \"原始密码错误\"\n        else:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"用户不存在\"\n\n        return result\n\n    def __delete(self, args):\n        result = {\"status\": \"success\", \"msg\": \"用户删除成功\"}\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/users/\" + args[\"username\"]\n\n        if exists_path(user_path):\n            config = json.load(codecs.open(user_path + '/config.json', 'r', 'utf-8'))\n            if len(config[\"data\"]) > 0:\n                result[\"status\"] = \"fail\"\n                result[\"msg\"] = \"请先删除该用户拥有的项目\"\n            else:\n                remove_dir(user_path)\n\n        else:\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"用户不存在，删除失败\"\n\n        return result\n\n    def __save(self, args):\n        result = {\"status\": \"success\", \"msg\": \"保存成功\"}\n\n        user_path = self.app.config[\"AUTO_HOME\"] + \"/workspace/%s%s\" % (session[\"username\"], args[\"path\"])\n\n        if not write_file(user_path, args[\"data\"]):\n            result[\"status\"] = \"fail\"\n            result[\"msg\"] = \"保存失败\"\n\n        return result\n"
  },
  {
    "path": "auto/www/app.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\nimport os\nimport json\nimport codecs\nfrom flask import Flask\nfrom flask_login import LoginManager\n# from flask_mail import Mail\nfrom flask_apscheduler import APScheduler\nfrom auto.configuration import config\nfrom utils.file import list_dir\nfrom utils.run import robot_job\n\n# mail = Mail()\nscheduler = APScheduler()\nlogin_manager = LoginManager()\nlogin_manager.login_view = 'auto.login'\n\n\ndef load_all_task(app):\n    with app.app_context():\n        user_path = app.config[\"AUTO_HOME\"] + \"/users/\"\n        users = list_dir(user_path)\n        for user in users:\n            if os.path.exists(user_path + user):\n                if not os.path.exists(user_path + user + '/config.json'):\n                    continue\n\n                conf = json.load(codecs.open(user_path + user + '/config.json', 'r', 'utf-8'))\n                data = conf['data']\n                # 遍历项目\n                for p in data:\n                    if p[\"cron\"] == \"* * * * * *\":\n                        continue\n\n                    cron = p[\"cron\"].replace(\"\\n\", \"\").strip().split(\" \")\n                    if scheduler.get_job(\"%s_%s\" % (user, p[\"name\"])) is None:\n                        scheduler.add_job(id=\"%s_%s\" % (user, p[\"name\"]),\n                                          name=p[\"name\"],\n                                          func=robot_job,\n                                          args=(app, p[\"name\"], user),\n                                          trigger=\"cron\",\n                                          replace_existing=True,\n                                          second=cron[0],\n                                          minute=cron[1],\n                                          hour=cron[2],\n                                          day=cron[3],\n                                          month=cron[4],\n                                          day_of_week=cron[5])\n                    else:\n                        scheduler.remove_job(\"%s_%s\" % (user, p[\"name\"]))\n                        scheduler.add_job(id=\"%s_%s\" % (user, p[\"name\"]),\n                                          name=p[\"name\"],\n                                          func=robot_job,\n                                          args=(app, p[\"name\"], user),\n                                          trigger=\"cron\",\n                                          replace_existing=True,\n                                          second=cron[0],\n                                          minute=cron[1],\n                                          hour=cron[2],\n                                          day=cron[3],\n                                          month=cron[4],\n                                          day_of_week=cron[5])\n\n\ndef create_app(config_name):\n    app = Flask(__name__)\n    app.config.from_object(config[config_name])\n    config[config_name].init_app(app)\n\n    login_manager.init_app(app)\n\n    # mail.init_app(app)\n\n    # app.config[\"MAIL\"] = mail\n\n    scheduler.init_app(app)\n    scheduler.start()\n\n    # for blueprints\n    from .blueprints import routes as routes_blueprint\n    app.register_blueprint(routes_blueprint)\n\n    from .api import api_bp as api_blueprint\n    app.register_blueprint(api_blueprint, url_prefix='/api/v1')\n\n    if app.config['SSL_REDIRECT']:\n        from flask_sslify import SSLify\n        sslify = SSLify(app)\n\n    return app\n"
  },
  {
    "path": "auto/www/blueprints.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\nfrom flask import Blueprint, render_template, session, redirect, url_for, current_app, send_file, request\nfrom flask_login import login_required\nfrom utils.file import get_splitext, exists_path\n\nroutes = Blueprint('routes', __name__)\n\n\n@routes.before_request\ndef before_routes():\n    if 'username' in session:\n        pass\n    else:\n        pass\n        # return redirect(url_for('routes.index'))\n\n\n@routes.route('/')\ndef index():\n    return render_template('login.html')\n\n\n@routes.route('/dashboard', methods=['GET', 'POST'])\ndef dashboard():\n    if 'username' in session:\n        return render_template('dashboard.html', username=session['username'])\n    else:\n        return render_template('login.html')\n\n\n@routes.route('/tree_demo')\ndef tree_demo():\n    return render_template('tree_data1.json')\n\n\n@routes.route(\"/editor/<project>/<suite>/<case>\")\ndef editor(project, suite, case):\n    t = get_splitext(case)\n\n    default = \"default.html\"\n    if t[1] in (\".txt\", \".robot\", \".py\", \".js\"):\n        default = \"editor.html\"\n    elif t[1] in (\".bmp\", \".jpg\", \".jpeg\", \".png\", \".gif\"):\n        default = \"view_img.html\"\n\n    return render_template(default, project=project, suite=suite, case=case)\n\n\n@routes.route(\"/task_list/<name>\")\ndef task_list(name):\n    return render_template('task_list.html', project=name)\n\n\n@routes.route(\"/scheduler/\")\ndef scheduler():\n    return render_template('scheduler.html')\n\n\n@routes.route(\"/user/\")\ndef user():\n    return render_template('user.html')\n\n\n@routes.route(\"/view_report/<project>/<task>\")\ndef view_report(project, task):\n    app = current_app._get_current_object()\n\n    job_path = app.config[\"AUTO_HOME\"] + \"/jobs/%s/%s/%s/log.html\" % (session['username'], project, task)\n\n    return send_file(job_path)\n\n\n@routes.route(\"/q_view_report/<username>/<project>/<task>\")\ndef q_view_report(username, project, task):\n    app = current_app._get_current_object()\n\n    job_path = app.config[\"AUTO_HOME\"] + \"/jobs/%s/%s/%s/log.html\" % (username, project, task)\n\n    return send_file(job_path)\n\n\n@routes.route(\"/view_img\")\ndef view_img():\n    args = request.args.to_dict()\n    app = current_app._get_current_object()\n    img_path = app.config[\"AUTO_HOME\"] + \"/workspace/%s\" % session['username'] + args[\"path\"]\n    img_path.replace(\"\\\\\", \"/\")\n    if exists_path(img_path):\n        return send_file(img_path)\n\n    return False\n\n\n@routes.route(\"/welcome\")\ndef welcome():\n    return render_template(\"welcome.html\")\n"
  },
  {
    "path": "auto/www/static/css/auto.css",
    "content": ".icon-editor{\n\tbackground:url('../img/editor.png') no-repeat center center;\n}\n\n.icon-expand{\n\tbackground:url('../img/expand.png') no-repeat center center;\n}\n\n.icon-collapse{\n\tbackground:url('../img/collapse.png') no-repeat center center;\n}\n\n.icon-logout{\n\tbackground:url('../img/logout.png') no-repeat center center;\n}\n\n.icon-workspace{\n\tbackground:url('../img/workspace.png') no-repeat center center;\n}\n\n.icon-suite{\n\tbackground:url('../img/suite.png') no-repeat center center;\n}\n\n.icon-suite-open{\n\tbackground:url('../img/suite.open.png') no-repeat center center;\n}\n\n.icon-project{\n    background:url('../img/project.png') no-repeat center center;\n}\n\n.icon-robot{\n    background:url('../img/robot.png') no-repeat center center;\n}\n\n.icon-resource{\n    background:url('../img/resource.png') no-repeat center center;\n}\n\n.icon-file-default{\n    background:url('../img/file.default.png') no-repeat center center;\n}\n\n\n.icon-remove{\n    background:url('../img/delete.png') no-repeat center center;\n}\n\n.icon-debug{\n    background:url('../img/bug.png') no-repeat center center;\n}\n\n.icon-run{\n    background:url('../img/run.png') no-repeat center center;\n}\n\n.icon-task-list{\n    background:url('../img/task.list.png') no-repeat center center;\n}\n\n.icon-task{\n    background:url('../img/task.png') no-repeat center center;\n}\n\n.icon-image{\n    background:url('../img/image.png') no-repeat center center;\n}\n\n.icon-keyword-list{\n    background:url('../img/keyword.list.png') no-repeat center center;\n}\n\n.icon-keyword{\n    background:url('../img/keyword.png') no-repeat center center;\n}\n\n.icon-keyword-help{\n    background:url('../img/keyword.help.png') no-repeat center center;\n}\n\n.icon-refresh{\n    background:url('../img/refresh.png') no-repeat center center;\n}\n\n.icon-stop{\n    background:url('../img/stop.png') no-repeat center center;\n}\n\n.icon-edit{\n    background:url('../img/edit.png') no-repeat center center;\n}\n\n.icon-user{\n    background:url('../img/user.png') no-repeat center center;\n}\n\n.icon-python{\n    background:url('../img/python.text.png') no-repeat center center;\n}\n\n.icon-excel{\n    background:url('../img/excel.png') no-repeat center center;\n}\n\n.icon-scheduler{\n    background:url('../img/scheduler.png') no-repeat center center;\n}\n\n.icon-update{\n    background:url('../img/update.png') no-repeat center center;\n}\n\n.icon-settings{\n    background:url('../img/settings.png') no-repeat center center;\n}\n\n.icon-email{\n    background:url('../img/email.png') no-repeat center center;\n}\n\n.icon-notification{\n    background:url('../img/notification.png') no-repeat center center;\n}\n\n.icon-library{\n    background:url('../img/library.png') no-repeat center center;\n}\n\n.icon-variable{\n    background:url('../img/variable.png') no-repeat center center;\n}\n\n.icon-step{\n    background:url('../img/step.png') no-repeat center center;\n}\n\n.icon-user-keyword{\n    background:url('../img/user-keyword.png') no-repeat center center;\n}\n\n.CodeMirror {\n    border: 1px solid #eee;\n    height: auto;\n}\n\n.CodeMirror-selected  {\n    background-color: green !important;\n}\n\n.CodeMirror-selectedtext {\n    color: white;\n}\n\n.styled-background {\n    background-color: #ff7;\n}\n\n.CodeMirror-empty {\n    outline: 1px solid #c22;\n}\n\n.CodeMirror-empty.CodeMirror-focused {\n    outline: none;\n}\n\n.CodeMirror pre.CodeMirror-placeholder {\n    color: #999;\n}\n"
  },
  {
    "path": "auto/www/static/css/base.css",
    "content": "/*\n * Copyright (c) 2014-2018, b3log.org & hacpai.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * themes for base.\n *\n * @author <a href=\"http://vanessa.b3log.org\">Liyuan Li</a>\n * @version 0.1.0.0, Dec 6, 2015\n */\n/* start reset & function */\n::-webkit-scrollbar {\n    background: none;\n    width: 16px;\n    height: 16px;\n}\n\n::-webkit-scrollbar-corner {\n    display: none;\n    background-color: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n    border: solid 0 rgba(0, 0, 0, 0);\n    border-right-width: 4px;\n    border-left-width: 4px;\n    border-radius: 9px;\n    box-shadow: inset 0 0 0 1px rgba(128, 128, 128, 0.2), inset 0 0 0 4px rgba(128, 128, 128, 0.2);\n}\n\n::-webkit-scrollbar-thumb:horizontal {\n    border-bottom-width: 4px;\n    border-top-width: 4px;\n}\n\nbody {\n    font-size: 13px;\n    margin: 0;\n    color: #000;\n    overflow: hidden;\n    font-family: Helvetica;\n}\n\nul {\n    padding: 0;\n    margin: 0;\n    list-style: none;\n}\n\n* {\n    box-sizing: border-box;\n}\n\na {\n    color: #4183c4;\n    text-decoration: none;\n}\n\na:hover {\n    text-decoration: underline;\n}\n\nimg {\n    vertical-align: middle;\n}\n\ninput,\nbutton {\n    font-family: Helvetica;\n}\n\n.fn-left {\n    float: left;\n}\n\n.fn-right {\n    float: right;\n}\n\n.fn-clear:before,\n.fn-clear:after {\n    display: table;\n    content: \"\";\n}\n\n.fn-clear:after {\n    clear: both;\n}\n\n.fn-none {\n    display: none;\n}\n/* end reset & function */\n\n/* start common */\n.ft-small {\n    color: #999;\n    font-size: 12px;\n}\n\n.ft-red {\n    color: #9d0000;\n}\n\n.list li {\n    cursor: pointer;\n    line-height: 20px;\n    padding: 0 3px;\n    word-wrap: normal;\n    word-break: normal;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n}\n\n.list li.selected,\n.list li:hover {\n    background-color: #3875d7;\n    color: #FFF;\n}\n\n.list li.selected .ft-small,\n.list li:hover .ft-small {\n    color: #FFF;\n}\n/* end common */\n\n/* start icon */\n@font-face {\n    font-family: 'icomoon';\n    src:url('fonts/icomoon.eot?35cb2z');\n    src:url('fonts/icomoon.eot?#iefix35cb2z') format('embedded-opentype'),\n        url('fonts/icomoon.woff?35cb2z') format('woff'),\n        url('fonts/icomoon.ttf?35cb2z') format('truetype'),\n        url('fonts/icomoon.svg?35cb2z#icomoon') format('svg');\n    font-weight: normal;\n    font-style: normal;\n}\n\n.font-ico,\n[class^=\"ico-\"] {\n    font-family: 'icomoon';\n    /* Better Font Rendering =========== */\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n    cursor: pointer;\n    font-size: 13px;\n    line-height: 20px;\n}\n\n.ico-book:before {\n    content: \"\\e623\";\n}\n\n.ico-price:before {\n    content: \"\\e616\";\n}\n\n.ico-start:before {\n    content: \"\\e9d7\";\n}\n\n.ico-share:before {\n    content: \"\\e61f\";\n}\n\n.ico-github:before {\n    content: \"\\f00a\";\n}\n\n.ico-git:before {\n    content: \"\\e624\";\n}\n\n.ico-tencent:before {\n    content: \"\\e622\";\n}\n\n.ico-weibo:before {\n    content: \"\\e621\";\n}\n\n.ico-googleplus:before {\n    content: \"\\e61a\";\n}\n\n.ico-twitter:before {\n    content: \"\\e61c\";\n}\n\n.ico-email:before {\n    content: \"\\e619\";\n}\n\n.ico-facebook:before {\n    content: \"\\e61b\";\n}\n\n.ico-moveup:before {\n    content: \"\\f148\";\n}\n\n.ico-movedown:before {\n    content: \"\\f149\";\n}\n\n.ico-keyboard:before {\n    content: \"\\f11c\";\n}\n\n.ico-findfiles:before {\n    content: \"\\e603\";\n}\n\n.ico-find:before {\n    content: \"\\e602\";\n}\n\n.ico-editor:before {\n    content: \"\\e604\";\n}\n\n.ico-tree:before {\n    content: \"\\e600\";\n}\n\n.ico-build:before {\n    content: \"\\e601\";\n}\n\n.ico-notification:before {\n    content: \"\\e607\";\n}\n\n.ico-report:before {\n    content: \"\\e605\";\n}\n\n.ico-comment:before {\n    content: \"\\e620\";\n}\n\n.ico-goline:before {\n    content: \"\\e61e\";\n}\n\n.ico-info:before {\n    content: \"\\e61d\";\n}\n\n.ico-signup:before {\n    content: \"\\e606\";\n}\n\n.ico-signout:before {\n    content: \"\\e618\";\n}\n\n.ico-redo:before {\n    content: \"\\e615\";\n}\n\n.ico-undo:before {\n    content: \"\\e60e\";\n}\n\n.ico-about:before {\n    content: \"\\e60d\";\n}\n\n.ico-import:before {\n    content: \"\\f0ee\";\n}\n\n.ico-export:before {\n    content: \"\\f0ed\";\n}\n\n.ico-refresh:before {\n    content: \"\\f021\";\n}\n\n.ico-remove:before {\n    content: \"\\e60b\";\n}\n\n.ico-save:before {\n    content: \"\\f0c7\";\n}\n\n.ico-max:before {\n    content: \"\\e609\";\n}\n\n.ico-format:before {\n    content: \"\\e612\";\n}\n\n.ico-buildrun:before {\n    content: \"\\e60c\";\n}\n\n.ico-stop:before {\n    content: \"\\e60f\";\n}\n\n.ico-restore:before {\n    content: \"\\e613\";\n}\n\n.toolbars .ico-restore:before {\n    content: \"\\e60a\";\n}\n\n.ico-min:before {\n    content: \"\\e614\";\n    position: absolute;\n    right: 5px;\n}\n\n.ico-close:before {\n    content: \"\\e611\";\n}\n/* end ico */"
  },
  {
    "path": "auto/www/static/css/sign.css",
    "content": "/*\n * Copyright (c) 2014-2018, b3log.org & hacpai.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n.wrapper {\n    margin: 0 auto;\n    width: 980px;\n}\n\n.header .logo {\n    float: left;\n    height: 32px;\n    margin-top: 3px;\n}\n\n.header {\n    margin: 8px 0;\n}\n\n.header li {\n    float: left;\n    margin-left: 25px;\n}\n\n.header a {\n    display: block;\n    font-weight: bold;\n    padding: 5px 0;\n    color: #333;\n    line-height: 30px;\n    text-decoration: none;\n}\n\n.header a:hover {\n    color: #4183C4;\n}\n\n.content {\n    border-top: 1px solid #A4A4A4;\n    border-bottom: 1px solid #919191;\n    background-color: #202021;\n    height: 632px;\n    padding-top: 222px;\n}\n\n.content h2 {\n    color: #FFF;\n    font-size: 70px;\n    margin: 0 0 60px;\n}\n\n.content h3 {\n    color: #4183c4;\n    font-size: 21px;\n}\n\n.content .form {\n    width: 320px;\n    margin-top: -18px;\n    position: relative;\n}\n\n.content .form input {\n    border: 1px solid #ccc;\n    border-radius: 3px;\n    width: 100%;\n    background-color: #fafafa; \n    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075) inset;\n    color: #333;\n    min-height: 34px;\n    outline: medium none;\n    vertical-align: middle;\n    font-size: 16px;\n    border: 1px solid #FFF;\n    padding: 10px;\n    margin-top: 20px;\n}\n\n.content .form input:focus {\n    background-color: #FFF;\n    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075) inset, 0 0 12px rgba(255, 255, 255, 0.75);\n}\n\n.btn {\n    width: 100%;\n    color: #fff;\n    text-shadow: 0 -1px 0 rgba(0,0,0,0.25);\n    background-color: #60b044;\n    background-image: linear-gradient(#8add6d, #60b044);\n    border: 1px solid #5ca941;\n    padding: 10px;\n    border-radius: 3px;\n    cursor: pointer;\n}\n\n.btn:hover {\n    background-color: #569e3d;\n    background-image: linear-gradient(#79d858, #569e3d);\n    border-color: #4a993e;\n}\n\n.btn.btn-white,\n.btn.btn-red {\n    color: #333;\n    text-shadow: 0 1px 0 rgba(255,255,255,0.9);\n    background-color: #eee;\n    background-image: linear-gradient(#fcfcfc, #eee);\n    border-color: #d5d5d5;\n}\n\n.btn.btn-red {\n    color: #9d0000;\n}\n\n.btn.btn-white:hover {\n    color: #333;\n    text-shadow: 0 1px 0 rgba(255,255,255,0.9);\n    background-color: #ddd;\n    background-image: linear-gradient(#eee, #ddd);\n    border-color: #ccc;\n}\n\n.btn.btn-red:hover {\n    background-color: #b33630;\n    background-image: linear-gradient(#dc5f59, #b33630);\n    background-repeat: repeat-x;\n    border-color: #cd504a;\n    color: #fff;\n    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3);\n}\n\n#msg {\n    background-color: #fcdede;\n    border: 1px solid #d2b2b2;\n    padding: 15px;\n    font-size: 14px;\n    color: #911;\n    position: absolute;\n    width: 100%;\n    top: -48px;\n}\n\n.footer {\n    line-height: 30px;\n    color: #777;\n    font-size: 12px;\n    text-align: center;\n    position: relative;\n}\n\n.footer a {\n    text-decoration: none;\n    color: #4183c4;\n}\n\n.footer a:hover {\n    text-decoration: underline;\n}\n\n.footer .github-btns {\n    height: 25px;\n    position: absolute;\n    top: 5px;\n    right: 0;\n}\n\n/* start sign up */\n.dir {\n    color: #4183c4;\n    font-size: 18px;\n    word-wrap: break-word;\n    margin-top: 20px;\n}\n\n#dir {\n    color: #999; \n    font-size: 13px;\n}\n\n.form.sign-up {\n    margin-top: -108px;\n}\n\n#loginBtn,\n#signUpBtn {\n    margin-top: 20px;\n    font-size: 16px;\n}\n/* end sign up */"
  },
  {
    "path": "auto/www/static/js/auto.js",
    "content": "/*\n * auto.js\n *\n * 作者: 苦叶子\n *\n * 公众号: 开源优测\n *\n * Email: lymking@foxmail.com\n *\n*/\n\n/*\n * for string format\n*/\nString.prototype.lym_format = function() {\n    if (arguments.length == 0) {\n        return this;\n    }\n    for (var StringFormat_s = this, StringFormat_i = 0; StringFormat_i < arguments.length; StringFormat_i++) {\n        StringFormat_s = StringFormat_s.replace(new RegExp(\"\\\\{\" + StringFormat_i + \"\\\\}\", \"g\"), arguments[StringFormat_i]);\n    }\n    return StringFormat_s;\n};\n\n/*\n * to json\n*/\n$.fn.serializeObject = function() {\n    var json = {};\n    var arrObj = this.serializeArray();\n    $.each(arrObj, function() {\n      if (json[this.name]) {\n           if (!json[this.name].push) {\n            json[this.name] = [ json[this.name] ];\n           }\n           json[this.name].push(this.value || '');\n      } else {\n           json[this.name] = this.value || '';\n      }\n    });\n\n    return json;\n};\n\n/*\n * 显示消息\n*/\nfunction show_msg(title, msg){\n    $.messager.show({\n        title: title,\n        msg: msg,\n        timeout: 3000,\n        showType: 'slide'\n    });\n}\n\nfunction do_refresh(data){\n    location.href = data.url;\n}\n\nfunction do_nop(data){\n    // 空函数\n}\n\nfunction do_msg(data){\n    show_msg('提示信息', data.msg);\n}\n\nfunction do_init(data){\n    if(data.data == \"\"){\n        //editor.setValue(\"*** Settings ***\\n\\n\\n*** Variables ***\\n\\n\\n*** Test Cases ***\\n\\n\\n*** Keywords ***\\n\\n\");\n        if(data.ext==\".txt\"){\n            editor.setValue(\"*** Settings ***\\n\\n\\n*** Variables ***\\n\\n\\n\");\n        }\n        else if(data.ext ==\".robot\"){\n            editor.setValue(\"*** Settings ***\\n\\n\\n*** Variables ***\\n\\n\\n*** Test Cases ***\\n\\n\\n*** Keywords ***\\n\\n\");\n        }\n    }\n    else{\n        editor.setValue(data.data);\n    }\n}\n\nfunction do_ajax(type, url, data, func){\n    $.ajax({\n        type : type,\n        url : url,\n        data: data,\n        success : func\n    });\n}\n\nfunction do_login(fm_id){\n    var data = $('#{0}'.lym_format(fm_id)).serializeObject();\n    do_ajax('post', '/api/v1/auth/', data, do_refresh);\n}\n\nfunction do_logout(username){\n    do_ajax('get', '/api/v1/auth/', '', do_refresh)\n}\n\nfunction do_run(){\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        var category = node.attributes[\"category\"];\n        var data ={\"method\": \"run\", \"category\": category};\n        if(category ==\"project\"){\n            data[\"project\"] = node.attributes[\"name\"];\n        }\n        else if(category == \"suite\"){\n            var project = $('#project_tree').tree('getParent', node.target);\n            data[\"project\"] = project.attributes[\"name\"];\n            data[\"suite\"] = node.attributes[\"name\"];\n        }\n        else if(category == \"case\"){\n            var suite = $('#project_tree').tree('getParent', node.target);\n            var project = $('#project_tree').tree('getParent', suite.target);\n            data[\"project\"] = project.attributes[\"name\"];\n            data[\"suite\"] = suite.attributes[\"name\"];\n            data[\"case\"] = node.attributes[\"name\"] + node.attributes['splitext'];\n        }\n        do_ajax('post',\n            '/api/v1/task/',\n            data,\n            do_msg);\n    }\n}\n\nfunction do_task_list(){\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        var project = node.attributes[\"name\"];\n        addTab(project, \"/task_list/{0}\".lym_format(project), 'icon-task')\n    }\n}\n\nfunction do_in_array(str, array){\n    for(a in array){\n        if(array[a] == str){\n            return true;\n        }\n    }\n\n    return false;\n}\n\nfunction onDblClick(node) {\n    var category = node.attributes.category;\n    var steps = new Array(\"library\", \"variable\", \"step\", \"user_keyword\");\n    if(category == \"case\"){\n        var suite = $('#project_tree').tree('getParent', node.target);\n        var project = $('#project_tree').tree('getParent', suite.target);\n        addTab(node.attributes['name'], '/editor/{0}/{1}/{2}{3}'.lym_format(\n            project.attributes['name'],\n            suite.attributes['name'],\n            node.attributes['name'],\n            node.attributes['splitext']\n            ), \"icon-editor\");\n    }\n    else if(do_in_array(category, steps)){\n        var testcase = $('#project_tree').tree('getParent', node.target);\n        var suite = $('#project_tree').tree('getParent', testcase.target);\n        var project = $('#project_tree').tree('getParent', suite.target);\n        addTab(testcase.attributes['name'], '/editor/{0}/{1}/{2}{3}'.lym_format(\n            project.attributes['name'],\n            suite.attributes['name'],\n            testcase.attributes['name'],\n            testcase.attributes['splitext']\n            ), \"icon-editor\");\n    }\n    else if(category == \"keyword\"){\n        var step = $('#project_tree').tree('getParent', node.target);\n        var testcase = $('#project_tree').tree('getParent', step.target);\n        var suite = $('#project_tree').tree('getParent', testcase.target);\n        var project = $('#project_tree').tree('getParent', suite.target);\n        addTab(testcase.attributes['name'], '/editor/{0}/{1}/{2}{3}'.lym_format(\n            project.attributes['name'],\n            suite.attributes['name'],\n            testcase.attributes['name'],\n            testcase.attributes['splitext']\n            ), \"icon-editor\");\n    }\n}\n\nfunction onContextMenu(e, node){\n    e.preventDefault();\n    // select the node\n    $('#project_tree').tree('select', node.target);\n    // display context menu\n\n    $('#{0}_menu'.lym_format(node.attributes['category'])).menu('show', {\n        left: e.pageX,\n        top: e.pageY\n    });\n}\n\nfunction addTab(title, url, icon){\n    var editor_tabs = $(\"#editor_tabs\");\n    if (editor_tabs.tabs('exists', title)){\n        //如果tab已经存在,则选中并刷新该tab\n        editor_tabs.tabs('select', title);\n        refreshTab({title: title, url: url});\n    }\n    else {\n        var content='<iframe scrolling=\"yes\" frameborder=\"0\"  src=\"{0}\" style=\"width:100%;height:100%\"></iframe>'.lym_format(url);\n        editor_tabs.tabs('add',{\n            title: title,\n            closable: true,\n            content: content,\n            iconCls: icon||'icon-default'\n        });\n    }\n}\n\nfunction refreshTab(cfg){\n    var tab = cfg.title?$('#editor_tabs').tabs('getTab',cfg.title):$('#editor_tabs').tabs('getSelected');\n    if(tab && tab.find('iframe').length > 0){\n        var frame = tab.find('iframe')[0];\n        var url = cfg.url?cfg.url:fram.src;\n        frame.contentWindow.location.href = url;\n    }\n}\n\nfunction collapse(){\n    var node = $('#project_tree').tree('getSelected');\n    $('#project_tree').tree('collapse',node.target);\n}\n\nfunction expand(){\n    var node = $('#project_tree').tree('getSelected');\n    $('#project_tree').tree('expand',node.target);\n}\n\n\nfunction onBeforeExpand(node){\n    if(node){\n        var param = $(\"#project_tree\").tree(\"options\").queryParams;\n        param.category = node.attributes.category;\n        param.name = node.attributes.name;\n        if(node.attributes.category == \"suite\"){\n            var parent = $(\"#project_tree\").tree('getParent', node.target);\n            param.project = parent.attributes.name;\n\n        }\n        else if(node.attributes.category == \"case\")\n        {\n            var suite = $(\"#project_tree\").tree('getParent', node.target);\n            param.suite = suite.attributes.name;\n            var project = $(\"#project_tree\").tree('getParent', suite.target);\n            param.project = project.attributes.name;\n            param.splitext = node.attributes.splitext;\n        }\n    }\n}\n\n\nfunction manage_project(win_id, ff_id, method){\n    if(method == \"create\"){\n        clear_form(ff_id);\n\n    }\n    else if(method == \"edit\"){\n        var node = $('#project_tree').tree('getSelected');\n        if(node){\n            $(\"#{0} input#new_name\".lym_format(ff_id)).textbox('setValue', node.attributes['name']);\n        }\n    }\n    open_win(win_id);\n}\n\nfunction refresh_workspace(data){\n    var param = $(\"#project_tree\").tree(\"options\").queryParams\n    param.category = \"root\";\n\n    $('#project_tree').tree(\"reload\");\n\n    show_msg('提示信息', data.msg);\n}\n\nfunction refresh_project_node(data){\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        var param = $(\"#project_tree\").tree(\"options\").queryParams;\n        param.category = \"project\";\n        param.name = node.attributes.name;\n        $('#project_tree').tree('reload', node.target);\n    }\n    show_msg('提示信息', data.msg);\n}\n\nfunction refresh_suite_node(data){\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        parent = $('#project_tree').tree('getParent', node.target);\n\n        var param = $(\"#project_tree\").tree(\"options\").queryParams;\n\n        param.category = \"project\";\n        param.name = parent.attributes.name;\n\n        $('#project_tree').tree(\"reload\", parent.target);\n    }\n    show_msg('提示信息', data.msg);\n}\n\nfunction refresh_case_node(data){\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        var param = $(\"#project_tree\").tree(\"options\").queryParams;\n        param.category = \"suite\";\n        var suite = $('#project_tree').tree('getParent', node.target);\n        param.suite = suite.attributes.name\n        var project = $(\"#project_tree\").tree('getParent', suite.target);\n        param.project = project.attributes.name;\n\n        $('#project_tree').tree(\"reload\", suite.target);\n    }\n    show_msg('提示信息', data.msg);\n}\n\nfunction create_project(win_id, ff_id){\n    var data = $(\"#{0}\".lym_format(ff_id)).serializeObject();\n    data[\"method\"] = \"create\";\n\n    do_ajax('post', '/api/v1/project/', data, refresh_workspace);\n\n    close_win(win_id);\n}\n\nfunction rename_project(win_id, ff_id){\n    var data = $(\"#{0}\".lym_format(ff_id)).serializeObject();\n    var node = $('#project_tree').tree('getSelected');\n    data[\"name\"] = node.attributes['name'];\n    data[\"method\"] = \"edit\";\n    do_ajax('post', '/api/v1/project/', data, refresh_workspace);\n\n    close_win(win_id);\n}\n\nfunction delete_project(){\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        $.messager.confirm('删除提示', '<br>确定删除项目: {0}?'.lym_format(node.attributes['name']), function(r){\n            if (r){\n                var data = {\n                        \"name\": node.attributes['name'],\n                        \"method\": \"delete\"\n                    };\n\n                do_ajax('post', \"/api/v1/project/\", data, refresh_workspace);\n            }\n        });\n    }\n}\n\nfunction manage_suite(win_id, ff_id, method){\n    if(method == \"create\"){\n        clear_form(ff_id);\n\n    }\n    else if(method == \"edit\"){\n        var node = $('#project_tree').tree('getSelected');\n        if(node){\n            $(\"#{0} input#new_name\".lym_format(ff_id)).textbox('setValue', node.attributes['name']);\n        }\n    }\n    open_win(win_id);\n}\n\nfunction create_suite(win_id, ff_id){\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        var data = $(\"#{0}\".lym_format(ff_id)).serializeObject();\n        data[\"method\"] = \"create\";\n        data[\"project_name\"] = node.attributes['name'];\n\n        do_ajax('post', '/api/v1/suite/', data, refresh_project_node);\n\n        close_win(win_id);\n    }\n}\n\nfunction rename_suite(win_id, ff_id){\n    var data = $(\"#{0}\".lym_format(ff_id)).serializeObject();\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        var project = $('#project_tree').tree('getParent', node.target);\n        data[\"name\"] = node.attributes['name'];\n        data[\"project_name\"] = project.attributes['name'];\n        data[\"method\"] = \"edit\";\n        do_ajax('post', '/api/v1/suite/', data, refresh_suite_node);\n\n        close_win(win_id);\n    }\n}\n\nfunction delete_suite(){\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        $.messager.confirm('删除提示', '<br>确定删除目录: {0}?'.lym_format(node.attributes['name']), function(r){\n            if (r){\n                var project = $('#project_tree').tree('getParent', node.target);\n                var data = {\n                        \"name\": node.attributes['name'],\n                        \"project_name\": project.attributes['name'],\n                        \"method\": \"delete\"\n                    };\n\n                do_ajax('post', \"/api/v1/suite/\", data, refresh_suite_node);\n            }\n        });\n    }\n}\n\nfunction manage_file(win_id, ff_id, method){\n    if(method == \"create\"){\n        clear_form(ff_id);\n\n    }\n    else if(method == \"edit\"){\n        var node = $('#project_tree').tree('getSelected');\n        if(node){\n            $(\"#{0} select#new_category\".lym_format(ff_id)).combobox(\"setValue\", node.attributes['splitext']);\n            $(\"#{0} input#new_name\".lym_format(ff_id)).textbox('setValue', node.attributes['name']);\n        }\n    }\n    open_win(win_id);\n}\n\nfunction create_file(win_id, ff_id){\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        var project = $('#project_tree').tree('getParent', node.target);\n        var data = $(\"#{0}\".lym_format(ff_id)).serializeObject();\n        data[\"method\"] = \"create\";\n        data[\"suite_name\"] = node.attributes['name'];\n        data[\"project_name\"] =  project.attributes['name'];\n\n        do_ajax('post', '/api/v1/case/', data, refresh_suite_node);\n\n        close_win(win_id);\n    }\n}\n\nfunction rename_file(win_id, ff_id){\n    var data = $(\"#{0}\".lym_format(ff_id)).serializeObject();\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        var suite = $('#project_tree').tree('getParent', node.target);\n        var project = $('#project_tree').tree('getParent', suite.target);\n        data[\"name\"] = node.attributes['name'];\n        data[\"category\"] = node.attributes['splitext'];\n        data[\"suite_name\"] = suite.attributes['name'];\n        data[\"project_name\"] = project.attributes['name'];\n        data[\"method\"] = \"edit\";\n\n        do_ajax('post', '/api/v1/case/', data, refresh_case_node);\n\n        close_win(win_id);\n    }\n}\n\n\nfunction delete_file(){\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        $.messager.confirm('删除提示',\n            '<br>确定删除文件: {0}{1}?'.lym_format(node.attributes['name'], node.attributes['splitext']),\n            function(r){\n                if (r){\n                    var suite = $('#project_tree').tree('getParent', node.target);\n                    var project = $('#project_tree').tree('getParent', suite.target);\n                    var data = {\n                            \"name\": node.attributes['name'],\n                            \"suite_name\": suite.attributes['name'],\n                            \"project_name\": project.attributes['name'],\n                            \"category\": node.attributes['splitext'],\n                            \"method\": \"delete\"\n                        };\n\n                    do_ajax('post', \"/api/v1/case/\", data, refresh_case_node);\n                }\n        });\n    }\n}\n\nfunction do_upload(win_id, ff_id){\n    var node = $('#project_tree').tree('getSelected');\n    if(node){\n        var project = $('#project_tree').tree('getParent', node.target);\n        $(\"#{0} input#path\".lym_format(ff_id)).val(\"/{0}/{1}/\".lym_format(project.attributes['name'], node.attributes['name']));\n        $(\"#{0}\".lym_format(ff_id)).form('submit', {\n            success: function (result) {\n                //var node = $('#project_tree').tree('getSelected');\n                var d = JSON.parse(result);\n                //show_msg('提示信息', d.msg);\n                refresh_suite_node(d);\n                close_win(win_id);\n            }\n        });\n    }\n}\n\nfunction do_download(ff_id){\n    var node = $('#project_tree').tree('getSelected');\n    if(node && node.attributes['category'] == 'case'){\n        var suite = $('#project_tree').tree('getParent', node.target);\n        var project = $('#project_tree').tree('getParent', suite.target);\n        var path = \"/{0}/{1}/{2}{3}\".lym_format(project.attributes['name'], suite.attributes['name'], node.attributes['name'], node.attributes['splitext']);\n        $(\"#{0} input#path\".lym_format(ff_id)).val(\"{0}\".lym_format(path));\n        $(\"#{0}\".lym_format(ff_id)).form('submit', {\n            success: function (result) {\n\n            }\n        });\n    }\n}\n\nfunction show_img(value, row, index){\n    return '<img width=\"24px\" height=\"24px\" border=\"0\" src=\"{0}\"/>'.lym_format(value) ;\n}\n\nfunction do_open_editor(){\n    var node = $('#project_tree').tree('getSelected');\n    if(node && node.attributes['category'] == 'case'){\n        var suite = $('#project_tree').tree('getParent', node.target);\n        var project = $('#project_tree').tree('getParent', suite.target);\n        addTab(node.attributes['name'], '/editor/{0}/{1}/{2}{3}'.lym_format(\n            project.attributes['name'],\n            suite.attributes['name'],\n            node.attributes['name'],\n            node.attributes['splitext']\n            ), \"icon-editor\");\n    }\n}\n\nfunction refresh_user_list(data){\n    $('#user_list').datagrid(\"reload\");\n\n    show_msg('提示信息', data.msg);\n}\n\nfunction manage_user(win_id, ff_id, method){\n    if(method == \"create\"){\n        clear_form(ff_id);\n\n    }\n    else if(method == \"edit\"){\n\n    }\n    open_win(win_id);\n}\n\nfunction create_user(win_id, ff_id){\n    var data = $(\"#{0}\".lym_format(ff_id)).serializeObject();\n    data[\"method\"] = \"create\";\n\n    do_ajax('post', '/api/v1/user/', data, refresh_user_list);\n\n    close_win(win_id);\n}\n\nfunction edit_user(win_id, ff_id){\n    var data = $(\"#{0}\".lym_format(ff_id)).serializeObject();\n    data[\"method\"] = \"edit\";\n\n    do_ajax('post', '/api/v1/user/', data, refresh_user_list);\n\n    close_win(win_id);\n}\n\nfunction close_win(id){\n    $('#{0}'.lym_format(id)).window('close');\n}\n\nfunction open_win(id){\n    $('#{0}'.lym_format(id)).window('open');\n}\n\nfunction clear_form(id){\n    $('#{0}'.lym_format(id)).form('clear');\n}\n\nfunction load_smtp(data){\n    $(\"#edit_smtp_ff\").form(\"load\", data);\n    $(\"#edit_smtp_ff input#ssl\").prop(\"checked\", data[\"ssl\"]);\n}\n\nfunction init_smtp_ff(){\n    var data = {\"method\": \"smtp\"};\n    do_ajax('get', '/api/v1/settings/', data, load_smtp);\n}\n\nfunction load_email(data){\n    $(\"#notify_ff\").form(\"load\", data);\n}\n\nfunction init_email_ff(name){\n    var data = {\"method\": \"email\", \"project\": name};\n    do_ajax('get', '/api/v1/settings/', data, load_email);\n}\n\nfunction do_smtp(win_id, ff_id){\n    var data = $(\"#{0}\".lym_format(ff_id)).serializeObject();\n    data[\"method\"] = \"smtp\";\n\n    do_ajax('post', '/api/v1/settings/', data, do_nop);\n\n    close_win(win_id);\n}"
  },
  {
    "path": "auto/www/static/js/autocomplete.js",
    "content": "var auto_complete=['Call Method\t[object]\t[method_name]\t[*args]\t[**kwargs]','Catenate\t[*items]','Comment\t[*messages]','Continue For Loop','Continue For Loop If\t[condition]','Convert To Binary\t[item]\t[base=None]\t[prefix=None]\t[length=None]','Convert To Boolean\t[item]','Convert To Bytes\t[input]\t[input_type=text]','Convert To Hex\t[item]\t[base=None]\t[prefix=None]\t[length=None]\t[lowercase=False]','Convert To Integer\t[item]\t[base=None]','Convert To Number\t[item]\t[precision=None]','Convert To Octal\t[item]\t[base=None]\t[prefix=None]\t[length=None]','Convert To String\t[item]','Create Dictionary\t[*items]','Create List\t[*items]','Evaluate\t[expression]\t[modules=None]\t[namespace=None]','Exit For Loop','Exit For Loop If\t[condition]','Fail\t[msg=None]\t[*tags]','Fatal Error\t[msg=None]','Get Count\t[item1]\t[item2]','Get Length\t[item]','Get Library Instance\t[name=None]\t[all=False]','Get Time\t[format=timestamp]\t[time_=NOW]','Get Variable Value\t[name]\t[default=None]','Get Variables\t[no_decoration=False]','Import Library\t[name]\t[*args]','Import Resource\t[path]','Import Variables\t[path]\t[*args]','Keyword Should Exist\t[name]\t[msg=None]','Length Should Be\t[item]\t[length]\t[msg=None]','Log\t[message]\t[level=INFO]\t[html=False]\t[console=False]\t[repr=False]','Log Many\t[*messages]','Log To Console\t[message]\t[stream=STDOUT]\t[no_newline=False]','Log Variables\t[level=INFO]','No Operation','Pass Execution\t[message]\t[*tags]','Pass Execution If\t[condition]\t[message]\t[*tags]','Regexp Escape\t[*patterns]','Reload Library\t[name_or_instance]','Remove Tags\t[*tags]','Repeat Keyword\t[repeat]\t[name]\t[*args]','Replace Variables\t[text]','Return From Keyword\t[*return_values]','Return From Keyword If\t[condition]\t[*return_values]','Run Keyword\t[name]\t[*args]','Run Keyword And Continue On Failure\t[name]\t[*args]','Run Keyword And Expect Error\t[expected_error]\t[name]\t[*args]','Run Keyword And Ignore Error\t[name]\t[*args]','Run Keyword And Return\t[name]\t[*args]','Run Keyword And Return If\t[condition]\t[name]\t[*args]','Run Keyword And Return Status\t[name]\t[*args]','Run Keyword If\t[condition]\t[name]\t[*args]','Run Keyword If All Critical Tests Passed\t[name]\t[*args]','Run Keyword If All Tests Passed\t[name]\t[*args]','Run Keyword If Any Critical Tests Failed\t[name]\t[*args]','Run Keyword If Any Tests Failed\t[name]\t[*args]','Run Keyword If Test Failed\t[name]\t[*args]','Run Keyword If Test Passed\t[name]\t[*args]','Run Keyword If Timeout Occurred\t[name]\t[*args]','Run Keyword Unless\t[condition]\t[name]\t[*args]','Run Keywords\t[*keywords]','Set Global Variable\t[name]\t[*values]','Set Library Search Order\t[*search_order]','Set Log Level\t[level]','Set Suite Documentation\t[doc]\t[append=False]\t[top=False]','Set Suite Metadata\t[name]\t[value]\t[append=False]\t[top=False]','Set Suite Variable\t[name]\t[*values]','Set Tags\t[*tags]','Set Test Documentation\t[doc]\t[append=False]','Set Test Message\t[message]\t[append=False]','Set Test Variable\t[name]\t[*values]','Set Variable\t[*values]','Set Variable If\t[condition]\t[*values]','Should Be Empty\t[item]\t[msg=None]','Should Be Equal\t[first]\t[second]\t[msg=None]\t[values=True]\t[ignore_case=False]','Should Be Equal As Integers\t[first]\t[second]\t[msg=None]\t[values=True]\t[base=None]','Should Be Equal As Numbers\t[first]\t[second]\t[msg=None]\t[values=True]\t[precision=6]','Should Be Equal As Strings\t[first]\t[second]\t[msg=None]\t[values=True]\t[ignore_case=False]','Should Be True\t[condition]\t[msg=None]','Should Contain\t[container]\t[item]\t[msg=None]\t[values=True]\t[ignore_case=False]','Should Contain Any\t[container]\t[*items]\t[**configuration]','Should Contain X Times\t[item1]\t[item2]\t[count]\t[msg=None]\t[ignore_case=False]','Should End With\t[str1]\t[str2]\t[msg=None]\t[values=True]\t[ignore_case=False]','Should Match\t[string]\t[pattern]\t[msg=None]\t[values=True]\t[ignore_case=False]','Should Match Regexp\t[string]\t[pattern]\t[msg=None]\t[values=True]','Should Not Be Empty\t[item]\t[msg=None]','Should Not Be Equal\t[first]\t[second]\t[msg=None]\t[values=True]\t[ignore_case=False]','Should Not Be Equal As Integers\t[first]\t[second]\t[msg=None]\t[values=True]\t[base=None]','Should Not Be Equal As Numbers\t[first]\t[second]\t[msg=None]\t[values=True]\t[precision=6]','Should Not Be Equal As Strings\t[first]\t[second]\t[msg=None]\t[values=True]\t[ignore_case=False]','Should Not Be True\t[condition]\t[msg=None]','Should Not Contain\t[container]\t[item]\t[msg=None]\t[values=True]\t[ignore_case=False]','Should Not Contain Any\t[container]\t[*items]\t[**configuration]','Should Not End With\t[str1]\t[str2]\t[msg=None]\t[values=True]\t[ignore_case=False]','Should Not Match\t[string]\t[pattern]\t[msg=None]\t[values=True]\t[ignore_case=False]','Should Not Match Regexp\t[string]\t[pattern]\t[msg=None]\t[values=True]','Should Not Start With\t[str1]\t[str2]\t[msg=None]\t[values=True]\t[ignore_case=False]','Should Start With\t[str1]\t[str2]\t[msg=None]\t[values=True]\t[ignore_case=False]','Sleep\t[time_]\t[reason=None]','Variable Should Exist\t[name]\t[msg=None]','Variable Should Not Exist\t[name]\t[msg=None]','Wait Until Keyword Succeeds\t[retry]\t[retry_interval]\t[name]\t[*args]','Append To List\t[list_]\t[*values]','Combine Lists\t[*lists]','Convert To Dictionary\t[item]','Convert To List\t[item]','Copy Dictionary\t[dictionary]','Copy List\t[list_]','Count Values In List\t[list_]\t[value]\t[start=0]\t[end=None]','Dictionaries Should Be Equal\t[dict1]\t[dict2]\t[msg=None]\t[values=True]','Dictionary Should Contain Item\t[dictionary]\t[key]\t[value]\t[msg=None]','Dictionary Should Contain Key\t[dictionary]\t[key]\t[msg=None]','Dictionary Should Contain Sub Dictionary\t[dict1]\t[dict2]\t[msg=None]\t[values=True]','Dictionary Should Contain Value\t[dictionary]\t[value]\t[msg=None]','Dictionary Should Not Contain Key\t[dictionary]\t[key]\t[msg=None]','Dictionary Should Not Contain Value\t[dictionary]\t[value]\t[msg=None]','Get Dictionary Items\t[dictionary]','Get Dictionary Keys\t[dictionary]','Get Dictionary Values\t[dictionary]','Get From Dictionary\t[dictionary]\t[key]','Get From List\t[list_]\t[index]','Get Index From List\t[list_]\t[value]\t[start=0]\t[end=None]','Get Match Count\t[list]\t[pattern]\t[case_insensitive=False]\t[whitespace_insensitive=False]','Get Matches\t[list]\t[pattern]\t[case_insensitive=False]\t[whitespace_insensitive=False]','Get Slice From List\t[list_]\t[start=0]\t[end=None]','Insert Into List\t[list_]\t[index]\t[value]','Keep In Dictionary\t[dictionary]\t[*keys]','List Should Contain Sub List\t[list1]\t[list2]\t[msg=None]\t[values=True]','List Should Contain Value\t[list_]\t[value]\t[msg=None]','List Should Not Contain Duplicates\t[list_]\t[msg=None]','List Should Not Contain Value\t[list_]\t[value]\t[msg=None]','Lists Should Be Equal\t[list1]\t[list2]\t[msg=None]\t[values=True]\t[names=None]','Log Dictionary\t[dictionary]\t[level=INFO]','Log List\t[list_]\t[level=INFO]','Pop From Dictionary\t[dictionary]\t[key]\t[default=]','Remove Duplicates\t[list_]','Remove From Dictionary\t[dictionary]\t[*keys]','Remove From List\t[list_]\t[index]','Remove Values From List\t[list_]\t[*values]','Reverse List\t[list_]','Set List Value\t[list_]\t[index]\t[value]','Set To Dictionary\t[dictionary]\t[*key_value_pairs]\t[**items]','Should Contain Match\t[list]\t[pattern]\t[msg=None]\t[case_insensitive=False]\t[whitespace_insensitive=False]','Should Not Contain Match\t[list]\t[pattern]\t[msg=None]\t[case_insensitive=False]\t[whitespace_insensitive=False]','Sort List\t[list_]','Add Time To Date\t[date]\t[time]\t[result_format=timestamp]\t[exclude_millis=False]\t[date_format=None]','Add Time To Time\t[time1]\t[time2]\t[result_format=number]\t[exclude_millis=False]','Convert Date\t[date]\t[result_format=timestamp]\t[exclude_millis=False]\t[date_format=None]','Convert Time\t[time]\t[result_format=number]\t[exclude_millis=False]','Get Current Date\t[time_zone=local]\t[increment=0]\t[result_format=timestamp]\t[exclude_millis=False]','Subtract Date From Date\t[date1]\t[date2]\t[result_format=number]\t[exclude_millis=False]\t[date1_format=None]\t[date2_format=None]','Subtract Time From Date\t[date]\t[time]\t[result_format=timestamp]\t[exclude_millis=False]\t[date_format=None]','Subtract Time From Time\t[time1]\t[time2]\t[result_format=number]\t[exclude_millis=False]','Append To Environment Variable\t[name]\t[*values]\t[**config]','Append To File\t[path]\t[content]\t[encoding=UTF-8]','Copy Directory\t[source]\t[destination]','Copy File\t[source]\t[destination]','Copy Files\t[*sources_and_destination]','Count Directories In Directory\t[path]\t[pattern=None]','Count Files In Directory\t[path]\t[pattern=None]','Count Items In Directory\t[path]\t[pattern=None]','Create Binary File\t[path]\t[content]','Create Directory\t[path]','Create File\t[path]\t[content=]\t[encoding=UTF-8]','Directory Should Be Empty\t[path]\t[msg=None]','Directory Should Exist\t[path]\t[msg=None]','Directory Should Not Be Empty\t[path]\t[msg=None]','Directory Should Not Exist\t[path]\t[msg=None]','Empty Directory\t[path]','Environment Variable Should Be Set\t[name]\t[msg=None]','Environment Variable Should Not Be Set\t[name]\t[msg=None]','File Should Be Empty\t[path]\t[msg=None]','File Should Exist\t[path]\t[msg=None]','File Should Not Be Empty\t[path]\t[msg=None]','File Should Not Exist\t[path]\t[msg=None]','Get Binary File\t[path]','Get Environment Variable\t[name]\t[default=None]','Get Environment Variables','Get File\t[path]\t[encoding=UTF-8]\t[encoding_errors=strict]','Get File Size\t[path]','Get Modified Time\t[path]\t[format=timestamp]','Grep File\t[path]\t[pattern]\t[encoding=UTF-8]\t[encoding_errors=strict]','Join Path\t[base]\t[*parts]','Join Paths\t[base]\t[*paths]','List Directories In Directory\t[path]\t[pattern=None]\t[absolute=False]','List Directory\t[path]\t[pattern=None]\t[absolute=False]','List Files In Directory\t[path]\t[pattern=None]\t[absolute=False]','Log Environment Variables\t[level=INFO]','Log File\t[path]\t[encoding=UTF-8]\t[encoding_errors=strict]','Move Directory\t[source]\t[destination]','Move File\t[source]\t[destination]','Move Files\t[*sources_and_destination]','Normalize Path\t[path]','Remove Directory\t[path]\t[recursive=False]','Remove Environment Variable\t[*names]','Remove File\t[path]','Remove Files\t[*paths]','Run\t[command]','Run And Return Rc\t[command]','Run And Return Rc And Output\t[command]','Set Environment Variable\t[name]\t[value]','Set Modified Time\t[path]\t[mtime]','Should Exist\t[path]\t[msg=None]','Should Not Exist\t[path]\t[msg=None]','Split Extension\t[path]','Split Path\t[path]','Touch\t[path]','Wait Until Created\t[path]\t[timeout=1 minute]','Wait Until Removed\t[path]\t[timeout=1 minute]','Get Process Id\t[handle=None]','Get Process Object\t[handle=None]','Get Process Result\t[handle=None]\t[rc=False]\t[stdout=False]\t[stderr=False]\t[stdout_path=False]\t[stderr_path=False]','Is Process Running\t[handle=None]','Join Command Line\t[*args]','Process Should Be Running\t[handle=None]\t[error_message=Process is not running.]','Process Should Be Stopped\t[handle=None]\t[error_message=Process is running.]','Run Process\t[command]\t[*arguments]\t[**configuration]','Send Signal To Process\t[signal]\t[handle=None]\t[group=False]','Split Command Line\t[args]\t[escaping=False]','Start Process\t[command]\t[*arguments]\t[**configuration]','Switch Process\t[handle]','Terminate All Processes\t[kill=False]','Terminate Process\t[handle=None]\t[kill=False]','Wait For Process\t[handle=None]\t[timeout=None]\t[on_timeout=continue]','Convert To Lowercase\t[string]','Convert To Uppercase\t[string]','Decode Bytes To String\t[bytes]\t[encoding]\t[errors=strict]','Encode String To Bytes\t[string]\t[encoding]\t[errors=strict]','Fetch From Left\t[string]\t[marker]','Fetch From Right\t[string]\t[marker]','Generate Random String\t[length=8]\t[chars=[LETTERS][NUMBERS]]','Get Line\t[string]\t[line_number]','Get Line Count\t[string]','Get Lines Containing String\t[string]\t[pattern]\t[case_insensitive=False]','Get Lines Matching Pattern\t[string]\t[pattern]\t[case_insensitive=False]','Get Lines Matching Regexp\t[string]\t[pattern]\t[partial_match=False]','Get Regexp Matches\t[string]\t[pattern]\t[*groups]','Get Substring\t[string]\t[start]\t[end=None]','Remove String\t[string]\t[*removables]','Remove String Using Regexp\t[string]\t[*patterns]','Replace String\t[string]\t[search_for]\t[replace_with]\t[count=-1]','Replace String Using Regexp\t[string]\t[pattern]\t[replace_with]\t[count=-1]','Should Be Byte String\t[item]\t[msg=None]','Should Be Lowercase\t[string]\t[msg=None]','Should Be String\t[item]\t[msg=None]','Should Be Titlecase\t[string]\t[msg=None]','Should Be Unicode String\t[item]\t[msg=None]','Should Be Uppercase\t[string]\t[msg=None]','Should Not Be String\t[item]\t[msg=None]','Split String\t[string]\t[separator=None]\t[max_split=-1]','Split String From Right\t[string]\t[separator=None]\t[max_split=-1]','Split String To Characters\t[string]','Split To Lines\t[string]\t[start=0]\t[end=None]','Strip String\t[string]\t[mode=both]\t[characters=None]','Set Screenshot Directory\t[path]','Take Screenshot\t[name=screenshot]\t[width=800px]','Take Screenshot Without Embedding\t[name=screenshot]','Close All Connections','Close Connection\t[loglevel=None]','Execute Command\t[command]\t[loglevel=None]\t[strip_prompt=False]','Login\t[username]\t[password]\t[login_prompt=login: ]\t[password_prompt=Password: ]\t[login_timeout=1 second]\t[login_incorrect=Login incorrect]','Open Connection\t[host]\t[alias=None]\t[port=23]\t[timeout=None]\t[newline=None]\t[prompt=None]\t[prompt_is_regexp=False]\t[encoding=None]\t[encoding_errors=None]\t[default_log_level=None]\t[window_size=None]\t[environ_user=None]\t[terminal_emulation=None]\t[terminal_type=None]\t[telnetlib_log_level=None]\t[connection_timeout=None]','Read\t[loglevel=None]','Read Until\t[expected]\t[loglevel=None]','Read Until Prompt\t[loglevel=None]\t[strip_prompt=False]','Read Until Regexp\t[*expected]','Set Default Log Level\t[level]','Set Encoding\t[encoding=None]\t[errors=None]','Set Newline\t[newline]','Set Prompt\t[prompt]\t[prompt_is_regexp=False]','Set Telnetlib Log Level\t[level]','Set Timeout\t[timeout]','Switch Connection\t[index_or_alias]','Write\t[text]\t[loglevel=None]','Write Bare\t[text]','Write Control Character\t[character]','Write Until Expected Output\t[text]\t[expected]\t[timeout]\t[retry_interval]\t[loglevel=None]','Background App\t[seconds=5]','Capture Page Screenshot\t[filename=None]','Clear Text\t[locator]','Click A Point\t[x=0]\t[y=0]\t[duration=100]','Click Button\t[index_or_name]','Click Element\t[locator]','Click Element At Coordinates\t[coordinate_X]\t[coordinate_Y]','Click Text\t[text]\t[exact_match=False]','Close All Applications','Close Application','Element Attribute Should Match\t[locator]\t[attr_name]\t[match_pattern]\t[regexp=False]','Element Name Should Be\t[locator]\t[expected]','Element Should Be Disabled\t[locator]\t[loglevel=INFO]','Element Should Be Enabled\t[locator]\t[loglevel=INFO]','Element Should Be Visible\t[locator]\t[loglevel=INFO]','Element Should Contain Text\t[locator]\t[expected]\t[message=]','Element Should Not Contain Text\t[locator]\t[expected]\t[message=]','Element Text Should Be\t[locator]\t[expected]\t[message=]','Element Value Should Be\t[locator]\t[expected]','Get Activity','Get Appium SessionId','Get Appium Timeout','Get Capability\t[capability_name]','Get Contexts','Get Current Context','Get Element Attribute\t[locator]\t[attribute]','Get Element Location\t[locator]','Get Element Size\t[locator]','Get Matching Xpath Count\t[xpath]','Get Network Connection Status','Get Source','Get Text\t[locator]','Get Webelement\t[locator]','Get Webelements\t[locator]','Get Window Height','Get Window Width','Go Back','Go To Url\t[url]','Hide Keyboard\t[key_name=None]','Input Password\t[locator]\t[text]','Input Text\t[locator]\t[text]','Input Value\t[locator]\t[text]','Install App\t[app_path]\t[app_package]','Landscape','Launch Application','Lock\t[seconds=5]','Log Source\t[loglevel=INFO]','Long Press\t[locator]','Long Press Keycode\t[keycode]\t[metastate=None]','Open Application\t[remote_url]\t[alias=None]\t[**kwargs]','Page Should Contain Element\t[locator]\t[loglevel=INFO]','Page Should Contain Text\t[text]\t[loglevel=INFO]','Page Should Not Contain Element\t[locator]\t[loglevel=INFO]','Page Should Not Contain Text\t[text]\t[loglevel=INFO]','Pinch\t[locator]\t[percent=200%]\t[steps=1]','Portrait','Press Keycode\t[keycode]\t[metastate=None]','Pull File\t[path]\t[decode=False]','Pull Folder\t[path]\t[decode=False]','Push File\t[path]\t[data]\t[encode=False]','Quit Application','Register Keyword To Run On Failure\t[keyword]','Remove Application\t[application_id]','Reset Application','Scroll\t[start_locator]\t[end_locator]','Scroll Down\t[locator]','Scroll Up\t[locator]','Set Appium Timeout\t[seconds]','Set Network Connection Status\t[connectionStatus]','Shake','Start Activity\t[appPackage]\t[appActivity]\t[**opts]','Swipe\t[start_x]\t[start_y]\t[offset_x]\t[offset_y]\t[duration=1000]','Swipe By Percent\t[start_x]\t[start_y]\t[end_x]\t[end_y]\t[duration=1000]','Switch Application\t[index_or_alias]','Switch To Context\t[context_name]','Tap\t[locator]\t[x_offset=None]\t[y_offset=None]\t[count=1]','Text Should Be Visible\t[text]\t[exact_match=False]\t[loglevel=INFO]','Wait Activity\t[activity]\t[timeout]\t[interval=1]','Wait Until Element Is Visible\t[locator]\t[timeout=None]\t[error=None]','Wait Until Page Contains\t[text]\t[timeout=None]\t[error=None]','Wait Until Page Contains Element\t[locator]\t[timeout=None]\t[error=None]','Wait Until Page Does Not Contain\t[text]\t[timeout=None]\t[error=None]','Wait Until Page Does Not Contain Element\t[locator]\t[timeout=None]\t[error=None]','Xpath Should Match X Times\t[xpath]\t[count]\t[error=None]\t[loglevel=INFO]','Zoom\t[locator]\t[percent=200%]\t[steps=1]','Create Digest Session\t[alias]\t[url]\t[auth]\t[headers={}]\t[cookies=None]\t[timeout=None]\t[proxies=None]\t[verify=False]\t[debug=0]\t[max_retries=3]\t[backoff_factor=0.1]\t[disable_warnings=0]','Create Ntlm Session\t[alias]\t[url]\t[auth]\t[headers={}]\t[cookies=None]\t[timeout=None]\t[proxies=None]\t[verify=False]\t[debug=0]\t[max_retries=3]\t[backoff_factor=0.1]\t[disable_warnings=0]','Create Session\t[alias]\t[url]\t[headers={}]\t[cookies=None]\t[auth=None]\t[timeout=None]\t[proxies=None]\t[verify=False]\t[debug=0]\t[max_retries=3]\t[backoff_factor=0.1]\t[disable_warnings=0]','Delete\t[alias]\t[uri]\t[data=()]\t[headers=None]\t[allow_redirects=None]\t[timeout=None]','Delete All Sessions','Delete Request\t[alias]\t[uri]\t[data=()]\t[params=None]\t[headers=None]\t[allow_redirects=None]\t[timeout=None]','Get\t[alias]\t[uri]\t[params=None]\t[headers=None]\t[allow_redirects=None]\t[timeout=None]','Get Request\t[alias]\t[uri]\t[headers=None]\t[json=None]\t[params=None]\t[allow_redirects=None]\t[timeout=None]','Head\t[alias]\t[uri]\t[headers=None]\t[allow_redirects=None]\t[timeout=None]','Head Request\t[alias]\t[uri]\t[headers=None]\t[allow_redirects=None]\t[timeout=None]','Options\t[alias]\t[uri]\t[headers=None]\t[allow_redirects=None]\t[timeout=None]','Options Request\t[alias]\t[uri]\t[headers=None]\t[allow_redirects=None]\t[timeout=None]','Patch\t[alias]\t[uri]\t[data={}]\t[headers=None]\t[files={}]\t[allow_redirects=None]\t[timeout=None]','Patch Request\t[alias]\t[uri]\t[data=None]\t[params=None]\t[headers=None]\t[files=None]\t[allow_redirects=None]\t[timeout=None]','Post\t[alias]\t[uri]\t[data={}]\t[headers=None]\t[files=None]\t[allow_redirects=None]\t[timeout=None]','Post Request\t[alias]\t[uri]\t[data=None]\t[params=None]\t[headers=None]\t[files=None]\t[allow_redirects=None]\t[timeout=None]','Put\t[alias]\t[uri]\t[data=None]\t[headers=None]\t[allow_redirects=None]\t[timeout=None]','Put Request\t[alias]\t[uri]\t[data=None]\t[params=None]\t[files=None]\t[headers=None]\t[allow_redirects=None]\t[timeout=None]','To Json\t[content]\t[pretty_print=False]','Add Cookie\t[name]\t[value]\t[path=None]\t[domain=None]\t[secure=None]\t[expiry=None]','Add Location Strategy\t[strategy_name]\t[strategy_keyword]\t[persist=False]','Alert Should Be Present\t[text=]\t[action=ACCEPT]\t[timeout=None]','Alert Should Not Be Present\t[action=ACCEPT]\t[timeout=0]','Assign Id To Element\t[locator]\t[id]','Capture Page Screenshot\t[filename=selenium-screenshot-{index}.png]','Checkbox Should Be Selected\t[locator]','Checkbox Should Not Be Selected\t[locator]','Choose Cancel On Next Confirmation','Choose File\t[locator]\t[file_path]','Choose Ok On Next Confirmation','Clear Element Text\t[locator]','Click Button\t[locator]','Click Element\t[locator]','Click Element At Coordinates\t[locator]\t[xoffset]\t[yoffset]','Click Image\t[locator]','Click Link\t[locator]','Close All Browsers','Close Browser','Close Window','Confirm Action','Create Webdriver\t[driver_name]\t[alias=None]\t[kwargs={}]\t[**init_kwargs]','Current Frame Contains\t[text]\t[loglevel=INFO]','Current Frame Should Contain\t[text]\t[loglevel=INFO]','Current Frame Should Not Contain\t[text]\t[loglevel=INFO]','Delete All Cookies','Delete Cookie\t[name]','Dismiss Alert\t[accept=True]','Double Click Element\t[locator]','Drag And Drop\t[locator]\t[target]','Drag And Drop By Offset\t[locator]\t[xoffset]\t[yoffset]','Element Should Be Disabled\t[locator]','Element Should Be Enabled\t[locator]','Element Should Be Focused\t[locator]','Element Should Be Visible\t[locator]\t[message=None]','Element Should Contain\t[locator]\t[expected]\t[message=None]\t[ignore_case=False]','Element Should Not Be Visible\t[locator]\t[message=None]','Element Should Not Contain\t[locator]\t[expected]\t[message=None]\t[ignore_case=False]','Element Text Should Be\t[locator]\t[expected]\t[message=None]\t[ignore_case=False]','Element Text Should Not Be\t[locator]\t[not_expected]\t[message=None]\t[ignore_case=False]','Execute Async Javascript\t[*code]','Execute Javascript\t[*code]','Focus\t[locator]','Frame Should Contain\t[locator]\t[text]\t[loglevel=INFO]','Get Alert Message\t[dismiss=True]','Get All Links','Get Cookie\t[name]','Get Cookie Value\t[name]','Get Cookies','Get Element Attribute\t[locator]\t[attribute=None]','Get Element Count\t[locator]','Get Element Size\t[locator]','Get Horizontal Position\t[locator]','Get List Items\t[locator]\t[values=False]','Get Location','Get Locations','Get Matching Xpath Count\t[xpath]\t[return_str=True]','Get Selected List Label\t[locator]','Get Selected List Labels\t[locator]','Get Selected List Value\t[locator]','Get Selected List Values\t[locator]','Get Selenium Implicit Wait','Get Selenium Speed','Get Selenium Timeout','Get Source','Get Table Cell\t[locator]\t[row]\t[column]\t[loglevel=INFO]','Get Text\t[locator]','Get Title','Get Value\t[locator]','Get Vertical Position\t[locator]','Get WebElement\t[locator]','Get WebElements\t[locator]','Get Window Handles','Get Window Identifiers','Get Window Names','Get Window Position','Get Window Size','Get Window Titles','Go Back','Go To\t[url]','Handle Alert\t[action=ACCEPT]\t[timeout=None]','Input Password\t[locator]\t[password]','Input Text\t[locator]\t[text]','Input Text Into Alert\t[text]\t[action=ACCEPT]\t[timeout=None]','Input Text Into Prompt\t[text]','List Selection Should Be\t[locator]\t[*expected]','List Should Have No Selections\t[locator]','List Windows','Location Should Be\t[url]','Location Should Contain\t[expected]','Locator Should Match X Times\t[locator]\t[x]\t[message=None]\t[loglevel=INFO]','Log Location','Log Source\t[loglevel=INFO]','Log Title','Maximize Browser Window','Mouse Down\t[locator]','Mouse Down On Image\t[locator]','Mouse Down On Link\t[locator]','Mouse Out\t[locator]','Mouse Over\t[locator]','Mouse Up\t[locator]','Open Browser\t[url]\t[browser=firefox]\t[alias=None]\t[remote_url=False]\t[desired_capabilities=None]\t[ff_profile_dir=None]','Open Context Menu\t[locator]','Page Should Contain\t[text]\t[loglevel=INFO]','Page Should Contain Button\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Contain Checkbox\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Contain Element\t[locator]\t[message=None]\t[loglevel=INFO]\t[limit=None]','Page Should Contain Image\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Contain Link\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Contain List\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Contain Radio Button\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Contain Textfield\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Not Contain\t[text]\t[loglevel=INFO]','Page Should Not Contain Button\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Not Contain Checkbox\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Not Contain Element\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Not Contain Image\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Not Contain Link\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Not Contain List\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Not Contain Radio Button\t[locator]\t[message=None]\t[loglevel=INFO]','Page Should Not Contain Textfield\t[locator]\t[message=None]\t[loglevel=INFO]','Press Key\t[locator]\t[key]','Radio Button Should Be Set To\t[group_name]\t[value]','Radio Button Should Not Be Selected\t[group_name]','Register Keyword To Run On Failure\t[keyword]','Reload Page','Remove Location Strategy\t[strategy_name]','Select All From List\t[locator]','Select Checkbox\t[locator]','Select Frame\t[locator]','Select From List\t[locator]\t[*options]','Select From List By Index\t[locator]\t[*indexes]','Select From List By Label\t[locator]\t[*labels]','Select From List By Value\t[locator]\t[*values]','Select Radio Button\t[group_name]\t[value]','Select Window\t[locator=MAIN]','Set Browser Implicit Wait\t[value]','Set Focus To Element\t[locator]','Set Screenshot Directory\t[path]\t[persist=DEPRECATED]','Set Selenium Implicit Wait\t[value]','Set Selenium Speed\t[value]','Set Selenium Timeout\t[value]','Set Window Position\t[x]\t[y]','Set Window Size\t[width]\t[height]','Simulate\t[locator]\t[event]','Simulate Event\t[locator]\t[event]','Submit Form\t[locator=None]','Switch Browser\t[index_or_alias]','Table Cell Should Contain\t[locator]\t[row]\t[column]\t[expected]\t[loglevel=INFO]','Table Column Should Contain\t[locator]\t[column]\t[expected]\t[loglevel=INFO]','Table Footer Should Contain\t[locator]\t[expected]\t[loglevel=INFO]','Table Header Should Contain\t[locator]\t[expected]\t[loglevel=INFO]','Table Row Should Contain\t[locator]\t[row]\t[expected]\t[loglevel=INFO]','Table Should Contain\t[locator]\t[expected]\t[loglevel=INFO]','Textarea Should Contain\t[locator]\t[expected]\t[message=None]','Textarea Value Should Be\t[locator]\t[expected]\t[message=None]','Textfield Should Contain\t[locator]\t[expected]\t[message=None]','Textfield Value Should Be\t[locator]\t[expected]\t[message=None]','Title Should Be\t[title]\t[message=None]','Unselect All From List\t[locator]','Unselect Checkbox\t[locator]','Unselect Frame','Unselect From List\t[locator]\t[*items]','Unselect From List By Index\t[locator]\t[*indexes]','Unselect From List By Label\t[locator]\t[*labels]','Unselect From List By Value\t[locator]\t[*values]','Wait For Condition\t[condition]\t[timeout=None]\t[error=None]','Wait Until Element Contains\t[locator]\t[text]\t[timeout=None]\t[error=None]','Wait Until Element Does Not Contain\t[locator]\t[text]\t[timeout=None]\t[error=None]','Wait Until Element Is Enabled\t[locator]\t[timeout=None]\t[error=None]','Wait Until Element Is Not Visible\t[locator]\t[timeout=None]\t[error=None]','Wait Until Element Is Visible\t[locator]\t[timeout=None]\t[error=None]','Wait Until Page Contains\t[text]\t[timeout=None]\t[error=None]','Wait Until Page Contains Element\t[locator]\t[timeout=None]\t[error=None]','Wait Until Page Does Not Contain\t[text]\t[timeout=None]\t[error=None]','Wait Until Page Does Not Contain Element\t[locator]\t[timeout=None]\t[error=None]','Xpath Should Match X Times\t[xpath]\t[x]\t[message=None]\t[loglevel=INFO]','Close All Connections','Close Connection','Create Local Ssh Tunnel\t[local_port]\t[remote_host]\t[remote_port]','Directory Should Exist\t[path]','Directory Should Not Exist\t[path]','Enable Ssh Logging\t[logfile]','Execute Command\t[command]\t[return_stdout=True]\t[return_stderr=False]\t[return_rc=False]\t[sudo=False]\t[sudo_password=None]','File Should Exist\t[path]','File Should Not Exist\t[path]','Get Connection\t[index_or_alias=None]\t[index=False]\t[host=False]\t[alias=False]\t[port=False]\t[timeout=False]\t[newline=False]\t[prompt=False]\t[term_type=False]\t[width=False]\t[height=False]\t[encoding=False]','Get Connections','Get Directory\t[source]\t[destination=.]\t[recursive=False]','Get File\t[source]\t[destination=.]','Get Pre Login Banner\t[host=None]\t[port=22]','List Directories In Directory\t[path]\t[pattern=None]\t[absolute=False]','List Directory\t[path]\t[pattern=None]\t[absolute=False]','List Files In Directory\t[path]\t[pattern=None]\t[absolute=False]','Login\t[username]\t[password]\t[delay=0.5 seconds]','Login With Public Key\t[username]\t[keyfile]\t[password=]\t[allow_agent=False]\t[look_for_keys=False]\t[delay=0.5 seconds]','Open Connection\t[host]\t[alias=None]\t[port=22]\t[timeout=None]\t[newline=None]\t[prompt=None]\t[term_type=None]\t[width=None]\t[height=None]\t[path_separator=None]\t[encoding=None]','Put Directory\t[source]\t[destination=.]\t[mode=0744]\t[newline=]\t[recursive=False]','Put File\t[source]\t[destination=.]\t[mode=0744]\t[newline=]','Read\t[loglevel=None]\t[delay=None]','Read Command Output\t[return_stdout=True]\t[return_stderr=False]\t[return_rc=False]','Read Until\t[expected]\t[loglevel=None]','Read Until Prompt\t[loglevel=None]','Read Until Regexp\t[regexp]\t[loglevel=None]','Set Client Configuration\t[timeout=None]\t[newline=None]\t[prompt=None]\t[term_type=None]\t[width=None]\t[height=None]\t[path_separator=None]\t[encoding=None]','Set Default Configuration\t[timeout=None]\t[newline=None]\t[prompt=None]\t[loglevel=None]\t[term_type=None]\t[width=None]\t[height=None]\t[path_separator=None]\t[encoding=None]','Start Command\t[command]\t[sudo=False]\t[sudo_password=None]','Switch Connection\t[index_or_alias]','Write\t[text]\t[loglevel=None]','Write Bare\t[text]','Write Until Expected Output\t[text]\t[expected]\t[timeout]\t[retry_interval]\t[loglevel=None]','Call Stored Procedure\t[spName]\t[spParams=None]\t[sansTran=False]','Check If Exists In Database\t[selectStatement]\t[sansTran=False]','Check If Not Exists In Database\t[selectStatement]\t[sansTran=False]','Connect To Database\t[dbapiModuleName=None]\t[dbName=None]\t[dbUsername=None]\t[dbPassword=None]\t[dbHost=None]\t[dbPort=None]\t[dbCharset=None]\t[dbConfigFile=./resources/db.cfg]','Connect To Database Using Custom Params\t[dbapiModuleName=None]\t[db_connect_string=]','Delete All Rows From Table\t[tableName]\t[sansTran=False]','Description\t[selectStatement]\t[sansTran=False]','Disconnect From Database','Execute Sql Script\t[sqlScriptFileName]\t[sansTran=False]','Execute Sql String\t[sqlString]\t[sansTran=False]','Query\t[selectStatement]\t[sansTran=False]','Row Count\t[selectStatement]\t[sansTran=False]','Row Count Is 0\t[selectStatement]\t[sansTran=False]','Row Count Is Equal To X\t[selectStatement]\t[numRows]\t[sansTran=False]','Row Count Is Greater Than X\t[selectStatement]\t[numRows]\t[sansTran=False]','Row Count Is Less Than X\t[selectStatement]\t[numRows]\t[sansTran=False]','Table Must Exist\t[tableName]\t[sansTran=False]'];"
  },
  {
    "path": "auto/www/static/js/highlight.js",
    "content": "var high_light=['Call Method','Catenate','Comment','Continue For Loop','Continue For Loop If','Convert To Binary','Convert To Boolean','Convert To Bytes','Convert To Hex','Convert To Integer','Convert To Number','Convert To Octal','Convert To String','Create Dictionary','Create List','Evaluate','Exit For Loop','Exit For Loop If','Fail','Fatal Error','Get Count','Get Length','Get Library Instance','Get Time','Get Variable Value','Get Variables','Import Library','Import Resource','Import Variables','Keyword Should Exist','Length Should Be','Log','Log Many','Log To Console','Log Variables','No Operation','Pass Execution','Pass Execution If','Regexp Escape','Reload Library','Remove Tags','Repeat Keyword','Replace Variables','Return From Keyword','Return From Keyword If','Run Keyword','Run Keyword And Continue On Failure','Run Keyword And Expect Error','Run Keyword And Ignore Error','Run Keyword And Return','Run Keyword And Return If','Run Keyword And Return Status','Run Keyword If','Run Keyword If All Critical Tests Passed','Run Keyword If All Tests Passed','Run Keyword If Any Critical Tests Failed','Run Keyword If Any Tests Failed','Run Keyword If Test Failed','Run Keyword If Test Passed','Run Keyword If Timeout Occurred','Run Keyword Unless','Run Keywords','Set Global Variable','Set Library Search Order','Set Log Level','Set Suite Documentation','Set Suite Metadata','Set Suite Variable','Set Tags','Set Test Documentation','Set Test Message','Set Test Variable','Set Variable','Set Variable If','Should Be Empty','Should Be Equal','Should Be Equal As Integers','Should Be Equal As Numbers','Should Be Equal As Strings','Should Be True','Should Contain','Should Contain Any','Should Contain X Times','Should End With','Should Match','Should Match Regexp','Should Not Be Empty','Should Not Be Equal','Should Not Be Equal As Integers','Should Not Be Equal As Numbers','Should Not Be Equal As Strings','Should Not Be True','Should Not Contain','Should Not Contain Any','Should Not End With','Should Not Match','Should Not Match Regexp','Should Not Start With','Should Start With','Sleep','Variable Should Exist','Variable Should Not Exist','Wait Until Keyword Succeeds','Append To List','Combine Lists','Convert To Dictionary','Convert To List','Copy Dictionary','Copy List','Count Values In List','Dictionaries Should Be Equal','Dictionary Should Contain Item','Dictionary Should Contain Key','Dictionary Should Contain Sub Dictionary','Dictionary Should Contain Value','Dictionary Should Not Contain Key','Dictionary Should Not Contain Value','Get Dictionary Items','Get Dictionary Keys','Get Dictionary Values','Get From Dictionary','Get From List','Get Index From List','Get Match Count','Get Matches','Get Slice From List','Insert Into List','Keep In Dictionary','List Should Contain Sub List','List Should Contain Value','List Should Not Contain Duplicates','List Should Not Contain Value','Lists Should Be Equal','Log Dictionary','Log List','Pop From Dictionary','Remove Duplicates','Remove From Dictionary','Remove From List','Remove Values From List','Reverse List','Set List Value','Set To Dictionary','Should Contain Match','Should Not Contain Match','Sort List','Add Time To Date','Add Time To Time','Convert Date','Convert Time','Get Current Date','Subtract Date From Date','Subtract Time From Date','Subtract Time From Time','Append To Environment Variable','Append To File','Copy Directory','Copy File','Copy Files','Count Directories In Directory','Count Files In Directory','Count Items In Directory','Create Binary File','Create Directory','Create File','Directory Should Be Empty','Directory Should Exist','Directory Should Not Be Empty','Directory Should Not Exist','Empty Directory','Environment Variable Should Be Set','Environment Variable Should Not Be Set','File Should Be Empty','File Should Exist','File Should Not Be Empty','File Should Not Exist','Get Binary File','Get Environment Variable','Get Environment Variables','Get File','Get File Size','Get Modified Time','Grep File','Join Path','Join Paths','List Directories In Directory','List Directory','List Files In Directory','Log Environment Variables','Log File','Move Directory','Move File','Move Files','Normalize Path','Remove Directory','Remove Environment Variable','Remove File','Remove Files','Run','Run And Return Rc','Run And Return Rc And Output','Set Environment Variable','Set Modified Time','Should Exist','Should Not Exist','Split Extension','Split Path','Touch','Wait Until Created','Wait Until Removed','Get Process Id','Get Process Object','Get Process Result','Is Process Running','Join Command Line','Process Should Be Running','Process Should Be Stopped','Run Process','Send Signal To Process','Split Command Line','Start Process','Switch Process','Terminate All Processes','Terminate Process','Wait For Process','Convert To Lowercase','Convert To Uppercase','Decode Bytes To String','Encode String To Bytes','Fetch From Left','Fetch From Right','Generate Random String','Get Line','Get Line Count','Get Lines Containing String','Get Lines Matching Pattern','Get Lines Matching Regexp','Get Regexp Matches','Get Substring','Remove String','Remove String Using Regexp','Replace String','Replace String Using Regexp','Should Be Byte String','Should Be Lowercase','Should Be String','Should Be Titlecase','Should Be Unicode String','Should Be Uppercase','Should Not Be String','Split String','Split String From Right','Split String To Characters','Split To Lines','Strip String','Set Screenshot Directory','Take Screenshot','Take Screenshot Without Embedding','Close All Connections','Close Connection','Execute Command','Login','Open Connection','Read','Read Until','Read Until Prompt','Read Until Regexp','Set Default Log Level','Set Encoding','Set Newline','Set Prompt','Set Telnetlib Log Level','Set Timeout','Switch Connection','Write','Write Bare','Write Control Character','Write Until Expected Output','Background App','Capture Page Screenshot','Clear Text','Click A Point','Click Button','Click Element','Click Element At Coordinates','Click Text','Close All Applications','Close Application','Element Attribute Should Match','Element Name Should Be','Element Should Be Disabled','Element Should Be Enabled','Element Should Be Visible','Element Should Contain Text','Element Should Not Contain Text','Element Text Should Be','Element Value Should Be','Get Activity','Get Appium SessionId','Get Appium Timeout','Get Capability','Get Contexts','Get Current Context','Get Element Attribute','Get Element Location','Get Element Size','Get Matching Xpath Count','Get Network Connection Status','Get Source','Get Text','Get Webelement','Get Webelements','Get Window Height','Get Window Width','Go Back','Go To Url','Hide Keyboard','Input Password','Input Text','Input Value','Install App','Landscape','Launch Application','Lock','Log Source','Long Press','Long Press Keycode','Open Application','Page Should Contain Element','Page Should Contain Text','Page Should Not Contain Element','Page Should Not Contain Text','Pinch','Portrait','Press Keycode','Pull File','Pull Folder','Push File','Quit Application','Register Keyword To Run On Failure','Remove Application','Reset Application','Scroll','Scroll Down','Scroll Up','Set Appium Timeout','Set Network Connection Status','Shake','Start Activity','Swipe','Swipe By Percent','Switch Application','Switch To Context','Tap','Text Should Be Visible','Wait Activity','Wait Until Element Is Visible','Wait Until Page Contains','Wait Until Page Contains Element','Wait Until Page Does Not Contain','Wait Until Page Does Not Contain Element','Xpath Should Match X Times','Zoom','Create Digest Session','Create Ntlm Session','Create Session','Delete','Delete All Sessions','Delete Request','Get','Get Request','Head','Head Request','Options','Options Request','Patch','Patch Request','Post','Post Request','Put','Put Request','To Json','Add Cookie','Add Location Strategy','Alert Should Be Present','Alert Should Not Be Present','Assign Id To Element','Capture Page Screenshot','Checkbox Should Be Selected','Checkbox Should Not Be Selected','Choose Cancel On Next Confirmation','Choose File','Choose Ok On Next Confirmation','Clear Element Text','Click Button','Click Element','Click Element At Coordinates','Click Image','Click Link','Close All Browsers','Close Browser','Close Window','Confirm Action','Create Webdriver','Current Frame Contains','Current Frame Should Contain','Current Frame Should Not Contain','Delete All Cookies','Delete Cookie','Dismiss Alert','Double Click Element','Drag And Drop','Drag And Drop By Offset','Element Should Be Disabled','Element Should Be Enabled','Element Should Be Focused','Element Should Be Visible','Element Should Contain','Element Should Not Be Visible','Element Should Not Contain','Element Text Should Be','Element Text Should Not Be','Execute Async Javascript','Execute Javascript','Focus','Frame Should Contain','Get Alert Message','Get All Links','Get Cookie','Get Cookie Value','Get Cookies','Get Element Attribute','Get Element Count','Get Element Size','Get Horizontal Position','Get List Items','Get Location','Get Locations','Get Matching Xpath Count','Get Selected List Label','Get Selected List Labels','Get Selected List Value','Get Selected List Values','Get Selenium Implicit Wait','Get Selenium Speed','Get Selenium Timeout','Get Source','Get Table Cell','Get Text','Get Title','Get Value','Get Vertical Position','Get WebElement','Get WebElements','Get Window Handles','Get Window Identifiers','Get Window Names','Get Window Position','Get Window Size','Get Window Titles','Go Back','Go To','Handle Alert','Input Password','Input Text','Input Text Into Alert','Input Text Into Prompt','List Selection Should Be','List Should Have No Selections','List Windows','Location Should Be','Location Should Contain','Locator Should Match X Times','Log Location','Log Source','Log Title','Maximize Browser Window','Mouse Down','Mouse Down On Image','Mouse Down On Link','Mouse Out','Mouse Over','Mouse Up','Open Browser','Open Context Menu','Page Should Contain','Page Should Contain Button','Page Should Contain Checkbox','Page Should Contain Element','Page Should Contain Image','Page Should Contain Link','Page Should Contain List','Page Should Contain Radio Button','Page Should Contain Textfield','Page Should Not Contain','Page Should Not Contain Button','Page Should Not Contain Checkbox','Page Should Not Contain Element','Page Should Not Contain Image','Page Should Not Contain Link','Page Should Not Contain List','Page Should Not Contain Radio Button','Page Should Not Contain Textfield','Press Key','Radio Button Should Be Set To','Radio Button Should Not Be Selected','Register Keyword To Run On Failure','Reload Page','Remove Location Strategy','Select All From List','Select Checkbox','Select Frame','Select From List','Select From List By Index','Select From List By Label','Select From List By Value','Select Radio Button','Select Window','Set Browser Implicit Wait','Set Focus To Element','Set Screenshot Directory','Set Selenium Implicit Wait','Set Selenium Speed','Set Selenium Timeout','Set Window Position','Set Window Size','Simulate','Simulate Event','Submit Form','Switch Browser','Table Cell Should Contain','Table Column Should Contain','Table Footer Should Contain','Table Header Should Contain','Table Row Should Contain','Table Should Contain','Textarea Should Contain','Textarea Value Should Be','Textfield Should Contain','Textfield Value Should Be','Title Should Be','Unselect All From List','Unselect Checkbox','Unselect Frame','Unselect From List','Unselect From List By Index','Unselect From List By Label','Unselect From List By Value','Wait For Condition','Wait Until Element Contains','Wait Until Element Does Not Contain','Wait Until Element Is Enabled','Wait Until Element Is Not Visible','Wait Until Element Is Visible','Wait Until Page Contains','Wait Until Page Contains Element','Wait Until Page Does Not Contain','Wait Until Page Does Not Contain Element','Xpath Should Match X Times','Close All Connections','Close Connection','Create Local Ssh Tunnel','Directory Should Exist','Directory Should Not Exist','Enable Ssh Logging','Execute Command','File Should Exist','File Should Not Exist','Get Connection','Get Connections','Get Directory','Get File','Get Pre Login Banner','List Directories In Directory','List Directory','List Files In Directory','Login','Login With Public Key','Open Connection','Put Directory','Put File','Read','Read Command Output','Read Until','Read Until Prompt','Read Until Regexp','Set Client Configuration','Set Default Configuration','Start Command','Switch Connection','Write','Write Bare','Write Until Expected Output','Call Stored Procedure','Check If Exists In Database','Check If Not Exists In Database','Connect To Database','Connect To Database Using Custom Params','Delete All Rows From Table','Description','Disconnect From Database','Execute Sql Script','Execute Sql String','Query','Row Count','Row Count Is 0','Row Count Is Equal To X','Row Count Is Greater Than X','Row Count Is Less Than X','Table Must Exist'];"
  },
  {
    "path": "auto/www/templates/dashboard.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <title>AutoLink - 开源优测自动化测试集成解决方案</title>\n\n        <meta name=\"keywords\" content=\"AutoLink, 开源优测, 苦叶子, web ide\"/>\n        <meta name=\"description\" content=\"A Web-based IDE for Auto Testing using Auto Open Source Testing Framework, do your development anytime, anywhere.\"/>\n        <meta name=\"author\" content=\"苦叶子\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/bootstrap/easyui.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/icon.css') }}\">\n\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/auto.css') }}\">\n\n        <link rel=\"icon\" type=\"image/x-icon\" href=\"{{ url_for('static', filename='favicon.ico') }}\" />\n        <style type=\"text/css\">\n            .lines-no .datagrid-body td{\n                border-right:1px dotted transparent;\n                border-bottom:1px dotted transparent;\n            }\n        </style>\n    </head>\n    <body class=\"easyui-layout\">\n        <div data-options=\"region:'north',border:true\" style=\"padding:0;height:60px;\">\n            <div class=\"easyui-panel\" style=\"padding:0\">\n                <a href=\"https://github.com/small99\" target=\"_blank\" class=\"easyui-linkbutton\" data-options=\"plain:true\"><img src=\"{{ url_for('static', filename='img/logo.min.png') }}\" alt=\"开源优测\" style=\"padding-top:8px;\"/></a>\n                <a href=\"#\" onclick=\"addTab('调度管理', '/scheduler/', 'icon-scheduler')\" class=\"easyui-linkbutton\" data-options=\"iconCls:'icon-scheduler'\">调度管理</a>\n                <a href=\"#\" onclick=\"addTab('系统管理', '/user', 'icon-settings')\" class=\"easyui-linkbutton\" data-options=\"iconCls:'icon-settings'\">系统管理</a>\n                <a href=\"https://github.com/small99/AutoLink/tree/master/docs\" target=\"_blank\" class=\"easyui-linkbutton\" data-options=\"iconCls:'icon-keyword-help'\">用户指南</a>\n\n                <!--<a href=\"https://github.com/small99/AutoLink/blob/master/UPDATEING.md\" target=\"_blank\" class=\"easyui-linkbutton\" data-options=\"iconCls:'icon-update'\">更新清单</a>-->\n\n\n                <!--<a href=\"#\" class=\"easyui-menubutton\" data-options=\"plain:true\">源码</a>\n                <a href=\"#\" class=\"easyui-menubutton\" data-options=\"plain:true\">运行</a>\n                <a href=\"#\" class=\"easyui-menubutton\" data-options=\"plain:true\">工具</a>\n                <a href=\"#\" class=\"easyui-menubutton\" data-options=\"menu:'#file'\">文件</a>\n                <a href=\"#\" class=\"easyui-menubutton\" data-options=\"plain:true, menu:'#mm1'\">编辑</a>\n\n                <a href=\"#\" class=\"easyui-menubutton\" data-options=\"plain:true, menu:'#mm2'\">Help</a>\n                <a href=\"#\" class=\"easyui-menubutton\" data-options=\"plain:true, menu:'#mm3'\">About</a></a>-->\n                <a href=\"#\" onclick=\"do_logout('{{ username }}');\" class=\"easyui-linkbutton\" data-options=\"iconCls:'icon-logout'\" style=\"float:right;margin-top:10px;margin-right:10px;\">注销</a>\n            </div>\n            <!--\n            <div id=\"file\" style=\"width:150px;\">\n                <div data-options=\"iconCls:'icon-undo'\">Undo</div>\n                <div data-options=\"iconCls:'icon-redo'\">Redo</div>\n                <div class=\"menu-sep\"></div>\n                <div onclick=\"do_logout('{{ username }}');\" data-options=\"iconCls:'icon-back'\">退出</div>\n            </div>\n\n\n            <div id=\"mm1\" style=\"width:150px;\">\n                <div data-options=\"iconCls:'icon-undo'\">Undo</div>\n                <div data-options=\"iconCls:'icon-redo'\">Redo</div>\n                <div class=\"menu-sep\"></div>\n                <div>Cut</div>\n                <div>Copy</div>\n                <div>Paste</div>\n                <div class=\"menu-sep\"></div>\n                <div>\n                    <span>Toolbar</span>\n                    <div>\n                        <div>Address</div>\n                        <div>Link</div>\n                        <div>Navigation Toolbar</div>\n                        <div>Bookmark Toolbar</div>\n                        <div class=\"menu-sep\"></div>\n                        <div>New Toolbar...</div>\n                    </div>\n                </div>\n                <div data-options=\"iconCls:'icon-remove'\">Delete</div>\n                <div>Select All</div>\n            </div>\n            <div id=\"mm2\" style=\"width:100px;\">\n                <div>Help</div>\n                <div>Update</div>\n                <div>About</div>\n            </div>\n            <div id=\"mm3\" class=\"menu-content\" style=\"background:#f0f0f0;padding:10px;text-align:left\">\n                <img src=\"http://www.jeasyui.com/images/logo1.png\" style=\"width:150px;height:50px\">\n                <p style=\"font-size:14px;color:#444;\">Try jQuery EasyUI to build your modern, interactive, javascript applications.</p>\n            </div>\n            -->\n        </div>\n        <div data-options=\"region:'west',split:true\" style=\"width:240px;padding:5px;\">\n            <ul id=\"project_tree\" class=\"easyui-tree\"\n                data-options=\"\n                    url:'/api/v1/project_list/',\n                    method:'get',\n                    queryParams: {},\n                    animate:true,\n                    lines:true,\n                    onBeforeExpand: onBeforeExpand,\n                    onContextMenu: onContextMenu,\n                    onDblClick: onDblClick\n                \">\n\n            </ul>\n        </div>\n        <!--<div data-options=\"region:'east',split:true,collapsed:true,title:'工具栏'\" style=\"width:100px;padding:10px;\">east region</div>-->\n        <!--\n        <div data-options=\"region:'south',border:false,split:true,\" style=\"height:150px;padding:0\">\n            <div id=\"logger_tabs\" class=\"easyui-tabs\" style=\"width:100%;height:100%\">\n                <div title=\"输出\" style=\"padding:10px\">\n                    <p style=\"font-size:14px\">jQuery EasyUI framework helps you build your web pages easily.</p>\n                    <ul>\n                        <li>easyui is a collection of user-interface plugin based on jQuery.</li>\n                        <li>easyui provides essential functionality for building modem, interactive, javascript applications.</li>\n                        <li>using easyui you don't need to write many javascript code, you usually defines user-interface by writing some HTML markup.</li>\n                        <li>complete framework for HTML5 web page.</li>\n                        <li>easyui save your time and scales while developing your products.</li>\n                        <li>easyui is very easy but powerful.</li>\n                    </ul>\n                </div>\n                <div title=\"通知\" style=\"padding:10px\">\n                    <ul class=\"easyui-tree\" data-options=\"url:'',method:'get',animate:true\"></ul>\n                </div>\n            </div>\n\n        </div>\n        -->\n        <div data-options=\"region:'center'\">\n            <div id=\"editor_tabs\" class=\"easyui-tabs\" style=\"width:100%;height:100%\">\n\n            </div>\n\n        </div>\n\n        <div id=\"root_menu\" class=\"easyui-menu\" style=\"width:160px;\">\n            <div onclick=\"addTab('欢迎页', '/welcome', 'icon-workspace');\" data-options=\"iconCls:'icon-workspace'\">欢迎页</div>\n            <div onclick=\"manage_project('create_project', 'create_project_ff', 'create')\" data-options=\"iconCls:'icon-project'\">创建项目</div>\n            <div class=\"menu-sep\"></div>\n            <div onclick=\"expand()\" data-options=\"iconCls:'icon-expand'\">展开</div>\n            <div onclick=\"collapse()\" data-options=\"iconCls:'icon-collapse'\">折叠</div>\n            <div class=\"menu-sep\"></div>\n            {% if username == \"AutoLink\" %}\n            <div onclick=\"addTab('用户管理', '/user', 'icon-user')\" data-options=\"iconCls:'icon-user'\">用户管理</div>\n            <div class=\"menu-sep\"></div>\n            {% endif %}\n            <div onclick=\"do_logout('{{ username }}');\" data-options=\"iconCls:'icon-logout'\">退出</div>\n        </div>\n        <div id=\"project_menu\" class=\"easyui-menu\" style=\"width:160px;\">\n            <div onclick=\"manage_suite('create_suite', 'create_suite_ff', 'create')\" data-options=\"iconCls:'icon-suite'\">创建目录</div>\n            <div class=\"menu-sep\"></div>\n            <div onclick=\"do_run();\" data-options=\"iconCls:'icon-run'\">运行</div>\n            <div onclick=\"do_task_list();\" data-options=\"iconCls:'icon-task'\">查看任务</div>\n            <!--<div onclick=\"manage_project('edit_project', 'edit_project_ff', 'edit')\" data-options=\"iconCls:'icon-debug'\">调试</div>-->\n            <!--<div onclick=\"addTab('任务管理', '#', 'icon-task')\" data-options=\"iconCls:'icon-task'\">任务管理</div>-->\n            <div class=\"menu-sep\"></div>\n            <div onclick=\"manage_project('edit_project', 'edit_project_ff', 'edit')\">重命名</div>\n            <div onclick=\"delete_project()\" data-options=\"iconCls:'icon-remove'\">删除</div>\n            <div class=\"menu-sep\"></div>\n            <div onclick=\"expand()\" data-options=\"iconCls:'icon-expand'\">展开</div>\n            <div onclick=\"collapse()\" data-options=\"iconCls:'icon-collapse'\">折叠</div>\n        </div>\n        <div id=\"suite_menu\" class=\"easyui-menu\" style=\"width:160px;\">\n            <div onclick=\"manage_file('create_file', 'create_file_ff', 'create')\">创建文件</div>\n            <div onclick=\"open_win('upload_win');\">上传文件</div>\n            <div class=\"menu-sep\"></div>\n            <div onclick=\"do_run();\" data-options=\"iconCls:'icon-run'\">运行</div>\n            <div class=\"menu-sep\"></div>\n            <div onclick=\"manage_suite('edit_suite', 'edit_suite_ff', 'edit')\">重命名</div>\n            <div onclick=\"delete_suite()\" data-options=\"iconCls:'icon-remove'\">删除</div>\n            <div class=\"menu-sep\"></div>\n            <div onclick=\"expand()\" data-options=\"iconCls:'icon-expand'\">展开</div>\n            <div onclick=\"collapse()\" data-options=\"iconCls:'icon-collapse'\">折叠</div>\n        </div>\n        <div id=\"case_menu\" class=\"easyui-menu\" style=\"width:160px;\">\n            <div onclick=\"do_open_editor();\">打开</div>\n            <div onclick=\"do_download('download_ff');\">下载</div>\n            <div class=\"menu-sep\"></div>\n            <div onclick=\"do_run();\" data-options=\"iconCls:'icon-run'\">运行</div>\n            <div class=\"menu-sep\"></div>\n            <div onclick=\"manage_file('edit_file', 'edit_file_ff', 'edit')\">重命名</div>\n            <div onclick=\"delete_file()\" data-options=\"iconCls:'icon-remove'\">删除</div>\n        </div>\n        <!-- project start -->\n        <div id=\"create_project\" class=\"easyui-window\" title=\"创建项目\"\n             data-options=\"modal:true,closed:true,minimizable:false,maximizable:false,collapsible:false\"\n             style=\"width:400px;height:170px;padding:10px;\">\n            <form id=\"create_project_ff\" method=\"post\">\n                <div style=\"margin-bottom:10px\">\n                    <input class=\"easyui-textbox\" id=\"name\" name=\"name\" label=\"名称\" labelPosition=\"top\" style=\"width:100%\" data-options=\"required:true\">\n                </div>\n            </form>\n            <div style=\"text-align:right;padding:5px 0\">\n                <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"close_win('create_project')\" style=\"width:60px\">取消</a>\n                <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"create_project('create_project', 'create_project_ff')\" style=\"width:60px\">创建</a>\n            </div>\n        </div>\n        <div id=\"edit_project\" class=\"easyui-window\" title=\"项目重命名\"\n             data-options=\"modal:true,closed:true,minimizable:false,maximizable:false,collapsible:false\"\n             style=\"width:400px;height:170px;padding:10px;\">\n            <form id=\"edit_project_ff\" method=\"post\">\n                <div style=\"margin-bottom:10px;text-align:center\">\n                    <input class=\"easyui-textbox\" id=\"new_name\" name=\"new_name\" style=\"width:100%;\" labelPosition=\"top\" data-options=\"label:'名称',required:true\">\n                </div>\n            </form>\n            <div style=\"text-align:right;padding:5px 0\">\n                <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"close_win('edit_project')\" style=\"width:60px\">取消</a>\n                <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"rename_project('edit_project', 'edit_project_ff')\" style=\"width:60px\">保存</a>\n            </div>\n        </div>\n        <!-- project end -->\n\n        <!-- suite start -->\n        <div id=\"create_suite\" class=\"easyui-window\" title=\"创建目录\"\n             data-options=\"modal:true,closed:true,minimizable:false,maximizable:false,collapsible:false\"\n             style=\"width:400px;height:170px;padding:10px;\">\n            <form id=\"create_suite_ff\" method=\"post\">\n                <div style=\"margin-bottom:10px\">\n                    <input class=\"easyui-textbox\" id=\"name\" name=\"name\" label=\"名称\" labelPosition=\"top\" style=\"width:100%\" data-options=\"required:true\">\n                </div>\n            </form>\n            <div style=\"text-align:right;padding:5px 0\">\n                <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"close_win('create_suite')\" style=\"width:60px\">取消</a>\n                <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"create_suite('create_suite', 'create_suite_ff')\" style=\"width:60px\">创建</a>\n            </div>\n        </div>\n        <div id=\"edit_suite\" class=\"easyui-window\" title=\"目录重命名\"\n             data-options=\"modal:true,closed:true,minimizable:false,maximizable:false,collapsible:false\"\n             style=\"width:400px;height:170px;padding:10px;\">\n            <form id=\"edit_suite_ff\" method=\"post\">\n                <div style=\"margin-bottom:10px;text-align:center\">\n                    <input class=\"easyui-textbox\" id=\"new_name\" name=\"new_name\" style=\"width:100%;\" labelPosition=\"top\" data-options=\"label:'名称',required:true\">\n                </div>\n            </form>\n            <div style=\"text-align:right;padding:5px 0\">\n                <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"close_win('edit_suite')\" style=\"width:60px\">取消</a>\n                <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"rename_suite('edit_suite', 'edit_suite_ff')\" style=\"width:60px\">保存</a>\n            </div>\n        </div>\n        <!-- suite end -->\n\n        <!-- file start -->\n        <div id=\"create_file\" class=\"easyui-window\" title=\"创建文件\"\n             data-options=\"modal:true,closed:true,minimizable:false,maximizable:false,collapsible:false\"\n             style=\"width:400px;height:240px;padding:10px;\">\n            <form id=\"create_file_ff\" method=\"post\">\n                <div style=\"margin-bottom:10px\">\n                    <select class=\"easyui-combobox\" id=\"category\" name=\"category\" label=\"类型\" labelPosition=\"top\" style=\"width:100%\" data-options=\"required:true\">\n                        <option value=\".txt\">资源文件(.txt)</option>\n                        <option value=\".robot\">用例文件(.robot)</option>\n                    </select>\n                </div>\n                <div style=\"margin-bottom:10px\">\n                    <input class=\"easyui-textbox\" id=\"name\" name=\"name\" label=\"名称\" labelPosition=\"top\" style=\"width:100%\" data-options=\"required:true\">\n                </div>\n            </form>\n            <div style=\"text-align:right;padding:5px 0\">\n                <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"close_win('create_file')\" style=\"width:60px\">取消</a>\n                <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"create_file('create_file', 'create_file_ff')\" style=\"width:60px\">创建</a>\n            </div>\n        </div>\n        <div id=\"edit_file\" class=\"easyui-window\" title=\"文件重命名\"\n             data-options=\"modal:true,closed:true,minimizable:false,maximizable:false,collapsible:false\"\n             style=\"width:400px;height:240px;padding:10px;\">\n            <form id=\"edit_file_ff\" method=\"post\">\n                <div style=\"margin-bottom:10px\">\n                    <select class=\"easyui-combobox\" id=\"new_category\" name=\"new_category\" label=\"类型\" labelPosition=\"top\" style=\"width:100%\" data-options=\"required:true\">\n                        <option value=\".txt\">资源文件(.txt)</option>\n                        <option value=\".robot\">用例文件(.robot)</option>\n                    </select>\n                </div>\n                <div style=\"margin-bottom:10px\">\n                    <input class=\"easyui-textbox\" id=\"new_name\" name=\"new_name\" label=\"名称\" labelPosition=\"top\" style=\"width:100%\" data-options=\"required:true\">\n                </div>\n            </form>\n            <div style=\"text-align:right;padding:5px 0\">\n                <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"close_win('edit_file')\" style=\"width:60px\">取消</a>\n                <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"rename_file('edit_file', 'edit_file_ff')\" style=\"width:60px\">保存</a>\n            </div>\n        </div>\n        <!-- file end -->\n\n        <!-- file upload start -->\n        <div id=\"upload_win\" class=\"easyui-window\" title=\"文件上传\"\n         data-options=\"modal:true,closed:true,minimizable:false,maximizable:false,collapsible:false,iconCls:'icon-case'\"\n            style=\"width:500px;height:140px;padding:10px;\">\n            <form id=\"upload_ff\" method=\"POST\" action=\"/api/v1/manage_file/\" enctype=\"multipart/form-data\">\n                <div style=\"margin-bottom:20px\">\n                    <input name=\"method\" id=\"method\" value=\"upload\" hidden>\n                    <input name=\"path\" id=\"path\" value=\"/\" hidden>\n                    <input id=\"files\" name=\"files\" class=\"easyui-filebox\" label=\"\" labelPosition=\"top\"\n                           data-options=\"prompt:'请选择一个文件...',buttonText:'选择'\" style=\"width:100%\">\n                </div>\n                <div style=\"text-align:right;\">\n                    <a href=\"#\" class=\"easyui-linkbutton\" style=\"width:80px;\" onclick=\"do_upload('upload_win', 'upload_ff')\">上 传</a>\n                </div>\n\n            </form>\n        </div>\n        <!-- file upload end -->\n\n        <!-- file download -->\n        <form id=\"download_ff\" method=\"post\" action=\"/api/v1/manage_file/\">\n            <input name=\"method\" id=\"method\" value=\"download\" hidden>\n            <input name=\"path\" id=\"path\" value=\"/\" hidden>\n        </form>\n        <!-- file download end-->\n\n\n\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.easyui.min.js') }}\"></script>\n        <!-- 自定义js库 -->\n        <script type=\"text/javascript\" src=\"{{ url_for('static', filename='js/auto.js') }}\"></script>\n    </body>\n    <SCRIPT type=\"text/javascript\">\n        $(function () {\n            addTab(\"欢迎页\", \"/welcome\", \"icon-workspace\");\n        });\n    </SCRIPT>\n</html>"
  },
  {
    "path": "auto/www/templates/default.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <title>AutoLink - 开源优测自动化测试集成解决方案</title>\n\n        <meta name=\"keywords\" content=\"AutoLink, 开源优测, 苦叶子, web ide\"/>\n        <meta name=\"description\" content=\"A Web-based IDE for Auto Testing using Auto Open Source Testing Framework, do your development anytime, anywhere.\"/>\n        <meta name=\"author\" content=\"苦叶子\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/icon.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/bootstrap/easyui.css') }}\">\n\n        <!--<link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldgutter.css') }}\">-->\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/auto.css') }}\">\n\n        <link rel=\"icon\" type=\"image/x-icon\" href=\"{{ url_for('static', filename='favicon.ico') }}\" />\n    </head>\n    <body class=\"easyui-layout\">\n\n\n        <div data-options=\"region:'center'\" style=\"padding-left:20px;\">\n            <p>\n                <br>\n                <br>\n                <h1>\n                   不支持预览该类型的文件<br><br>请右击下载到本地进行查看\n                </h1>\n                <h3>有建议请直接至https://github.com/small99/AutoLink提交issues<br><br>或是<br><br>发邮件至lymking#foxmail.com提需要</h3>\n            </p>\n        </div>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.easyui.min.js') }}\"></script>\n\n        <!-- 自定义js库 -->\n        <script type=\"text/javascript\" src=\"{{ url_for('static', filename='js/auto.js') }}\"></script>\n    </body>\n</html>"
  },
  {
    "path": "auto/www/templates/editor.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <title>AutoLink - 开源优测自动化测试集成解决方案</title>\n\n        <meta name=\"keywords\" content=\"AutoLink, 开源优测, 苦叶子, web ide\"/>\n        <meta name=\"description\" content=\"A Web-based IDE for Auto Testing using Auto Open Source Testing Framework, do your development anytime, anywhere.\"/>\n        <meta name=\"author\" content=\"苦叶子\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/icon.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/bootstrap/easyui.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/codemirror/lib/codemirror.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/codemirror/addon/hint/show-hint.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/codemirror/theme/dracula.css') }}\">\n        <!--<link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/codemirror/addon/fold/foldgutter.css') }}\">-->\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/auto.css') }}\">\n\n        <link rel=\"icon\" type=\"image/x-icon\" href=\"{{ url_for('static', filename='favicon.ico') }}\" />\n    </head>\n    <body class=\"easyui-layout\">\n\n\n        <div data-options=\"region:'center'\">\n            <textarea id=\"code_editor\" name=\"code_editor\"></textarea>\n            <div id=\"keyword_help\" style=\"padding:5px;background-color: #F5F5DC;\"></div>\n        </div>\n        <div data-options=\"region:'east',split:true\" title=\"关键字\" style=\"width:200px;\">\n            <ul id=\"keyword_list\" class=\"easyui-tree\" data-options=\"\n                    url:'/api/v1/keyword?category=robot',\n                    method:'get',\n                    animate:true,\n                    onDblClick: onKwDblClick,\n                    onClick: onClick\n                    \">\n            </ul>\n        </div>\n\n\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.easyui.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/codemirror/lib/codemirror.js') }}\"></script>\n        <script type=\"text/javascript\" src=\"{{ url_for('static', filename='js/highlight.js') }}\"></script>\n        <script type=\"text/javascript\" src=\"{{ url_for('static', filename='js/autocomplete.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/codemirror/mode/robot/robot_v1.js') }}\"></script>\n        <!--<script src=\"{{ url_for('static', filename='lib/codemirror/mode/textile/textile.js') }}\"></script>>-->\n\n        <script src=\"{{ url_for('static', filename='lib/codemirror/addon/display/placeholder.js') }}\"></script>\n        <!--\n        <script src=\"{{ url_for('static', filename='lib/codemirror/addon/fold/foldcode.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/codemirror/addon/fold/foldgutter.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/codemirror/addon/fold/brace-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/codemirror/addon/fold/xml-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/codemirror/addon/fold/indent-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/codemirror/addon/fold/markdown-fold.js') }}\"></script>-->\n        <script src=\"{{ url_for('static', filename='lib/codemirror//addon/hint/show-hint.js') }}\"></script>\n        <!--<script src=\"{{ url_for('static', filename='lib/codemirror//addon/hint/anyword-hint.js') }}\"></script>-->\n        <script src=\"{{ url_for('static', filename='lib/codemirror//addon/hint/robot-hint.js') }}\"></script>\n        <!-- 自定义js库 -->\n        <script type=\"text/javascript\" src=\"{{ url_for('static', filename='js/auto.js') }}\"></script>\n\n        <SCRIPT type=\"text/javascript\">\n            $(document).ready(function(){\n                CodeMirror.commands.autocomplete = function(cm) {\n                  cm.showHint({hint: CodeMirror.hint.anyword});\n\n                }\n                var path = \"/{{ project }}/{{ suite }}/{{ case }}\";\n                editor = CodeMirror.fromTextArea(document.getElementById(\"code_editor\"), {\n                    mode: 'robot',\n                    lineNumbers: true,\n                    lineWrapping: true,\n                    styleActiveLine: true,\n                    styleSelectedText: true,\n                    theme: \"dracula\",\n                    indentUnit:4,\n                    completeSingle: false,\n                    extraKeys: {\n                        \"Ctrl\": \"autocomplete\"\n                    }\n                    //keyMap: \"sublime\",\n                    //foldGutter: true,\n                    //highlightSelectionMatches: {showToken: /\\w/, annotateScrollbar: true},\n                    //gutters: [\"CodeMirror-linenumbers\", \"CodeMirror-foldgutter\"]\n                });\n                editor.on(\"change\", function(cm, event)\n                {\n\n                    var data = {\n                            \"method\": \"save\",\n                            \"path\": path,\n                            \"data\": editor.getValue()\n                        };\n\n                    do_ajax(\"post\", \"/api/v1/case/\", data, do_nop);\n\n                });\n                editor.on(\"keypress\", function(cm, event)\n                {\n                    cm.showHint({hint: CodeMirror.hint.robotScript});\n                });\n                editor.markText({line: 6, ch: 26}, {line: 6, ch: 42}, {className: \"styled-background\"});\n\n                do_ajax(\"get\", \"/api/v1/case/\", {\"path\": path}, do_init);\n                //editor.setValue(\"*** Settings ***\\n\\n\\n*** Variables ***\\n\\n\\n*** Test Cases ***\\n\\n\\n*** Keywords ***\\n\\n\");\n            });\n\n            function onKwDblClick(node) {\n                var category = node.attributes.category;\n                if(category == \"keyword\"){\n                    editor.replaceSelection(node.attributes.keyword + node.attributes.params);\n                }\n            }\n\n            function onClick(node){\n                var category = node.attributes.category;\n\n                if(category == \"keyword\"){\n                    $(\"#keyword_help\").html(node.attributes.doc);\n                }\n            }\n\n            function newTab(cm){\n                /*if (cm.somethingSelected()) {\n                    cm.indentSelection('add');\n                } else {\n                    cm.replaceSelection(cm.getOption) ? \"\\t\" : Array(cm.getOption(\"indentUnit\") + 1).join(\" \"), \"end\", \"+input\");\n                }*/\n            }\n\n        </SCRIPT>\n    </body>\n</html>"
  },
  {
    "path": "auto/www/templates/login.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <title>AutoLink - 开源优测自动化测试集成解决方案</title>\n\n        <meta name=\"keywords\" content=\"AutoLink, 开源优测, AutoLine, 苦叶子, web ide\"/>\n        <meta name=\"description\" content=\"A Web-based IDE for Auto Testing using Auto Open Source Testing Framework, do your development anytime, anywhere.\"/>\n        <meta name=\"author\" content=\"苦叶子\">\n\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/base.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/sign.css') }}\">\n        <link rel=\"icon\" type=\"image/x-icon\" href=\"{{ url_for('static', filename='favicon.ico') }}\" />\n    </head>\n    <body>\n        <div class=\"header\">\n            <div class=\"wrapper fn-clear\">\n                <a href=\"/\" rel=\"login\">\n                    <img title=\"AutoLink, A Web-based IDE for Auto Testing using Auto Open Source Testing Framework.\" src=\"{{ url_for('static', filename='img/logo.png') }}\"\n                         class=\"logo\"/></a>\n                <ul class=\"fn-right\">\n                    <li><a href=\"http://weixin.sogou.com/weixin?type=1&s_from=input&query=%E5%BC%80%E6%BA%90%E4%BC%98%E6%B5%8B&ie=utf8&_sug_=n&_sug_type_=\" target=\"_blank\" style=\"color: #cd504a\">公众号</a></li>\n                    <li><a rel=\"help\" href=\"#\" target=\"_blank\">读书会</a></li>\n                    <li><a href=\"https://github.com/small99\" target=\"_blank\">GitHub</a></li>\n                    <li><a href=\"https://gitee.com/lym51\" target=\"_blank\">码云</a></li>\n                </ul>\n            </div>\n        </div>\n        <div class=\"content\">\n            <div class=\"wrapper fn-clear\">\n                <div class=\"fn-left\">\n                    <h2>All in AutoLink</h2>\n                    <h3>Auto Testing with Open Source Framework on the AutoLink way.</h3>\n                </div>\n                <div class=\"form fn-right\">\n                    <div id=\"msg\" class=\"fn-none\"></div>\n                    <form id=\"login_fm\" action=\"/api/v1/auth/\" method=\"post\">\n                        <input id=\"username\" name=\"username\" placeholder=\"用户名\"/><br/>\n                        <input id=\"password\" name=\"password\" type=\"password\" placeholder=\"密码\"/><br/>\n                        <input class=\"btn-white btn\" type=\"button\" value=\"登录\" onclick=\"do_login('login_fm')\"></input>\n                    </form>\n                </div>\n            </div>\n        </div>\n        <div class=\"footer\">\n            <span class=\"wrapper\">\n                &copy;  苦叶子\n            </span>\n        </div>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.easyui.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='js/auto.js') }}\"></script>\n        <script>\n            $(function () {\n                $(\"body\").keydown(function(event) {\n                    if (event.keyCode == \"13\") {//keyCode=13是回车键\n                        if($(\"#username\").val() == \"\"){\n                            $(\"#username\").focus();\n                        }\n                        else if($(\"#password\").val() == \"\"){\n                            $(\"#password\").focus();\n                        }\n                        else{\n                            do_login('login_fm');\n                        }\n                    }\n                });\n            });\n        </script>\n    </body>\n</html>"
  },
  {
    "path": "auto/www/templates/report.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <title>AutoLink - 开源优测自动化测试集成解决方案</title>\n\n        <meta name=\"keywords\" content=\"AutoLink, 开源优测, 苦叶子, web ide\"/>\n        <meta name=\"description\" content=\"A Web-based IDE for Auto Testing using Auto Open Source Testing Framework, do your development anytime, anywhere.\"/>\n        <meta name=\"author\" content=\"苦叶子\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/icon.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/bootstrap/easyui.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/lib/codemirror.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/addon/hint/show-hint.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/theme/dracula.css') }}\">\n        <!--<link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldgutter.css') }}\">-->\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/auto.css') }}\">\n\n        <link rel=\"icon\" type=\"image/x-icon\" href=\"{{ url_for('static', filename='favicon.ico') }}\" />\n        <style type=\"text/css\">\n            .lines-no .datagrid-body td{\n                border-right:1px dotted transparent;\n                border-bottom:1px dotted transparent;\n            }\n        </style>\n    </head>\n    <body class=\"easyui-layout\" style=\"padding:10\">\n\n        <div data-options=\"region:'center'\" style=\"padding: 5px 5px 5px 5px\">\n            <table id=\"task_scheduler\" class=\"easyui-datagrid\" style=\"width:100%;height:auto;\"\n                    data-options=\"singleSelect:true,\n                        fitColumns:true,\n                        url:'/api/v1/task_list/?method=query',\n                        method:'post',\n                        toolbar: toolbar\"\n                    >\n                <thead>\n                    <tr>\n                        <!--<th data-options=\"field:'status',align:'center' , halign: 'center',formatter:show_img\">状态</th>-->\n                        <th data-options=\"field:'name'\">名称</th>\n                        <!--<th data-options=\"field:'last_success',align:'center' , halign: 'center'\">上次成功</th>\n                        <th data-options=\"field:'last_fail',align:'center' , halign: 'center'\">上次失败</th>\n                        <th data-options=\"field:'duration'\">持续时间</th>\n                        <th data-options=\"field:'enable',align:'center' , halign: 'center'\">是否启用调度</th>-->\n                        <th data-options=\"field:'next_time',align:'center' , halign: 'center'\">下一次调度时间</th>\n                        <th data-options=\"field:'cron',align:'center' , halign: 'center'\">cron表达式</th>\n                        <!--<th data-options=\"field:'boolean'\"></th>-->\n                    </tr>\n                </thead>\n            </table>\n        </div>\n        <!-- edit project cron -->\n        <div id=\"cron_win\" class=\"easyui-window\" title=\"编辑Cron表达式\"\n             data-options=\"iconCls:'icon-edit',collapsible:false,minimizable:false,maximizable:false\"\n             style=\"width:400px;height:140px;padding:10px;\">\n            <form id=\"cron_ff\" method=\"post\">\n\n                <!--\n                <div style=\"margin-bottom:10px\">\n                    <label for=\"enable\" class=\"textbox-label\">启用:</label>\n                    <input id=\"enable\" type=\"checkbox\" name=\"enable\" value=\"true\" checked>\n                </div>\n                -->\n                <div style=\"margin-bottom:20px\">\n                    <select class=\"easyui-combobox\" name=\"cron\" id=\"cron\" label=\"Cron表达式\" style=\"width:100%\">\n                        <option value=\"* * * * * *\">默认无调度(* * * * * *)</option>\n                        <option value=\"0 0 1 * * *\">每天凌晨1点执行一次(0 0 1 * * *)</option>\n                        <option value=\"0 0 12 * * *\">每天12点调度一次(0 0 12 * * *)</option>\n                        <option value=\"0 0 23 * * *\">每天23点调度一次(0 0 23 * * *)</option>\n                        <option value=\"0 0/30 * * * *\">每隔30分钟调度一次(0 0/5 * * * *)</option>\n                        <option value=\"0 0 0/1 0 0 6/7\">周末每小时调度一次(0 0 0/1 0 0 6/7)</option>\n                        <option value=\"0 0 0/1 0 0 1/5\">工作日每小时调度一次(0 0 0/1 0 0 1/5)</option>\n                    </select>\n                </div>\n                <div style=\"text-align:right;margin-bottom:10px\">\n                    <a href=\"#\" class=\"easyui-linkbutton\" style=\"width:80px;height:32px\" onclick=\"do_edit();\">更新</a>\n                </div>\n            </form>\n        </div>\n        <!-- -->\n\n\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.easyui.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/lib/codemirror.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/mode/robot/robot.js') }}\"></script>\n        <!--<script src=\"{{ url_for('static', filename='lib/CodeMirror/mode/textile/textile.js') }}\"></script>>-->\n\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/display/placeholder.js') }}\"></script>\n        <!--\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldcode.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldgutter.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/brace-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/xml-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/indent-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/markdown-fold.js') }}\"></script>-->\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror//addon/hint/show-hint.js') }}\"></script>\n        <!--<script src=\"{{ url_for('static', filename='lib/CodeMirror//addon/hint/anyword-hint.js') }}\"></script>-->\n        <!-- 自定义js库 -->\n        <script type=\"text/javascript\" src=\"{{ url_for('static', filename='js/auto.js') }}\"></script>\n        <SCRIPT type=\"text/javascript\">\n            $(document).ready(function(){\n               $('#task_scheduler').datagrid('getPanel').removeClass('lines-both lines-no lines-right lines-bottom').addClass('lines-no');\n               $(\"#cron_win\").window('close');\n            });\n            var toolbar = [\n                {\n                    text:'刷新',\n                    iconCls:'icon-refresh',\n                    handler:function(){\n                        $(\"#task_scheduler\").datagrid('reload');\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'启动',\n                    iconCls:'icon-run',\n                    handler:function(){\n                        var row = $('#task_scheduler').datagrid('getSelected');\n                        if(row){\n                            var data ={\"method\": \"start\", \"name\": \"{0}\".lym_format(row.name), \"cron\": \"{0}\".lym_format(row.cron) };\n                            do_ajax('post',\n                                '/api/v1/task_list/',\n                                data,\n                                do_msg);\n\n                            $(\"#task_scheduler\").datagrid('reload');\n                        }\n                        else{\n                            show_msg(\"提示信息\", \"请选择要启动调度的项目\");\n                        }\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'停止',\n                    iconCls:'icon-stop',\n                    handler:function(){\n                        $.messager.confirm('提示信息', '现在停止无法生成报告，确定停止?', function(r){\n                            if (r){\n                                var row = $('#task_scheduler').datagrid('getSelected');\n                                if(row){\n                                    var data ={\"method\": \"stop\", \"name\": \"{0}\".lym_format(row.name) };\n                                    do_ajax('post',\n                                        '/api/v1/task_list/',\n                                        data,\n                                        do_msg);\n                                    $(\"#task_scheduler\").datagrid('reload');\n                                }\n                                else{\n                                    show_msg(\"提示信息\", \"请选择要停止调度的项目\");\n                                }\n                            }\n                        });\n\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'编辑',\n                    iconCls:'icon-edit',\n                    handler:function(){\n                        var row = $('#task_scheduler').datagrid('getSelected');\n                        if(row){\n                            open_win('cron_win');\n                        }\n                        else{\n                            show_msg(\"提示信息\", \"请选择要编辑的项目\");\n                        }\n                    }\n\t\t\t    }\n\t\t\t ];\n\n\t\t\tfunction do_edit(){\n\t\t\t    var row = $('#task_scheduler').datagrid('getSelected');\n\t\t\t    var data = {\"name\": row[\"name\"], \"method\": \"edit\", \"cron\": $(\"#cron\").combobox(\"getValue\")};\n\t\t\t    do_ajax('post',\n                    '/api/v1/task_list/',\n                    data,\n                    do_msg);\n                $(\"#task_scheduler\").datagrid('reload');\n                close_win(\"cron_win\");\n\t\t\t}\n        </SCRIPT>\n    </body>\n</html>"
  },
  {
    "path": "auto/www/templates/scheduler.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <title>AutoLink - 开源优测自动化测试集成解决方案</title>\n\n        <meta name=\"keywords\" content=\"AutoLink, 开源优测, 苦叶子, web ide\"/>\n        <meta name=\"description\" content=\"A Web-based IDE for Auto Testing using Auto Open Source Testing Framework, do your development anytime, anywhere.\"/>\n        <meta name=\"author\" content=\"苦叶子\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/icon.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/bootstrap/easyui.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/lib/codemirror.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/addon/hint/show-hint.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/theme/dracula.css') }}\">\n        <!--<link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldgutter.css') }}\">-->\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/auto.css') }}\">\n\n        <link rel=\"icon\" type=\"image/x-icon\" href=\"{{ url_for('static', filename='favicon.ico') }}\" />\n        <style type=\"text/css\">\n            .lines-no .datagrid-body td{\n                border-right:1px dotted transparent;\n                border-bottom:1px dotted transparent;\n            }\n        </style>\n    </head>\n    <body class=\"easyui-layout\" style=\"padding:10\">\n\n        <div data-options=\"region:'center'\" style=\"padding: 5px 5px 5px 5px\">\n            <table id=\"task_scheduler\" class=\"easyui-datagrid\" style=\"width:100%;height:auto;\"\n                    data-options=\"singleSelect:true,\n                        fitColumns:true,\n                        url:'/api/v1/task_list/?method=query',\n                        method:'post',\n                        toolbar: toolbar\"\n                    >\n                <thead>\n                    <tr>\n                        <!--<th data-options=\"field:'status',align:'center' , halign: 'center',formatter:show_img\">状态</th>-->\n                        <th data-options=\"field:'status',align:'center' , halign: 'center',formatter:do_view_status\"></th>\n                        <th data-options=\"field:'name'\">名称</th>\n                        <!--<th data-options=\"field:'last_success',align:'center' , halign: 'center'\">上次成功</th>\n                        <th data-options=\"field:'last_fail',align:'center' , halign: 'center'\">上次失败</th>\n                        <th data-options=\"field:'duration'\">持续时间</th>\n                        <th data-options=\"field:'enable',align:'center' , halign: 'center'\">是否启用调度</th>-->\n                        <th data-options=\"field:'next_time',align:'center' , halign: 'center'\">下一次调度时间</th>\n                        <th data-options=\"field:'cron',align:'center' , halign: 'center'\">cron表达式</th>\n                        <th data-options=\"field:'task',align:'center' , halign: 'center',formatter:do_view_task\">查看任务</th>\n\n                        <!--<th data-options=\"field:'boolean'\"></th>-->\n                    </tr>\n                </thead>\n            </table>\n        </div>\n        <!-- edit project cron -->\n        <div id=\"cron_win\" class=\"easyui-window\" title=\"编辑Cron表达式\"\n             data-options=\"iconCls:'icon-edit',collapsible:false,minimizable:false,maximizable:false\"\n             style=\"width:400px;height:140px;padding:10px;\">\n            <form id=\"cron_ff\" method=\"post\">\n\n                <!--\n                <div style=\"margin-bottom:10px\">\n                    <label for=\"enable\" class=\"textbox-label\">启用:</label>\n                    <input id=\"enable\" type=\"checkbox\" name=\"enable\" value=\"true\" checked>\n                </div>\n                -->\n                <div style=\"margin-bottom:20px\">\n                    <select class=\"easyui-combobox\" name=\"cron\" id=\"cron\" label=\"Cron表达式\" style=\"width:100%\">\n                        <option value=\"* * * * * *\">默认无调度(* * * * * *)</option>\n                        <option value=\"0 0 1 * * *\">每天凌晨1点执行一次(0 0 1 * * *)</option>\n                        <option value=\"0 0 12 * * *\">每天12点调度一次(0 0 12 * * *)</option>\n                        <option value=\"0 0 23 * * *\">每天23点调度一次(0 0 23 * * *)</option>\n                        <option value=\"0 0/30 * * * *\">每隔30分钟调度一次(0 0/30 * * * *)</option>\n                        <option value=\"0 0 0/1 0 0 6/7\">周末每小时调度一次(0 0 0/1 0 0 6/7)</option>\n                        <option value=\"0 0 0/1 0 0 1/5\">工作日每小时调度一次(0 0 0/1 0 0 1/5)</option>\n                    </select>\n                </div>\n                <div style=\"text-align:right;margin-bottom:10px\">\n                    <a href=\"#\" class=\"easyui-linkbutton\" style=\"width:80px;height:32px\" onclick=\"do_edit();\">更新</a>\n                </div>\n            </form>\n        </div>\n        <!-- -->\n        <!-- edit project notifys -->\n        <div id=\"notify_win\" class=\"easyui-window\" title=\"邮件通知\"\n             data-options=\"iconCls:'icon-notification',collapsible:false,minimizable:false,maximizable:false\"\n             style=\"width:400px;height:210px;padding:10px;\">\n            <form id=\"notify_ff\" method=\"post\">\n\n                <div style=\"margin-bottom:10px\">\n                    <input class=\"easyui-textbox\" id=\"success_list\" name=\"success_list\" label=\"成功列表\" labelPosition=\"left\" style=\"width:100%;height:50px;\" data-options=\"multiline:true\">\n                </div>\n                <div style=\"margin-bottom:10px\">\n                    <input class=\"easyui-textbox\" id=\"fail_list\" name=\"fail_list\" label=\"失败列表\" labelPosition=\"left\" style=\"width:100%;height:50px;\" data-options=\"multiline:true\">\n                </div>\n\n                <div style=\"text-align:right;margin-bottom:10px\">\n                    <a href=\"#\" class=\"easyui-linkbutton\" style=\"width:80px;height:32px\" onclick=\"do_notify_edit();\">保存</a>\n                </div>\n            </form>\n        </div>\n        <!-- -->\n\n\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.easyui.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/lib/codemirror.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/mode/robot/robot.js') }}\"></script>\n        <!--<script src=\"{{ url_for('static', filename='lib/CodeMirror/mode/textile/textile.js') }}\"></script>>-->\n\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/display/placeholder.js') }}\"></script>\n        <!--\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldcode.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldgutter.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/brace-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/xml-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/indent-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/markdown-fold.js') }}\"></script>-->\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror//addon/hint/show-hint.js') }}\"></script>\n        <!--<script src=\"{{ url_for('static', filename='lib/CodeMirror//addon/hint/anyword-hint.js') }}\"></script>-->\n        <!-- 自定义js库 -->\n        <script type=\"text/javascript\" src=\"{{ url_for('static', filename='js/auto.js') }}\"></script>\n        <SCRIPT type=\"text/javascript\">\n            $(document).ready(function(){\n               $('#task_scheduler').datagrid('getPanel').removeClass('lines-both lines-no lines-right lines-bottom').addClass('lines-no');\n               $(\"#cron_win\").window('close');\n               $(\"#notify_win\").window('close');\n            });\n            var toolbar = [\n                {\n                    text:'刷新',\n                    iconCls:'icon-refresh',\n                    handler:function(){\n                        $(\"#task_scheduler\").datagrid('reload');\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'启动',\n                    iconCls:'icon-run',\n                    handler:function(){\n                        var row = $('#task_scheduler').datagrid('getSelected');\n                        if(row){\n                            var data ={\"method\": \"start\", \"name\": \"{0}\".lym_format(row.name), \"cron\": \"{0}\".lym_format(row.cron) };\n                            do_ajax('post',\n                                '/api/v1/task_list/',\n                                data,\n                                do_msg);\n\n                            $(\"#task_scheduler\").datagrid('reload');\n                        }\n                        else{\n                            show_msg(\"提示信息\", \"请选择要启动调度的项目\");\n                        }\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'停止',\n                    iconCls:'icon-stop',\n                    handler:function(){\n                        $.messager.confirm('提示信息', '现在停止无法生成报告，确定停止?', function(r){\n                            if (r){\n                                var row = $('#task_scheduler').datagrid('getSelected');\n                                if(row){\n                                    var data ={\"method\": \"stop\", \"name\": \"{0}\".lym_format(row.name) };\n                                    do_ajax('post',\n                                        '/api/v1/task_list/',\n                                        data,\n                                        do_msg);\n                                    $(\"#task_scheduler\").datagrid('reload');\n                                }\n                                else{\n                                    show_msg(\"提示信息\", \"请选择要停止调度的项目\");\n                                }\n                            }\n                        });\n\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'编辑',\n                    iconCls:'icon-edit',\n                    handler:function(){\n                        var row = $('#task_scheduler').datagrid('getSelected');\n                        if(row){\n                            open_win('cron_win');\n                        }\n                        else{\n                            show_msg(\"提示信息\", \"请选择要编辑的项目\");\n                        }\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'通知',\n                    iconCls:'icon-notification',\n                    handler:function(){\n                        var row = $('#task_scheduler').datagrid('getSelected');\n                        if(row){\n                            init_email_ff(row[\"name\"]);\n                            open_win(\"notify_win\");\n                        }\n                        else{\n                            show_msg(\"提示信息\", \"请选择项目以便编辑其通知\");\n                        }\n                    }\n\t\t\t    }\n\t\t\t ];\n\n\t\t\tfunction do_edit(){\n\t\t\t    var row = $('#task_scheduler').datagrid('getSelected');\n\t\t\t    var data = {\"name\": row[\"name\"], \"method\": \"edit\", \"cron\": $(\"#cron\").combobox(\"getValue\")};\n\t\t\t    do_ajax('post',\n                    '/api/v1/task_list/',\n                    data,\n                    do_msg);\n                $(\"#task_scheduler\").datagrid('reload');\n                close_win(\"cron_win\");\n\t\t\t}\n\n\t\t\tfunction do_notify_edit(){\n\t\t\t    var row = $('#task_scheduler').datagrid('getSelected');\n\t\t\t    var data = {\n\t\t\t        \"method\": \"email\",\n\t\t\t        \"project\": row[\"name\"],\n\t\t\t        \"success_list\": $(\"#notify_ff input#success_list\").textbox('getValue'),\n\t\t\t        \"fail_list\": $(\"#notify_ff input#fail_list\").textbox('getValue'),\n\t\t\t    };\n\t\t\t    do_ajax('post',\n                    '/api/v1/settings/',\n                    data,\n                    do_msg);\n                close_win(\"notify_win\");\n\t\t\t}\n\n\t\t\tfunction do_view_task(value, row, index){\n                return '<a href=\"#\" onclick=\"parent.addTab(\\'{0}\\', \\'/task_list/{1}\\', \\'icon-task\\');\">查看</a>'.lym_format(row.name, row.name);\n            }\n\n            function do_view_status(value, row, index){\n                return '<img width=\"24px\" height=\"24px\" border=\"0\" src=\"{0}\"/>'.lym_format(row.status) ;\n            }\n        </SCRIPT>\n    </body>\n</html>"
  },
  {
    "path": "auto/www/templates/task_list.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <title>AutoLink - 开源优测自动化测试集成解决方案</title>\n\n        <meta name=\"keywords\" content=\"AutoLink, 开源优测, 苦叶子, web ide\"/>\n        <meta name=\"description\" content=\"A Web-based IDE for Auto Testing using Auto Open Source Testing Framework, do your development anytime, anywhere.\"/>\n        <meta name=\"author\" content=\"苦叶子\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/icon.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/bootstrap/easyui.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/lib/codemirror.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/addon/hint/show-hint.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/theme/dracula.css') }}\">\n        <!--<link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldgutter.css') }}\">-->\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/auto.css') }}\">\n\n        <link rel=\"icon\" type=\"image/x-icon\" href=\"{{ url_for('static', filename='favicon.ico') }}\" />\n        <style type=\"text/css\">\n            .lines-no .datagrid-body td{\n                border-right:1px dotted transparent;\n                border-bottom:1px dotted transparent;\n            }\n        </style>\n    </head>\n    <body class=\"easyui-layout\" style=\"padding:10\">\n\n        <div data-options=\"region:'center'\" style=\"padding: 5px 5px 5px 5px\">\n            <table id=\"task_list\" class=\"easyui-datagrid\" style=\"width:100%;height:auto;\"\n                    data-options=\"singleSelect:true,\n                        fitColumns:true,\n                        url:'/api/v1/task_list?name={{ project }}',\n                        method:'get',\n                        toolbar: toolbar\"\n                    >\n                <thead>\n                    <tr>\n                        <th data-options=\"field:'status',align:'center' , halign: 'center',formatter:show_img\">状态</th>\n                        <th data-options=\"field:'name'\">名称</th>\n                        <th data-options=\"field:'success',align:'center' , halign: 'center'\">成功</th>\n                        <th data-options=\"field:'fail',align:'center' , halign: 'center'\">失败</th>\n                        <th data-options=\"field:'starttime',align:'center' , halign: 'center'\">开始时间</th>\n                        <th data-options=\"field:'endtime',align:'center' , halign: 'center'\">结束时间</th>\n                        <th data-options=\"field:'elapsedtime',align:'center' , halign: 'center'\">持续时间(ms)</th>\n                        <th data-options=\"field:'note',align:'center' , halign: 'center'\">备注</th>\n                        <!--<th data-options=\"field:'cron'\">cron表达式</th>\n                        <th data-options=\"field:'boolean'\"></th>-->\n                    </tr>\n                </thead>\n            </table>\n        </div>\n\n\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.easyui.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/lib/codemirror.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/mode/robot/robot.js') }}\"></script>\n        <!--<script src=\"{{ url_for('static', filename='lib/CodeMirror/mode/textile/textile.js') }}\"></script>>-->\n\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/display/placeholder.js') }}\"></script>\n        <!--\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldcode.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldgutter.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/brace-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/xml-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/indent-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/markdown-fold.js') }}\"></script>-->\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror//addon/hint/show-hint.js') }}\"></script>\n        <!--<script src=\"{{ url_for('static', filename='lib/CodeMirror//addon/hint/anyword-hint.js') }}\"></script>-->\n        <!-- 自定义js库 -->\n        <script type=\"text/javascript\" src=\"{{ url_for('static', filename='js/auto.js') }}\"></script>\n        <SCRIPT type=\"text/javascript\">\n            $(document).ready(function(){\n               $('#task_list').datagrid('getPanel').removeClass('lines-both lines-no lines-right lines-bottom').addClass('lines-no');\n            });\n            var toolbar = [\n                {\n                    text:'刷新',\n                    iconCls:'icon-refresh',\n                    handler:function(){\n                        $(\"#task_list\").datagrid('reload');\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'运行',\n                    iconCls:'icon-run',\n                    handler:function(){\n                        var data ={\"method\": \"run\", \"category\": \"project\", \"project\": \"{{ project }}\" };\n                        do_ajax('post',\n                            '/api/v1/task/',\n                            data,\n                            do_msg);\n\n                        $(\"#task_list\").datagrid('reload');\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'停止',\n                    iconCls:'icon-stop',\n                    handler:function(){\n                        $.messager.confirm('提示信息', '现在停止无法生成报告，确定停止?', function(r){\n                            if (r){\n                                var data ={\"method\": \"stop\", \"category\": \"project\", \"project\": \"{{ project }}\" };\n                                do_ajax('post',\n                                    '/api/v1/task/',\n                                    data,\n                                    do_msg);\n\n                                $(\"#task_list\").datagrid('reload');\n                            }\n                        });\n\n                    }\n\t\t\t    }\n\t\t\t    , '-',\n\t\t\t    {\n\t\t\t        text:'删除',\n                    iconCls:'icon-remove',\n                    handler:function(){\n                        $.messager.confirm('提示信息', '确定删除该记录?', function(r){\n                            if (r){\n                                var row = $('#task_list').datagrid('getSelected');\n                                if(row){\n                                    var data ={\"method\": \"delete\", \"category\": \"project\", \"project\": \"{{ project }}\", \"task_no\": row.task_no };\n                                    do_ajax('post',\n                                        '/api/v1/task/',\n                                        data,\n                                        do_msg);\n                                }\n                                else{\n                                    show_msg(\"提示信息\", \"请先选中要删除的记录\");\n                                }\n\n                                $(\"#task_list\").datagrid('reload');\n                            }\n                        });\n\n                    }\n\t\t\t    }\n\t\t\t ];\n        </SCRIPT>\n    </body>\n</html>"
  },
  {
    "path": "auto/www/templates/user.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <title>AutoLink - 开源优测自动化测试集成解决方案</title>\n\n        <meta name=\"keywords\" content=\"AutoLink, 开源优测, 苦叶子, web ide\"/>\n        <meta name=\"description\" content=\"A Web-based IDE for Auto Testing using Auto Open Source Testing Framework, do your development anytime, anywhere.\"/>\n        <meta name=\"author\" content=\"苦叶子\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/icon.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/bootstrap/easyui.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/lib/codemirror.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/addon/hint/show-hint.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/theme/dracula.css') }}\">\n        <!--<link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldgutter.css') }}\">-->\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/auto.css') }}\">\n\n        <link rel=\"icon\" type=\"image/x-icon\" href=\"{{ url_for('static', filename='favicon.ico') }}\" />\n        <style type=\"text/css\">\n            .lines-no .datagrid-body td{\n                border-right:1px dotted transparent;\n                border-bottom:1px dotted transparent;\n            }\n        </style>\n    </head>\n    <body class=\"easyui-layout\" style=\"padding:10\">\n\n        <div data-options=\"region:'center'\" style=\"padding: 5px 5px 5px 5px\">\n            <table id=\"user_list\" class=\"easyui-datagrid\" style=\"width:100%;height:auto;\"\n                    data-options=\"singleSelect:true,\n                        fitColumns:true,\n                        url:'/api/v1/user/',\n                        method:'get',\n                        toolbar: toolbar\"\n                    >\n                <thead>\n                    <tr>\n                        <th data-options=\"field:'name',align:'center' , halign: 'center'\">用户名</th>\n                        <th data-options=\"field:'fullname',align:'center' , halign: 'center'\">昵称</th>\n                        <th data-options=\"field:'email',align:'center' , halign: 'center'\">Email</th>\n                        <th data-options=\"field:'category',align:'center' , halign: 'center'\">类型</th>\n                        <!--<th data-options=\"field:'duration'\">持续时间</th>-->\n                        <!--<th data-options=\"field:'cron'\">cron表达式</th>\n                        <th data-options=\"field:'boolean'\"></th>-->\n                    </tr>\n                </thead>\n            </table>\n            <!-- create user -->\n            <div id=\"create_user\" class=\"easyui-window\" title=\"创建用户\"\n                 data-options=\"modal:true,closed:true,minimizable:false,maximizable:false,collapsible:false\"\n                 style=\"width:320px;height:270px;padding:10px;\">\n                <form id=\"create_user_ff\" method=\"post\">\n                    <div style=\"margin-bottom:10px\">\n                        <input class=\"easyui-textbox\" id=\"fullname\" name=\"fullname\" label=\"昵称\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\">\n                    </div>\n                    <div style=\"margin-bottom:10px\">\n                        <input class=\"easyui-textbox\" id=\"username\" name=\"username\" label=\"用户名\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\">\n                    </div>\n                    <div style=\"margin-bottom:10px\">\n                        <input class=\"easyui-textbox\" id=\"email\" name=\"email\" label=\"邮件\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\">\n                    </div>\n                    <div style=\"margin-bottom:20px\">\n                        <input class=\"easyui-textbox\" id=\"password\" name=\"password\" label=\"密码\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\">\n                    </div>\n                </form>\n                <div style=\"text-align:right;padding:5px 0\">\n                    <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"close_win('create_user')\" style=\"width:60px\">取消</a>\n                    <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"create_user('create_user', 'create_user_ff')\" style=\"width:60px\">创建</a>\n                </div>\n            </div>\n            <!-- end -->\n            <!-- edit user -->\n            <div id=\"edit_user\" class=\"easyui-window\" title=\"编辑用户\"\n                 data-options=\"modal:true,closed:true,minimizable:false,maximizable:false,collapsible:false\"\n                 style=\"width:320px;height:300px;padding:10px;\">\n                <form id=\"edit_user_ff\" method=\"post\">\n                    <div style=\"margin-bottom:10px\">\n                        <input class=\"easyui-textbox\" id=\"fullname\" name=\"fullname\" label=\"昵称\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\">\n                    </div>\n                    <div style=\"margin-bottom:10px\">\n                        <input class=\"easyui-textbox\" id=\"username\" name=\"username\" label=\"用户名\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\" readonly>\n                    </div>\n                    <div style=\"margin-bottom:10px\">\n                        <input class=\"easyui-textbox\" id=\"email\" name=\"email\" label=\"邮件\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\">\n                    </div>\n                    <div style=\"margin-bottom:10px\">\n                        <input class=\"easyui-textbox\" id=\"password\" name=\"password\" label=\"原密码\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\">\n                    </div>\n                    <div style=\"margin-bottom:15px\">\n                        <input class=\"easyui-textbox\" id=\"new_password\" name=\"new_password\" label=\"新密码\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\">\n                    </div>\n                </form>\n                <div style=\"text-align:right;padding:5px 0\">\n                    <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"close_win('edit_user')\" style=\"width:60px\">取消</a>\n                    <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"edit_user('edit_user', 'edit_user_ff')\" style=\"width:60px\">保存</a>\n                </div>\n            </div>\n            <!-- end -->\n            <!-- edit smtp -->\n            <div id=\"edit_smtp\" class=\"easyui-window\" title=\"配置SMTP\"\n                 data-options=\"modal:true,closed:true,minimizable:false,maximizable:false,collapsible:false\"\n                 style=\"width:320px;height:290px;padding:10px;\">\n                <form id=\"edit_smtp_ff\" method=\"post\">\n                    <div style=\"margin-bottom:10px\">\n                        <input type=\"checkbox\" id=\"ssl\" name=\"ssl\" checked>\n                        <span>启用SSL</span>\n                    </div>\n                    <div style=\"margin-bottom:10px\">\n                        <input class=\"easyui-textbox\" id=\"server\" name=\"server\" label=\"服务器\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\">\n                    </div>\n                    <div style=\"margin-bottom:10px\">\n                        <input class=\"easyui-textbox\" id=\"port\" name=\"port\" label=\"端口\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\">\n                    </div>\n                    <div style=\"margin-bottom:10px\">\n                        <input class=\"easyui-textbox\" id=\"username\" name=\"username\" label=\"用户名\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\">\n                    </div>\n                    <div style=\"margin-bottom:10px\">\n                        <input class=\"easyui-textbox\" id=\"password\" name=\"password\" label=\"密码\" labelPosition=\"left\" style=\"width:100%\" data-options=\"required:true\">\n                    </div>\n                </form>\n                <div style=\"text-align:right;padding:5px 0\">\n                    <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"close_win('edit_smtp')\" style=\"width:60px\">取消</a>\n                    <a href=\"javascript:void(0)\" class=\"easyui-linkbutton\" onclick=\"do_smtp('edit_smtp', 'edit_smtp_ff')\" style=\"width:60px\">保存</a>\n                </div>\n            </div>\n            <!-- end -->\n        </div>\n\n\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.easyui.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/lib/codemirror.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/mode/robot/robot.js') }}\"></script>\n        <!--<script src=\"{{ url_for('static', filename='lib/CodeMirror/mode/textile/textile.js') }}\"></script>>-->\n\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/display/placeholder.js') }}\"></script>\n        <!--\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldcode.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldgutter.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/brace-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/xml-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/indent-fold.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/markdown-fold.js') }}\"></script>-->\n        <script src=\"{{ url_for('static', filename='lib/CodeMirror//addon/hint/show-hint.js') }}\"></script>\n        <!--<script src=\"{{ url_for('static', filename='lib/CodeMirror//addon/hint/anyword-hint.js') }}\"></script>-->\n        <!-- 自定义js库 -->\n        <script type=\"text/javascript\" src=\"{{ url_for('static', filename='js/auto.js') }}\"></script>\n        <SCRIPT type=\"text/javascript\">\n            $(document).ready(function(){\n                //$('#edit_smtp_ff').form('load','/api/v1/settings?method=smtp');\n\n            });\n            var toolbar = [\n                {\n                    text:'刷新',\n                    iconCls:'icon-refresh',\n                    handler:function(){\n                        $(\"#user_list\").datagrid('reload');\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'新增',\n                    iconCls:'icon-expand',\n                    handler:function(){\n                        manage_user(\"create_user\", \"create_user_ff\", \"create\");\n                        /*var data ={\"method\": \"run\", \"category\": \"project\", \"project\": \"{{ project }}\" };\n                        do_ajax('post',\n                            '/api/v1/task/',\n                            data,\n                            do_msg);\n\n                        $(\"#user_list\").datagrid('reload');\n                        */\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'编辑',\n                    iconCls:'icon-edit',\n                    handler:function(){\n                        var row = $('#user_list').datagrid('getSelected');\n                        if(row){\n                            $(\"#edit_user_ff input#fullname\").textbox(\"setValue\", row.fullname);\n                            $(\"#edit_user_ff input#username\").textbox(\"setValue\", row.name);\n                            $(\"#edit_user_ff input#email\").textbox(\"setValue\", row.email);\n                            open_win('edit_user');\n                        }\n                        else{\n                            show_msg(\"提示信息\", \"请选择要编辑的项目\");\n                        }\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'删除',\n                    iconCls:'icon-remove',\n                    handler:function(){\n                        var row = $('#user_list').datagrid('getSelected');\n                        if(row){\n                            $.messager.confirm('提示信息', '确定删除该用户?', function(r){\n                                if (r){\n                                    var data ={\"method\": \"delete\", \"username\": \"{0}\".lym_format(row.name) };\n                                    do_ajax('post',\n                                        '/api/v1/user/',\n                                        data,\n                                        do_msg);\n\n                                    $(\"#user_list\").datagrid('reload');\n                                }\n                            });\n                        }\n                    }\n\t\t\t    }, '-',\n\t\t\t    {\n\t\t\t        text:'SMTP',\n                    iconCls:'icon-email',\n                    handler:function(){\n                        init_smtp_ff();\n                        open_win(\"edit_smtp\");\n\n                    }\n\t\t\t    }\n\t\t\t ];\n        </SCRIPT>\n    </body>\n</html>"
  },
  {
    "path": "auto/www/templates/view_img.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <title>AutoLink - 开源优测自动化测试集成解决方案</title>\n\n        <meta name=\"keywords\" content=\"AutoLink, 开源优测, 苦叶子, web ide\"/>\n        <meta name=\"description\" content=\"A Web-based IDE for Auto Testing using Auto Open Source Testing Framework, do your development anytime, anywhere.\"/>\n        <meta name=\"author\" content=\"苦叶子\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/icon.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/bootstrap/easyui.css') }}\">\n\n        <!--<link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/CodeMirror/addon/fold/foldgutter.css') }}\">-->\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/auto.css') }}\">\n\n        <link rel=\"icon\" type=\"image/x-icon\" href=\"{{ url_for('static', filename='favicon.ico') }}\" />\n    </head>\n    <body class=\"easyui-layout\">\n\n\n        <div data-options=\"region:'center'\">\n           <img src=\"/view_img?path=/{{ project }}/{{ suite }}/{{ case }}\">\n        </div>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.easyui.min.js') }}\"></script>\n\n        <!-- 自定义js库 -->\n        <script type=\"text/javascript\" src=\"{{ url_for('static', filename='js/auto.js') }}\"></script>\n    </body>\n</html>"
  },
  {
    "path": "auto/www/templates/welcome.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <title>AutoLink - 开源优测自动化测试集成解决方案</title>\n\n        <meta name=\"keywords\" content=\"AutoLink, 开源优测, 苦叶子, web ide\"/>\n        <meta name=\"description\" content=\"A Web-based IDE for Auto Testing using Auto Open Source Testing Framework, do your development anytime, anywhere.\"/>\n        <meta name=\"author\" content=\"苦叶子\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/bootstrap/easyui.css') }}\">\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='lib/easyui/themes/icon.css') }}\">\n\n        <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/auto.css') }}\">\n\n        <link rel=\"icon\" type=\"image/x-icon\" href=\"{{ url_for('static', filename='favicon.ico') }}\" />\n        <style type=\"text/css\">\n            .lines-no .datagrid-body td{\n                border-right:1px dotted transparent;\n                border-bottom:1px dotted transparent;\n            }\n        </style>\n    </head>\n    <body class=\"easyui-layout\">\n        <div title=\"欢迎页\" style=\"padding:10px\">\n            <p style=\"font-size:14px\">AutoLink开源自动化测试集成解决方案.</p>\n            <ul>\n                <li>AutoLink是RobotFramework的web集成开发环境.</li>\n                <li>AutoLink支持RobotFramework语法高亮，自动提示等功能.</li>\n                <li>AutoLink可以帮助你轻易的构建web自动化测试脚本、HTTP接口自动化测试脚本以及移动自动化测试脚本.</li>\n                <li>AutoLink完美的支持RobotFramework所有的关键字.</li>\n                <li>AutoLink可以直接应用到你的企业实践中，节省框架开发成本.</li>\n                <li>AutoLink是很简单的，但也很容易使用.</li>\n            </ul>\n            <br>\n            <p>苦叶子发起</p>\n            <table>\n                <tr align=\"center\">\n                    <td>\n                        公众号<br><img src=\"{{ url_for('static', filename='img/公众号.jpg') }}\" />\n                    </td>\n                    <td>\n                        读书会<br><img src=\"{{ url_for('static', filename='img/读书会.png') }}\" />\n                    </td>\n                    <td>\n                        开源优测<br><img src=\"{{ url_for('static', filename='img/开源优测.png') }}\" />\n                    </td>\n                    <td>\n                        打赏苦叶子<br><img src=\"{{ url_for('static', filename='img/赞赏码.png') }}\" />\n                    </td>\n                </tr>\n            </table>\n            <br>\n            <p>项目源码</p>\n            <table>\n                <tr align=\"center\">\n                    <td>\n                        <a target=\"_blank\" href=\"https://github.com/small99\">github</a>\n                    </td>\n                    <td>\n                        <a target=\"_blank\" href=\"https://gitee.com/lym51\">码云</a>\n                    </td>\n                </tr>\n            </table>\n        </div>\n\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.min.js') }}\"></script>\n        <script src=\"{{ url_for('static', filename='lib/easyui/jquery.easyui.min.js') }}\"></script>\n        <!-- 自定义js库 -->\n        <script type=\"text/javascript\" src=\"{{ url_for('static', filename='js/auto.js') }}\"></script>\n    </body>\n    <SCRIPT type=\"text/javascript\">\n\n\n        </SCRIPT>\n</html>"
  },
  {
    "path": "docs/README.md",
    "content": "## 阅读地址\n\n- 用户指南是目前AutoLink最新由官方在维护的使用指南\n\n- 如果现在有任何不理解或有歧义的地方，可以在github提交issue指出来，我们会第一时间修订\n\n- 项目源码: [Github源码托管地址](https://github.com/small99/AutoLink)\n\n- 用户指南: [用户指南](https://github.com/small99/AutoLink/blob/master/docs)\n\n## 如何参与和联系我们\n\n\n- 可以关注我的[公众号]，不定期分享开源测试技术\n\n- 加入免费的[读书会]和上千人沟通交流开源测试技术\n\n- 加入付费的[知识星球]与更多的牛人一起交流\n\n- 觉得AutoLink还不错的话，可以打赏一杯咖啡，谢谢\n\n公众号|读书会|知识星球|赞赏码\n---|---|---|---\n![公众号](../auto/www/static/img/公众号.jpg)|![读书会](../auto/www/static/img/读书会.png)|![知识星球](../auto/www/static/img/开源优测.png)|![赞赏码](../auto/www/static/img/赞赏码.png)\n\n## 许可证\n\nAutoLink采用 Apache License 2.0 国际许可协议 进行许可. 传播此文档时请注意遵循以上许可协议. 关于本许可证的更多详情可参考 http://www.apache.org/licenses/LICENSE-2.0\n\n## 贡献者列表\n成员|email|github/博客\n---|---|---\n苦叶子|lymking@foxmail.com|https://github.com/small99\n乐大爷|cquchenle@foxmail.com|https://github.com/catleer\nJay.lee|xhidexj@gmail.com|https://github.com/hatjay/\n\n## 指南目录\n\n- [安装与启动](./安装与启动.md)\n- [如何创建测试项目](./如何创建测试项目.md)\n- [如何运行测试项目](./如何运行测试项目.md)\n- [如何管理用例顺序](./如何管理测试项目中用例顺序.md)\n- [使用关键字快捷键](./如何使用自动提示快捷输入关键字.md)\n- [关键字概要说明](./关键字概要说明.md)\n- [如何使用调度管理](./如何使用调度管理.md)\n- [如何创建HTTP接口测试用例](./如何创建HTTP接口测试用例.md)\n- [上传和下载RobotFramework用例](./上传和下载RobotFramework用例.md)\n- [Python自定义关键字](./如何调用Python自定义库.md)\n- [如何查看关键字详细文档](./如何查看关键字详细文档.md)\n- [查看测试报告](./查看测试报告.md)"
  },
  {
    "path": "docs/上传和下载RobotFramework用例.md",
    "content": "## 简介\n\n在AutoLink中，支持文件的上传和下载功能，具体的操作如下\n\n## 上传文件\n\n在用例层级的节点右击，单击\"上传文件\"菜单，选择本地的要上传的文件，即可将文件上传，如图：\n\n![上传文件](./img/upload.png)\n\n## 下载文件\n\n选择本地的要上传的文件，即可将文件上传，即可将文件下载到本地硬盘，如图：\n\n![下载文件](./img/download.png)\n\n## 小结\n\n可以上传任意格式的文件，所以你可以上传图片和python文件等你在写用例过程中用到的各种资源。"
  },
  {
    "path": "docs/关键字概要说明.md",
    "content": "## 简介\n\nAutoLink是基于RobotFramework进行二次开发的，所以其关键字直接继承了RobotFramework所有的关键字，意味着你如果熟悉RobotFramework那么可以使用AutoLink直接进行企业实践\n\n## 分类\n下面我们通过一张图看下AutoLink的关键字有哪些分类：\n![关键字分类](./img/keywords.png)\n\n## 说明\n下面对各分类进行概要性说明：\n- BuiltIn  \n提供了系列通用的关键字，不需要导入，可直接使用\n\n以下库，需要导入才能使用，导入方式为\n\nLibrary xxx\n\nxxx即为下面的库名\n\n- Collections  \n提供了处理Python列表和字典的关键字\n\n- DateTime  \n提供了日期时间处理能力的关键字\n\n- OperatingSystem  \n运行RobotFramework中运行各种与操作系统相关的任务，例如执行批处理、shell等等\n\n- Process  \n提供了在系统中运行进程的库，需要RobotFramework2.8及以上版本\n\n- String  \n提供了字符串处理的库，例如生成、修改、断言验证\n\n- Screenshot\n提供了截图功能的库\n\n- Telnet  \n提供了Telnet功能的库\n\n- AppiumLibrary  \n提供了移动测试的库，支持android和ios\n\n- RequestsLibrary  \n提供了HTTP接口测试的库\n\n- SeleniumLibrary  \n提供selenium webdriver强大的web自动化测试的库\n\n注： Selenium2Library官方已经不再维护，改为用SeleniumLibrary库替代了\n\n## 详细文档\n各库中关键字详细的使用请参考官方文档：http://robotframework.org/#libraries\n"
  },
  {
    "path": "docs/如何使用自动提示快捷输入关键字.md",
    "content": "## 快捷键应用一\n\n在AutoLink的编辑器中支持Ctrl键，快速调出支持的关键字列表,如下图所示：\n\n![关键字列表](./img/ctrl_keys.png)\n\n## 快捷键应用二\n\n在输入一个字符的情况下，按下Ctrl键，快速调出改字符开头的所有关键字列表,如下图以输入I为例所示：\n\n![关键字列表](./img/ctrl_keys_start.png)"
  },
  {
    "path": "docs/如何使用调度管理.md",
    "content": "## 简介\n在AutoLink中集成了APscheduler作为调度管理器。对APscheduler源码或使用有兴趣的可以参见其官网：https://apscheduler.readthedocs.io/en/latest/userguide.html\n\n在AutoLink中我们主要使用了APscheduler的cron调度风格\n\n下面我们看下在AutoLink中cron表达式各段的含义\n\n![cron表达式](./img/cron.png)\n\n## 管理调度\n\n下面我们通过一张图来说明调度管理\n\n![调度管理](./img/scheduler.png)\n\n## 默认cron表达式\n\n下面我们看下AutoLink默认提供的一些cron表达式\n\n![默认cron值](./img/cron_default.png)\n\n这几种默认的cron表达式，基本可以满足日常需要，当然也可以自己写对应的cron表达式\n"
  },
  {
    "path": "docs/如何创建HTTP接口测试用例.md",
    "content": "## 如何创建HTTP接口测试用例\n\n### 关于RequestsLibrary\n- RequestsLibrary是RobotFramework的第三方库，是基于python的第三方库requests实现的。\n因此，RequestsLibrary具有创建连接、采用HTTP方法发送请求、处理响应结果等关键字，用于实现在RobotFramework中编写HTTP接口测试用例。\n\n- [RequestsLibrary的Github源码地址](https://github.com/bulkan/robotframework-requests),源码中的关键字介绍、对应的tests示例，应该会对你编写HTTP接口测试用例有一定启示。\n### 采用AutoLink创建HTTP接口测试用例\n- 在正式编写用例之前，对用例结构进行规划是必不可少的步骤；\n- AutoLink中支持2种格式的文件：```txt```和```robot```，用于实现数据与用例分离；\n- Demo示例采用AutoLink的api作为被测接口，采用excel简单梳理了auth和ueser接口的功能，如下:\n\n![接口功能梳理](./img/AutoLink_api_tests_demo_xls.png)\n- Demo示例的组织结构如下：\n\n ![demo组织结构](./img/http_tests_demo.png)\n\n ### HTTP接口测试用例demo路径\n\n - [AutoLink_api_tests_demo](../.beats/workspace/AutoLink/AutoLink_api_tests_demo)"
  },
  {
    "path": "docs/如何创建测试项目.md",
    "content": "## 如何添加项目\n\n1. 在左边工作区根节点右击，在弹出的菜单选择\"创建项目\", 如图:\n![创建项目](./img/create_project.png)\n\n2. 在弹出是创建项目对话框中，输入项目名称，单击\"创建\"按钮，完成项目创建，如下图\n![创建项目](./img/input_project_name.png)"
  },
  {
    "path": "docs/如何查看关键字详细文档.md",
    "content": "如图所示\n\n![关键字帮助文档](./img/keyword_help.png)"
  },
  {
    "path": "docs/如何管理测试项目中用例顺序.md",
    "content": "## 标准测试项目\n\n下面我们通过一张图看下标准的测试项目组织是怎么样的，如图：\n\n![标准项目组织](./img/stand_project.png)\n\n由图我们知道在AutoLink中项目由三层组织构成：\n\n1. 项目层\n2. 套件层\n3. 用例层\n\n## 如何定义顺序\n\n请注意上图中套件名和用例名前面的数值序号，在AutoLink中通过在名称前添加数值序号来控制顺序，即：即时组织顺序，也是执行顺序"
  },
  {
    "path": "docs/如何调用Python自定义库.md",
    "content": "## 简介\n\n在AutoLink中，我们如何用Python写我们自己的测试库呢？\n\n\n## 项目结构\n\n如下图所示，在AutoLink中写Python\n\n![Python项目示例](./img/python_project.png)\n\n## 步骤\n\n1. 新增一个Python文件，写上你的Python代码，如图\n\n![python源码](./img/python_code.png)\n\n2. 通过相对路径导入上述库，如图\n\n![python关键字](./img/python_keyword.png)"
  },
  {
    "path": "docs/如何运行测试项目.md",
    "content": "## 如何运行测试项目\n\n### 方式一\n在项目节点上右击，选择\"运行\",即可立即运行该测试项目,如图\n![运行项目](./img/run_project.png)\n\n### 方式二\n\n在项目节点上右击，选择\"查看任务\" -> 在查看任务页，单击\"运行\"按钮，即可，如图\n\n1. ![查看任务](./img/view_task.png)\n\n2. ![运行项目](./img/run_task.png) "
  },
  {
    "path": "docs/安装与启动.md",
    "content": "## 安装\n\n1. 安装Python3及以上版本，确保加入环境变量，pip命令可用\n\n2. 从[AutoLink Github项目](https://github.com/small99/AutoLink)下载源码\n\n3. cd到AutoLink目录下，执行以下命令安装AutoLink依赖\n\n> pip install -r requirements.txt\n\n注：在上述依赖安装过程中，如果出现任何问题，请仔细看出错信息，若因为网络超时导致无法安装的，请再次执行上述命令即可\n\n## 本机访问启动\n\n1. 执行以下命令启动AutoLink服务\n\n> python AutoLink.py runserver\n\n2. 访问以下网址，即可\n\nhttp://127.0.0.1:5000\n\n## 外网访问启动\n1. 执行以下命令可外网访问\n\n> python AutoLink.py runserver -h 0.0.0.0 -p 8000\n通过\n\n2. 即可通过你的IP地址来访问\n\nhttp://ip:8000\n\n- -h选项指定为0.0.0.0即为绑定本机ip启动，网络其他用户通过你的ip和-p指定的端口即可访问AutoLink\n\n- -p指定AutoLink服务启动时的端口\n\n## 默认账号\n\n默认账号: AutoLink  \n\n默认密码: 123456\n\n## Selenium浏览器驱动安装\n\n1. [下载地址](https://docs.seleniumhq.org/download/), 最好有梯子，避免被墙\n\n2. 下载selenium webdriver对应的浏览器驱动放在driver目录即可\n"
  },
  {
    "path": "docs/查看测试报告.md",
    "content": "## 步骤\n\n1. 在对应的项目节点上右击选择\"查看任务\"， 如图\n\n![1](./img/report_1.png)\n\n2. 在该项目任务tab页，单击对应的项目及编号即可\n\n![2](./img/report_2.png)\n\n3. 查看报告\n\n![3](./img/report_3.png)"
  },
  {
    "path": "docs/配置SMTP服务及邮件通知.md",
    "content": "## smtp配置\n\n以QQ邮件smtp配置为例\n\n![smtp](./img/smtp.png)\n\n## 通知列表配置\n\n![mail](./img/mail_list.png)\n\n## 邮件通知结果\n\n![邮件](./img/mail_report.png)"
  },
  {
    "path": "driver/readme.md",
    "content": "\n请自行到以下地址或其他你知道的地址获取相应的驱动，并解压放在driver\n\n1. chromedriver下载地址   \nhttps://code.google.com/p/chromedriver/downloads/list\n\n2. Firefox的驱动geckodriver下载地址   \nhttps://github.com/mozilla/geckodriver/releases/\n\n3. IE的驱动IEdriver下载地址   \nhttp://www.nuget.org/packages/Selenium.WebDriver.IEDriver/\n\n\n或去官方下载：\n\nhttps://docs.seleniumhq.org/download/\n\n"
  },
  {
    "path": "keyword/AppiumLibrary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"AppiumLibrary\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:44\">\n<version>1.4.6</version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>AppiumLibrary is a Mobile App testing library for Robot Framework.\n\n= Locating or specifying elements =\n\nAll keywords in AppiumLibrary that need to find an element on the page\ntake an argument, either a ``locator`` or a ``webelement``. ``locator``\nis a string that describes how to locate an element using a syntax\nspecifying different location strategies. ``webelement`` is a variable that\nholds a WebElement instance, which is a representation of the element.\n\n== Using locators ==\n\nBy default, when a locator is provided, it is matched against the key attributes\nof the particular element type. For iOS and Android, key attribute is ``id`` for\nall elements and locating elements is easy using just the ``id``. For example:\n\n| Click Element    id=my_element\n\nNew in AppiumLibrary 1.4, ``id`` and ``xpath`` are not required to be specified,\nhowever ``xpath`` should start with ``//`` else just use ``xpath`` locator as explained below.\n\nFor example:\n\n| Click Element    my_element\n| Wait Until Page Contains Element    //*[@type=\"android.widget.EditText\"]\n\n\nAppium additionally supports some of the [https://w3c.github.io/webdriver/webdriver-spec.html|Mobile JSON Wire Protocol] locator strategies.\nIt is also possible to specify the approach AppiumLibrary should take\nto find an element by specifying a lookup strategy with a locator\nprefix. Supported strategies are:\n\n| *Strategy*        | *Example*                                                      | *Description*                     | *Note*                      |\n| identifier        | Click Element `|` identifier=my_element                        | Matches by @id attribute          |                             |\n| id                | Click Element `|` id=my_element                                | Matches by @resource-id attribute |                             |\n| accessibility_id  | Click Element `|` accessibility_id=button3                     | Accessibility options utilize.    |                             |\n| xpath             | Click Element `|` xpath=//UIATableView/UIATableCell/UIAButton  | Matches with arbitrary XPath      |                             |\n| class             | Click Element `|` class=UIAPickerWheel                         | Matches by class                  |                             |\n| android           | Click Element `|` android=UiSelector().description('Apps')     | Matches by Android UI Automator   |                             |\n| ios               | Click Element `|` ios=.buttons().withName('Apps')              | Matches by iOS UI Automation      |                             |\n| css               | Click Element `|` css=.green_button                            | Matches by css in webview         |                             |\n| name              | Click Element `|` name=my_element                              | Matches by @name attribute        | *Only valid* for Selendroid |\n\n== Using webelements ==\n\nStarting with version 1.4 of the AppiumLibrary, one can pass an argument\nthat contains a WebElement instead of a string locator. To get a WebElement,\nuse the new `Get WebElements` or `Get WebElement` keyword.\n\nFor example:\n| @{elements}    Get Webelements    class=UIAButton\n| Click Element    @{elements}[2]</doc>\n<init>\n<arguments>\n<arg>timeout=5</arg>\n<arg>run_on_failure=Capture Page Screenshot</arg>\n</arguments>\n<doc>AppiumLibrary can be imported with optional arguments.\n\n``timeout`` is the default timeout used to wait for all waiting actions.\nIt can be later set with `Set Appium Timeout`.\n\n``run_on_failure`` specifies the name of a keyword (from any available\nlibraries) to execute when a AppiumLibrary keyword fails.\n\nBy default `Capture Page Screenshot` will be used to take a screenshot of the current page.\nUsing the value `No Operation` will disable this feature altogether. See\n`Register Keyword To Run On Failure` keyword for more information about this\nfunctionality.\n\nExamples:\n| Library | AppiumLibrary | 10 | # Sets default timeout to 10 seconds                                                                             |\n| Library | AppiumLibrary | timeout=10 | run_on_failure=No Operation | # Sets default timeout to 10 seconds and does nothing on failure           |</doc>\n<tags>\n</tags>\n</init>\n<kw name=\"Background App\">\n<arguments>\n<arg>seconds=5</arg>\n</arguments>\n<doc>Puts the application in the background on the device for a certain\nduration.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Capture Page Screenshot\">\n<arguments>\n<arg>filename=None</arg>\n</arguments>\n<doc>Takes a screenshot of the current page and embeds it into the log.\n\n`filename` argument specifies the name of the file to write the\nscreenshot into. If no `filename` is given, the screenshot is saved into file\n`appium-screenshot-&lt;counter&gt;.png` under the directory where\nthe Robot Framework log file is written into. The `filename` is\nalso considered relative to the same directory, if it is not\ngiven in absolute format.\n\n`css` can be used to modify how the screenshot is taken. By default\nthe bakground color is changed to avoid possible problems with\nbackground leaking when the page layout is somehow broken.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Clear Text\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Clears the text field identified by `locator`.\n\nSee `introduction` for details about locating elements.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Click A Point\">\n<arguments>\n<arg>x=0</arg>\n<arg>y=0</arg>\n<arg>duration=100</arg>\n</arguments>\n<doc>Click on a point</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Click Button\">\n<arguments>\n<arg>index_or_name</arg>\n</arguments>\n<doc>Click button</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Click Element\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Click element identified by `locator`.\n\nKey attributes for arbitrary elements are `index` and `name`. See\n`introduction` for details about locating elements.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Click Element At Coordinates\">\n<arguments>\n<arg>coordinate_X</arg>\n<arg>coordinate_Y</arg>\n</arguments>\n<doc>click element at a certain coordinate</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Click Text\">\n<arguments>\n<arg>text</arg>\n<arg>exact_match=False</arg>\n</arguments>\n<doc>Click text identified by ``text``.\n\nBy default tries to click first text involves given ``text``, if you would\nlike to click exactly matching text, then set ``exact_match`` to `True`.\n\nIf there are multiple use  of ``text`` and you do not want first one,\nuse `locator` with `Get Web Elements` instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Close All Applications\">\n<arguments>\n</arguments>\n<doc>Closes all open applications.\n\nThis keyword is meant to be used in test or suite teardown to\nmake sure all the applications are closed before the test execution\nfinishes.\n\nAfter this keyword, the application indices returned by `Open Application`\nare reset and start from `1`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Close Application\">\n<arguments>\n</arguments>\n<doc>Closes the current application and also close webdriver session.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Attribute Should Match\">\n<arguments>\n<arg>locator</arg>\n<arg>attr_name</arg>\n<arg>match_pattern</arg>\n<arg>regexp=False</arg>\n</arguments>\n<doc>Verify that an attribute of an element matches the expected criteria.\n\nThe element is identified by _locator_. See `introduction` for details\nabout locating elements. If more than one element matches, the first element is selected.\n\nThe _attr_name_ is the name of the attribute within the selected element.\n\nThe _match_pattern_ is used for the matching, if the match_pattern is\n- boolean or 'True'/'true'/'False'/'false' String then a boolean match is applied\n- any other string is cause a string match\n\nThe _regexp_ defines whether the string match is done using regular expressions (i.e. BuiltIn Library's\n[http://robotframework.org/robotframework/latest/libraries/BuiltIn.html#Should%20Match%20Regexp|Should\nMatch Regexp] or string pattern match (i.e. BuiltIn Library's\n[http://robotframework.org/robotframework/latest/libraries/BuiltIn.html#Should%20Match|Should\nMatch])\n\n\nExamples:\n\n| Element Attribute Should Match | xpath = //*[contains(@text,'foo')] | text | *foobar |\n| Element Attribute Should Match | xpath = //*[contains(@text,'foo')] | text | f.*ar | regexp = True |\n| Element Attribute Should Match | xpath = //*[contains(@text,'foo')] | enabled | True |\n\n| 1. is a string pattern match i.e. the 'text' attribute should end with the string 'foobar'\n| 2. is a regular expression match i.e. the regexp 'f.*ar' should be within the 'text' attribute\n| 3. is a boolead match i.e. the 'enabled' attribute should be True\n\n\n_*NOTE: *_\nOn Android the supported attribute names are hard-coded in the\n[https://github.com/appium/appium/blob/master/lib/devices/android/bootstrap/src/io/appium/android/bootstrap/AndroidElement.java|AndroidElement]\nClass's getBoolAttribute() and getStringAttribute() methods.\nCurrently supported (appium v1.4.11):\n_contentDescription, text, className, resourceId, enabled, checkable, checked, clickable, focusable, focused, longClickable, scrollable, selected, displayed_\n\n\n_*NOTE: *_\nSome attributes can be evaluated in two different ways e.g. these evaluate the same thing:\n\n| Element Attribute Should Match | xpath = //*[contains(@text,'example text')] | name | txt_field_name |\n| Element Name Should Be         | xpath = //*[contains(@text,'example text')] | txt_field_name |      |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Name Should Be\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n</arguments>\n<doc></doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Be Disabled\">\n<arguments>\n<arg>locator</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that element identified with locator is disabled.\n\nKey attributes for arbitrary elements are `id` and `name`. See\n`introduction` for details about locating elements.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Be Enabled\">\n<arguments>\n<arg>locator</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that element identified with locator is enabled.\n\nKey attributes for arbitrary elements are `id` and `name`. See\n`introduction` for details about locating elements.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Be Visible\">\n<arguments>\n<arg>locator</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that element identified with locator is visible.\n\nKey attributes for arbitrary elements are `id` and `name`. See\n`introduction` for details about locating elements.\n\nNew in AppiumLibrary 1.4.5</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Contain Text\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>message=</arg>\n</arguments>\n<doc>Verifies element identified by ``locator`` contains text ``expected``.\n\nIf you wish to assert an exact (not a substring) match on the text\nof the element, use `Element Text Should Be`.\n\nKey attributes for arbitrary elements are ``id`` and ``xpath``. ``message`` can be used to override the default error message.\n\nNew in AppiumLibrary 1.4.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Not Contain Text\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>message=</arg>\n</arguments>\n<doc>Verifies element identified by ``locator`` does not contain text ``expected``.\n\n``message`` can be used to override the default error message.\nSee `Element Should Contain Text` for more details.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Text Should Be\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>message=</arg>\n</arguments>\n<doc>Verifies element identified by ``locator`` exactly contains text ``expected``.\n\nIn contrast to `Element Should Contain Text`, this keyword does not try\na substring match but an exact match on the element identified by ``locator``.\n\n``message`` can be used to override the default error message.\n\nNew in AppiumLibrary 1.4.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Value Should Be\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n</arguments>\n<doc></doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Activity\">\n<arguments>\n</arguments>\n<doc>Retrieves the current activity on the device.\n\nAndroid only.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Appium SessionId\">\n<arguments>\n</arguments>\n<doc>Returns the current session ID as a reference</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Appium Timeout\">\n<arguments>\n</arguments>\n<doc>Gets the timeout in seconds that is used by various keywords.\n\nSee `Set Appium Timeout` for an explanation.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Capability\">\n<arguments>\n<arg>capability_name</arg>\n</arguments>\n<doc>Return the desired capability value by desired capability name</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Contexts\">\n<arguments>\n</arguments>\n<doc>Get available contexts.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Current Context\">\n<arguments>\n</arguments>\n<doc>Get current context.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Element Attribute\">\n<arguments>\n<arg>locator</arg>\n<arg>attribute</arg>\n</arguments>\n<doc>Get element attribute using given attribute: name, value,...\n\nExamples:\n\n| Get Element Attribute | locator | name |\n| Get Element Attribute | locator | value |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Element Location\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Get element location\n\nKey attributes for arbitrary elements are `id` and `name`. See\n`introduction` for details about locating elements.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Element Size\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Get element size\n\nKey attributes for arbitrary elements are `id` and `name`. See\n`introduction` for details about locating elements.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Matching Xpath Count\">\n<arguments>\n<arg>xpath</arg>\n</arguments>\n<doc>Returns number of elements matching ``xpath``\n\nOne should not use the `xpath=` prefix for 'xpath'. XPath is assumed.\n\n| *Correct:* |\n| ${count}  | Get Matching Xpath Count | //android.view.View[@text='Test'] |\n| Incorrect:  |\n| ${count}  | Get Matching Xpath Count | xpath=//android.view.View[@text='Test'] |\n\nIf you wish to assert the number of matching elements, use\n`Xpath Should Match X Times`.\n\nNew in AppiumLibrary 1.4.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Network Connection Status\">\n<arguments>\n</arguments>\n<doc>Returns an integer bitmask specifying the network connection type.\n\nAndroid only.\n\nSee `set network connection status` for more details.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Source\">\n<arguments>\n</arguments>\n<doc>Returns the entire source of the current page.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Text\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Get element text (for hybrid and mobile browser use `xpath` locator, others might cause problem)\n\nExample:\n\n| ${text} | Get Text | //*[contains(@text,'foo')] |\n\nNew in AppiumLibrary 1.4.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Webelement\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns the first [http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement|WebElement] object matching ``locator``.\n\nExample:\n| ${element}     | Get Webelement | id=my_element |\n| Click Element  | ${element}     |               |\n\nNew in AppiumLibrary 1.4.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Webelements\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns list of [http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement|WebElement] objects matching ``locator``.\n\nExample:\n| @{elements}    | Get Webelements | id=my_element |\n| Click Element  | @{elements}[2]  |               |\n\nThis keyword was changed in AppiumLibrary 1.4 in following ways:\n- Name is changed from `Get Elements` to current one.\n- Deprecated argument ``fail_on_error``, use `Run Keyword and Ignore Error` if necessary.\n\nNew in AppiumLibrary 1.4.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Window Height\">\n<arguments>\n</arguments>\n<doc>Get current device height.\n\nExample:\n| ${width}       | Get Window Height |\n| ${height}      | Get Window Height |\n| Click A Point  | ${width           | ${height} |\n       \nNew in AppiumLibrary 1.4.5</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Window Width\">\n<arguments>\n</arguments>\n<doc>Get current device width.\n\nExample:\n| ${width}       | Get Window Height |\n| ${height}      | Get Window Height |\n| Click A Point  | ${width           | ${height} |\n\nNew in AppiumLibrary 1.4.5</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Go Back\">\n<arguments>\n</arguments>\n<doc>Goes one step backward in the browser history.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Go To Url\">\n<arguments>\n<arg>url</arg>\n</arguments>\n<doc>Opens URL in default web browser.\n\nExample:\n| Open Application  | http://localhost:4755/wd/hub | platformName=iOS | platformVersion=7.0 | deviceName='iPhone Simulator' | browserName=Safari |\n| Go To URL         | http://m.webapp.com          |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Hide Keyboard\">\n<arguments>\n<arg>key_name=None</arg>\n</arguments>\n<doc>Hides the software keyboard on the device. (optional) In iOS, use `key_name` to press\na particular key, ex. `Done`. In Android, no parameters are used.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Input Password\">\n<arguments>\n<arg>locator</arg>\n<arg>text</arg>\n</arguments>\n<doc>Types the given password into text field identified by `locator`.\n\nDifference between this keyword and `Input Text` is that this keyword\ndoes not log the given password. See `introduction` for details about\nlocating elements.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Input Text\">\n<arguments>\n<arg>locator</arg>\n<arg>text</arg>\n</arguments>\n<doc>Types the given `text` into text field identified by `locator`.\n\nSee `introduction` for details about locating elements.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Input Value\">\n<arguments>\n<arg>locator</arg>\n<arg>text</arg>\n</arguments>\n<doc>Sets the given value into text field identified by `locator`. This is an IOS only keyword, input value makes use of set_value\n\nSee `introduction` for details about locating elements.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Install App\">\n<arguments>\n<arg>app_path</arg>\n<arg>app_package</arg>\n</arguments>\n<doc>Install App via Appium\n\nAndroid only.\n\n- app_path - path to app\n- app_package - package of install app to verify</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Landscape\">\n<arguments>\n</arguments>\n<doc>Set the device orientation to LANDSCAPE</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Launch Application\">\n<arguments>\n</arguments>\n<doc>Launch application. Application can be launched while Appium session running.\nThis keyword can be used to launch application during test case or between test cases.\n\nThis keyword works while `Open Application` has a test running. This is good practice to `Launch Application`\nand `Quit Application` between test cases. As Suite Setup is `Open Application`, `Test Setup` can be used to `Launch Application`\n\nExample (syntax is just a representation, refer to RF Guide for usage of Setup/Teardown):\n| [Setup Suite] |\n|  | Open Application | http://localhost:4723/wd/hub | platformName=Android | deviceName=192.168.56.101:5555 | app=${CURDIR}/demoapp/OrangeDemoApp.apk |\n| [Test Setup] |\n|  | Launch Application |\n|  |  | &lt;&lt;&lt;test execution&gt;&gt;&gt; |\n|  |  | &lt;&lt;&lt;test execution&gt;&gt;&gt; |\n| [Test Teardown] |\n|  | Quit Application |\n| [Suite Teardown] |\n|  | Close Application |\n\nSee `Quit Application` for quiting application but keeping Appium sesion running.\n\nNew in AppiumLibrary 1.4.6</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Lock\">\n<arguments>\n<arg>seconds=5</arg>\n</arguments>\n<doc>Lock the device for a certain period of time. iOS only.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log Source\">\n<arguments>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Logs and returns the entire html source of the current page or frame.\n\nThe `loglevel` argument defines the used log level. Valid log levels are\n`WARN`, `INFO` (default), `DEBUG`, `TRACE` and `NONE` (no logging).</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Long Press\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Long press the element</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Long Press Keycode\">\n<arguments>\n<arg>keycode</arg>\n<arg>metastate=None</arg>\n</arguments>\n<doc>Sends a long press of keycode to the device.\n\nAndroid only.\n\nSee `press keycode` for more details.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Open Application\">\n<arguments>\n<arg>remote_url</arg>\n<arg>alias=None</arg>\n<arg>**kwargs</arg>\n</arguments>\n<doc>Opens a new application to given Appium server.\nCapabilities of appium server, Android and iOS,\nPlease check http://appium.io/slate/en/master/?python#appium-server-capabilities\n| *Option*            | *Man.* | *Description*     |\n| remote_url          | Yes    | Appium server url |\n| alias               | no     | alias             |\n\nExamples:\n| Open Application | http://localhost:4723/wd/hub | alias=Myapp1         | platformName=iOS      | platformVersion=7.0            | deviceName='iPhone Simulator'           | app=your.app                         |\n| Open Application | http://localhost:4723/wd/hub | platformName=Android | platformVersion=4.2.2 | deviceName=192.168.56.101:5555 | app=${CURDIR}/demoapp/OrangeDemoApp.apk | appPackage=com.netease.qa.orangedemo | appActivity=MainActivity |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Contain Element\">\n<arguments>\n<arg>locator</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that current page contains `locator` element.\n\nIf this keyword fails, it automatically logs the page source\nusing the log level specified with the optional `loglevel` argument.\nGiving `NONE` as level disables logging.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Contain Text\">\n<arguments>\n<arg>text</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that current page contains `text`.\n\nIf this keyword fails, it automatically logs the page source\nusing the log level specified with the optional `loglevel` argument.\nGiving `NONE` as level disables logging.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Not Contain Element\">\n<arguments>\n<arg>locator</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that current page not contains `locator` element.\n\nIf this keyword fails, it automatically logs the page source\nusing the log level specified with the optional `loglevel` argument.\nGiving `NONE` as level disables logging.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Not Contain Text\">\n<arguments>\n<arg>text</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that current page not contains `text`.\n\nIf this keyword fails, it automatically logs the page source\nusing the log level specified with the optional `loglevel` argument.\nGiving `NONE` as level disables logging.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Pinch\">\n<arguments>\n<arg>locator</arg>\n<arg>percent=200%</arg>\n<arg>steps=1</arg>\n</arguments>\n<doc>Pinch in on an element a certain amount.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Portrait\">\n<arguments>\n</arguments>\n<doc>Set the device orientation to PORTRAIT</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Press Keycode\">\n<arguments>\n<arg>keycode</arg>\n<arg>metastate=None</arg>\n</arguments>\n<doc>Sends a press of keycode to the device.\n\nAndroid only.\n\nPossible keycodes &amp; meta states can be found in\nhttp://developer.android.com/reference/android/view/KeyEvent.html\n\nMeta state describe the pressed state of key modifiers such as\nShift, Ctrl &amp; Alt keys. The Meta State is an integer in which each\nbit set to 1 represents a pressed meta key.\n\nFor example\n- META_SHIFT_ON = 1\n- META_ALT_ON = 2\n\n| metastate=1 --&gt; Shift is pressed\n| metastate=2 --&gt; Alt is pressed\n| metastate=3 --&gt; Shift+Alt is pressed\n\n - _keycode- - the keycode to be sent to the device\n - _metastate- - status of the meta keys</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Pull File\">\n<arguments>\n<arg>path</arg>\n<arg>decode=False</arg>\n</arguments>\n<doc>Retrieves the file at `path` and return it's content.\n\nAndroid only.\n\n - _path_ - the path to the file on the device\n - _decode_ - True/False decode the data (base64) before returning it (default=False)</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Pull Folder\">\n<arguments>\n<arg>path</arg>\n<arg>decode=False</arg>\n</arguments>\n<doc>Retrieves a folder at `path`. Returns the folder's contents zipped.\n\nAndroid only.\n\n - _path_ - the path to the folder on the device\n - _decode_ - True/False decode the data (base64) before returning it (default=False)</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Push File\">\n<arguments>\n<arg>path</arg>\n<arg>data</arg>\n<arg>encode=False</arg>\n</arguments>\n<doc>Puts the data in the file specified as `path`.\n\nAndroid only.\n\n - _path_ - the path on the device\n - _data_ - data to be written to the file\n - _encode_ - True/False encode the data as base64 before writing it to the file (default=False)</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Quit Application\">\n<arguments>\n</arguments>\n<doc>Quit application. Application can be quit while Appium session is kept alive. \nThis keyword can be used to close application during test case or between test cases.\n\nSee `Launch Application` for an explanation.\n\nNew in AppiumLibrary 1.4.6</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Register Keyword To Run On Failure\">\n<arguments>\n<arg>keyword</arg>\n</arguments>\n<doc>Sets the keyword to execute when a AppiumLibrary keyword fails.\n\n`keyword_name` is the name of a keyword (from any available\nlibraries) that  will be executed if a AppiumLibrary keyword fails.\nIt is not possible to use a keyword that requires arguments.\nUsing the value \"Nothing\" will disable this feature altogether.\n\nThe initial keyword to use is set in `importing`, and the\nkeyword that is used by default is `Capture Page Screenshot`.\nTaking a screenshot when something failed is a very useful\nfeature, but notice that it can slow down the execution.\n\nThis keyword returns the name of the previously registered\nfailure keyword. It can be used to restore the original\nvalue later.\n\nExample:\n| Register Keyword To Run On Failure  | Log Source | # Run `Log Source` on failure. |\n| ${previous kw}= | Register Keyword To Run On Failure  | Nothing    | # Disables run-on-failure functionality and stores the previous kw name in a variable. |\n| Register Keyword To Run On Failure  | ${previous kw} | # Restore to the previous keyword. |\n\nThis run-on-failure functionality only works when running tests on Python/Jython 2.4\nor newer and it does not work on IronPython at all.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Application\">\n<arguments>\n<arg>application_id</arg>\n</arguments>\n<doc>Removes the application that is identified with an application id\n\nExample:\n| Remove Application |  com.netease.qa.orangedemo |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Reset Application\">\n<arguments>\n</arguments>\n<doc>Reset application. Open Application can be reset while Appium session is kept alive.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Scroll\">\n<arguments>\n<arg>start_locator</arg>\n<arg>end_locator</arg>\n</arguments>\n<doc>Scrolls from one element to another\nKey attributes for arbitrary elements are `id` and `name`. See\n`introduction` for details about locating elements.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Scroll Down\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Scrolls down to element</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Scroll Up\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Scrolls up to element</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Appium Timeout\">\n<arguments>\n<arg>seconds</arg>\n</arguments>\n<doc>Sets the timeout in seconds used by various keywords.\n\nThere are several `Wait ...` keywords that take timeout as an\nargument. All of these timeout arguments are optional. The timeout\nused by all of them can be set globally using this keyword.\n\nThe previous timeout value is returned by this keyword and can\nbe used to set the old value back later. The default timeout\nis 5 seconds, but it can be altered in `importing`.\n\nExample:\n| ${orig timeout} = | Set Appium Timeout | 15 seconds |\n| Open page that loads slowly |\n| Set Appium Timeout | ${orig timeout} |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Network Connection Status\">\n<arguments>\n<arg>connectionStatus</arg>\n</arguments>\n<doc>Sets the network connection Status.\n\nAndroid only.\n\nPossible values:\n    | =Value= | =Alias=          | =Data= | =Wifi= | =Airplane Mode=  |\n    |  0      | (None)           | 0      |   0    | 0                |\n    |  1      | (Airplane Mode)  | 0      |   0    | 1                |\n    |  2      | (Wifi only)      | 0      |   1    | 0                |\n    |  4      | (Data only)      | 1      |   0    | 0                |\n    |  6      | (All network on) | 1      |   1    | 0                |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Shake\">\n<arguments>\n</arguments>\n<doc>Shake the device</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Start Activity\">\n<arguments>\n<arg>appPackage</arg>\n<arg>appActivity</arg>\n<arg>**opts</arg>\n</arguments>\n<doc>Opens an arbitrary activity during a test. If the activity belongs to\nanother application, that application is started and the activity is opened.\n\nAndroid only.\n\n- _appPackage_ - The package containing the activity to start.\n- _appActivity_ - The activity to start.\n- _appWaitPackage_ - Begin automation after this package starts (optional).\n- _appWaitActivity_ - Begin automation after this activity starts (optional).\n- _intentAction_ - Intent to start (opt_ional).\n- _intentCategory_ - Intent category to start (optional).\n- _intentFlags_ - Flags to send to the intent (optional).\n- _optionalIntentArguments_ - Optional arguments to the intent (optional).\n- _stopAppOnReset_ - Should the app be stopped on reset (optional)?</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Swipe\">\n<arguments>\n<arg>start_x</arg>\n<arg>start_y</arg>\n<arg>offset_x</arg>\n<arg>offset_y</arg>\n<arg>duration=1000</arg>\n</arguments>\n<doc>Swipe from one point to another point, for an optional duration.\n\nArgs:\n - start_x - x-coordinate at which to start\n - start_y - y-coordinate at which to start\n - offset_x - x-coordinate distance from start_x at which to stop\n - offset_y - y-coordinate distance from start_y at which to stop\n - duration - (optional) time to take the swipe, in ms.\n\nUsage:\n| Swipe | 500 | 100 | 100 | 0 | 1000 |\n\n_*NOTE: *_ \nAndroid 'Swipe' is not working properly, use ``offset_x`` and ``offset_y`` as if these are destination points.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Swipe By Percent\">\n<arguments>\n<arg>start_x</arg>\n<arg>start_y</arg>\n<arg>end_x</arg>\n<arg>end_y</arg>\n<arg>duration=1000</arg>\n</arguments>\n<doc>Swipe from one percent of the screen to another percent, for an optional duration. \nNormal swipe fails to scale for different screen resolutions, this can be avoided using percent.\n\nArgs:\n - start_x - x-percent at which to start\n - start_y - y-percent at which to start\n - end_x - x-percent distance from start_x at which to stop\n - end_y - y-percent distance from start_y at which to stop\n - duration - (optional) time to take the swipe, in ms.\n \nUsage:\n| Swipe By Percent | 90 | 50 | 10 | 50 | # Swipes screen from right to left. |\n\n_*NOTE: *_\nThis also considers swipe acts different between iOS and Android.\n\nNew in AppiumLibrary 1.4.5</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Switch Application\">\n<arguments>\n<arg>index_or_alias</arg>\n</arguments>\n<doc>Switches the active application by index or alias.\n\n`index_or_alias` is either application index (an integer) or alias\n(a string). Index is got as the return value of `Open Application`.\n\nThis keyword returns the index of the previous active application,\nwhich can be used to switch back to that application later.\n\nExample:\n| ${appium1}=              | Open Application  | http://localhost:4723/wd/hub                   | alias=MyApp1 | platformName=iOS | platformVersion=7.0 | deviceName='iPhone Simulator' | app=your.app |\n| ${appium2}=              | Open Application  | http://localhost:4755/wd/hub                   | alias=MyApp2 | platformName=iOS | platformVersion=7.0 | deviceName='iPhone Simulator' | app=your.app |\n| Click Element            | sendHello         | # Executed on appium running at localhost:4755 |\n| Switch Application       | ${appium1}        | # Switch using index                           |\n| Click Element            | ackHello          | # Executed on appium running at localhost:4723 |\n| Switch Application       | MyApp2            | # Switch using alias                           |\n| Page Should Contain Text | ackHello Received | # Executed on appium running at localhost:4755 |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Switch To Context\">\n<arguments>\n<arg>context_name</arg>\n</arguments>\n<doc>Switch to a new context</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Tap\">\n<arguments>\n<arg>locator</arg>\n<arg>x_offset=None</arg>\n<arg>y_offset=None</arg>\n<arg>count=1</arg>\n</arguments>\n<doc>Tap element identified by ``locator``.\n\nArgs:\n- ``x_offset`` - (optional) x coordinate to tap, relative to the top left corner of the element.\n- ``y_offset`` - (optional) y coordinate. If y is used, x must also be set, and vice versa\n- ``count`` - can be used for multiple times of tap on that element</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Text Should Be Visible\">\n<arguments>\n<arg>text</arg>\n<arg>exact_match=False</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that element identified with text is visible.\n\nNew in AppiumLibrary 1.4.5</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Activity\">\n<arguments>\n<arg>activity</arg>\n<arg>timeout</arg>\n<arg>interval=1</arg>\n</arguments>\n<doc>Wait for an activity: block until target activity presents\nor time out.\n\nAndroid only.\n\n - _activity_ - target activity\n - _timeout_ - max wait time, in seconds\n - _interval_ - sleep interval between retries, in seconds</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Element Is Visible\">\n<arguments>\n<arg>locator</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until element specified with `locator` is visible.\n\nFails if `timeout` expires before the element is visible. See\n`introduction` for more information about `timeout` and its\ndefault value.\n\n`error` can be used to override the default error message.\n\nSee also `Wait Until Page Contains`, `Wait Until Page Contains \nElement`, `Wait For Condition` and BuiltIn keyword `Wait Until Keyword\nSucceeds`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Page Contains\">\n<arguments>\n<arg>text</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until `text` appears on current page.\n\nFails if `timeout` expires before the text appears. See\n`introduction` for more information about `timeout` and its\ndefault value.\n\n`error` can be used to override the default error message.\n\nSee also `Wait Until Page Does Not Contain`,\n`Wait Until Page Contains Element`,\n`Wait Until Page Does Not Contain Element` and\nBuiltIn keyword `Wait Until Keyword Succeeds`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Page Contains Element\">\n<arguments>\n<arg>locator</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until element specified with `locator` appears on current page.\n\nFails if `timeout` expires before the element appears. See\n`introduction` for more information about `timeout` and its\ndefault value.\n\n`error` can be used to override the default error message.\n\nSee also `Wait Until Page Contains`,\n`Wait Until Page Does Not Contain`\n`Wait Until Page Does Not Contain Element`\nand BuiltIn keyword `Wait Until Keyword Succeeds`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Page Does Not Contain\">\n<arguments>\n<arg>text</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until `text` disappears from current page.\n\nFails if `timeout` expires before the `text` disappears. See\n`introduction` for more information about `timeout` and its\ndefault value.\n\n`error` can be used to override the default error message.\n\nSee also `Wait Until Page Contains`,\n`Wait Until Page Contains Element`,\n`Wait Until Page Does Not Contain Element` and\nBuiltIn keyword `Wait Until Keyword Succeeds`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Page Does Not Contain Element\">\n<arguments>\n<arg>locator</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until element specified with `locator` disappears from current page.\n\nFails if `timeout` expires before the element disappears. See\n`introduction` for more information about `timeout` and its\ndefault value.\n\n`error` can be used to override the default error message.\n\nSee also `Wait Until Page Contains`,\n`Wait Until Page Does Not Contain`,\n`Wait Until Page Contains Element` and\nBuiltIn keyword `Wait Until Keyword Succeeds`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Xpath Should Match X Times\">\n<arguments>\n<arg>xpath</arg>\n<arg>count</arg>\n<arg>error=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that the page contains the given number of elements located by the given ``xpath``.\n\nOne should not use the `xpath=` prefix for 'xpath'. XPath is assumed.\n\n| *Correct:* |\n| Xpath Should Match X Times | //android.view.View[@text='Test'] | 1 |\n| Incorrect: |\n| Xpath Should Match X Times | xpath=//android.view.View[@text='Test'] | 1 |\n\n``error`` can be used to override the default error message.\n\nSee `Log Source` for explanation about ``loglevel`` argument.\n\nNew in AppiumLibrary 1.4.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Zoom\">\n<arguments>\n<arg>locator</arg>\n<arg>percent=200%</arg>\n<arg>steps=1</arg>\n</arguments>\n<doc>Zooms in on an element a certain amount.</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/BuiltIn.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"BuiltIn\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:40\">\n<version>3.0.3</version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>An always available standard library with often needed keywords.\n\n``BuiltIn`` is Robot Framework's standard library that provides a set\nof generic keywords needed often. It is imported automatically and\nthus always available. The provided keywords can be used, for example,\nfor verifications (e.g. `Should Be Equal`, `Should Contain`),\nconversions (e.g. `Convert To Integer`) and for various other purposes\n(e.g. `Log`, `Sleep`, `Run Keyword If`, `Set Global Variable`).\n\n== Table of contents ==\n\n- `HTML error messages`\n- `Evaluating expressions`\n- `Boolean arguments`\n- `Multiline string comparisons`\n- `Shortcuts`\n- `Keywords`\n\n= HTML error messages =\n\nMany of the keywords accept an optional error message to use if the keyword\nfails. Starting from Robot Framework 2.8, it is possible to use HTML in\nthese messages by prefixing them with ``*HTML*``. See `Fail` keyword for\na usage example. Notice that using HTML in messages is not limited to\nBuiltIn library but works with any error message.\n\n= Evaluating expressions =\n\nMany keywords, such as `Evaluate`, `Run Keyword If` and `Should Be True`,\naccept an expression that is evaluated in Python. These expressions are\nevaluated using Python's\n[https://docs.python.org/2/library/functions.html#eval|eval] function so\nthat all Python built-ins like ``len()`` and ``int()`` are available.\n`Evaluate` allows configuring the execution namespace with custom modules,\nand other keywords have [https://docs.python.org/2/library/os.html|os]\nand [https://docs.python.org/2/library/sys.html|sys] modules available\nautomatically.\n\nExamples:\n| `Run Keyword If` | os.sep == '/' | Log                  | Not on Windows |\n| ${random int} =  | `Evaluate`    | random.randint(0, 5) | modules=random |\n\nWhen a variable is used in the expressing using the normal ``${variable}``\nsyntax, its value is replaces before the expression is evaluated. This\nmeans that the value used in the expression will be the string\nrepresentation of the variable value, not the variable value itself.\nThis is not a problem with numbers and other objects that have a string\nrepresentation that can be evaluated directly, but with other objects\nthe behavior depends on the string representation. Most importantly,\nstrings must always be quoted, and if they can contain newlines, they must\nbe triple quoted.\n\nExamples:\n| `Should Be True` | ${rc} &lt; 10                | Return code greater than 10 |\n| `Run Keyword If` | '${status}' == 'PASS'     | Log | Passed                |\n| `Run Keyword If` | 'FAIL' in '''${output}''' | Log | Output contains FAIL  |\n\nStarting from Robot Framework 2.9, variables themselves are automatically\navailable in the evaluation namespace. They can be accessed using special\nvariable syntax without the curly braces like ``$variable``. These\nvariables should never be quoted, and in fact they are not even replaced\ninside strings.\n\nExamples:\n| `Should Be True` | $rc &lt; 10          | Return code greater than 10  |\n| `Run Keyword If` | $status == 'PASS' | `Log` | Passed               |\n| `Run Keyword If` | 'FAIL' in $output | `Log` | Output contains FAIL |\n| `Should Be True` | len($result) &gt; 1 and $result[1] == 'OK' |\n\nUsing the ``$variable`` syntax slows down expression evaluation a little.\nThis should not typically matter, but should be taken into account if\ncomplex expressions are evaluated often and there are strict time\nconstrains.\n\nNotice that instead of creating complicated expressions, it is often better\nto move the logic into a test library. That eases maintenance and can also\nenhance execution speed.\n\n= Boolean arguments =\n\nSome keywords accept arguments that are handled as Boolean values true or\nfalse. If such an argument is given as a string, it is considered false if\nit is either an empty string or case-insensitively equal to ``false``,\n``none`` or ``no``. Keywords verifying something that allow dropping actual\nand expected values from the possible error message also consider string\n``no values`` to be false. Other strings are considered true regardless\ntheir value, and other argument types are tested using the same\n[http://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules\nas in Python].\n\nTrue examples:\n| `Should Be Equal` | ${x} | ${y}  | Custom error | values=True    | # Strings are generally true.    |\n| `Should Be Equal` | ${x} | ${y}  | Custom error | values=yes     | # Same as the above.             |\n| `Should Be Equal` | ${x} | ${y}  | Custom error | values=${TRUE} | # Python ``True`` is true.       |\n| `Should Be Equal` | ${x} | ${y}  | Custom error | values=${42}   | # Numbers other than 0 are true. |\n\nFalse examples:\n| `Should Be Equal` | ${x} | ${y}  | Custom error | values=False     | # String ``false`` is false.   |\n| `Should Be Equal` | ${x} | ${y}  | Custom error | values=no        | # Also string ``no`` is false. |\n| `Should Be Equal` | ${x} | ${y}  | Custom error | values=${EMPTY}  | # Empty string is false.       |\n| `Should Be Equal` | ${x} | ${y}  | Custom error | values=${FALSE}  | # Python ``False`` is false.   |\n| `Should Be Equal` | ${x} | ${y}  | Custom error | values=no values | # ``no values`` works with ``values`` argument |\n\nPrior to Robot Framework 2.9 some keywords considered all\nnon-empty strings, including ``false`` and ``no``, to be true.\nConsidering ``none`` false is new in Robot Framework 3.0.3.\n\n= Multiline string comparisons =\n\n`Should Be Equal` and `Should Be Equal As Strings` report the failures using\n[https://en.wikipedia.org/wiki/Diff_utility#Unified_format|unified diff\nformat] if both strings have more than two lines. New in Robot Framework\n2.9.1.\n\nExample:\n| ${first} =  | `Catenate` | SEPARATOR=\\n | Not in second | Same | Differs | Same |\n| ${second} = | `Catenate` | SEPARATOR=\\n | Same | Differs2 | Same | Not in first |\n| `Should Be Equal` | ${first} | ${second} |\n\nResults in the following error message:\n\n| Multiline strings are different:\n| --- first\n| +++ second\n| @@ -1,4 +1,4 @@\n| -Not in second\n|  Same\n| -Differs\n| +Differs2\n|  Same\n| +Not in first</doc>\n<kw name=\"Call Method\">\n<arguments>\n<arg>object</arg>\n<arg>method_name</arg>\n<arg>*args</arg>\n<arg>**kwargs</arg>\n</arguments>\n<doc>Calls the named method of the given object with the provided arguments.\n\nThe possible return value from the method is returned and can be\nassigned to a variable. Keyword fails both if the object does not have\na method with the given name or if executing the method raises an\nexception.\n\nSupport for ``**kwargs`` is new in Robot Framework 2.9. Since that\npossible equal signs in other arguments must be escaped with a\nbackslash like ``\\=``.\n\nExamples:\n| Call Method      | ${hashtable} | put          | myname  | myvalue |\n| ${isempty} =     | Call Method  | ${hashtable} | isEmpty |         |\n| Should Not Be True | ${isempty} |              |         |         |\n| ${value} =       | Call Method  | ${hashtable} | get     | myname  |\n| Should Be Equal  | ${value}     | myvalue      |         |         |\n| Call Method      | ${object}    | kwargs    | name=value | foo=bar |\n| Call Method      | ${object}    | positional   | escaped\\=equals  |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Catenate\">\n<arguments>\n<arg>*items</arg>\n</arguments>\n<doc>Catenates the given items together and returns the resulted string.\n\nBy default, items are catenated with spaces, but if the first item\ncontains the string ``SEPARATOR=&lt;sep&gt;``, the separator ``&lt;sep&gt;`` is\nused instead. Items are converted into strings when necessary.\n\nExamples:\n| ${str1} = | Catenate | Hello         | world |       |\n| ${str2} = | Catenate | SEPARATOR=--- | Hello | world |\n| ${str3} = | Catenate | SEPARATOR=    | Hello | world |\n=&gt;\n| ${str1} = 'Hello world'\n| ${str2} = 'Hello---world'\n| ${str3} = 'Helloworld'</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Comment\">\n<arguments>\n<arg>*messages</arg>\n</arguments>\n<doc>Displays the given messages in the log file as keyword arguments.\n\nThis keyword does nothing with the arguments it receives, but as they\nare visible in the log, this keyword can be used to display simple\nmessages. Given arguments are ignored so thoroughly that they can even\ncontain non-existing variables. If you are interested about variable\nvalues, you can use the `Log` or `Log Many` keywords.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Continue For Loop\">\n<arguments>\n</arguments>\n<doc>Skips the current for loop iteration and continues from the next.\n\nSkips the remaining keywords in the current for loop iteration and\ncontinues from the next one. Can be used directly in a for loop or\nin a keyword that the loop uses.\n\nExample:\n| :FOR | ${var}         | IN                     | @{VALUES}         |\n|      | Run Keyword If | '${var}' == 'CONTINUE' | Continue For Loop |\n|      | Do Something   | ${var}                 |\n\nSee `Continue For Loop If` to conditionally continue a for loop without\nusing `Run Keyword If` or other wrapper keywords.\n\nNew in Robot Framework 2.8.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Continue For Loop If\">\n<arguments>\n<arg>condition</arg>\n</arguments>\n<doc>Skips the current for loop iteration if the ``condition`` is true.\n\nA wrapper for `Continue For Loop` to continue a for loop based on\nthe given condition. The condition is evaluated using the same\nsemantics as with `Should Be True` keyword.\n\nExample:\n| :FOR | ${var}               | IN                     | @{VALUES} |\n|      | Continue For Loop If | '${var}' == 'CONTINUE' |\n|      | Do Something         | ${var}                 |\n\nNew in Robot Framework 2.8.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert To Binary\">\n<arguments>\n<arg>item</arg>\n<arg>base=None</arg>\n<arg>prefix=None</arg>\n<arg>length=None</arg>\n</arguments>\n<doc>Converts the given item to a binary string.\n\nThe ``item``, with an optional ``base``, is first converted to an\ninteger using `Convert To Integer` internally. After that it\nis converted to a binary number (base 2) represented as a\nstring such as ``1011``.\n\nThe returned value can contain an optional ``prefix`` and can be\nrequired to be of minimum ``length`` (excluding the prefix and a\npossible minus sign). If the value is initially shorter than\nthe required length, it is padded with zeros.\n\nExamples:\n| ${result} = | Convert To Binary | 10 |         |           | # Result is 1010   |\n| ${result} = | Convert To Binary | F  | base=16 | prefix=0b | # Result is 0b1111 |\n| ${result} = | Convert To Binary | -2 | prefix=B | length=4 | # Result is -B0010 |\n\nSee also `Convert To Integer`, `Convert To Octal` and `Convert To Hex`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert To Boolean\">\n<arguments>\n<arg>item</arg>\n</arguments>\n<doc>Converts the given item to Boolean true or false.\n\nHandles strings ``True`` and ``False`` (case-insensitive) as expected,\notherwise returns item's\n[http://docs.python.org/2/library/stdtypes.html#truth|truth value]\nusing Python's ``bool()`` method.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert To Bytes\">\n<arguments>\n<arg>input</arg>\n<arg>input_type=text</arg>\n</arguments>\n<doc>Converts the given ``input`` to bytes according to the ``input_type``.\n\nValid input types are listed below:\n\n- ``text:`` Converts text to bytes character by character. All\n  characters with ordinal below 256 can be used and are converted to\n  bytes with same values. Many characters are easiest to represent\n  using escapes like ``\\x00`` or ``\\xff``. Supports both Unicode\n  strings and bytes.\n\n- ``int:`` Converts integers separated by spaces to bytes. Similarly as\n  with `Convert To Integer`, it is possible to use binary, octal, or\n  hex values by prefixing the values with ``0b``, ``0o``, or ``0x``,\n  respectively.\n\n- ``hex:`` Converts hexadecimal values to bytes. Single byte is always\n  two characters long (e.g. ``01`` or ``FF``). Spaces are ignored and\n  can be used freely as a visual separator.\n\n- ``bin:`` Converts binary values to bytes. Single byte is always eight\n  characters long (e.g. ``00001010``). Spaces are ignored and can be\n  used freely as a visual separator.\n\nIn addition to giving the input as a string, it is possible to use\nlists or other iterables containing individual characters or numbers.\nIn that case numbers do not need to be padded to certain length and\nthey cannot contain extra spaces.\n\nExamples (last column shows returned bytes):\n| ${bytes} = | Convert To Bytes | hyvä    |     | # hyv\\xe4        |\n| ${bytes} = | Convert To Bytes | \\xff\\x07 |     | # \\xff\\x07      |\n| ${bytes} = | Convert To Bytes | 82 70      | int | # RF              |\n| ${bytes} = | Convert To Bytes | 0b10 0x10  | int | # \\x02\\x10      |\n| ${bytes} = | Convert To Bytes | ff 00 07   | hex | # \\xff\\x00\\x07 |\n| ${bytes} = | Convert To Bytes | 5246212121 | hex | # RF!!!           |\n| ${bytes} = | Convert To Bytes | 0000 1000  | bin | # \\x08           |\n| ${input} = | Create List      | 1          | 2   | 12                |\n| ${bytes} = | Convert To Bytes | ${input}   | int | # \\x01\\x02\\x0c |\n| ${bytes} = | Convert To Bytes | ${input}   | hex | # \\x01\\x02\\x12 |\n\nUse `Encode String To Bytes` in ``String`` library if you need to\nconvert text to bytes using a certain encoding.\n\nNew in Robot Framework 2.8.2.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert To Hex\">\n<arguments>\n<arg>item</arg>\n<arg>base=None</arg>\n<arg>prefix=None</arg>\n<arg>length=None</arg>\n<arg>lowercase=False</arg>\n</arguments>\n<doc>Converts the given item to a hexadecimal string.\n\nThe ``item``, with an optional ``base``, is first converted to an\ninteger using `Convert To Integer` internally. After that it\nis converted to a hexadecimal number (base 16) represented as\na string such as ``FF0A``.\n\nThe returned value can contain an optional ``prefix`` and can be\nrequired to be of minimum ``length`` (excluding the prefix and a\npossible minus sign). If the value is initially shorter than\nthe required length, it is padded with zeros.\n\nBy default the value is returned as an upper case string, but the\n``lowercase`` argument a true value (see `Boolean arguments`) turns\nthe value (but not the given prefix) to lower case.\n\nExamples:\n| ${result} = | Convert To Hex | 255 |           |              | # Result is FF    |\n| ${result} = | Convert To Hex | -10 | prefix=0x | length=2     | # Result is -0x0A |\n| ${result} = | Convert To Hex | 255 | prefix=X | lowercase=yes | # Result is Xff   |\n\nSee also `Convert To Integer`, `Convert To Binary` and `Convert To Octal`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert To Integer\">\n<arguments>\n<arg>item</arg>\n<arg>base=None</arg>\n</arguments>\n<doc>Converts the given item to an integer number.\n\nIf the given item is a string, it is by default expected to be an\ninteger in base 10. There are two ways to convert from other bases:\n\n- Give base explicitly to the keyword as ``base`` argument.\n\n- Prefix the given string with the base so that ``0b`` means binary\n  (base 2), ``0o`` means octal (base 8), and ``0x`` means hex (base 16).\n  The prefix is considered only when ``base`` argument is not given and\n  may itself be prefixed with a plus or minus sign.\n\nThe syntax is case-insensitive and possible spaces are ignored.\n\nExamples:\n| ${result} = | Convert To Integer | 100    |    | # Result is 100   |\n| ${result} = | Convert To Integer | FF AA  | 16 | # Result is 65450 |\n| ${result} = | Convert To Integer | 100    | 8  | # Result is 64    |\n| ${result} = | Convert To Integer | -100   | 2  | # Result is -4    |\n| ${result} = | Convert To Integer | 0b100  |    | # Result is 4     |\n| ${result} = | Convert To Integer | -0x100 |    | # Result is -256  |\n\nSee also `Convert To Number`, `Convert To Binary`, `Convert To Octal`,\n`Convert To Hex`, and `Convert To Bytes`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert To Number\">\n<arguments>\n<arg>item</arg>\n<arg>precision=None</arg>\n</arguments>\n<doc>Converts the given item to a floating point number.\n\nIf the optional ``precision`` is positive or zero, the returned number\nis rounded to that number of decimal digits. Negative precision means\nthat the number is rounded to the closest multiple of 10 to the power\nof the absolute precision. If a number is equally close to a certain\nprecision, it is always rounded away from zero.\n\nExamples:\n| ${result} = | Convert To Number | 42.512 |    | # Result is 42.512 |\n| ${result} = | Convert To Number | 42.512 | 1  | # Result is 42.5   |\n| ${result} = | Convert To Number | 42.512 | 0  | # Result is 43.0   |\n| ${result} = | Convert To Number | 42.512 | -1 | # Result is 40.0   |\n\nNotice that machines generally cannot store floating point numbers\naccurately. This may cause surprises with these numbers in general\nand also when they are rounded. For more information see, for example,\nthese resources:\n\n- http://docs.python.org/2/tutorial/floatingpoint.html\n- http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition\n\nIf you need an integer number, use `Convert To Integer` instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert To Octal\">\n<arguments>\n<arg>item</arg>\n<arg>base=None</arg>\n<arg>prefix=None</arg>\n<arg>length=None</arg>\n</arguments>\n<doc>Converts the given item to an octal string.\n\nThe ``item``, with an optional ``base``, is first converted to an\ninteger using `Convert To Integer` internally. After that it\nis converted to an octal number (base 8) represented as a\nstring such as ``775``.\n\nThe returned value can contain an optional ``prefix`` and can be\nrequired to be of minimum ``length`` (excluding the prefix and a\npossible minus sign). If the value is initially shorter than\nthe required length, it is padded with zeros.\n\nExamples:\n| ${result} = | Convert To Octal | 10 |            |          | # Result is 12      |\n| ${result} = | Convert To Octal | -F | base=16    | prefix=0 | # Result is -017    |\n| ${result} = | Convert To Octal | 16 | prefix=oct | length=4 | # Result is oct0020 |\n\nSee also `Convert To Integer`, `Convert To Binary` and `Convert To Hex`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert To String\">\n<arguments>\n<arg>item</arg>\n</arguments>\n<doc>Converts the given item to a Unicode string.\n\nUses ``__unicode__`` or ``__str__`` method with Python objects and\n``toString`` with Java objects.\n\nUse `Encode String To Bytes` and `Decode Bytes To String` keywords\nin ``String`` library if you need to convert between Unicode and byte\nstrings using different encodings. Use `Convert To Bytes` if you just\nwant to create byte strings.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Create Dictionary\">\n<arguments>\n<arg>*items</arg>\n</arguments>\n<doc>Creates and returns a dictionary based on the given ``items``.\n\nItems are typically given using the ``key=value`` syntax same way as\n``&amp;{dictionary}`` variables are created in the Variable table. Both\nkeys and values can contain variables, and possible equal sign in key\ncan be escaped with a backslash like ``escaped\\=key=value``. It is\nalso possible to get items from existing dictionaries by simply using\nthem like ``&amp;{dict}``.\n\nAlternatively items can be specified so that keys and values are given\nseparately. This and the ``key=value`` syntax can even be combined,\nbut separately given items must be first.\n\nIf same key is used multiple times, the last value has precedence.\nThe returned dictionary is ordered, and values with strings as keys\ncan also be accessed using a convenient dot-access syntax like\n``${dict.key}``.\n\nExamples:\n| &amp;{dict} = | Create Dictionary | key=value | foo=bar | | | # key=value syntax |\n| Should Be True | ${dict} == {'key': 'value', 'foo': 'bar'} |\n| &amp;{dict2} = | Create Dictionary | key | value | foo | bar | # separate key and value |\n| Should Be Equal | ${dict} | ${dict2} |\n| &amp;{dict} = | Create Dictionary | ${1}=${2} | &amp;{dict} | foo=new | | # using variables |\n| Should Be True | ${dict} == {1: 2, 'key': 'value', 'foo': 'new'} |\n| Should Be Equal | ${dict.key} | value | | | | # dot-access |\n\nThis keyword was changed in Robot Framework 2.9 in many ways:\n- Moved from ``Collections`` library to ``BuiltIn``.\n- Support also non-string keys in ``key=value`` syntax.\n- Returned dictionary is ordered and dot-accessible.\n- Old syntax to give keys and values separately was deprecated, but\n  deprecation was later removed in RF 3.0.1.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Create List\">\n<arguments>\n<arg>*items</arg>\n</arguments>\n<doc>Returns a list containing given items.\n\nThe returned list can be assigned both to ``${scalar}`` and ``@{list}``\nvariables.\n\nExamples:\n| @{list} =   | Create List | a    | b    | c    |\n| ${scalar} = | Create List | a    | b    | c    |\n| ${ints} =   | Create List | ${1} | ${2} | ${3} |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Evaluate\">\n<arguments>\n<arg>expression</arg>\n<arg>modules=None</arg>\n<arg>namespace=None</arg>\n</arguments>\n<doc>Evaluates the given expression in Python and returns the results.\n\n``expression`` is evaluated in Python as explained in `Evaluating\nexpressions`.\n\n``modules`` argument can be used to specify a comma separated\nlist of Python modules to be imported and added to the evaluation\nnamespace.\n\n``namespace`` argument can be used to pass a custom evaluation\nnamespace as a dictionary. Possible ``modules`` are added to this\nnamespace. This is a new feature in Robot Framework 2.8.4.\n\nVariables used like ``${variable}`` are replaced in the expression\nbefore evaluation. Variables are also available in the evaluation\nnamespace and can be accessed using special syntax ``$variable``.\nThis is a new feature in Robot Framework 2.9 and it is explained more\nthoroughly in `Evaluating expressions`.\n\nExamples (expecting ``${result}`` is 3.14):\n| ${status} = | Evaluate | 0 &lt; ${result} &lt; 10 | # Would also work with string '3.14' |\n| ${status} = | Evaluate | 0 &lt; $result &lt; 10   | # Using variable itself, not string representation |\n| ${random} = | Evaluate | random.randint(0, sys.maxint) | modules=random, sys |\n| ${ns} =     | Create Dictionary | x=${4}    | y=${2}              |\n| ${result} = | Evaluate | x*10 + y           | namespace=${ns}     |\n=&gt;\n| ${status} = True\n| ${random} = &lt;random integer&gt;\n| ${result} = 42</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Exit For Loop\">\n<arguments>\n</arguments>\n<doc>Stops executing the enclosing for loop.\n\nExits the enclosing for loop and continues execution after it.\nCan be used directly in a for loop or in a keyword that the loop uses.\n\nExample:\n| :FOR | ${var}         | IN                 | @{VALUES}     |\n|      | Run Keyword If | '${var}' == 'EXIT' | Exit For Loop |\n|      | Do Something   | ${var} |\n\nSee `Exit For Loop If` to conditionally exit a for loop without\nusing `Run Keyword If` or other wrapper keywords.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Exit For Loop If\">\n<arguments>\n<arg>condition</arg>\n</arguments>\n<doc>Stops executing the enclosing for loop if the ``condition`` is true.\n\nA wrapper for `Exit For Loop` to exit a for loop based on\nthe given condition. The condition is evaluated using the same\nsemantics as with `Should Be True` keyword.\n\nExample:\n| :FOR | ${var}           | IN                 | @{VALUES} |\n|      | Exit For Loop If | '${var}' == 'EXIT' |\n|      | Do Something     | ${var}             |\n\nNew in Robot Framework 2.8.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Fail\">\n<arguments>\n<arg>msg=None</arg>\n<arg>*tags</arg>\n</arguments>\n<doc>Fails the test with the given message and optionally alters its tags.\n\nThe error message is specified using the ``msg`` argument.\nIt is possible to use HTML in the given error message, similarly\nas with any other keyword accepting an error message, by prefixing\nthe error with ``*HTML*``.\n\nIt is possible to modify tags of the current test case by passing tags\nafter the message. Tags starting with a hyphen (e.g. ``-regression``)\nare removed and others added. Tags are modified using `Set Tags` and\n`Remove Tags` internally, and the semantics setting and removing them\nare the same as with these keywords.\n\nExamples:\n| Fail | Test not ready   |             | | # Fails with the given message.    |\n| Fail | *HTML*&lt;b&gt;Test not ready&lt;/b&gt; | | | # Fails using HTML in the message. |\n| Fail | Test not ready   | not-ready   | | # Fails and adds 'not-ready' tag.  |\n| Fail | OS not supported | -regression | | # Removes tag 'regression'.        |\n| Fail | My message       | tag    | -t*  | # Removes all tags starting with 't' except the newly added 'tag'. |\n\nSee `Fatal Error` if you need to stop the whole test execution.\n\nSupport for modifying tags was added in Robot Framework 2.7.4 and\nHTML message support in 2.8.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Fatal Error\">\n<arguments>\n<arg>msg=None</arg>\n</arguments>\n<doc>Stops the whole test execution.\n\nThe test or suite where this keyword is used fails with the provided\nmessage, and subsequent tests fail with a canned message.\nPossible teardowns will nevertheless be executed.\n\nSee `Fail` if you only want to stop one test case unconditionally.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Count\">\n<arguments>\n<arg>item1</arg>\n<arg>item2</arg>\n</arguments>\n<doc>Returns and logs how many times ``item2`` is found from ``item1``.\n\nThis keyword works with Python strings and lists and all objects\nthat either have ``count`` method or can be converted to Python lists.\n\nExample:\n| ${count} = | Get Count | ${some item} | interesting value |\n| Should Be True | 5 &lt; ${count} &lt; 10 |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Length\">\n<arguments>\n<arg>item</arg>\n</arguments>\n<doc>Returns and logs the length of the given item as an integer.\n\nThe item can be anything that has a length, for example, a string,\na list, or a mapping. The keyword first tries to get the length with\nthe Python function ``len``, which calls the  item's ``__len__`` method\ninternally. If that fails, the keyword tries to call the item's\npossible ``length`` and ``size`` methods directly. The final attempt is\ntrying to get the value of the item's ``length`` attribute. If all\nthese attempts are unsuccessful, the keyword fails.\n\nExamples:\n| ${length} = | Get Length    | Hello, world! |        |\n| Should Be Equal As Integers | ${length}     | 13     |\n| @{list} =   | Create List   | Hello,        | world! |\n| ${length} = | Get Length    | ${list}       |        |\n| Should Be Equal As Integers | ${length}     | 2      |\n\nSee also `Length Should Be`, `Should Be Empty` and `Should Not Be\nEmpty`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Library Instance\">\n<arguments>\n<arg>name=None</arg>\n<arg>all=False</arg>\n</arguments>\n<doc>Returns the currently active instance of the specified test library.\n\nThis keyword makes it easy for test libraries to interact with\nother test libraries that have state. This is illustrated by\nthe Python example below:\n\n| from robot.libraries.BuiltIn import BuiltIn\n|\n| def title_should_start_with(expected):\n|     seleniumlib = BuiltIn().get_library_instance('SeleniumLibrary')\n|     title = seleniumlib.get_title()\n|     if not title.startswith(expected):\n|         raise AssertionError(\"Title '%s' did not start with '%s'\"\n|                              % (title, expected))\n\nIt is also possible to use this keyword in the test data and\npass the returned library instance to another keyword. If a\nlibrary is imported with a custom name, the ``name`` used to get\nthe instance must be that name and not the original library name.\n\nIf the optional argument ``all`` is given a true value, then a\ndictionary mapping all library names to instances will be returned.\nThis feature is new in Robot Framework 2.9.2.\n\nExample:\n| &amp;{all libs} = | Get library instance | all=True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Time\">\n<arguments>\n<arg>format=timestamp</arg>\n<arg>time_=NOW</arg>\n</arguments>\n<doc>Returns the given time in the requested format.\n\n*NOTE:* DateTime library added in Robot Framework 2.8.5 contains\nmuch more flexible keywords for getting the current date and time\nand for date and time handling in general.\n\nHow time is returned is determined based on the given ``format``\nstring as follows. Note that all checks are case-insensitive.\n\n1) If ``format`` contains the word ``epoch``, the time is returned\n   in seconds after the UNIX epoch (1970-01-01 00:00:00 UTC).\n   The return value is always an integer.\n\n2) If ``format`` contains any of the words ``year``, ``month``,\n   ``day``, ``hour``, ``min``, or ``sec``, only the selected parts are\n   returned. The order of the returned parts is always the one\n   in the previous sentence and the order of words in ``format``\n   is not significant. The parts are returned as zero-padded\n   strings (e.g. May -&gt; ``05``).\n\n3) Otherwise (and by default) the time is returned as a\n   timestamp string in the format ``2006-02-24 15:08:31``.\n\nBy default this keyword returns the current local time, but\nthat can be altered using ``time`` argument as explained below.\nNote that all checks involving strings are case-insensitive.\n\n1) If ``time`` is a number, or a string that can be converted to\n   a number, it is interpreted as seconds since the UNIX epoch.\n   This documentation was originally written about 1177654467\n   seconds after the epoch.\n\n2) If ``time`` is a timestamp, that time will be used. Valid\n   timestamp formats are ``YYYY-MM-DD hh:mm:ss`` and\n   ``YYYYMMDD hhmmss``.\n\n3) If ``time`` is equal to ``NOW`` (default), the current local\n   time is used. This time is got using Python's ``time.time()``\n   function.\n\n4) If ``time`` is equal to ``UTC``, the current time in\n   [http://en.wikipedia.org/wiki/Coordinated_Universal_Time|UTC]\n   is used. This time is got using ``time.time() + time.altzone``\n   in Python.\n\n5) If ``time`` is in the format like ``NOW - 1 day`` or ``UTC + 1 hour\n   30 min``, the current local/UTC time plus/minus the time\n   specified with the time string is used. The time string format\n   is described in an appendix of Robot Framework User Guide.\n\nExamples (expecting the current local time is 2006-03-29 15:06:21):\n| ${time} = | Get Time |             |  |  |\n| ${secs} = | Get Time | epoch       |  |  |\n| ${year} = | Get Time | return year |  |  |\n| ${yyyy}   | ${mm}    | ${dd} =     | Get Time | year,month,day |\n| @{time} = | Get Time | year month day hour min sec |  |  |\n| ${y}      | ${s} =   | Get Time    | seconds and year |  |\n=&gt;\n| ${time} = '2006-03-29 15:06:21'\n| ${secs} = 1143637581\n| ${year} = '2006'\n| ${yyyy} = '2006', ${mm} = '03', ${dd} = '29'\n| @{time} = ['2006', '03', '29', '15', '06', '21']\n| ${y} = '2006'\n| ${s} = '21'\n\nExamples (expecting the current local time is 2006-03-29 15:06:21 and\nUTC time is 2006-03-29 12:06:21):\n| ${time} = | Get Time |              | 1177654467          | # Time given as epoch seconds        |\n| ${secs} = | Get Time | sec          | 2007-04-27 09:14:27 | # Time given as a timestamp          |\n| ${year} = | Get Time | year         | NOW                 | # The local time of execution        |\n| @{time} = | Get Time | hour min sec | NOW + 1h 2min 3s    | # 1h 2min 3s added to the local time |\n| @{utc} =  | Get Time | hour min sec | UTC                 | # The UTC time of execution          |\n| ${hour} = | Get Time | hour         | UTC - 1 hour        | # 1h subtracted from the UTC  time   |\n=&gt;\n| ${time} = '2007-04-27 09:14:27'\n| ${secs} = 27\n| ${year} = '2006'\n| @{time} = ['16', '08', '24']\n| @{utc} = ['12', '06', '21']\n| ${hour} = '11'\n\nSupport for UTC time was added in Robot Framework 2.7.5 but it did not\nwork correctly until 2.7.7.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Variable Value\">\n<arguments>\n<arg>name</arg>\n<arg>default=None</arg>\n</arguments>\n<doc>Returns variable value or ``default`` if the variable does not exist.\n\nThe name of the variable can be given either as a normal variable name\n(e.g. ``${NAME}``) or in escaped format (e.g. ``\\${NAME}``). Notice\nthat the former has some limitations explained in `Set Suite Variable`.\n\nExamples:\n| ${x} = | Get Variable Value | ${a} | default |\n| ${y} = | Get Variable Value | ${a} | ${b}    |\n| ${z} = | Get Variable Value | ${z} |         |\n=&gt;\n| ${x} gets value of ${a} if ${a} exists and string 'default' otherwise\n| ${y} gets value of ${a} if ${a} exists and value of ${b} otherwise\n| ${z} is set to Python None if it does not exist previously\n\nSee `Set Variable If` for another keyword to set variables dynamically.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Variables\">\n<arguments>\n<arg>no_decoration=False</arg>\n</arguments>\n<doc>Returns a dictionary containing all variables in the current scope.\n\nVariables are returned as a special dictionary that allows accessing\nvariables in space, case, and underscore insensitive manner similarly\nas accessing variables in the test data. This dictionary supports all\nsame operations as normal Python dictionaries and, for example,\nCollections library can be used to access or modify it. Modifying the\nreturned dictionary has no effect on the variables available in the\ncurrent scope.\n\nBy default variables are returned with ``${}``, ``@{}`` or ``&amp;{}``\ndecoration based on variable types. Giving a true value (see `Boolean\narguments`) to the optional argument ``no_decoration`` will return\nthe variables without the decoration. This option is new in Robot\nFramework 2.9.\n\nExample:\n| ${example_variable} =         | Set Variable | example value         |\n| ${variables} =                | Get Variables |                      |\n| Dictionary Should Contain Key | ${variables} | \\${example_variable} |\n| Dictionary Should Contain Key | ${variables} | \\${ExampleVariable}  |\n| Set To Dictionary             | ${variables} | \\${name} | value     |\n| Variable Should Not Exist     | \\${name}    |           |           |\n| ${no decoration} =            | Get Variables | no_decoration=Yes |\n| Dictionary Should Contain Key | ${no decoration} | example_variable |\n\nNote: Prior to Robot Framework 2.7.4 variables were returned as\na custom object that did not support all dictionary methods.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Import Library\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Imports a library with the given name and optional arguments.\n\nThis functionality allows dynamic importing of libraries while tests\nare running. That may be necessary, if the library itself is dynamic\nand not yet available when test data is processed. In a normal case,\nlibraries should be imported using the Library setting in the Setting\ntable.\n\nThis keyword supports importing libraries both using library\nnames and physical paths. When paths are used, they must be\ngiven in absolute format or found from\n[http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#pythonpath-jythonpath-and-ironpythonpath|\nsearch path]. Forward slashes can be used as path separators in all\noperating systems.\n\nIt is possible to pass arguments to the imported library and also\nnamed argument syntax works if the library supports it. ``WITH NAME``\nsyntax can be used to give a custom name to the imported library.\n\nExamples:\n| Import Library | MyLibrary |\n| Import Library | ${CURDIR}/../Library.py | arg1 | named=arg2 |\n| Import Library | ${LIBRARIES}/Lib.java | arg | WITH NAME | JavaLib |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Import Resource\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Imports a resource file with the given path.\n\nResources imported with this keyword are set into the test suite scope\nsimilarly when importing them in the Setting table using the Resource\nsetting.\n\nThe given path must be absolute or found from\n[http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#pythonpath-jythonpath-and-ironpythonpath|\nsearch path]. Forward slashes can be used as path separator regardless\nthe operating system.\n\nExamples:\n| Import Resource | ${CURDIR}/resource.txt |\n| Import Resource | ${CURDIR}/../resources/resource.html |\n| Import Resource | found_from_pythonpath.robot |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Import Variables\">\n<arguments>\n<arg>path</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Imports a variable file with the given path and optional arguments.\n\nVariables imported with this keyword are set into the test suite scope\nsimilarly when importing them in the Setting table using the Variables\nsetting. These variables override possible existing variables with\nthe same names. This functionality can thus be used to import new\nvariables, for example, for each test in a test suite.\n\nThe given path must be absolute or found from\n[http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#pythonpath-jythonpath-and-ironpythonpath|\nsearch path]. Forward slashes can be used as path separator regardless\nthe operating system.\n\nExamples:\n| Import Variables | ${CURDIR}/variables.py   |      |      |\n| Import Variables | ${CURDIR}/../vars/env.py | arg1 | arg2 |\n| Import Variables | file_from_pythonpath.py  |      |      |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Keyword Should Exist\">\n<arguments>\n<arg>name</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails unless the given keyword exists in the current scope.\n\nFails also if there are more than one keywords with the same name.\nWorks both with the short name (e.g. ``Log``) and the full name\n(e.g. ``BuiltIn.Log``).\n\nThe default error message can be overridden with the ``msg`` argument.\n\nSee also `Variable Should Exist`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Length Should Be\">\n<arguments>\n<arg>item</arg>\n<arg>length</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Verifies that the length of the given item is correct.\n\nThe length of the item is got using the `Get Length` keyword. The\ndefault error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log\">\n<arguments>\n<arg>message</arg>\n<arg>level=INFO</arg>\n<arg>html=False</arg>\n<arg>console=False</arg>\n<arg>repr=False</arg>\n</arguments>\n<doc>Logs the given message with the given level.\n\nValid levels are TRACE, DEBUG, INFO (default), HTML, WARN, and ERROR.\nMessages below the current active log level are ignored. See\n`Set Log Level` keyword and ``--loglevel`` command line option\nfor more details about setting the level.\n\nMessages logged with the WARN or ERROR levels will be automatically\nvisible also in the console and in the Test Execution Errors section\nin the log file.\n\nLogging can be configured using optional ``html``, ``console`` and\n``repr`` arguments. They are off by default, but can be enabled\nby giving them a true value. See `Boolean arguments` section for more\ninformation about true and false values.\n\nIf the ``html`` argument is given a true value, the message will be\nconsidered HTML and special characters such as ``&lt;`` in it are not\nescaped. For example, logging ``&lt;img src=\"image.png\"&gt;`` creates an\nimage when ``html`` is true, but otherwise the message is that exact\nstring. An alternative to using the ``html`` argument is using the HTML\npseudo log level. It logs the message as HTML using the INFO level.\n\nIf the ``console`` argument is true, the message will be written to\nthe console where test execution was started from in addition to\nthe log file. This keyword always uses the standard output stream\nand adds a newline after the written message. Use `Log To Console`\ninstead if either of these is undesirable,\n\nIf the ``repr`` argument is true, the given item will be passed through\na custom version of Python's ``pprint.pformat()`` function before\nlogging it. This is useful, for example, when working with strings or\nbytes containing invisible characters, or when working with nested data\nstructures. The custom version differs from the standard one so that it\nomits the ``u`` prefix from Unicode strings and adds ``b`` prefix to\nbyte strings.\n\nExamples:\n| Log | Hello, world!        |          |   | # Normal INFO message.   |\n| Log | Warning, world!      | WARN     |   | # Warning.               |\n| Log | &lt;b&gt;Hello&lt;/b&gt;, world! | html=yes |   | # INFO message as HTML.  |\n| Log | &lt;b&gt;Hello&lt;/b&gt;, world! | HTML     |   | # Same as above.         |\n| Log | &lt;b&gt;Hello&lt;/b&gt;, world! | DEBUG    | html=true | # DEBUG as HTML. |\n| Log | Hello, console!   | console=yes | | # Log also to the console. |\n| Log | Hyvä \\x00     | repr=yes    | | # Log ``'Hyv\\xe4 \\x00'``. |\n\nSee `Log Many` if you want to log multiple messages in one go, and\n`Log To Console` if you only want to write to the console.\n\nArguments ``html``, ``console``, and ``repr`` are new in Robot Framework\n2.8.2.\n\nPprint support when ``repr`` is used is new in Robot Framework 2.8.6,\nand it was changed to drop the ``u`` prefix and add the ``b`` prefix\nin Robot Framework 2.9.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log Many\">\n<arguments>\n<arg>*messages</arg>\n</arguments>\n<doc>Logs the given messages as separate entries using the INFO level.\n\nSupports also logging list and dictionary variable items individually.\n\nExamples:\n| Log Many | Hello   | ${var}  |\n| Log Many | @{list} | &amp;{dict} |\n\nSee `Log` and `Log To Console` keywords if you want to use alternative\nlog levels, use HTML, or log to the console.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log To Console\">\n<arguments>\n<arg>message</arg>\n<arg>stream=STDOUT</arg>\n<arg>no_newline=False</arg>\n</arguments>\n<doc>Logs the given message to the console.\n\nBy default uses the standard output stream. Using the standard error\nstream is possibly by giving the ``stream`` argument value ``STDERR``\n(case-insensitive).\n\nBy default appends a newline to the logged message. This can be\ndisabled by giving the ``no_newline`` argument a true value (see\n`Boolean arguments`).\n\nExamples:\n| Log To Console | Hello, console!             |                 |\n| Log To Console | Hello, stderr!              | STDERR          |\n| Log To Console | Message starts here and is  | no_newline=true |\n| Log To Console | continued without newline.  |                 |\n\nThis keyword does not log the message to the normal log file. Use\n`Log` keyword, possibly with argument ``console``, if that is desired.\n\nNew in Robot Framework 2.8.2.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log Variables\">\n<arguments>\n<arg>level=INFO</arg>\n</arguments>\n<doc>Logs all variables in the current scope with given log level.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"No Operation\">\n<arguments>\n</arguments>\n<doc>Does absolutely nothing.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Pass Execution\">\n<arguments>\n<arg>message</arg>\n<arg>*tags</arg>\n</arguments>\n<doc>Skips rest of the current test, setup, or teardown with PASS status.\n\nThis keyword can be used anywhere in the test data, but the place where\nused affects the behavior:\n\n- When used in any setup or teardown (suite, test or keyword), passes\n  that setup or teardown. Possible keyword teardowns of the started\n  keywords are executed. Does not affect execution or statuses\n  otherwise.\n- When used in a test outside setup or teardown, passes that particular\n  test case. Possible test and keyword teardowns are executed.\n\nPossible continuable failures before this keyword is used, as well as\nfailures in executed teardowns, will fail the execution.\n\nIt is mandatory to give a message explaining why execution was passed.\nBy default the message is considered plain text, but starting it with\n``*HTML*`` allows using HTML formatting.\n\nIt is also possible to modify test tags passing tags after the message\nsimilarly as with `Fail` keyword. Tags starting with a hyphen\n(e.g. ``-regression``) are removed and others added. Tags are modified\nusing `Set Tags` and `Remove Tags` internally, and the semantics\nsetting and removing them are the same as with these keywords.\n\nExamples:\n| Pass Execution | All features available in this version tested. |\n| Pass Execution | Deprecated test. | deprecated | -regression    |\n\nThis keyword is typically wrapped to some other keyword, such as\n`Run Keyword If`, to pass based on a condition. The most common case\ncan be handled also with `Pass Execution If`:\n\n| Run Keyword If    | ${rc} &lt; 0 | Pass Execution | Negative values are cool. |\n| Pass Execution If | ${rc} &lt; 0 | Negative values are cool. |\n\nPassing execution in the middle of a test, setup or teardown should be\nused with care. In the worst case it leads to tests that skip all the\nparts that could actually uncover problems in the tested application.\nIn cases where execution cannot continue do to external factors,\nit is often safer to fail the test case and make it non-critical.\n\nNew in Robot Framework 2.8.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Pass Execution If\">\n<arguments>\n<arg>condition</arg>\n<arg>message</arg>\n<arg>*tags</arg>\n</arguments>\n<doc>Conditionally skips rest of the current test, setup, or teardown with PASS status.\n\nA wrapper for `Pass Execution` to skip rest of the current test,\nsetup or teardown based the given ``condition``. The condition is\nevaluated similarly as with `Should Be True` keyword, and ``message``\nand ``*tags`` have same semantics as with `Pass Execution`.\n\nExample:\n| :FOR | ${var}            | IN                     | @{VALUES}               |\n|      | Pass Execution If | '${var}' == 'EXPECTED' | Correct value was found |\n|      | Do Something      | ${var}                 |\n\nNew in Robot Framework 2.8.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Regexp Escape\">\n<arguments>\n<arg>*patterns</arg>\n</arguments>\n<doc>Returns each argument string escaped for use as a regular expression.\n\nThis keyword can be used to escape strings to be used with\n`Should Match Regexp` and `Should Not Match Regexp` keywords.\n\nEscaping is done with Python's ``re.escape()`` function.\n\nExamples:\n| ${escaped} = | Regexp Escape | ${original} |\n| @{strings} = | Regexp Escape | @{strings}  |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Reload Library\">\n<arguments>\n<arg>name_or_instance</arg>\n</arguments>\n<doc>Rechecks what keywords the specified library provides.\n\nCan be called explicitly in the test data or by a library itself\nwhen keywords it provides have changed.\n\nThe library can be specified by its name or as the active instance of\nthe library. The latter is especially useful if the library itself\ncalls this keyword as a method.\n\nNew in Robot Framework 2.9.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Tags\">\n<arguments>\n<arg>*tags</arg>\n</arguments>\n<doc>Removes given ``tags`` from the current test or all tests in a suite.\n\nTags can be given exactly or using a pattern where ``*`` matches\nanything and ``?`` matches one character.\n\nThis keyword can affect either one test case or all test cases in a\ntest suite similarly as `Set Tags` keyword.\n\nThe current tags are available as a built-in variable ``@{TEST TAGS}``.\n\nExample:\n| Remove Tags | mytag | something-* | ?ython |\n\nSee `Set Tags` if you want to add certain tags and `Fail` if you want\nto fail the test case after setting and/or removing tags.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Repeat Keyword\">\n<arguments>\n<arg>repeat</arg>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Executes the specified keyword multiple times.\n\n``name`` and ``args`` define the keyword that is executed similarly as\nwith `Run Keyword`. ``repeat`` specifies how many times (as a count) or\nhow long time (as a timeout) the keyword should be executed.\n\nIf ``repeat`` is given as count, it specifies how many times the\nkeyword should be executed. ``repeat`` can be given as an integer or\nas a string that can be converted to an integer. If it is a string,\nit can have postfix ``times`` or ``x`` (case and space insensitive)\nto make the expression more explicit.\n\nIf ``repeat`` is given as timeout, it must be in Robot Framework's\ntime format (e.g. ``1 minute``, ``2 min 3 s``). Using a number alone\n(e.g. ``1`` or ``1.5``) does not work in this context.\n\nIf ``repeat`` is zero or negative, the keyword is not executed at\nall. This keyword fails immediately if any of the execution\nrounds fails.\n\nExamples:\n| Repeat Keyword | 5 times   | Go to Previous Page |\n| Repeat Keyword | ${var}    | Some Keyword | arg1 | arg2 |\n| Repeat Keyword | 2 minutes | Some Keyword | arg1 | arg2 |\n\nSpecifying ``repeat`` as a timeout is new in Robot Framework 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Replace Variables\">\n<arguments>\n<arg>text</arg>\n</arguments>\n<doc>Replaces variables in the given text with their current values.\n\nIf the text contains undefined variables, this keyword fails.\nIf the given ``text`` contains only a single variable, its value is\nreturned as-is and it can be any object. Otherwise this keyword\nalways returns a string.\n\nExample:\n\nThe file ``template.txt`` contains ``Hello ${NAME}!`` and variable\n``${NAME}`` has the value ``Robot``.\n\n| ${template} =   | Get File          | ${CURDIR}/template.txt |\n| ${message} =    | Replace Variables | ${template}            |\n| Should Be Equal | ${message}        | Hello Robot!           |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Return From Keyword\">\n<arguments>\n<arg>*return_values</arg>\n</arguments>\n<doc>Returns from the enclosing user keyword.\n\nThis keyword can be used to return from a user keyword with PASS status\nwithout executing it fully. It is also possible to return values\nsimilarly as with the ``[Return]`` setting. For more detailed information\nabout working with the return values, see the User Guide.\n\nThis keyword is typically wrapped to some other keyword, such as\n`Run Keyword If` or `Run Keyword If Test Passed`, to return based\non a condition:\n\n| Run Keyword If | ${rc} &lt; 0 | Return From Keyword |\n| Run Keyword If Test Passed | Return From Keyword |\n\nIt is possible to use this keyword to return from a keyword also inside\na for loop. That, as well as returning values, is demonstrated by the\n`Find Index` keyword in the following somewhat advanced example.\nNotice that it is often a good idea to move this kind of complicated\nlogic into a test library.\n\n| ***** Variables *****\n| @{LIST} =    foo    baz\n|\n| ***** Test Cases *****\n| Example\n|     ${index} =    Find Index    baz    @{LIST}\n|     Should Be Equal    ${index}    ${1}\n|     ${index} =    Find Index    non existing    @{LIST}\n|     Should Be Equal    ${index}    ${-1}\n|\n| ***** Keywords *****\n| Find Index\n|    [Arguments]    ${element}    @{items}\n|    ${index} =    Set Variable    ${0}\n|    :FOR    ${item}    IN    @{items}\n|    \\    Run Keyword If    '${item}' == '${element}'    Return From Keyword    ${index}\n|    \\    ${index} =    Set Variable    ${index + 1}\n|    Return From Keyword    ${-1}    # Also [Return] would work here.\n\nThe most common use case, returning based on an expression, can be\naccomplished directly with `Return From Keyword If`. Both of these\nkeywords are new in Robot Framework 2.8.\n\nSee also `Run Keyword And Return` and `Run Keyword And Return If`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Return From Keyword If\">\n<arguments>\n<arg>condition</arg>\n<arg>*return_values</arg>\n</arguments>\n<doc>Returns from the enclosing user keyword if ``condition`` is true.\n\nA wrapper for `Return From Keyword` to return based on the given\ncondition. The condition is evaluated using the same semantics as\nwith `Should Be True` keyword.\n\nGiven the same example as in `Return From Keyword`, we can rewrite the\n`Find Index` keyword as follows:\n\n| ***** Keywords *****\n| Find Index\n|    [Arguments]    ${element}    @{items}\n|    ${index} =    Set Variable    ${0}\n|    :FOR    ${item}    IN    @{items}\n|    \\    Return From Keyword If    '${item}' == '${element}'    ${index}\n|    \\    ${index} =    Set Variable    ${index + 1}\n|    Return From Keyword    ${-1}    # Also [Return] would work here.\n\nSee also `Run Keyword And Return` and `Run Keyword And Return If`.\n\nNew in Robot Framework 2.8.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Executes the given keyword with the given arguments.\n\nBecause the name of the keyword to execute is given as an argument, it\ncan be a variable and thus set dynamically, e.g. from a return value of\nanother keyword or from the command line.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword And Continue On Failure\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the keyword and continues execution even if a failure occurs.\n\nThe keyword name and arguments work as with `Run Keyword`.\n\nExample:\n| Run Keyword And Continue On Failure | Fail | This is a stupid example |\n| Log | This keyword is executed |\n\nThe execution is not continued if the failure is caused by invalid syntax,\ntimeout, or fatal exception.\nSince Robot Framework 2.9, variable errors are caught by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword And Expect Error\">\n<arguments>\n<arg>expected_error</arg>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the keyword and checks that the expected error occurred.\n\nThe expected error must be given in the same format as in\nRobot Framework reports. It can be a pattern containing\ncharacters ``?``, which matches to any single character and\n``*``, which matches to any number of any characters. ``name`` and\n``*args`` have same semantics as with `Run Keyword`.\n\nIf the expected error occurs, the error message is returned and it can\nbe further processed/tested, if needed. If there is no error, or the\nerror does not match the expected error, this keyword fails.\n\nExamples:\n| Run Keyword And Expect Error | My error | Some Keyword | arg1 | arg2 |\n| ${msg} = | Run Keyword And Expect Error | * | My KW |\n| Should Start With | ${msg} | Once upon a time in |\n\nErrors caused by invalid syntax, timeouts, or fatal exceptions are not\ncaught by this keyword.\nSince Robot Framework 2.9, variable errors are caught by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword And Ignore Error\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the given keyword with the given arguments and ignores possible error.\n\nThis keyword returns two values, so that the first is either string\n``PASS`` or ``FAIL``, depending on the status of the executed keyword.\nThe second value is either the return value of the keyword or the\nreceived error message. See `Run Keyword And Return Status` If you are\nonly interested in the execution status.\n\nThe keyword name and arguments work as in `Run Keyword`. See\n`Run Keyword If` for a usage example.\n\nErrors caused by invalid syntax, timeouts, or fatal exceptions are not\ncaught by this keyword. Otherwise this keyword itself never fails.\nSince Robot Framework 2.9, variable errors are caught by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword And Return\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the specified keyword and returns from the enclosing user keyword.\n\nThe keyword to execute is defined with ``name`` and ``*args`` exactly\nlike with `Run Keyword`. After running the keyword, returns from the\nenclosing user keyword and passes possible return value from the\nexecuted keyword further. Returning from a keyword has exactly same\nsemantics as with `Return From Keyword`.\n\nExample:\n| `Run Keyword And Return`  | `My Keyword` | arg1 | arg2 |\n| # Above is equivalent to: |\n| ${result} =               | `My Keyword` | arg1 | arg2 |\n| `Return From Keyword`     | ${result}    |      |      |\n\nUse `Run Keyword And Return If` if you want to run keyword and return\nbased on a condition.\n\nNew in Robot Framework 2.8.2.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword And Return If\">\n<arguments>\n<arg>condition</arg>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the specified keyword and returns from the enclosing user keyword.\n\nA wrapper for `Run Keyword And Return` to run and return based on\nthe given ``condition``. The condition is evaluated using the same\nsemantics as with `Should Be True` keyword.\n\nExample:\n| `Run Keyword And Return If` | ${rc} &gt; 0 | `My Keyword` | arg1 | arg2 |\n| # Above is equivalent to:   |\n| `Run Keyword If`            | ${rc} &gt; 0 | `Run Keyword And Return` | `My Keyword ` | arg1 | arg2 |\n\nUse `Return From Keyword If` if you want to return a certain value\nbased on a condition.\n\nNew in Robot Framework 2.8.2.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword And Return Status\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the given keyword with given arguments and returns the status as a Boolean value.\n\nThis keyword returns Boolean ``True`` if the keyword that is executed\nsucceeds and ``False`` if it fails. This is useful, for example, in\ncombination with `Run Keyword If`. If you are interested in the error\nmessage or return value, use `Run Keyword And Ignore Error` instead.\n\nThe keyword name and arguments work as in `Run Keyword`.\n\nExample:\n| ${passed} = | `Run Keyword And Return Status` | Keyword | args |\n| `Run Keyword If` | ${passed} | Another keyword |\n\nErrors caused by invalid syntax, timeouts, or fatal exceptions are not\ncaught by this keyword. Otherwise this keyword itself never fails.\n\nNew in Robot Framework 2.7.6.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword If\">\n<arguments>\n<arg>condition</arg>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the given keyword with the given arguments, if ``condition`` is true.\n\nThe given ``condition`` is evaluated in Python as explained in\n`Evaluating expressions`, and ``name`` and ``*args`` have same\nsemantics as with `Run Keyword`.\n\nExample, a simple if/else construct:\n| ${status} | ${value} = | `Run Keyword And Ignore Error` | `My Keyword` |\n| `Run Keyword If`     | '${status}' == 'PASS' | `Some Action`    | arg |\n| `Run Keyword Unless` | '${status}' == 'PASS' | `Another Action` |\n\nIn this example, only either `Some Action` or `Another Action` is\nexecuted, based on the status of `My Keyword`. Instead of `Run Keyword\nAnd Ignore Error` you can also use `Run Keyword And Return Status`.\n\nVariables used like ``${variable}``, as in the examples above, are\nreplaced in the expression before evaluation. Variables are also\navailable in the evaluation namespace and can be accessed using special\nsyntax ``$variable``. This is a new feature in Robot Framework 2.9\nand it is explained more thoroughly in `Evaluating expressions`.\n\nExample:\n| `Run Keyword If` | $result is None or $result == 'FAIL' | `Keyword` |\n\nStarting from Robot version 2.7.4, this keyword supports also optional\nELSE and ELSE IF branches. Both of these are defined in ``*args`` and\nmust use exactly format ``ELSE`` or ``ELSE IF``, respectively. ELSE\nbranches must contain first the name of the keyword to execute and then\nits possible arguments. ELSE IF branches must first contain a condition,\nlike the first argument to this keyword, and then the keyword to execute\nand its possible arguments. It is possible to have ELSE branch after\nELSE IF and to have multiple ELSE IF branches.\n\nGiven previous example, if/else construct can also be created like this:\n| ${status} | ${value} = | `Run Keyword And Ignore Error` | My Keyword |\n| `Run Keyword If` | '${status}' == 'PASS' | `Some Action` | arg | ELSE | `Another Action` |\n\nThe return value is the one of the keyword that was executed or None if\nno keyword was executed (i.e. if ``condition`` was false). Hence, it is\nrecommended to use ELSE and/or ELSE IF branches to conditionally assign\nreturn values from keyword to variables (to conditionally assign fixed\nvalues to variables, see `Set Variable If`). This is illustrated by the\nexample below:\n\n| ${var1} =   | `Run Keyword If` | ${rc} == 0     | `Some keyword returning a value` |\n| ...         | ELSE IF          | 0 &lt; ${rc} &lt; 42 | `Another keyword` |\n| ...         | ELSE IF          | ${rc} &lt; 0      | `Another keyword with args` | ${rc} | arg2 |\n| ...         | ELSE             | `Final keyword to handle abnormal cases` | ${rc} |\n| ${var2} =   | `Run Keyword If` | ${condition}  | `Some keyword` |\n\nIn this example, ${var2} will be set to None if ${condition} is false.\n\nNotice that ``ELSE`` and ``ELSE IF`` control words must be used\nexplicitly and thus cannot come from variables. If you need to use\nliteral ``ELSE`` and ``ELSE IF`` strings as arguments, you can escape\nthem with a backslash like ``\\ELSE`` and ``\\ELSE IF``.\n\nStarting from Robot Framework 2.8, Python's\n[http://docs.python.org/2/library/os.html|os] and\n[http://docs.python.org/2/library/sys.html|sys] modules are\nautomatically imported when evaluating the ``condition``.\nAttributes they contain can thus be used in the condition:\n\n| `Run Keyword If` | os.sep == '/' | `Unix Keyword`        |\n| ...              | ELSE IF       | sys.platform.startswith('java') | `Jython Keyword` |\n| ...              | ELSE          | `Windows Keyword`     |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword If All Critical Tests Passed\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the given keyword with the given arguments, if all critical tests passed.\n\nThis keyword can only be used in suite teardown. Trying to use it in\nany other place will result in an error.\n\nOtherwise, this keyword works exactly like `Run Keyword`, see its\ndocumentation for more details.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword If All Tests Passed\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the given keyword with the given arguments, if all tests passed.\n\nThis keyword can only be used in a suite teardown. Trying to use it\nanywhere else results in an error.\n\nOtherwise, this keyword works exactly like `Run Keyword`, see its\ndocumentation for more details.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword If Any Critical Tests Failed\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the given keyword with the given arguments, if any critical tests failed.\n\nThis keyword can only be used in a suite teardown. Trying to use it\nanywhere else results in an error.\n\nOtherwise, this keyword works exactly like `Run Keyword`, see its\ndocumentation for more details.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword If Any Tests Failed\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the given keyword with the given arguments, if one or more tests failed.\n\nThis keyword can only be used in a suite teardown. Trying to use it\nanywhere else results in an error.\n\nOtherwise, this keyword works exactly like `Run Keyword`, see its\ndocumentation for more details.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword If Test Failed\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the given keyword with the given arguments, if the test failed.\n\nThis keyword can only be used in a test teardown. Trying to use it\nanywhere else results in an error.\n\nOtherwise, this keyword works exactly like `Run Keyword`, see its\ndocumentation for more details.\n\nPrior to Robot Framework 2.9 failures in test teardown itself were\nnot detected by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword If Test Passed\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the given keyword with the given arguments, if the test passed.\n\nThis keyword can only be used in a test teardown. Trying to use it\nanywhere else results in an error.\n\nOtherwise, this keyword works exactly like `Run Keyword`, see its\ndocumentation for more details.\n\nPrior to Robot Framework 2.9 failures in test teardown itself were\nnot detected by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword If Timeout Occurred\">\n<arguments>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the given keyword if either a test or a keyword timeout has occurred.\n\nThis keyword can only be used in a test teardown. Trying to use it\nanywhere else results in an error.\n\nOtherwise, this keyword works exactly like `Run Keyword`, see its\ndocumentation for more details.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keyword Unless\">\n<arguments>\n<arg>condition</arg>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the given keyword with the given arguments, if ``condition`` is false.\n\nSee `Run Keyword If` for more information and an example.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Keywords\">\n<arguments>\n<arg>*keywords</arg>\n</arguments>\n<doc>Executes all the given keywords in a sequence.\n\nThis keyword is mainly useful in setups and teardowns when they need\nto take care of multiple actions and creating a new higher level user\nkeyword would be an overkill.\n\nBy default all arguments are expected to be keywords to be executed.\n\nExamples:\n| Run Keywords | Initialize database | Start servers | Clear logs |\n| Run Keywords | ${KW 1} | ${KW 2} |\n| Run Keywords | @{KEYWORDS} |\n\nStarting from Robot Framework 2.7.6, keywords can also be run with\narguments using upper case ``AND`` as a separator between keywords.\nThe keywords are executed so that the first argument is the first\nkeyword and proceeding arguments until the first ``AND`` are arguments\nto it. First argument after the first ``AND`` is the second keyword and\nproceeding arguments until the next ``AND`` are its arguments. And so on.\n\nExamples:\n| Run Keywords | Initialize database | db1 | AND | Start servers | server1 | server2 |\n| Run Keywords | Initialize database | ${DB NAME} | AND | Start servers | @{SERVERS} | AND | Clear logs |\n| Run Keywords | ${KW} | AND | @{KW WITH ARGS} |\n\nNotice that the ``AND`` control argument must be used explicitly and\ncannot itself come from a variable. If you need to use literal ``AND``\nstring as argument, you can either use variables or escape it with\na backslash like ``\\AND``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Global Variable\">\n<arguments>\n<arg>name</arg>\n<arg>*values</arg>\n</arguments>\n<doc>Makes a variable available globally in all tests and suites.\n\nVariables set with this keyword are globally available in all test\ncases and suites executed after setting them. Setting variables with\nthis keyword thus has the same effect as creating from the command line\nusing the options ``--variable`` or ``--variablefile``. Because this\nkeyword can change variables everywhere, it should be used with care.\n\nSee `Set Suite Variable` for more information and examples.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Library Search Order\">\n<arguments>\n<arg>*search_order</arg>\n</arguments>\n<doc>Sets the resolution order to use when a name matches multiple keywords.\n\nThe library search order is used to resolve conflicts when a keyword\nname in the test data matches multiple keywords. The first library\n(or resource, see below) containing the keyword is selected and that\nkeyword implementation used. If the keyword is not found from any library\n(or resource), test executing fails the same way as when the search\norder is not set.\n\nWhen this keyword is used, there is no need to use the long\n``LibraryName.Keyword Name`` notation.  For example, instead of\nhaving\n\n| MyLibrary.Keyword | arg |\n| MyLibrary.Another Keyword |\n| MyLibrary.Keyword | xxx |\n\nyou can have\n\n| Set Library Search Order | MyLibrary |\n| Keyword | arg |\n| Another Keyword |\n| Keyword | xxx |\n\nThis keyword can be used also to set the order of keywords in different\nresource files. In this case resource names must be given without paths\nor extensions like:\n\n| Set Library Search Order | resource | another_resource |\n\n*NOTE:*\n- The search order is valid only in the suite where this keywords is used.\n- Keywords in resources always have higher priority than\n  keywords in libraries regardless the search order.\n- The old order is returned and can be used to reset the search order later.\n- Library and resource names in the search order are both case and space\n  insensitive.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Log Level\">\n<arguments>\n<arg>level</arg>\n</arguments>\n<doc>Sets the log threshold to the specified level and returns the old level.\n\nMessages below the level will not logged. The default logging level is\nINFO, but it can be overridden with the command line option\n``--loglevel``.\n\nThe available levels: TRACE, DEBUG, INFO (default), WARN, ERROR and NONE (no\nlogging).</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Suite Documentation\">\n<arguments>\n<arg>doc</arg>\n<arg>append=False</arg>\n<arg>top=False</arg>\n</arguments>\n<doc>Sets documentation for the current test suite.\n\nBy default the possible existing documentation is overwritten, but\nthis can be changed using the optional ``append`` argument similarly\nas with `Set Test Message` keyword.\n\nThis keyword sets the documentation of the current suite by default.\nIf the optional ``top`` argument is given a true value (see `Boolean\narguments`), the documentation of the top level suite is altered\ninstead.\n\nThe documentation of the current suite is available as a built-in\nvariable ``${SUITE DOCUMENTATION}``.\n\nNew in Robot Framework 2.7. Support for ``append`` and ``top`` were\nadded in 2.7.7.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Suite Metadata\">\n<arguments>\n<arg>name</arg>\n<arg>value</arg>\n<arg>append=False</arg>\n<arg>top=False</arg>\n</arguments>\n<doc>Sets metadata for the current test suite.\n\nBy default possible existing metadata values are overwritten, but\nthis can be changed using the optional ``append`` argument similarly\nas with `Set Test Message` keyword.\n\nThis keyword sets the metadata of the current suite by default.\nIf the optional ``top`` argument is given a true value (see `Boolean\narguments`), the metadata of the top level suite is altered instead.\n\nThe metadata of the current suite is available as a built-in variable\n``${SUITE METADATA}`` in a Python dictionary. Notice that modifying this\nvariable directly has no effect on the actual metadata the suite has.\n\nNew in Robot Framework 2.7.4. Support for ``append`` and ``top`` were\nadded in 2.7.7.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Suite Variable\">\n<arguments>\n<arg>name</arg>\n<arg>*values</arg>\n</arguments>\n<doc>Makes a variable available everywhere within the scope of the current suite.\n\nVariables set with this keyword are available everywhere within the\nscope of the currently executed test suite. Setting variables with this\nkeyword thus has the same effect as creating them using the Variable\ntable in the test data file or importing them from variable files.\n\nPossible child test suites do not see variables set with this keyword\nby default. Starting from Robot Framework 2.9, that can be controlled\nby using ``children=&lt;option&gt;`` as the last argument. If the specified\n``&lt;option&gt;`` is a non-empty string or any other value considered true\nin Python, the variable is set also to the child suites. Parent and\nsibling suites will never see variables set with this keyword.\n\nThe name of the variable can be given either as a normal variable name\n(e.g. ``${NAME}``) or in escaped format as ``\\${NAME}`` or ``$NAME``.\nVariable value can be given using the same syntax as when variables\nare created in the Variable table.\n\nIf a variable already exists within the new scope, its value will be\noverwritten. Otherwise a new variable is created. If a variable already\nexists within the current scope, the value can be left empty and the\nvariable within the new scope gets the value within the current scope.\n\nExamples:\n| Set Suite Variable | ${SCALAR} | Hello, world! |\n| Set Suite Variable | ${SCALAR} | Hello, world! | children=true |\n| Set Suite Variable | @{LIST}   | First item    | Second item   |\n| Set Suite Variable | &amp;{DICT}   | key=value     | foo=bar       |\n| ${ID} =            | Get ID    |\n| Set Suite Variable | ${ID}     |\n\nTo override an existing value with an empty value, use built-in\nvariables ``${EMPTY}``, ``@{EMPTY}`` or ``&amp;{EMPTY}``:\n\n| Set Suite Variable | ${SCALAR} | ${EMPTY} |\n| Set Suite Variable | @{LIST}   | @{EMPTY} | # New in RF 2.7.4 |\n| Set Suite Variable | &amp;{DICT}   | &amp;{EMPTY} | # New in RF 2.9   |\n\n*NOTE:* If the variable has value which itself is a variable (escaped\nor not), you must always use the escaped format to set the variable:\n\nExample:\n| ${NAME} =          | Set Variable | \\${var} |\n| Set Suite Variable | ${NAME}      | value | # Sets variable ${var}  |\n| Set Suite Variable | \\${NAME}    | value | # Sets variable ${NAME} |\n\nThis limitation applies also to `Set Test Variable`, `Set Global\nVariable`, `Variable Should Exist`, `Variable Should Not Exist` and\n`Get Variable Value` keywords.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Tags\">\n<arguments>\n<arg>*tags</arg>\n</arguments>\n<doc>Adds given ``tags`` for the current test or all tests in a suite.\n\nWhen this keyword is used inside a test case, that test gets\nthe specified tags and other tests are not affected.\n\nIf this keyword is used in a suite setup, all test cases in\nthat suite, recursively, gets the given tags. It is a failure\nto use this keyword in a suite teardown.\n\nThe current tags are available as a built-in variable ``@{TEST TAGS}``.\n\nSee `Remove Tags` if you want to remove certain tags and `Fail` if\nyou want to fail the test case after setting and/or removing tags.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Test Documentation\">\n<arguments>\n<arg>doc</arg>\n<arg>append=False</arg>\n</arguments>\n<doc>Sets documentation for the current test case.\n\nBy default the possible existing documentation is overwritten, but\nthis can be changed using the optional ``append`` argument similarly\nas with `Set Test Message` keyword.\n\nThe current test documentation is available as a built-in variable\n``${TEST DOCUMENTATION}``. This keyword can not be used in suite\nsetup or suite teardown.\n\nNew in Robot Framework 2.7. Support for ``append`` was added in 2.7.7.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Test Message\">\n<arguments>\n<arg>message</arg>\n<arg>append=False</arg>\n</arguments>\n<doc>Sets message for the current test case.\n\nIf the optional ``append`` argument is given a true value (see `Boolean\narguments`), the given ``message`` is added after the possible earlier\nmessage by joining the messages with a space.\n\nIn test teardown this keyword can alter the possible failure message,\nbut otherwise failures override messages set by this keyword. Notice\nthat in teardown the message is available as a built-in variable\n``${TEST MESSAGE}``.\n\nIt is possible to use HTML format in the message by starting the message\nwith ``*HTML*``.\n\nExamples:\n| Set Test Message | My message           |                          |\n| Set Test Message | is continued.        | append=yes               |\n| Should Be Equal  | ${TEST MESSAGE}      | My message is continued. |\n| Set Test Message | `*`HTML`*` &lt;b&gt;Hello!&lt;/b&gt; |                      |\n\nThis keyword can not be used in suite setup or suite teardown.\n\nSupport for ``append`` was added in Robot Framework 2.7.7 and support\nfor HTML format in 2.8.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Test Variable\">\n<arguments>\n<arg>name</arg>\n<arg>*values</arg>\n</arguments>\n<doc>Makes a variable available everywhere within the scope of the current test.\n\nVariables set with this keyword are available everywhere within the\nscope of the currently executed test case. For example, if you set a\nvariable in a user keyword, it is available both in the test case level\nand also in all other user keywords used in the current test. Other\ntest cases will not see variables set with this keyword.\n\nSee `Set Suite Variable` for more information and examples.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Variable\">\n<arguments>\n<arg>*values</arg>\n</arguments>\n<doc>Returns the given values which can then be assigned to a variables.\n\nThis keyword is mainly used for setting scalar variables.\nAdditionally it can be used for converting a scalar variable\ncontaining a list to a list variable or to multiple scalar variables.\nIt is recommended to use `Create List` when creating new lists.\n\nExamples:\n| ${hi} =   | Set Variable | Hello, world! |\n| ${hi2} =  | Set Variable | I said: ${hi} |\n| ${var1}   | ${var2} =    | Set Variable | Hello | world |\n| @{list} = | Set Variable | ${list with some items} |\n| ${item1}  | ${item2} =   | Set Variable  | ${list with 2 items} |\n\nVariables created with this keyword are available only in the\nscope where they are created. See `Set Global Variable`,\n`Set Test Variable` and `Set Suite Variable` for information on how to\nset variables so that they are available also in a larger scope.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Variable If\">\n<arguments>\n<arg>condition</arg>\n<arg>*values</arg>\n</arguments>\n<doc>Sets variable based on the given condition.\n\nThe basic usage is giving a condition and two values. The\ngiven condition is first evaluated the same way as with the\n`Should Be True` keyword. If the condition is true, then the\nfirst value is returned, and otherwise the second value is\nreturned. The second value can also be omitted, in which case\nit has a default value None. This usage is illustrated in the\nexamples below, where ``${rc}`` is assumed to be zero.\n\n| ${var1} = | Set Variable If | ${rc} == 0 | zero     | nonzero |\n| ${var2} = | Set Variable If | ${rc} &gt; 0  | value1   | value2  |\n| ${var3} = | Set Variable If | ${rc} &gt; 0  | whatever |         |\n=&gt;\n| ${var1} = 'zero'\n| ${var2} = 'value2'\n| ${var3} = None\n\nIt is also possible to have 'else if' support by replacing the\nsecond value with another condition, and having two new values\nafter it. If the first condition is not true, the second is\nevaluated and one of the values after it is returned based on\nits truth value. This can be continued by adding more\nconditions without a limit.\n\n| ${var} = | Set Variable If | ${rc} == 0        | zero           |\n| ...      | ${rc} &gt; 0       | greater than zero | less then zero |\n|          |\n| ${var} = | Set Variable If |\n| ...      | ${rc} == 0      | zero              |\n| ...      | ${rc} == 1      | one               |\n| ...      | ${rc} == 2      | two               |\n| ...      | ${rc} &gt; 2       | greater than two  |\n| ...      | ${rc} &lt; 0       | less than zero    |\n\nUse `Get Variable Value` if you need to set variables\ndynamically based on whether a variable exist or not.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Be Empty\">\n<arguments>\n<arg>item</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Verifies that the given item is empty.\n\nThe length of the item is got using the `Get Length` keyword. The\ndefault error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Be Equal\">\n<arguments>\n<arg>first</arg>\n<arg>second</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails if the given objects are unequal.\n\nOptional ``msg`` and ``values`` arguments specify how to construct\nthe error message if this keyword fails:\n\n- If ``msg`` is not given, the error message is ``&lt;first&gt; != &lt;second&gt;``.\n- If ``msg`` is given and ``values`` gets a true value (default),\n  the error message is ``&lt;msg&gt;: &lt;first&gt; != &lt;second&gt;``.\n- If ``msg`` is given and ``values`` gets a false value, the error\n  message is simply ``&lt;msg&gt;``. See `Boolean arguments` for more details\n  about using false values.\n\nIf ``ignore_case`` is given a true value (see `Boolean arguments`) and\narguments are strings, it indicates that comparison should be\ncase-insensitive. New option in Robot Framework 3.0.1.\n\nIf both arguments are multiline strings, the comparison is done using\n`multiline string comparisons`.\n\nExamples:\n| Should Be Equal | ${x} | expected |\n| Should Be Equal | ${x} | expected | Custom error message |\n| Should Be Equal | ${x} | expected | Custom message | values=False |\n| Should Be Equal | ${x} | expected | ignore_case=True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Be Equal As Integers\">\n<arguments>\n<arg>first</arg>\n<arg>second</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>base=None</arg>\n</arguments>\n<doc>Fails if objects are unequal after converting them to integers.\n\nSee `Convert To Integer` for information how to convert integers from\nother bases than 10 using ``base`` argument or ``0b/0o/0x`` prefixes.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with ``msg`` and ``values``.\n\nExamples:\n| Should Be Equal As Integers | 42   | ${42} | Error message |\n| Should Be Equal As Integers | ABCD | abcd  | base=16 |\n| Should Be Equal As Integers | 0b1011 | 11  |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Be Equal As Numbers\">\n<arguments>\n<arg>first</arg>\n<arg>second</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>precision=6</arg>\n</arguments>\n<doc>Fails if objects are unequal after converting them to real numbers.\n\nThe conversion is done with `Convert To Number` keyword using the\ngiven ``precision``.\n\nExamples:\n| Should Be Equal As Numbers | ${x} | 1.1 | | # Passes if ${x} is 1.1 |\n| Should Be Equal As Numbers | 1.123 | 1.1 | precision=1  | # Passes |\n| Should Be Equal As Numbers | 1.123 | 1.4 | precision=0  | # Passes |\n| Should Be Equal As Numbers | 112.3 | 75  | precision=-2 | # Passes |\n\nAs discussed in the documentation of `Convert To Number`, machines\ngenerally cannot store floating point numbers accurately. Because of\nthis limitation, comparing floats for equality is problematic and\na correct approach to use depends on the context. This keyword uses\na very naive approach of rounding the numbers before comparing them,\nwhich is both prone to rounding errors and does not work very well if\nnumbers are really big or small. For more information about comparing\nfloats, and ideas on how to implement your own context specific\ncomparison algorithm, see\nhttp://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/.\n\nSee `Should Not Be Equal As Numbers` for a negative version of this\nkeyword and `Should Be Equal` for an explanation on how to override\nthe default error message with ``msg`` and ``values``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Be Equal As Strings\">\n<arguments>\n<arg>first</arg>\n<arg>second</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails if objects are unequal after converting them to strings.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with ``msg`` and ``values``.\n\nIf ``ignore_case`` is given a true value (see `Boolean arguments`), it\nindicates that comparison should be case-insensitive. New option in\nRobot Framework 3.0.1.\n\nIf both arguments are multiline strings, the comparison is done using\n`multiline string comparisons`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Be True\">\n<arguments>\n<arg>condition</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the given condition is not true.\n\nIf ``condition`` is a string (e.g. ``${rc} &lt; 10``), it is evaluated as\na Python expression as explained in `Evaluating expressions` and the\nkeyword status is decided based on the result. If a non-string item is\ngiven, the status is got directly from its\n[http://docs.python.org/2/library/stdtypes.html#truth|truth value].\n\nThe default error message (``&lt;condition&gt; should be true``) is not very\ninformative, but it can be overridden with the ``msg`` argument.\n\nExamples:\n| Should Be True | ${rc} &lt; 10            |\n| Should Be True | '${status}' == 'PASS' | # Strings must be quoted |\n| Should Be True | ${number}   | # Passes if ${number} is not zero |\n| Should Be True | ${list}     | # Passes if ${list} is not empty  |\n\nVariables used like ``${variable}``, as in the examples above, are\nreplaced in the expression before evaluation. Variables are also\navailable in the evaluation namespace and can be accessed using special\nsyntax ``$variable``. This is a new feature in Robot Framework 2.9\nand it is explained more thoroughly in `Evaluating expressions`.\n\nExamples:\n| Should Be True | $rc &lt; 10          |\n| Should Be True | $status == 'PASS' | # Expected string must be quoted |\n\nStarting from Robot Framework 2.8, `Should Be True` automatically\nimports Python's [http://docs.python.org/2/library/os.html|os] and\n[http://docs.python.org/2/library/sys.html|sys] modules that contain\nseveral useful attributes:\n\n| Should Be True | os.linesep == '\\n'             | # Unixy   |\n| Should Be True | os.linesep == '\\r\\n'          | # Windows |\n| Should Be True | sys.platform == 'darwin'        | # OS X    |\n| Should Be True | sys.platform.startswith('java') | # Jython  |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Contain\">\n<arguments>\n<arg>container</arg>\n<arg>item</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails if ``container`` does not contain ``item`` one or more times.\n\nWorks with strings, lists, and anything that supports Python's ``in``\noperator.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with arguments ``msg`` and ``values``.\n\nIf ``ignore_case`` is given a true value (see `Boolean arguments`) and\ncompared items are strings, it indicates that comparison should be\ncase-insensitive. If the ``container`` is a list-like object, string\nitems in it are compared case-insensitively. New option in Robot\nFramework 3.0.1.\n\nExamples:\n| Should Contain | ${output}    | PASS  |\n| Should Contain | ${some list} | value | msg=Failure! | values=False |\n| Should Contain | ${some list} | value | case_insensitive=True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Contain Any\">\n<arguments>\n<arg>container</arg>\n<arg>*items</arg>\n<arg>**configuration</arg>\n</arguments>\n<doc>Fails if ``container`` does not contain any of the ``*items``.\n\nWorks with strings, lists, and anything that supports Python's ``in``\noperator.\n\nSupports additional configuration parameters ``msg``, ``values``\nand ``ignore_case``, which have exactly the same semantics as arguments\nwith same names have with `Should Contain`. These arguments must\nalways be given using ``name=value`` syntax after all ``items``.\n\nNote that possible equal signs in ``items`` must be escaped with\na backslash (e.g. ``foo\\=bar``) to avoid them to be passed in\nas ``**configuration``.\n\nExamples:\n| Should Contain Any | ${string} | substring 1 | substring 2 |\n| Should Contain Any | ${list}   | item 1 | item 2 | item 3 |\n| Should Contain Any | ${list}   | item 1 | item 2 | item 3 | ignore_case=True |\n| Should Contain Any | ${list}   | @{items} | msg=Custom message | values=False |\n\nNew in Robot Framework 3.0.1.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Contain X Times\">\n<arguments>\n<arg>item1</arg>\n<arg>item2</arg>\n<arg>count</arg>\n<arg>msg=None</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails if ``item1`` does not contain ``item2`` ``count`` times.\n\nWorks with strings, lists and all objects that `Get Count` works\nwith. The default error message can be overridden with ``msg`` and\nthe actual count is always logged.\n\nIf ``ignore_case`` is given a true value (see `Boolean arguments`) and\ncompared items are strings, it indicates that comparison should be\ncase-insensitive. If the ``item1`` is a list-like object, string\nitems in it are compared case-insensitively. New option in Robot\nFramework 3.0.1.\n\nExamples:\n| Should Contain X Times | ${output}    | hello | 2 |\n| Should Contain X Times | ${some list} | value | 3 | ignore_case=True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should End With\">\n<arguments>\n<arg>str1</arg>\n<arg>str2</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails if the string ``str1`` does not end with the string ``str2``.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with ``msg`` and ``values``, as well as for semantics\nof the ``ignore_case`` option.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Match\">\n<arguments>\n<arg>string</arg>\n<arg>pattern</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails unless the given ``string`` matches the given ``pattern``.\n\nPattern matching is similar as matching files in a shell, and it is\nalways case-sensitive. In the pattern, ``*`` matches to anything and\n``?`` matches to any single character.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with ``msg`` and ``values``, as well as for semantics\nof the ``ignore_case`` option.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Match Regexp\">\n<arguments>\n<arg>string</arg>\n<arg>pattern</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n</arguments>\n<doc>Fails if ``string`` does not match ``pattern`` as a regular expression.\n\nRegular expression check is implemented using the Python\n[http://docs.python.org/2/library/re.html|re module]. Python's regular\nexpression syntax is derived from Perl, and it is thus also very\nsimilar to the syntax used, for example, in Java, Ruby and .NET.\n\nThings to note about the regexp syntax in Robot Framework test data:\n\n1) Backslash is an escape character in the test data, and possible\nbackslashes in the pattern must thus be escaped with another backslash\n(e.g. ``\\\\d\\\\w+``).\n\n2) Strings that may contain special characters, but should be handled\nas literal strings, can be escaped with the `Regexp Escape` keyword.\n\n3) The given pattern does not need to match the whole string. For\nexample, the pattern ``ello`` matches the string ``Hello world!``. If\na full match is needed, the ``^`` and ``$`` characters can be used to\ndenote the beginning and end of the string, respectively. For example,\n``^ello$`` only matches the exact string ``ello``.\n\n4) Possible flags altering how the expression is parsed (e.g.\n``re.IGNORECASE``, ``re.MULTILINE``) can be set by prefixing the\npattern with the ``(?iLmsux)`` group like ``(?im)pattern``. The\navailable flags are ``i`` (case-insensitive), ``m`` (multiline mode),\n``s`` (dotall mode), ``x`` (verbose), ``u`` (Unicode dependent) and\n``L`` (locale dependent).\n\nIf this keyword passes, it returns the portion of the string that\nmatched the pattern. Additionally, the possible captured groups are\nreturned.\n\nSee the `Should Be Equal` keyword for an explanation on how to override\nthe default error message with the ``msg`` and ``values`` arguments.\n\nExamples:\n| Should Match Regexp | ${output} | \\\\d{6}   | # Output contains six numbers  |\n| Should Match Regexp | ${output} | ^\\\\d{6}$ | # Six numbers and nothing more |\n| ${ret} = | Should Match Regexp | Foo: 42 | (?i)foo: \\\\d+ |\n| ${match} | ${group1} | ${group2} = |\n| ...      | Should Match Regexp | Bar: 43 | (Foo|Bar): (\\\\d+) |\n=&gt;\n| ${ret} = 'Foo: 42'\n| ${match} = 'Bar: 43'\n| ${group1} = 'Bar'\n| ${group2} = '43'</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Be Empty\">\n<arguments>\n<arg>item</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Verifies that the given item is not empty.\n\nThe length of the item is got using the `Get Length` keyword. The\ndefault error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Be Equal\">\n<arguments>\n<arg>first</arg>\n<arg>second</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails if the given objects are equal.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with ``msg`` and ``values``.\n\nIf ``ignore_case`` is given a true value (see `Boolean arguments`) and\nboth arguments are strings, it indicates that comparison should be\ncase-insensitive. New option in Robot Framework 3.0.1.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Be Equal As Integers\">\n<arguments>\n<arg>first</arg>\n<arg>second</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>base=None</arg>\n</arguments>\n<doc>Fails if objects are equal after converting them to integers.\n\nSee `Convert To Integer` for information how to convert integers from\nother bases than 10 using ``base`` argument or ``0b/0o/0x`` prefixes.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with ``msg`` and ``values``.\n\nSee `Should Be Equal As Integers` for some usage examples.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Be Equal As Numbers\">\n<arguments>\n<arg>first</arg>\n<arg>second</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>precision=6</arg>\n</arguments>\n<doc>Fails if objects are equal after converting them to real numbers.\n\nThe conversion is done with `Convert To Number` keyword using the\ngiven ``precision``.\n\nSee `Should Be Equal As Numbers` for examples on how to use\n``precision`` and why it does not always work as expected. See also\n`Should Be Equal` for an explanation on how to override the default\nerror message with ``msg`` and ``values``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Be Equal As Strings\">\n<arguments>\n<arg>first</arg>\n<arg>second</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails if objects are equal after converting them to strings.\n\nIf ``ignore_case`` is given a true value (see `Boolean arguments`), it\nindicates that comparison should be case-insensitive. New option in\nRobot Framework 3.0.1.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with ``msg`` and ``values``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Be True\">\n<arguments>\n<arg>condition</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the given condition is true.\n\nSee `Should Be True` for details about how ``condition`` is evaluated\nand how ``msg`` can be used to override the default error message.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Contain\">\n<arguments>\n<arg>container</arg>\n<arg>item</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails if ``container`` contains ``item`` one or more times.\n\nWorks with strings, lists, and anything that supports Python's ``in``\noperator.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with arguments ``msg`` and ``values``. ``ignore_case``\nhas exactly the same semantics as with `Should Contain`.\n\nExamples:\n| Should Not Contain | ${some list} | value  |\n| Should Not Contain | ${output}    | FAILED | ignore_case=True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Contain Any\">\n<arguments>\n<arg>container</arg>\n<arg>*items</arg>\n<arg>**configuration</arg>\n</arguments>\n<doc>Fails if ``container`` contains one or more of the ``*items``.\n\nWorks with strings, lists, and anything that supports Python's ``in``\noperator.\n\nSupports additional configuration parameters ``msg``, ``values``\nand ``ignore_case``, which have exactly the same semantics as arguments\nwith same names have with `Should Contain`. These arguments must\nalways be given using ``name=value`` syntax after all ``items``.\n\nNote that possible equal signs in ``items`` must be escaped with\na backslash (e.g. ``foo\\=bar``) to avoid them to be passed in\nas ``**configuration``.\n\nExamples:\n| Should Not Contain Any | ${string} | substring 1 | substring 2 |\n| Should Not Contain Any | ${list}   | item 1 | item 2 | item 3 |\n| Should Not Contain Any | ${list}   | item 1 | item 2 | item 3 | ignore_case=True |\n| Should Not Contain Any | ${list}   | @{items} | msg=Custom message | values=False |\n\nNew in Robot Framework 3.0.1.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not End With\">\n<arguments>\n<arg>str1</arg>\n<arg>str2</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails if the string ``str1`` ends with the string ``str2``.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with ``msg`` and ``values``, as well as for semantics\nof the ``ignore_case`` option.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Match\">\n<arguments>\n<arg>string</arg>\n<arg>pattern</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails if the given ``string`` matches the given ``pattern``.\n\nPattern matching is similar as matching files in a shell, and it is\nalways case-sensitive. In the pattern ``*`` matches to anything and\n``?`` matches to any single character.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with ``msg`` and ``values``, as well as for semantics\nof the ``ignore_case`` option.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Match Regexp\">\n<arguments>\n<arg>string</arg>\n<arg>pattern</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n</arguments>\n<doc>Fails if ``string`` matches ``pattern`` as a regular expression.\n\nSee `Should Match Regexp` for more information about arguments.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Start With\">\n<arguments>\n<arg>str1</arg>\n<arg>str2</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails if the string ``str1`` starts with the string ``str2``.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with ``msg`` and ``values``, as well as for semantics\nof the ``ignore_case`` option.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Start With\">\n<arguments>\n<arg>str1</arg>\n<arg>str2</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Fails if the string ``str1`` does not start with the string ``str2``.\n\nSee `Should Be Equal` for an explanation on how to override the default\nerror message with ``msg`` and ``values``, as well as for semantics\nof the ``ignore_case`` option.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Sleep\">\n<arguments>\n<arg>time_</arg>\n<arg>reason=None</arg>\n</arguments>\n<doc>Pauses the test executed for the given time.\n\n``time`` may be either a number or a time string. Time strings are in\na format such as ``1 day 2 hours 3 minutes 4 seconds 5milliseconds`` or\n``1d 2h 3m 4s 5ms``, and they are fully explained in an appendix of\nRobot Framework User Guide. Optional `reason` can be used to explain why\nsleeping is necessary. Both the time slept and the reason are logged.\n\nExamples:\n| Sleep | 42                   |\n| Sleep | 1.5                  |\n| Sleep | 2 minutes 10 seconds |\n| Sleep | 10s                  | Wait for a reply |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Variable Should Exist\">\n<arguments>\n<arg>name</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails unless the given variable exists within the current scope.\n\nThe name of the variable can be given either as a normal variable name\n(e.g. ``${NAME}``) or in escaped format (e.g. ``\\${NAME}``). Notice\nthat the former has some limitations explained in `Set Suite Variable`.\n\nThe default error message can be overridden with the ``msg`` argument.\n\nSee also `Variable Should Not Exist` and `Keyword Should Exist`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Variable Should Not Exist\">\n<arguments>\n<arg>name</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the given variable exists within the current scope.\n\nThe name of the variable can be given either as a normal variable name\n(e.g. ``${NAME}``) or in escaped format (e.g. ``\\${NAME}``). Notice\nthat the former has some limitations explained in `Set Suite Variable`.\n\nThe default error message can be overridden with the ``msg`` argument.\n\nSee also `Variable Should Exist` and `Keyword Should Exist`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Keyword Succeeds\">\n<arguments>\n<arg>retry</arg>\n<arg>retry_interval</arg>\n<arg>name</arg>\n<arg>*args</arg>\n</arguments>\n<doc>Runs the specified keyword and retries if it fails.\n\n``name`` and ``args`` define the keyword that is executed similarly\nas with `Run Keyword`. How long to retry running the keyword is\ndefined using ``retry`` argument either as timeout or count.\n``retry_interval`` is the time to wait before trying to run the\nkeyword again after the previous run has failed.\n\nIf ``retry`` is given as timeout, it must be in Robot Framework's\ntime format (e.g. ``1 minute``, ``2 min 3 s``, ``4.5``) that is\nexplained in an appendix of Robot Framework User Guide. If it is\ngiven as count, it must have ``times`` or ``x`` postfix (e.g.\n``5 times``, ``10 x``). ``retry_interval`` must always be given in\nRobot Framework's time format.\n\nIf the keyword does not succeed regardless of retries, this keyword\nfails. If the executed keyword passes, its return value is returned.\n\nExamples:\n| Wait Until Keyword Succeeds | 2 min | 5 sec | My keyword | argument |\n| ${result} = | Wait Until Keyword Succeeds | 3x | 200ms | My keyword |\n\nAll normal failures are caught by this keyword. Errors caused by\ninvalid syntax, test or keyword timeouts, or fatal exceptions (caused\ne.g. by `Fatal Error`) are not caught.\n\nRunning the same keyword multiple times inside this keyword can create\nlots of output and considerably increase the size of the generated\noutput files. Starting from Robot Framework 2.7, it is possible to\nremove unnecessary keywords from the outputs using\n``--RemoveKeywords WUKS`` command line option.\n\nSupport for specifying ``retry`` as a number of times to retry is\na new feature in Robot Framework 2.9.\nSince Robot Framework 2.9, variable errors are caught by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/Collections.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"Collections\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:40\">\n<version>3.0.3</version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>A test library providing keywords for handling lists and dictionaries.\n\n``Collections`` is Robot Framework's standard library that provides a\nset of keywords for handling Python lists and dictionaries. This\nlibrary has keywords, for example, for modifying and getting\nvalues from lists and dictionaries (e.g. `Append To List`, `Get\nFrom Dictionary`) and for verifying their contents (e.g. `Lists\nShould Be Equal`, `Dictionary Should Contain Value`).\n\n= Related keywords in BuiltIn =\n\nFollowing keywords in the BuiltIn library can also be used with\nlists and dictionaries:\n\n| = Keyword Name =             | = Applicable With = | = Comment = |\n| `Create List`                | lists |\n| `Create Dictionary`          | dicts | Was in Collections until RF 2.9. |\n| `Get Length`                 | both  |\n| `Length Should Be`           | both  |\n| `Should Be Empty`            | both  |\n| `Should Not Be Empty`        | both  |\n| `Should Contain`             | both  |\n| `Should Not Contain`         | both  |\n| `Should Contain X Times`     | lists |\n| `Should Not Contain X Times` | lists |\n| `Get Count`                  | lists |\n\n= Using with list-like and dictionary-like objects =\n\nList keywords that do not alter the given list can also be used\nwith tuples, and to some extend also with other iterables.\n`Convert To List` can be used to convert tuples and other iterables\nto Python ``list`` objects.\n\nSimilarly dictionary keywords can, for most parts, be used with other\nmappings. `Convert To Dictionary` can be used if real Python ``dict``\nobjects are needed.\n\n= Boolean arguments =\n\nSome keywords accept arguments that are handled as Boolean values true or\nfalse. If such an argument is given as a string, it is considered false if\nit is either an empty string or case-insensitively equal to ``false``,\n``none`` or ``no``. Keywords verifying something that allow dropping actual\nand expected values from the possible error message also consider string\n``no values`` to be false. Other strings are considered true regardless\ntheir value, and other argument types are tested using the same\n[http://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules\nas in Python].\n\nTrue examples:\n| `Should Contain Match` | ${list} | ${pattern} | case_insensitive=True    | # Strings are generally true.    |\n| `Should Contain Match` | ${list} | ${pattern} | case_insensitive=yes     | # Same as the above.             |\n| `Should Contain Match` | ${list} | ${pattern} | case_insensitive=${TRUE} | # Python ``True`` is true.       |\n| `Should Contain Match` | ${list} | ${pattern} | case_insensitive=${42}   | # Numbers other than 0 are true. |\n\nFalse examples:\n| `Should Contain Match` | ${list} | ${pattern} | case_insensitive=False    | # String ``false`` is false.   |\n| `Should Contain Match` | ${list} | ${pattern} | case_insensitive=no       | # Also string ``no`` is false. |\n| `Should Contain Match` | ${list} | ${pattern} | case_insensitive=${EMPTY} | # Empty string is false.       |\n| `Should Contain Match` | ${list} | ${pattern} | case_insensitive=${FALSE} | # Python ``False`` is false.   |\n| `Lists Should Be Equal` | ${x}   | ${y} | Custom error | values=no values | # ``no values`` works with ``values`` argument |\n\nNote that prior to Robot Framework 2.9 some keywords considered all\nnon-empty strings, including ``False``, to be true.\nConsidering ``none`` false is new in Robot Framework 3.0.3.\n\n= Data in examples =\n\nList related keywords use variables in format ``${Lx}`` in their examples.\nThey mean lists with as many alphabetic characters as specified by ``x``.\nFor example, ``${L1}`` means ``['a']`` and ``${L3}`` means\n``['a', 'b', 'c']``.\n\nDictionary keywords use similar ``${Dx}`` variables. For example, ``${D1}``\nmeans ``{'a': 1}`` and ``${D3}`` means ``{'a': 1, 'b': 2, 'c': 3}``.</doc>\n<kw name=\"Append To List\">\n<arguments>\n<arg>list_</arg>\n<arg>*values</arg>\n</arguments>\n<doc>Adds ``values`` to the end of ``list``.\n\nExample:\n| Append To List | ${L1} | xxx |   |   |\n| Append To List | ${L2} | x   | y | z |\n=&gt;\n| ${L1} = ['a', 'xxx']\n| ${L2} = ['a', 'b', 'x', 'y', 'z']</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Combine Lists\">\n<arguments>\n<arg>*lists</arg>\n</arguments>\n<doc>Combines the given ``lists`` together and returns the result.\n\nThe given lists are not altered by this keyword.\n\nExample:\n| ${x} = | Combine List | ${L1} | ${L2} |       |\n| ${y} = | Combine List | ${L1} | ${L2} | ${L1} |\n=&gt;\n| ${x} = ['a', 'a', 'b']\n| ${y} = ['a', 'a', 'b', 'a']\n| ${L1} and ${L2} are not changed.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert To Dictionary\">\n<arguments>\n<arg>item</arg>\n</arguments>\n<doc>Converts the given ``item`` to a Python ``dict`` type.\n\nMainly useful for converting other mappings to dictionaries. Use\n`Create Dictionary` from the BuiltIn library for constructing new\ndictionaries.\n\nNew in Robot Framework 2.9.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert To List\">\n<arguments>\n<arg>item</arg>\n</arguments>\n<doc>Converts the given ``item`` to a Python ``list`` type.\n\nMainly useful for converting tuples and other iterable to lists.\nUse `Create List` from the BuiltIn library for constructing new lists.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Copy Dictionary\">\n<arguments>\n<arg>dictionary</arg>\n</arguments>\n<doc>Returns a copy of the given dictionary.\n\nThe given dictionary is never altered by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Copy List\">\n<arguments>\n<arg>list_</arg>\n</arguments>\n<doc>Returns a copy of the given list.\n\nThe given list is never altered by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Count Values In List\">\n<arguments>\n<arg>list_</arg>\n<arg>value</arg>\n<arg>start=0</arg>\n<arg>end=None</arg>\n</arguments>\n<doc>Returns the number of occurrences of the given ``value`` in ``list``.\n\nThe search can be narrowed to the selected sublist by the ``start`` and\n``end`` indexes having the same semantics as with `Get Slice From List`\nkeyword. The given list is never altered by this keyword.\n\nExample:\n| ${x} = | Count Values In List | ${L3} | b |\n=&gt;\n| ${x} = 1\n| ${L3} is not changed</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Dictionaries Should Be Equal\">\n<arguments>\n<arg>dict1</arg>\n<arg>dict2</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n</arguments>\n<doc>Fails if the given dictionaries are not equal.\n\nFirst the equality of dictionaries' keys is checked and after that all\nthe key value pairs. If there are differences between the values, those\nare listed in the error message. The types of the dictionaries do not\nneed to be same.\n\nSee `Lists Should Be Equal` for more information about configuring\nthe error message with ``msg`` and ``values`` arguments.\n\nThe given dictionaries are never altered by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Dictionary Should Contain Item\">\n<arguments>\n<arg>dictionary</arg>\n<arg>key</arg>\n<arg>value</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>An item of ``key``/``value`` must be found in a `dictionary`.\n\nValue is converted to unicode for comparison.\n\nSee `Lists Should Be Equal` for an explanation of ``msg``.\nThe given dictionary is never altered by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Dictionary Should Contain Key\">\n<arguments>\n<arg>dictionary</arg>\n<arg>key</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if ``key`` is not found from ``dictionary``.\n\nSee `List Should Contain Value` for an explanation of ``msg``.\n\nThe given dictionary is never altered by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Dictionary Should Contain Sub Dictionary\">\n<arguments>\n<arg>dict1</arg>\n<arg>dict2</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n</arguments>\n<doc>Fails unless all items in ``dict2`` are found from ``dict1``.\n\nSee `Lists Should Be Equal` for more information about configuring\nthe error message with ``msg`` and ``values`` arguments.\n\nThe given dictionaries are never altered by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Dictionary Should Contain Value\">\n<arguments>\n<arg>dictionary</arg>\n<arg>value</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if ``value`` is not found from ``dictionary``.\n\nSee `List Should Contain Value` for an explanation of ``msg``.\n\nThe given dictionary is never altered by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Dictionary Should Not Contain Key\">\n<arguments>\n<arg>dictionary</arg>\n<arg>key</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if ``key`` is found from ``dictionary``.\n\nSee `List Should Contain Value` for an explanation of ``msg``.\n\nThe given dictionary is never altered by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Dictionary Should Not Contain Value\">\n<arguments>\n<arg>dictionary</arg>\n<arg>value</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if ``value`` is found from ``dictionary``.\n\nSee `List Should Contain Value` for an explanation of ``msg``.\n\nThe given dictionary is never altered by this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Dictionary Items\">\n<arguments>\n<arg>dictionary</arg>\n</arguments>\n<doc>Returns items of the given ``dictionary``.\n\nItems are returned sorted by keys. The given ``dictionary`` is not\naltered by this keyword.\n\nExample:\n| ${items} = | Get Dictionary Items | ${D3} |\n=&gt;\n| ${items} = ['a', 1, 'b', 2, 'c', 3]</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Dictionary Keys\">\n<arguments>\n<arg>dictionary</arg>\n</arguments>\n<doc>Returns keys of the given ``dictionary``.\n\nIf keys are sortable, they are returned in sorted order. The given\n``dictionary`` is never altered by this keyword.\n\nExample:\n| ${keys} = | Get Dictionary Keys | ${D3} |\n=&gt;\n| ${keys} = ['a', 'b', 'c']</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Dictionary Values\">\n<arguments>\n<arg>dictionary</arg>\n</arguments>\n<doc>Returns values of the given dictionary.\n\nValues are returned sorted according to keys. The given dictionary is\nnever altered by this keyword.\n\nExample:\n| ${values} = | Get Dictionary Values | ${D3} |\n=&gt;\n| ${values} = [1, 2, 3]</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get From Dictionary\">\n<arguments>\n<arg>dictionary</arg>\n<arg>key</arg>\n</arguments>\n<doc>Returns a value from the given ``dictionary`` based on the given ``key``.\n\nIf the given ``key`` cannot be found from the ``dictionary``, this\nkeyword fails.\n\nThe given dictionary is never altered by this keyword.\n\nExample:\n| ${value} = | Get From Dictionary | ${D3} | b |\n=&gt;\n| ${value} = 2</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get From List\">\n<arguments>\n<arg>list_</arg>\n<arg>index</arg>\n</arguments>\n<doc>Returns the value specified with an ``index`` from ``list``.\n\nThe given list is never altered by this keyword.\n\nIndex ``0`` means the first position, ``1`` the second, and so on.\nSimilarly, ``-1`` is the last position, ``-2`` the second last, and so on.\nUsing an index that does not exist on the list causes an error.\nThe index can be either an integer or a string that can be converted\nto an integer.\n\nExamples (including Python equivalents in comments):\n| ${x} = | Get From List | ${L5} | 0  | # L5[0]  |\n| ${y} = | Get From List | ${L5} | -2 | # L5[-2] |\n=&gt;\n| ${x} = 'a'\n| ${y} = 'd'\n| ${L5} is not changed</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Index From List\">\n<arguments>\n<arg>list_</arg>\n<arg>value</arg>\n<arg>start=0</arg>\n<arg>end=None</arg>\n</arguments>\n<doc>Returns the index of the first occurrence of the ``value`` on the list.\n\nThe search can be narrowed to the selected sublist by the ``start`` and\n``end`` indexes having the same semantics as with `Get Slice From List`\nkeyword. In case the value is not found, -1 is returned. The given list\nis never altered by this keyword.\n\nExample:\n| ${x} = | Get Index From List | ${L5} | d |\n=&gt;\n| ${x} = 3\n| ${L5} is not changed</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Match Count\">\n<arguments>\n<arg>list</arg>\n<arg>pattern</arg>\n<arg>case_insensitive=False</arg>\n<arg>whitespace_insensitive=False</arg>\n</arguments>\n<doc>Returns the count of matches to ``pattern`` in ``list``.\n\nFor more information on ``pattern``, ``case_insensitive``, and\n``whitespace_insensitive``, see `Should Contain Match`.\n\nExamples:\n| ${count}= | Get Match Count | ${list} | a* | # ${count} will be the count of strings beginning with 'a' |\n| ${count}= | Get Match Count | ${list} | regexp=a.* | # ${matches} will be the count of strings beginning with 'a' (regexp version) |\n| ${count}= | Get Match Count | ${list} | a* | case_insensitive=${True} | # ${matches} will be the count of strings beginning with 'a' or 'A' |\n\nNew in Robot Framework 2.8.6.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Matches\">\n<arguments>\n<arg>list</arg>\n<arg>pattern</arg>\n<arg>case_insensitive=False</arg>\n<arg>whitespace_insensitive=False</arg>\n</arguments>\n<doc>Returns a list of matches to ``pattern`` in ``list``.\n\nFor more information on ``pattern``, ``case_insensitive``, and\n``whitespace_insensitive``, see `Should Contain Match`.\n\nExamples:\n| ${matches}= | Get Matches | ${list} | a* | # ${matches} will contain any string beginning with 'a' |\n| ${matches}= | Get Matches | ${list} | regexp=a.* | # ${matches} will contain any string beginning with 'a' (regexp version) |\n| ${matches}= | Get Matches | ${list} | a* | case_insensitive=${True} | # ${matches} will contain any string beginning with 'a' or 'A' |\n\nNew in Robot Framework 2.8.6.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Slice From List\">\n<arguments>\n<arg>list_</arg>\n<arg>start=0</arg>\n<arg>end=None</arg>\n</arguments>\n<doc>Returns a slice of the given list between ``start`` and ``end`` indexes.\n\nThe given list is never altered by this keyword.\n\nIf both ``start`` and ``end`` are given, a sublist containing values\nfrom ``start`` to ``end`` is returned. This is the same as\n``list[start:end]`` in Python. To get all items from the beginning,\nuse 0 as the start value, and to get all items until and including\nthe end, use ``None`` (default) as the end value.\n\nUsing ``start`` or ``end`` not found on the list is the same as using\nthe largest (or smallest) available index.\n\nExamples (incl. Python equivalents in comments):\n| ${x} = | Get Slice From List | ${L5} | 2 | 4  | # L5[2:4]    |\n| ${y} = | Get Slice From List | ${L5} | 1 |    | # L5[1:None] |\n| ${z} = | Get Slice From List | ${L5} |   | -2 | # L5[0:-2]   |\n=&gt;\n| ${x} = ['c', 'd']\n| ${y} = ['b', 'c', 'd', 'e']\n| ${z} = ['a', 'b', 'c']\n| ${L5} is not changed</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Insert Into List\">\n<arguments>\n<arg>list_</arg>\n<arg>index</arg>\n<arg>value</arg>\n</arguments>\n<doc>Inserts ``value`` into ``list`` to the position specified with ``index``.\n\nIndex ``0`` adds the value into the first position, ``1`` to the second,\nand so on. Inserting from right works with negative indices so that\n``-1`` is the second last position, ``-2`` third last, and so on. Use\n`Append To List` to add items to the end of the list.\n\nIf the absolute value of the index is greater than\nthe length of the list, the value is added at the end\n(positive index) or the beginning (negative index). An index\ncan be given either as an integer or a string that can be\nconverted to an integer.\n\nExample:\n| Insert Into List | ${L1} | 0     | xxx |\n| Insert Into List | ${L2} | ${-1} | xxx |\n=&gt;\n| ${L1} = ['xxx', 'a']\n| ${L2} = ['a', 'xxx', 'b']</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Keep In Dictionary\">\n<arguments>\n<arg>dictionary</arg>\n<arg>*keys</arg>\n</arguments>\n<doc>Keeps the given ``keys`` in the ``dictionary`` and removes all other.\n\nIf the given ``key`` cannot be found from the ``dictionary``, it\nis ignored.\n\nExample:\n| Keep In Dictionary | ${D5} | b | x | d |\n=&gt;\n| ${D5} = {'b': 2, 'd': 4}</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Should Contain Sub List\">\n<arguments>\n<arg>list1</arg>\n<arg>list2</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n</arguments>\n<doc>Fails if not all of the elements in ``list2`` are found in ``list1``.\n\nThe order of values and the number of values are not taken into\naccount.\n\nSee `Lists Should Be Equal` for more information about configuring\nthe error message with ``msg`` and ``values`` arguments.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Should Contain Value\">\n<arguments>\n<arg>list_</arg>\n<arg>value</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the ``value`` is not found from ``list``.\n\nIf the keyword fails, the default error messages is ``&lt;list&gt; does\nnot contain value '&lt;value&gt;'``. A custom message can be given using\nthe ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Should Not Contain Duplicates\">\n<arguments>\n<arg>list_</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if any element in the ``list`` is found from it more than once.\n\nThe default error message lists all the elements that were found\nfrom the ``list`` multiple times, but it can be overridden by giving\na custom ``msg``. All multiple times found items and their counts are\nalso logged.\n\nThis keyword works with all iterables that can be converted to a list.\nThe original iterable is never altered.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Should Not Contain Value\">\n<arguments>\n<arg>list_</arg>\n<arg>value</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the ``value`` is found from ``list``.\n\nSee `List Should Contain Value` for an explanation of ``msg``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Lists Should Be Equal\">\n<arguments>\n<arg>list1</arg>\n<arg>list2</arg>\n<arg>msg=None</arg>\n<arg>values=True</arg>\n<arg>names=None</arg>\n</arguments>\n<doc>Fails if given lists are unequal.\n\nThe keyword first verifies that the lists have equal lengths, and then\nit checks are all their values equal. Possible differences between the\nvalues are listed in the default error message like ``Index 4: ABC !=\nAbc``. The types of the lists do not need to be the same. For example,\nPython tuple and list with same content are considered equal.\n\n\nThe error message can be configured using ``msg`` and ``values``\narguments:\n- If ``msg`` is not given, the default error message is used.\n- If ``msg`` is given and ``values`` gets a value considered true\n  (see `Boolean arguments`), the error message starts with the given\n  ``msg`` followed by a newline and the default message.\n- If ``msg`` is given and ``values``  is not given a true value,\n  the error message is just the given ``msg``.\n\nOptional ``names`` argument can be used for naming the indices shown in\nthe default error message. It can either be a list of names matching\nthe indices in the lists or a dictionary where keys are indices that\nneed to be named. It is not necessary to name all of the indices.  When\nusing a dictionary, keys can be either integers or strings that can be\nconverted to integers.\n\nExamples:\n| ${names} = | Create List | First Name | Family Name | Email |\n| Lists Should Be Equal | ${people1} | ${people2} | names=${names} |\n| ${names} = | Create Dictionary | 0=First Name | 2=Email |\n| Lists Should Be Equal | ${people1} | ${people2} | names=${names} |\n\nIf the items in index 2 would differ in the above examples, the error\nmessage would contain a row like ``Index 2 (email): name@foo.com !=\nname@bar.com``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log Dictionary\">\n<arguments>\n<arg>dictionary</arg>\n<arg>level=INFO</arg>\n</arguments>\n<doc>Logs the size and contents of the ``dictionary`` using given ``level``.\n\nValid levels are TRACE, DEBUG, INFO (default), and WARN.\n\nIf you only want to log the size, use keyword `Get Length` from\nthe BuiltIn library.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log List\">\n<arguments>\n<arg>list_</arg>\n<arg>level=INFO</arg>\n</arguments>\n<doc>Logs the length and contents of the ``list`` using given ``level``.\n\nValid levels are TRACE, DEBUG, INFO (default), and WARN.\n\nIf you only want to the length, use keyword `Get Length` from\nthe BuiltIn library.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Pop From Dictionary\">\n<arguments>\n<arg>dictionary</arg>\n<arg>key</arg>\n<arg>default=</arg>\n</arguments>\n<doc>Pops the given ``key`` from the ``dictionary`` and returns its value.\n\nBy default the keyword fails if the given ``key`` cannot be found from\nthe ``dictionary``. If optional ``default`` value is given, it will be\nreturned instead of failing.\n\nExample:\n| ${val}= | Pop From Dictionary | ${D3} | b |\n=&gt;\n| ${val} = 2\n| ${D3} = {'a': 1, 'c': 3}\n\nNew in Robot Framework 2.9.2.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Duplicates\">\n<arguments>\n<arg>list_</arg>\n</arguments>\n<doc>Returns a list without duplicates based on the given ``list``.\n\nCreates and returns a new list that contains all items in the given\nlist so that one item can appear only once. Order of the items in\nthe new list is the same as in the original except for missing\nduplicates. Number of the removed duplicates is logged.\n\nNew in Robot Framework 2.7.5.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove From Dictionary\">\n<arguments>\n<arg>dictionary</arg>\n<arg>*keys</arg>\n</arguments>\n<doc>Removes the given ``keys`` from the ``dictionary``.\n\nIf the given ``key`` cannot be found from the ``dictionary``, it\nis ignored.\n\nExample:\n| Remove From Dictionary | ${D3} | b | x | y |\n=&gt;\n| ${D3} = {'a': 1, 'c': 3}</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove From List\">\n<arguments>\n<arg>list_</arg>\n<arg>index</arg>\n</arguments>\n<doc>Removes and returns the value specified with an ``index`` from ``list``.\n\nIndex ``0`` means the first position, ``1`` the second and so on.\nSimilarly, ``-1`` is the last position, ``-2`` the second last, and so on.\nUsing an index that does not exist on the list causes an error.\nThe index can be either an integer or a string that can be converted\nto an integer.\n\nExample:\n| ${x} = | Remove From List | ${L2} | 0 |\n=&gt;\n| ${x} = 'a'\n| ${L2} = ['b']</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Values From List\">\n<arguments>\n<arg>list_</arg>\n<arg>*values</arg>\n</arguments>\n<doc>Removes all occurrences of given ``values`` from ``list``.\n\nIt is not an error if a value does not exist in the list at all.\n\nExample:\n| Remove Values From List | ${L4} | a | c | e | f |\n=&gt;\n| ${L4} = ['b', 'd']</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Reverse List\">\n<arguments>\n<arg>list_</arg>\n</arguments>\n<doc>Reverses the given list in place.\n\nNote that the given list is changed and nothing is returned. Use\n`Copy List` first, if you need to keep also the original order.\n\n| Reverse List | ${L3} |\n=&gt;\n| ${L3} = ['c', 'b', 'a']</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set List Value\">\n<arguments>\n<arg>list_</arg>\n<arg>index</arg>\n<arg>value</arg>\n</arguments>\n<doc>Sets the value of ``list`` specified by ``index`` to the given ``value``.\n\nIndex ``0`` means the first position, ``1`` the second and so on.\nSimilarly, ``-1`` is the last position, ``-2`` second last, and so on.\nUsing an index that does not exist on the list causes an error.\nThe index can be either an integer or a string that can be converted to\nan integer.\n\nExample:\n| Set List Value | ${L3} | 1  | xxx |\n| Set List Value | ${L3} | -1 | yyy |\n=&gt;\n| ${L3} = ['a', 'xxx', 'yyy']</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set To Dictionary\">\n<arguments>\n<arg>dictionary</arg>\n<arg>*key_value_pairs</arg>\n<arg>**items</arg>\n</arguments>\n<doc>Adds the given ``key_value_pairs`` and ``items`` to the ``dictionary``.\n\nGiving items as ``key_value_pairs`` means giving keys and values\nas separate arguments:\n\n| Set To Dictionary | ${D1} | key | value | second | ${2} |\n=&gt;\n| ${D1} = {'a': 1, 'key': 'value', 'second': 2}\n\nStarting from Robot Framework 2.8.1, items can also be given as kwargs\nusing ``key=value`` syntax:\n\n| Set To Dictionary | ${D1} | key=value | second=${2} |\n\nThe latter syntax is typically more convenient to use, but it has\na limitation that keys must be strings.\n\nIf given keys already exist in the dictionary, their values are updated.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Contain Match\">\n<arguments>\n<arg>list</arg>\n<arg>pattern</arg>\n<arg>msg=None</arg>\n<arg>case_insensitive=False</arg>\n<arg>whitespace_insensitive=False</arg>\n</arguments>\n<doc>Fails if ``pattern`` is not found in ``list``.\n\nSee `List Should Contain Value` for an explanation of ``msg``.\n\nBy default, pattern matching is similar to matching files in a shell\nand is case-sensitive and whitespace-sensitive. In the pattern syntax,\n``*`` matches to anything and ``?`` matches to any single character. You\ncan also prepend ``glob=`` to your pattern to explicitly use this pattern\nmatching behavior.\n\nIf you prepend ``regexp=`` to your pattern, your pattern will be used\naccording to the Python\n[http://docs.python.org/2/library/re.html|re module] regular expression\nsyntax. Important note: Backslashes are an escape character, and must\nbe escaped with another backslash (e.g. ``regexp=\\\\d{6}`` to search for\n``\\d{6}``). See `BuiltIn.Should Match Regexp` for more details.\n\nIf ``case_insensitive`` is given a true value (see `Boolean arguments`),\nthe pattern matching will ignore case.\n\nIf ``whitespace_insensitive`` is given a true value (see `Boolean\narguments`), the pattern matching will ignore whitespace.\n\nNon-string values in lists are ignored when matching patterns.\n\nThe given list is never altered by this keyword.\n\nSee also ``Should Not Contain Match``.\n\nExamples:\n| Should Contain Match | ${list} | a*              | | | # Match strings beginning with 'a'. |\n| Should Contain Match | ${list} | regexp=a.*      | | | # Same as the above but with regexp. |\n| Should Contain Match | ${list} | regexp=\\\\d{6} | | | # Match strings containing six digits. |\n| Should Contain Match | ${list} | a*  | case_insensitive=True       | | # Match strings beginning with 'a' or 'A'. |\n| Should Contain Match | ${list} | ab* | whitespace_insensitive=yes  | | # Match strings beginning with 'ab' with possible whitespace ignored. |\n| Should Contain Match | ${list} | ab* | whitespace_insensitive=true | case_insensitive=true | # Same as the above but also ignore case. |\n\nNew in Robot Framework 2.8.6.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Contain Match\">\n<arguments>\n<arg>list</arg>\n<arg>pattern</arg>\n<arg>msg=None</arg>\n<arg>case_insensitive=False</arg>\n<arg>whitespace_insensitive=False</arg>\n</arguments>\n<doc>Fails if ``pattern`` is found in ``list``.\n\nExact opposite of `Should Contain Match` keyword. See that keyword\nfor information about arguments and usage in general.\n\nNew in Robot Framework 2.8.6.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Sort List\">\n<arguments>\n<arg>list_</arg>\n</arguments>\n<doc>Sorts the given list in place.\n\nThe strings are sorted alphabetically and the numbers numerically.\n\nNote that the given list is changed and nothing is returned. Use\n`Copy List` first, if you need to keep also the original order.\n\n${L} = [2,1,'a','c','b']\n| Sort List | ${L} |\n=&gt;\n| ${L} = [1, 2, 'a', 'b', 'c']</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/DatabaseLibrary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"DatabaseLibrary\" type=\"library\" format=\"ROBOT\" generated=\"20180817 16:21:04\">\n<version></version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>Database Library contains utilities meant for Robot Framework's usage.\n\nThis can allow you to query your database after an action has been made to verify the results.\n\nThis is `compatible*` with any Database API Specification 2.0 module.\n\n\n\nReferences:\n\n + Database API Specification 2.0 - http://www.python.org/dev/peps/pep-0249/\n\n + Lists of DB API 2.0 - http://wiki.python.org/moin/DatabaseInterfaces\n\n + Python Database Programming - http://wiki.python.org/moin/DatabaseProgramming/\n\nNotes:\n\n\n\n`compatible* - or at least theoretically it should be compatible. Currently tested only with postgresql\n(using psycopg2).`\n\nExample Usage:\n| # Setup |\n| Connect to Database |\n| # Guard assertion (verify that test started in expected state). |\n| Check if not exists in database | select id from person where first_name = 'Franz Allan' and last_name = 'See' |\n| # Drive UI to do some action |\n| Go To | http://localhost/person/form.html | | # From selenium library |\n| Input Text |  name=first_name | Franz Allan | # From selenium library |\n| Input Text |  name=last_name | See | # From selenium library |\n| Click Button | Save | | # From selenium library |\n| # Log results |\n| @{queryResults} | Query | select * from person |\n| Log Many | @{queryResults} |\n| # Verify if persisted in the database |\n| Check if exists in database | select id from person where first_name = 'Franz Allan' and last_name = 'See' |\n| # Teardown |\n| Disconnect from Database |</doc>\n<kw name=\"Call Stored Procedure\">\n<arguments>\n<arg>spName</arg>\n<arg>spParams=None</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Uses the inputs of `spName` and 'spParams' to call a stored procedure. Set optional input `sansTran` to\nTrue to run command without an explicit transaction commit or rollback.\n\nspName should be the stored procedure name itself\nspParams [Optional] should be a List of the parameters being sent in.  The list can be one or multiple items.\n\nThe return from this keyword will always be a list.\n\nExample:\n| @{ParamList} = | Create List | FirstParam | SecondParam | ThirdParam |\n| @{QueryResults} = | Call Stored Procedure | DBName.SchemaName.StoredProcName | List of Parameters |\n\nExample:\n| @{ParamList} = | Create List | Testing | LastName |\n| Set Test Variable | ${SPName} = | DBTest.DBSchema.MyStoredProc |\n| @{QueryResults} = | Call Stored Procedure | ${SPName} | ${ParamList} |\n| Log List | @{QueryResults} |\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| @{QueryResults} = | Call Stored Procedure | DBName.SchemaName.StoredProcName | List of Parameters | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Check If Exists In Database\">\n<arguments>\n<arg>selectStatement</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Check if any row would be returned by given the input `selectStatement`. If there are no results, then this will\nthrow an AssertionError. Set optional input `sansTran` to True to run command without an explicit transaction\ncommit or rollback.\n\nFor example, given we have a table `person` with the following data:\n| id | first_name  | last_name |\n|  1 | Franz Allan | See       |\n\nWhen you have the following assertions in your robot\n| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' |\n| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'John' |\n\nThen you will get the following:\n| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' | # PASS |\n| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'John' | # FAIL |\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'John' | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Check If Not Exists In Database\">\n<arguments>\n<arg>selectStatement</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>This is the negation of `check_if_exists_in_database`.\n\nCheck if no rows would be returned by given the input `selectStatement`. If there are any results, then this\nwill throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit\ntransaction commit or rollback.\n\nFor example, given we have a table `person` with the following data:\n| id | first_name  | last_name |\n|  1 | Franz Allan | See       |\n\nWhen you have the following assertions in your robot\n| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'John' |\n| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' |\n\nThen you will get the following:\n| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'John' | # PASS |\n| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' | # FAIL |\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'John' | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Connect To Database\">\n<arguments>\n<arg>dbapiModuleName=None</arg>\n<arg>dbName=None</arg>\n<arg>dbUsername=None</arg>\n<arg>dbPassword=None</arg>\n<arg>dbHost=None</arg>\n<arg>dbPort=None</arg>\n<arg>dbCharset=None</arg>\n<arg>dbConfigFile=./resources/db.cfg</arg>\n</arguments>\n<doc>Loads the DB API 2.0 module given `dbapiModuleName` then uses it to\nconnect to the database using `dbName`, `dbUsername`, and `dbPassword`.\n\nOptionally, you can specify a `dbConfigFile` wherein it will load the\ndefault property values for `dbapiModuleName`, `dbName` `dbUsername`\nand `dbPassword` (note: specifying `dbapiModuleName`, `dbName`\n`dbUsername` or `dbPassword` directly will override the properties of\nthe same key in `dbConfigFile`). If no `dbConfigFile` is specified, it\ndefaults to `./resources/db.cfg`.\n\nThe `dbConfigFile` is useful if you don't want to check into your SCM\nyour database credentials.\n\nExample usage:\n| # explicitly specifies all db property values |\n| Connect To Database | psycopg2 | my_db | postgres | s3cr3t | tiger.foobar.com | 5432 |\n\n| # loads all property values from default.cfg |\n| Connect To Database | dbConfigFile=default.cfg |\n\n| # loads all property values from ./resources/db.cfg |\n| Connect To Database |\n\n| # uses explicit `dbapiModuleName` and `dbName` but uses the `dbUsername` and `dbPassword` in 'default.cfg' |\n| Connect To Database | psycopg2 | my_db_test | dbConfigFile=default.cfg |\n\n| # uses explicit `dbapiModuleName` and `dbName` but uses the `dbUsername` and `dbPassword` in './resources/db.cfg' |\n| Connect To Database | psycopg2 | my_db_test |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Connect To Database Using Custom Params\">\n<arguments>\n<arg>dbapiModuleName=None</arg>\n<arg>db_connect_string=</arg>\n</arguments>\n<doc>Loads the DB API 2.0 module given `dbapiModuleName` then uses it to\nconnect to the database using the map string `db_custom_param_string`.\n\nExample usage:\n| # for psycopg2 |\n| Connect To Database Using Custom Params | psycopg2 | database='my_db_test', user='postgres', password='s3cr3t', host='tiger.foobar.com', port=5432 |\n\n| # for JayDeBeApi |\n| Connect To Database Using Custom Params | JayDeBeApi | 'oracle.jdbc.driver.OracleDriver', 'my_db_test', 'system', 's3cr3t' |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Delete All Rows From Table\">\n<arguments>\n<arg>tableName</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Delete all the rows within a given table. Set optional input `sansTran` to True to run command without an\nexplicit transaction commit or rollback.\n\nFor example, given we have a table `person` in a database\n\nWhen you do the following:\n| Delete All Rows From Table | person |\n\nIf all the rows can be successfully deleted, then you will get:\n| Delete All Rows From Table | person | # PASS |\nIf the table doesn't exist or all the data can't be deleted, then you\nwill get:\n| Delete All Rows From Table | first_name | # FAIL |\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| Delete All Rows From Table | person | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Description\">\n<arguments>\n<arg>selectStatement</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Uses the input `selectStatement` to query a table in the db which will be used to determine the description. Set\noptional input `sansTran` to True to run command without an explicit transaction commit or rollback.\n\nFor example, given we have a table `person` with the following data:\n| id | first_name  | last_name |\n|  1 | Franz Allan | See       |\n\nWhen you do the following:\n| @{queryResults} | Description | SELECT * FROM person |\n| Log Many | @{queryResults} |\n\nYou will get the following:\n[Column(name='id', type_code=1043, display_size=None, internal_size=255, precision=None, scale=None, null_ok=None)]\n[Column(name='first_name', type_code=1043, display_size=None, internal_size=255, precision=None, scale=None, null_ok=None)]\n[Column(name='last_name', type_code=1043, display_size=None, internal_size=255, precision=None, scale=None, null_ok=None)]\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| @{queryResults} | Description | SELECT * FROM person | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Disconnect From Database\">\n<arguments>\n</arguments>\n<doc>Disconnects from the database.\n\nFor example:\n| Disconnect From Database | # disconnects from current connection to the database |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Execute Sql Script\">\n<arguments>\n<arg>sqlScriptFileName</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Executes the content of the `sqlScriptFileName` as SQL commands. Useful for setting the database to a known\nstate before running your tests, or clearing out your test data after running each a test. Set optional input\n`sansTran` to True to run command without an explicit transaction commit or rollback.\n\nSample usage :\n| Execute Sql Script | ${EXECDIR}${/}resources${/}DDL-setup.sql |\n| Execute Sql Script | ${EXECDIR}${/}resources${/}DML-setup.sql |\n| #interesting stuff here |\n| Execute Sql Script | ${EXECDIR}${/}resources${/}DML-teardown.sql |\n| Execute Sql Script | ${EXECDIR}${/}resources${/}DDL-teardown.sql |\n\nSQL commands are expected to be delimited by a semi-colon (';').\n\nFor example:\nDELETE FROM person_employee_table;\nDELETE FROM person_table;\nDELETE FROM employee_table;\n\nAlso, the last SQL command can optionally omit its trailing semi-colon.\n\nFor example:\nDELETE FROM person_employee_table;\nDELETE FROM person_table;\nDELETE FROM employee_table\n\nGiven this, that means you can create spread your SQL commands in several\nlines.\n\nFor example:\nDELETE\n  FROM person_employee_table;\nDELETE\n  FROM person_table;\nDELETE\n  FROM employee_table\n\nHowever, lines that starts with a number sign (`#`) are treated as a\ncommented line. Thus, none of the contents of that line will be executed.\n\nFor example:\n# Delete the bridging table first...\nDELETE\n  FROM person_employee_table;\n  # ...and then the bridged tables.\nDELETE\n  FROM person_table;\nDELETE\n  FROM employee_table\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| Execute Sql Script | ${EXECDIR}${/}resources${/}DDL-setup.sql | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Execute Sql String\">\n<arguments>\n<arg>sqlString</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Executes the sqlString as SQL commands. Useful to pass arguments to your sql. Set optional input `sansTran` to\nTrue to run command without an explicit transaction commit or rollback.\n\nSQL commands are expected to be delimited by a semi-colon (';').\n\nFor example:\n| Execute Sql String | DELETE FROM person_employee_table; DELETE FROM person_table |\n\nFor example with an argument:\n| Execute Sql String | SELECT * FROM person WHERE first_name = ${FIRSTNAME} |\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| Execute Sql String | DELETE FROM person_employee_table; DELETE FROM person_table | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Query\">\n<arguments>\n<arg>selectStatement</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Uses the input `selectStatement` to query for the values that will be returned as a list of tuples. Set optional\ninput `sansTran` to True to run command without an explicit transaction commit or rollback.\n\nTip: Unless you want to log all column values of the specified rows,\ntry specifying the column names in your select statements\nas much as possible to prevent any unnecessary surprises with schema\nchanges and to easily see what your [] indexing is trying to retrieve\n(i.e. instead of `\"select * from my_table\"`, try\n`\"select id, col_1, col_2 from my_table\"`).\n\nFor example, given we have a table `person` with the following data:\n| id | first_name  | last_name |\n|  1 | Franz Allan | See       |\n\nWhen you do the following:\n| @{queryResults} | Query | SELECT * FROM person |\n| Log Many | @{queryResults} |\n\nYou will get the following:\n[1, 'Franz Allan', 'See']\n\nAlso, you can do something like this:\n| ${queryResults} | Query | SELECT first_name, last_name FROM person |\n| Log | ${queryResults[0][1]}, ${queryResults[0][0]} |\n\nAnd get the following\nSee, Franz Allan\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| @{queryResults} | Query | SELECT * FROM person | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Row Count\">\n<arguments>\n<arg>selectStatement</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Uses the input `selectStatement` to query the database and returns the number of rows from the query. Set\noptional input `sansTran` to True to run command without an explicit transaction commit or rollback.\n\nFor example, given we have a table `person` with the following data:\n| id | first_name  | last_name |\n|  1 | Franz Allan | See       |\n|  2 | Jerry       | Schneider |\n\nWhen you do the following:\n| ${rowCount} | Row Count | SELECT * FROM person |\n| Log | ${rowCount} |\n\nYou will get the following:\n2\n\nAlso, you can do something like this:\n| ${rowCount} | Row Count | SELECT * FROM person WHERE id = 2 |\n| Log | ${rowCount} |\n\nAnd get the following\n1\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| ${rowCount} | Row Count | SELECT * FROM person | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Row Count Is 0\">\n<arguments>\n<arg>selectStatement</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Check if any rows are returned from the submitted `selectStatement`. If there are, then this will throw an\nAssertionError. Set optional input `sansTran` to True to run command without an explicit transaction commit or\nrollback.\n\nFor example, given we have a table `person` with the following data:\n| id | first_name  | last_name |\n|  1 | Franz Allan | See       |\n\nWhen you have the following assertions in your robot\n| Row Count is 0 | SELECT id FROM person WHERE first_name = 'Franz Allan' |\n| Row Count is 0 | SELECT id FROM person WHERE first_name = 'John' |\n\nThen you will get the following:\n| Row Count is 0 | SELECT id FROM person WHERE first_name = 'Franz Allan' | # FAIL |\n| Row Count is 0 | SELECT id FROM person WHERE first_name = 'John' | # PASS |\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| Row Count is 0 | SELECT id FROM person WHERE first_name = 'John' | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Row Count Is Equal To X\">\n<arguments>\n<arg>selectStatement</arg>\n<arg>numRows</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Check if the number of rows returned from `selectStatement` is equal to the value submitted. If not, then this\nwill throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit\ntransaction commit or rollback.\n\nFor example, given we have a table `person` with the following data:\n| id | first_name  | last_name |\n|  1 | Franz Allan | See       |\n|  2 | Jerry       | Schneider |\n\nWhen you have the following assertions in your robot\n| Row Count Is Equal To X | SELECT id FROM person | 1 |\n| Row Count Is Equal To X | SELECT id FROM person WHERE first_name = 'John' | 0 |\n\nThen you will get the following:\n| Row Count Is Equal To X | SELECT id FROM person | 1 | # FAIL |\n| Row Count Is Equal To X | SELECT id FROM person WHERE first_name = 'John' | 0 | # PASS |\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| Row Count Is Equal To X | SELECT id FROM person WHERE first_name = 'John' | 0 | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Row Count Is Greater Than X\">\n<arguments>\n<arg>selectStatement</arg>\n<arg>numRows</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Check if the number of rows returned from `selectStatement` is greater than the value submitted. If not, then\nthis will throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit\ntransaction commit or rollback.\n\nFor example, given we have a table `person` with the following data:\n| id | first_name  | last_name |\n|  1 | Franz Allan | See       |\n|  2 | Jerry       | Schneider |\n\nWhen you have the following assertions in your robot\n| Row Count Is Greater Than X | SELECT id FROM person | 1 |\n| Row Count Is Greater Than X | SELECT id FROM person WHERE first_name = 'John' | 0 |\n\nThen you will get the following:\n| Row Count Is Greater Than X | SELECT id FROM person | 1 | # PASS |\n| Row Count Is Greater Than X | SELECT id FROM person WHERE first_name = 'John' | 0 | # FAIL |\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| Row Count Is Greater Than X | SELECT id FROM person | 1 | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Row Count Is Less Than X\">\n<arguments>\n<arg>selectStatement</arg>\n<arg>numRows</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Check if the number of rows returned from `selectStatement` is less than the value submitted. If not, then this\nwill throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit\ntransaction commit or rollback.\n\nFor example, given we have a table `person` with the following data:\n| id | first_name  | last_name |\n|  1 | Franz Allan | See       |\n|  2 | Jerry       | Schneider |\n\nWhen you have the following assertions in your robot\n| Row Count Is Less Than X | SELECT id FROM person | 3 |\n| Row Count Is Less Than X | SELECT id FROM person WHERE first_name = 'John' | 1 |\n\nThen you will get the following:\n| Row Count Is Less Than X | SELECT id FROM person | 3 | # PASS |\n| Row Count Is Less Than X | SELECT id FROM person WHERE first_name = 'John' | 1 | # FAIL |\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| Row Count Is Less Than X | SELECT id FROM person | 3 | True |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Table Must Exist\">\n<arguments>\n<arg>tableName</arg>\n<arg>sansTran=False</arg>\n</arguments>\n<doc>Check if the table given exists in the database. Set optional input `sansTran` to True to run command without an\nexplicit transaction commit or rollback.\n\nFor example, given we have a table `person` in a database\n\nWhen you do the following:\n| Table Must Exist | person |\n\nThen you will get the following:\n| Table Must Exist | person | # PASS |\n| Table Must Exist | first_name | # FAIL |\n\nUsing optional `sansTran` to run command without an explicit transaction commit or rollback:\n| Table Must Exist | person | True |</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/DateTime.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"DateTime\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:40\">\n<version>3.0.3</version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>A test library for handling date and time values.\n\n``DateTime`` is a Robot Framework standard library that supports creating and\nconverting date and time values (e.g. `Get Current Date`, `Convert Time`),\nas well as doing simple calculations with them (e.g. `Subtract Time From Date`,\n`Add Time To Time`). It supports dates and times in various formats, and can\nalso be used by other libraries programmatically.\n\nThis library is new in Robot Framework 2.8.5.\n\n= Table of Contents =\n\n- `Terminology`\n- `Date formats`\n- `Time formats`\n- `Millisecond handling`\n- `Programmatic usage`\n- `Shortcuts`\n- `Keywords`\n\n= Terminology =\n\nIn the context of this library, ``date`` and ``time`` generally have following\nmeanings:\n\n- ``date``: An entity with both date and time components but without any\n   timezone information. For example, ``2014-06-11 10:07:42``.\n- ``time``: A time interval. For example, ``1 hour 20 minutes`` or ``01:20:00``.\n\nThis terminology differs from what Python's standard\n[https://docs.python.org/2/library/datetime.html|datetime] module uses.\nBasically its\n[https://docs.python.org/2/library/datetime.html#datetime-objects|datetime] and\n[https://docs.python.org/2/library/datetime.html#timedelta-objects|timedelta]\nobjects match ``date`` and ``time`` as defined by this library.\n\n= Date formats =\n\nDates can given to and received from keywords in `timestamp`, `custom\ntimestamp`, `Python datetime` and `epoch time` formats. These formats are\ndiscussed thoroughly in subsequent sections.\n\nInput format is determined automatically based on the given date except when\nusing custom timestamps, in which case it needs to be given using\n``date_format`` argument. Default result format is timestamp, but it can\nbe overridden using ``result_format`` argument.\n\n== Timestamp ==\n\nIf a date is given as a string, it is always considered to be a timestamp.\nIf no custom formatting is given using ``date_format`` argument, the timestamp\nis expected to be in [http://en.wikipedia.org/wiki/ISO_8601|ISO 8601] like\nformat ``YYYY-MM-DD hh:mm:ss.mil``, where any non-digit character can be used\nas a separator or separators can be omitted altogether. Additionally,\nonly the date part is mandatory, all possibly missing time components are\nconsidered to be zeros.\n\nDates can also be returned in the same ``YYYY-MM-DD hh:mm:ss.mil`` format by\nusing ``timestamp`` value with ``result_format`` argument. This is also the\ndefault format that keywords returning dates use. Milliseconds can be excluded\nusing ``exclude_millis`` as explained in `Millisecond handling` section.\n\nExamples:\n| ${date1} =      | Convert Date | 2014-06-11 10:07:42.000 |\n| ${date2} =      | Convert Date | 20140611 100742         | result_format=timestamp |\n| Should Be Equal | ${date1}     | ${date2}                |\n| ${date} =       | Convert Date | 20140612 12:57          | exclude_millis=yes |\n| Should Be Equal | ${date}      | 2014-06-12 12:57:00     |\n\n== Custom timestamp ==\n\nIt is possible to use custom timestamps in both input and output.\nThe custom format is same as accepted by Python's\n[https://docs.python.org/2/library/datetime.html#strftime-strptime-behavior|\ndatatime.strptime] function. For example, the default timestamp discussed\nin the previous section would match ``%Y-%m-%d %H:%M:%S.%f``.\n\nWhen using a custom timestamp in input, it must be specified using\n``date_format`` argument. The actual input value must be a string that matches\nthe specified format exactly. When using a custom timestamp in output, it must\nbe given using ``result_format`` argument.\n\nExamples:\n| ${date} =       | Convert Date | 28.05.2014 12:05        | date_format=%d.%m.%Y %H:%M |\n| Should Be Equal | ${date}      | 2014-05-28 12:05:00.000 |\n| ${date} =       | Convert Date | ${date}                 | result_format=%d.%m.%Y |\n| Should Be Equal | ${date}      | 28.05.2014              |\n\nNotice that locale aware directives like ``%b``  do not work correctly with\nJython on non-English locales: http://bugs.jython.org/issue2285\n\n== Python datetime ==\n\nPython's standard\n[https://docs.python.org/2/library/datetime.html#datetime.datetime|datetime]\nobjects can be used both in input and output. In input they are recognized\nautomatically, and in output it is possible to get them by giving ``datetime``\nvalue to ``result_format`` argument.\n\nOne nice benefit with datetime objects is that they have different time\ncomponents available as attributes that can be easily accessed using the\nextended variable syntax.\n\nExamples:\n| ${datetime} = | Convert Date | 2014-06-11 10:07:42.123 | datetime |\n| Should Be Equal As Integers | ${datetime.year}        | 2014   |\n| Should Be Equal As Integers | ${datetime.month}       | 6      |\n| Should Be Equal As Integers | ${datetime.day}         | 11     |\n| Should Be Equal As Integers | ${datetime.hour}        | 10     |\n| Should Be Equal As Integers | ${datetime.minute}      | 7      |\n| Should Be Equal As Integers | ${datetime.second}      | 42     |\n| Should Be Equal As Integers | ${datetime.microsecond} | 123000 |\n\n== Epoch time ==\n\nEpoch time is the time in seconds since the\n[http://en.wikipedia.org/wiki/Unix_time|UNIX epoch] i.e. 00:00:00.000 (UTC)\n1 January 1970. To give a date in epoch time, it must be given as a number\n(integer or float), not as a string. To return a date in epoch time,\nit is possible to use ``epoch`` value with ``result_format`` argument.\nEpoch time is returned as a floating point number.\n\nNotice that epoch time itself is independent on timezones and thus same\naround the world at a certain time. What local time a certain epoch time\nmatches obviously then depends on the timezone. For example, examples below\nwere tested in Finland but verifications would fail on other timezones.\n\nExamples:\n| ${date} =       | Convert Date | ${1000000000}           |\n| Should Be Equal | ${date}      | 2001-09-09 04:46:40.000 |\n| ${date} =       | Convert Date | 2014-06-12 13:27:59.279 | epoch |\n| Should Be Equal | ${date}      | ${1402568879.279}       |\n\n== Earliest supported date ==\n\nThe earliest date that is supported depends on the date format and to some\nextend on the platform:\n\n- Timestamps support year 1900 and above.\n- Python datetime objects support year 1 and above.\n- Epoch time supports 1970 and above on Windows with Python and IronPython.\n- On other platforms epoch time supports 1900 and above or even earlier.\n\nPrior to Robot Framework 2.9.2, all formats had same limitation as epoch time\nhas nowadays.\n\n= Time formats =\n\nSimilarly as dates, times can be given to and received from keywords in\nvarious different formats. Supported formats are `number`, `time string`\n(verbose and compact), `timer string` and `Python timedelta`.\n\nInput format for time is always determined automatically based on the input.\nResult format is number by default, but it can be customised using\n``result_format`` argument.\n\n== Number ==\n\nTime given as a number is interpreted to be seconds. It can be given\neither as an integer or a float, or it can be a string that can be converted\nto a number.\n\nTo return a time as a number, ``result_format`` argument must have value\n``number``, which is also the default. Returned number is always a float.\n\nExamples:\n| ${time} =       | Convert Time | 3.14    |\n| Should Be Equal | ${time}      | ${3.14} |\n| ${time} =       | Convert Time | ${time} | result_format=number |\n| Should Be Equal | ${time}      | ${3.14} |\n\n== Time string ==\n\nTime strings are strings in format like ``1 minute 42 seconds`` or ``1min 42s``.\nThe basic idea of this format is having first a number and then a text\nspecifying what time that number represents. Numbers can be either\nintegers or floating point numbers, the whole format is case and space\ninsensitive, and it is possible to add a minus prefix to specify negative\ntimes. The available time specifiers are:\n\n- ``days``, ``day``, ``d``\n- ``hours``, ``hour``, ``h``\n- ``minutes``, ``minute``, ``mins``, ``min``, ``m``\n- ``seconds``, ``second``, ``secs``, ``sec``, ``s``\n- ``milliseconds``, ``millisecond``, ``millis``, ``ms``\n\nWhen returning a time string, it is possible to select between ``verbose``\nand ``compact`` representations using ``result_format`` argument. The verbose\nformat uses long specifiers ``day``, ``hour``, ``minute``, ``second`` and\n``millisecond``, and adds ``s`` at the end when needed. The compact format uses\nshorter specifiers ``d``, ``h``, ``min``, ``s`` and ``ms``, and even drops\nthe space between the number and the specifier.\n\nExamples:\n| ${time} =       | Convert Time | 1 minute 42 seconds |\n| Should Be Equal | ${time}      | ${102}              |\n| ${time} =       | Convert Time | 4200                | verbose |\n| Should Be Equal | ${time}      | 1 hour 10 minutes   |\n| ${time} =       | Convert Time | - 1.5 hours         | compact |\n| Should Be Equal | ${time}      | - 1h 30min          |\n\n== Timer string ==\n\nTimer string is a string given in timer like format ``hh:mm:ss.mil``. In this\nformat both hour and millisecond parts are optional, leading and trailing\nzeros can be left out when they are not meaningful, and negative times can\nbe represented by adding a minus prefix.\n\nTo return a time as timer string, ``result_format`` argument must be given\nvalue ``timer``. Timer strings are by default returned in full ``hh:mm:ss.mil``\nformat, but milliseconds can be excluded using ``exclude_millis`` as explained\nin `Millisecond handling` section.\n\nExamples:\n| ${time} =       | Convert Time | 01:42        |\n| Should Be Equal | ${time}      | ${102}       |\n| ${time} =       | Convert Time | 01:10:00.123 |\n| Should Be Equal | ${time}      | ${4200.123}  |\n| ${time} =       | Convert Time | 102          | timer |\n| Should Be Equal | ${time}      | 00:01:42.000 |\n| ${time} =       | Convert Time | -101.567     | timer | exclude_millis=yes |\n| Should Be Equal | ${time}      | -00:01:42    |\n\n== Python timedelta ==\n\nPython's standard\n[https://docs.python.org/2/library/datetime.html#datetime.timedelta|timedelta]\nobjects are also supported both in input and in output. In input they are\nrecognized automatically, and in output it is possible to receive them by\ngiving ``timedelta`` value to ``result_format`` argument.\n\nExamples:\n| ${timedelta} =  | Convert Time                 | 01:10:02.123 | timedelta |\n| Should Be Equal | ${timedelta.total_seconds()} | ${4202.123}  |\n\n= Millisecond handling =\n\nThis library handles dates and times internally using the precision of the\ngiven input. With `timestamp`, `time string`, and `timer string` result\nformats seconds are, however, rounded to millisecond accuracy. Milliseconds\nmay also be included even if there would be none.\n\nAll keywords returning dates or times have an option to leave milliseconds out\nby giving a true value to ``exclude_millis`` argument. If the argument is given\nas a string, it is considered true unless it is empty or case-insensitively\nequal to ``false``, ``none`` or ``no``. Other argument types are tested using\nsame [http://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules\nas in Python]. Notice that prior to Robot Framework 2.9, all strings except\nthe empty string were considered true, and that considering ``none`` false is\nnew in Robot Framework 3.0.3.\n\nWhen milliseconds are excluded, seconds in returned dates and times are\nrounded to the nearest full second. With `timestamp` and `timer string`\nresult formats, milliseconds will also be removed from the returned string\naltogether.\n\nExamples:\n| ${date} =       | Convert Date | 2014-06-11 10:07:42     |\n| Should Be Equal | ${date}      | 2014-06-11 10:07:42.000 |\n| ${date} =       | Convert Date | 2014-06-11 10:07:42.500 | exclude_millis=yes |\n| Should Be Equal | ${date}      | 2014-06-11 10:07:43     |\n| ${dt} =         | Convert Date | 2014-06-11 10:07:42.500 | datetime | exclude_millis=yes |\n| Should Be Equal | ${dt.second} | ${43}        |\n| Should Be Equal | ${dt.microsecond} | ${0}    |\n| ${time} =       | Convert Time | 102          | timer | exclude_millis=false |\n| Should Be Equal | ${time}      | 00:01:42.000 |       |\n| ${time} =       | Convert Time | 102.567      | timer | exclude_millis=true |\n| Should Be Equal | ${time}      | 00:01:43     |       |\n\n= Programmatic usage =\n\nIn addition to be used as normal library, this library is intended to\nprovide a stable API for other libraries to use if they want to support\nsame date and time formats as this library. All the provided keywords\nare available as functions that can be easily imported:\n\n| from robot.libraries.DateTime import convert_time\n|\n| def example_keyword(timeout):\n|     seconds = convert_time(timeout)\n|     # ...\n\nAdditionally helper classes ``Date`` and ``Time`` can be used directly:\n\n| from robot.libraries.DateTime import Date, Time\n|\n| def example_keyword(date, interval):\n|     date = Date(date).convert('datetime')\n|     interval = Time(interval).convert('number')\n|     # ...</doc>\n<kw name=\"Add Time To Date\">\n<arguments>\n<arg>date</arg>\n<arg>time</arg>\n<arg>result_format=timestamp</arg>\n<arg>exclude_millis=False</arg>\n<arg>date_format=None</arg>\n</arguments>\n<doc>Adds time to date and returns the resulting date.\n\nArguments:\n- ``date:``           Date to add time to in one of the supported\n                      `date formats`.\n- ``time:``           Time that is added in one of the supported\n                      `time formats`.\n- ``result_format:``  Format of the returned date.\n- ``exclude_millis:`` When set to any true value, rounds and drops\n                      milliseconds as explained in `millisecond handling`.\n- ``date_format:``    Possible `custom timestamp` format of ``date``.\n\nExamples:\n| ${date} =       | Add Time To Date | 2014-05-28 12:05:03.111 | 7 days       |\n| Should Be Equal | ${date}          | 2014-06-04 12:05:03.111 |              |\n| ${date} =       | Add Time To Date | 2014-05-28 12:05:03.111 | 01:02:03:004 |\n| Should Be Equal | ${date}          | 2014-05-28 13:07:06.115 |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Add Time To Time\">\n<arguments>\n<arg>time1</arg>\n<arg>time2</arg>\n<arg>result_format=number</arg>\n<arg>exclude_millis=False</arg>\n</arguments>\n<doc>Adds time to another time and returns the resulting time.\n\nArguments:\n- ``time1:``          First time in one of the supported `time formats`.\n- ``time2:``          Second time in one of the supported `time formats`.\n- ``result_format:``  Format of the returned time.\n- ``exclude_millis:`` When set to any true value, rounds and drops\n                      milliseconds as explained in `millisecond handling`.\n\nExamples:\n| ${time} =       | Add Time To Time | 1 minute          | 42       |\n| Should Be Equal | ${time}          | ${102}            |\n| ${time} =       | Add Time To Time | 3 hours 5 minutes | 01:02:03 | timer | exclude_millis=yes |\n| Should Be Equal | ${time}          | 04:07:03          |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert Date\">\n<arguments>\n<arg>date</arg>\n<arg>result_format=timestamp</arg>\n<arg>exclude_millis=False</arg>\n<arg>date_format=None</arg>\n</arguments>\n<doc>Converts between supported `date formats`.\n\nArguments:\n- ``date:``           Date in one of the supported `date formats`.\n- ``result_format:``  Format of the returned date.\n- ``exclude_millis:`` When set to any true value, rounds and drops\n                      milliseconds as explained in `millisecond handling`.\n- ``date_format:``    Specifies possible `custom timestamp` format.\n\nExamples:\n| ${date} =       | Convert Date | 20140528 12:05:03.111   |\n| Should Be Equal | ${date}      | 2014-05-28 12:05:03.111 |\n| ${date} =       | Convert Date | ${date}                 | epoch |\n| Should Be Equal | ${date}      | ${1401267903.111}       |\n| ${date} =       | Convert Date | 5.28.2014 12:05         | exclude_millis=yes | date_format=%m.%d.%Y %H:%M |\n| Should Be Equal | ${date}      | 2014-05-28 12:05:00     |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert Time\">\n<arguments>\n<arg>time</arg>\n<arg>result_format=number</arg>\n<arg>exclude_millis=False</arg>\n</arguments>\n<doc>Converts between supported `time formats`.\n\nArguments:\n- ``time:``           Time in one of the supported `time formats`.\n- ``result_format:``  Format of the returned time.\n- ``exclude_millis:`` When set to any true value, rounds and drops\n                      milliseconds as explained in `millisecond handling`.\n\nExamples:\n| ${time} =       | Convert Time  | 10 seconds        |\n| Should Be Equal | ${time}       | ${10}             |\n| ${time} =       | Convert Time  | 1:00:01           | verbose |\n| Should Be Equal | ${time}       | 1 hour 1 second   |\n| ${time} =       | Convert Time  | ${3661.5} | timer | exclude_milles=yes |\n| Should Be Equal | ${time}       | 01:01:02          |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Current Date\">\n<arguments>\n<arg>time_zone=local</arg>\n<arg>increment=0</arg>\n<arg>result_format=timestamp</arg>\n<arg>exclude_millis=False</arg>\n</arguments>\n<doc>Returns current local or UTC time with an optional increment.\n\nArguments:\n- ``time_zone:``      Get the current time on this time zone. Currently only\n                      ``local`` (default) and ``UTC`` are supported.\n- ``increment:``      Optional time increment to add to the returned date in\n                      one of the supported `time formats`. Can be negative.\n- ``result_format:``  Format of the returned date (see `date formats`).\n- ``exclude_millis:`` When set to any true value, rounds and drops\n                      milliseconds as explained in `millisecond handling`.\n\nExamples:\n| ${date} =       | Get Current Date |\n| Should Be Equal | ${date}          | 2014-06-12 20:00:58.946 |\n| ${date} =       | Get Current Date | UTC                     |\n| Should Be Equal | ${date}          | 2014-06-12 17:00:58.946 |\n| ${date} =       | Get Current Date | increment=02:30:00      |\n| Should Be Equal | ${date}          | 2014-06-12 22:30:58.946 |\n| ${date} =       | Get Current Date | UTC                     | - 5 hours |\n| Should Be Equal | ${date}          | 2014-06-12 12:00:58.946 |\n| ${date} =       | Get Current Date | result_format=datetime  |\n| Should Be Equal | ${date.year}     | ${2014}                 |\n| Should Be Equal | ${date.month}    | ${6}                    |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Subtract Date From Date\">\n<arguments>\n<arg>date1</arg>\n<arg>date2</arg>\n<arg>result_format=number</arg>\n<arg>exclude_millis=False</arg>\n<arg>date1_format=None</arg>\n<arg>date2_format=None</arg>\n</arguments>\n<doc>Subtracts date from another date and returns time between.\n\nArguments:\n- ``date1:``          Date to subtract another date from in one of the\n                      supported `date formats`.\n- ``date2:``          Date that is subtracted in one of the supported\n                      `date formats`.\n- ``result_format:``  Format of the returned time (see `time formats`).\n- ``exclude_millis:`` When set to any true value, rounds and drops\n                      milliseconds as explained in `millisecond handling`.\n- ``date1_format:``   Possible `custom timestamp` format of ``date1``.\n- ``date2_format:``   Possible `custom timestamp` format of ``date2``.\n\n Examples:\n| ${time} =       | Subtract Date From Date | 2014-05-28 12:05:52     | 2014-05-28 12:05:10 |\n| Should Be Equal | ${time}                 | ${42}                   |\n| ${time} =       | Subtract Date From Date | 2014-05-28 12:05:52     | 2014-05-27 12:05:10 | verbose |\n| Should Be Equal | ${time}                 | 1 day 42 seconds        |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Subtract Time From Date\">\n<arguments>\n<arg>date</arg>\n<arg>time</arg>\n<arg>result_format=timestamp</arg>\n<arg>exclude_millis=False</arg>\n<arg>date_format=None</arg>\n</arguments>\n<doc>Subtracts time from date and returns the resulting date.\n\nArguments:\n- ``date:``           Date to subtract time from in one of the supported\n                      `date formats`.\n- ``time:``           Time that is subtracted in one of the supported\n                     `time formats`.\n- ``result_format:``  Format of the returned date.\n- ``exclude_millis:`` When set to any true value, rounds and drops\n                      milliseconds as explained in `millisecond handling`.\n- ``date_format:``    Possible `custom timestamp` format of ``date``.\n\nExamples:\n| ${date} =       | Subtract Time From Date | 2014-06-04 12:05:03.111 | 7 days |\n| Should Be Equal | ${date}                 | 2014-05-28 12:05:03.111 |\n| ${date} =       | Subtract Time From Date | 2014-05-28 13:07:06.115 | 01:02:03:004 |\n| Should Be Equal | ${date}                 | 2014-05-28 12:05:03.111 |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Subtract Time From Time\">\n<arguments>\n<arg>time1</arg>\n<arg>time2</arg>\n<arg>result_format=number</arg>\n<arg>exclude_millis=False</arg>\n</arguments>\n<doc>Subtracts time from another time and returns the resulting time.\n\nArguments:\n- ``time1:``          Time to subtract another time from in one of\n                      the supported `time formats`.\n- ``time2:``          Time to subtract in one of the supported `time formats`.\n- ``result_format:``  Format of the returned time.\n- ``exclude_millis:`` When set to any true value, rounds and drops\n                      milliseconds as explained in `millisecond handling`.\n\nExamples:\n| ${time} =       | Subtract Time From Time | 00:02:30 | 100      |\n| Should Be Equal | ${time}                 | ${50}    |\n| ${time} =       | Subtract Time From Time | ${time}  | 1 minute | compact |\n| Should Be Equal | ${time}                 | - 10s    |</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/Dialogs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"Dialogs\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:41\">\n<version>3.0.3</version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>A test library providing dialogs for interacting with users.\n\n``Dialogs`` is Robot Framework's standard library that provides means\nfor pausing the test execution and getting input from users. The\ndialogs are slightly different depending on whether tests are run on\nPython, IronPython or Jython but they provide the same functionality.\n\nLong lines in the provided messages are wrapped automatically since\nRobot Framework 2.8. If you want to wrap lines manually, you can add\nnewlines using the ``\\n`` character sequence.\n\nThe library has a known limitation that it cannot be used with timeouts\non Python. Support for IronPython was added in Robot Framework 2.9.2.</doc>\n<kw name=\"Execute Manual Step\">\n<arguments>\n<arg>message</arg>\n<arg>default_error=</arg>\n</arguments>\n<doc>Pauses test execution until user sets the keyword status.\n\nUser can press either ``PASS`` or ``FAIL`` button. In the latter case execution\nfails and an additional dialog is opened for defining the error message.\n\n``message`` is the instruction shown in the initial dialog and\n``default_error`` is the default value shown in the possible error message\ndialog.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Selection From User\">\n<arguments>\n<arg>message</arg>\n<arg>*values</arg>\n</arguments>\n<doc>Pauses test execution and asks user to select a value.\n\nThe selected value is returned. Pressing ``Cancel`` fails the keyword.\n\n``message`` is the instruction shown in the dialog and ``values`` are\nthe options given to the user.\n\nExample:\n| ${username} = | Get Selection From User | Select user name | user1 | user2 | admin |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Value From User\">\n<arguments>\n<arg>message</arg>\n<arg>default_value=</arg>\n<arg>hidden=False</arg>\n</arguments>\n<doc>Pauses test execution and asks user to input a value.\n\nValue typed by the user, or the possible default value, is returned.\nReturning an empty value is fine, but pressing ``Cancel`` fails the keyword.\n\n``message`` is the instruction shown in the dialog and ``default_value`` is\nthe possible default value shown in the input field.\n\nIf ``hidden`` is given a true value, the value typed by the user is hidden.\n``hidden`` is considered true if it is a non-empty string not equal to\n``false``, ``none`` or ``no``, case-insensitively. If it is not a string,\nits truth value is got directly using same\n[http://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules\nas in Python].\n\nExample:\n| ${username} = | Get Value From User | Input user name | default    |\n| ${password} = | Get Value From User | Input password  | hidden=yes |\n\nPossibility to hide the typed in value is new in Robot Framework 2.8.4.\nConsidering strings ``false`` and ``no`` to be false is new in RF 2.9\nand considering string ``none`` false is new in RF 3.0.3.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Pause Execution\">\n<arguments>\n<arg>message=Test execution paused. Press OK to continue.</arg>\n</arguments>\n<doc>Pauses test execution until user clicks ``Ok`` button.\n\n``message`` is the message shown in the dialog.</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/OperatingSystem.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"OperatingSystem\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:41\">\n<version>3.0.3</version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>A test library providing keywords for OS related tasks.\n\n``OperatingSystem`` is Robot Framework's standard library that\nenables various operating system related tasks to be performed in\nthe system where Robot Framework is running. It can, among other\nthings, execute commands (e.g. `Run`), create and remove files and\ndirectories (e.g. `Create File`, `Remove Directory`), check\nwhether files or directories exists or contain something\n(e.g. `File Should Exist`, `Directory Should Be Empty`) and\nmanipulate environment variables (e.g. `Set Environment Variable`).\n\n== Table of contents ==\n\n- `Path separators`\n- `Pattern matching`\n- `Tilde expansion`\n- `Boolean arguments`\n- `Example`\n- `Shortcuts`\n- `Keywords`\n\n= Path separators =\n\nBecause Robot Framework uses the backslash (``\\``) as an escape character\nin the test data, using a literal backslash requires duplicating it like\nin ``c:\\\\path\\\\file.txt``. That can be inconvenient especially with\nlonger Windows paths, and thus all keywords expecting paths as arguments\nconvert forward slashes to backslashes automatically on Windows. This also\nmeans that paths like ``${CURDIR}/path/file.txt`` are operating system\nindependent.\n\nNotice that the automatic path separator conversion does not work if\nthe path is only a part of an argument like with `Run` and `Start Process`\nkeywords. In these cases the built-in variable ``${/}`` that contains\n``\\`` or ``/``, depending on the operating system, can be used instead.\n\n= Pattern matching =\n\nSome keywords allow their arguments to be specified as _glob patterns_\nwhere:\n| ``*``        | matches anything, even an empty string |\n| ``?``        | matches any single character |\n| ``[chars]``  | matches any character inside square brackets (e.g. ``[abc]`` matches either ``a``, ``b`` or ``c``) |\n| ``[!chars]`` | matches any character not inside square brackets |\n\nUnless otherwise noted, matching is case-insensitive on\ncase-insensitive operating systems such as Windows. Pattern\nmatching is implemented using\n[http://docs.python.org/library/fnmatch.html|fnmatch module].\n\nStarting from Robot Framework 2.9.1, globbing is not done if the given path\nmatches an existing file even if it would contain a glob pattern.\n\n= Tilde expansion =\n\nPaths beginning with ``~`` or ``~username`` are expanded to the current or\nspecified user's home directory, respectively. The resulting path is\noperating system dependent, but typically e.g. ``~/robot`` is expanded to\n``C:\\Users\\&lt;user&gt;\\robot`` on Windows and ``/home/&lt;user&gt;/robot`` on\nUnixes.\n\nTilde expansion is a new feature in Robot Framework 2.8. The ``~username``\nform does not work on Jython\n\n= Boolean arguments =\n\nSome keywords accept arguments that are handled as Boolean values true or\nfalse. If such an argument is given as a string, it is considered false if\nit is either an empty string or case-insensitively equal to ``false``,\n``none`` or ``no``. Other strings are considered true regardless\ntheir value, and other argument types are tested using the same\n[http://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules\nas in Python].\n\nTrue examples:\n| `Remove Directory` | ${path} | recursive=True    | # Strings are generally true.    |\n| `Remove Directory` | ${path} | recursive=yes     | # Same as the above.             |\n| `Remove Directory` | ${path} | recursive=${TRUE} | # Python ``True`` is true.       |\n| `Remove Directory` | ${path} | recursive=${42}   | # Numbers other than 0 are true. |\n\nFalse examples:\n| `Remove Directory` | ${path} | recursive=False    | # String ``false`` is false.   |\n| `Remove Directory` | ${path} | recursive=no       | # Also string ``no`` is false. |\n| `Remove Directory` | ${path} | recursive=${EMPTY} | # Empty string is false.       |\n| `Remove Directory` | ${path} | recursive=${FALSE} | # Python ``False`` is false.   |\n\nPrior to Robot Framework 2.9, all non-empty strings, including ``false``\nand ``no``, were considered true. Considering ``none`` false is new in\nRobot Framework 3.0.3.\n\n= Example =\n\n|  =Setting=  |     =Value=     |\n| Library     | OperatingSystem |\n\n| =Variable=  |       =Value=         |\n| ${PATH}     | ${CURDIR}/example.txt |\n\n| =Test Case= |     =Action=      | =Argument= |    =Argument=        |\n| Example     | Create File       | ${PATH}    | Some text            |\n|             | File Should Exist | ${PATH}    |                      |\n|             | Copy File         | ${PATH}    | ~/file.txt           |\n|             | ${output} =       | Run | ${TEMPDIR}${/}script.py arg |</doc>\n<kw name=\"Append To Environment Variable\">\n<arguments>\n<arg>name</arg>\n<arg>*values</arg>\n<arg>**config</arg>\n</arguments>\n<doc>Appends given ``values`` to environment variable ``name``.\n\nIf the environment variable already exists, values are added after it,\nand otherwise a new environment variable is created.\n\nValues are, by default, joined together using the operating system\npath separator (``;`` on Windows, ``:`` elsewhere). This can be changed\nby giving a separator after the values like ``separator=value``. No\nother configuration parameters are accepted.\n\nExamples (assuming ``NAME`` and ``NAME2`` do not exist initially):\n| Append To Environment Variable | NAME     | first  |       |\n| Should Be Equal                | %{NAME}  | first  |       |\n| Append To Environment Variable | NAME     | second | third |\n| Should Be Equal                | %{NAME}  | first${:}second${:}third |\n| Append To Environment Variable | NAME2    | first  | separator=-     |\n| Should Be Equal                | %{NAME2} | first  |                 |\n| Append To Environment Variable | NAME2    | second | separator=-     |\n| Should Be Equal                | %{NAME2} | first-second             |\n\nNew in Robot Framework 2.8.4.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Append To File\">\n<arguments>\n<arg>path</arg>\n<arg>content</arg>\n<arg>encoding=UTF-8</arg>\n</arguments>\n<doc>Appends the given content to the specified file.\n\nIf the file does not exists, this keyword works exactly the same\nway as `Create File`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Copy Directory\">\n<arguments>\n<arg>source</arg>\n<arg>destination</arg>\n</arguments>\n<doc>Copies the source directory into the destination.\n\nIf the destination exists, the source is copied under it. Otherwise\nthe destination directory and the possible missing intermediate\ndirectories are created.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Copy File\">\n<arguments>\n<arg>source</arg>\n<arg>destination</arg>\n</arguments>\n<doc>Copies the source file into the destination.\n\nSource must be an existing file. Starting from Robot Framework 2.8.4,\nit can be given as a glob pattern (see `Pattern matching`) that matches\nexactly one file. How the destination is interpreted is explained below.\n\n1) If the destination is an existing file, the source file is copied\nover it.\n\n2) If the destination is an existing directory, the source file is\ncopied into it. A possible file with the same name as the source is\noverwritten.\n\n3) If the destination does not exist and it ends with a path\nseparator (``/`` or ``\\``), it is considered a directory. That\ndirectory is created and a source file copied into it.\nPossible missing intermediate directories are also created.\n\n4) If the destination does not exist and it does not end with a path\nseparator, it is considered a file. If the path to the file does not\nexist, it is created.\n\nThe resulting destination path is returned since Robot Framework 2.9.2.\n\nSee also `Copy Files`, `Move File`, and `Move Files`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Copy Files\">\n<arguments>\n<arg>*sources_and_destination</arg>\n</arguments>\n<doc>Copies specified files to the target directory.\n\nSource files can be given as exact paths and as glob patterns (see\n`Pattern matching`). At least one source must be given, but it is\nnot an error if it is a pattern that does not match anything.\n\nLast argument must be the destination directory. If the destination\ndoes not exist, it will be created.\n\nExamples:\n| Copy Files | ${dir}/file1.txt  | ${dir}/file2.txt | ${dir2} |\n| Copy Files | ${dir}/file-*.txt | ${dir2}          |         |\n\nSee also `Copy File`, `Move File`, and `Move Files`.\n\nNew in Robot Framework 2.8.4.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Count Directories In Directory\">\n<arguments>\n<arg>path</arg>\n<arg>pattern=None</arg>\n</arguments>\n<doc>Wrapper for `Count Items In Directory` returning only directory count.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Count Files In Directory\">\n<arguments>\n<arg>path</arg>\n<arg>pattern=None</arg>\n</arguments>\n<doc>Wrapper for `Count Items In Directory` returning only file count.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Count Items In Directory\">\n<arguments>\n<arg>path</arg>\n<arg>pattern=None</arg>\n</arguments>\n<doc>Returns and logs the number of all items in the given directory.\n\nThe argument ``pattern`` has the same semantics as with `List Directory`\nkeyword. The count is returned as an integer, so it must be checked e.g.\nwith the built-in keyword `Should Be Equal As Integers`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Create Binary File\">\n<arguments>\n<arg>path</arg>\n<arg>content</arg>\n</arguments>\n<doc>Creates a binary file with the given content.\n\nIf content is given as a Unicode string, it is first converted to bytes\ncharacter by character. All characters with ordinal below 256 can be\nused and are converted to bytes with same values. Using characters\nwith higher ordinal is an error.\n\nByte strings, and possible other types, are written to the file as is.\n\nIf the directory for the file does not exist, it is created, along\nwith missing intermediate directories.\n\nExamples:\n| Create Binary File | ${dir}/example.png | ${image content}     |\n| Create Binary File | ${path}            | \\x01\\x00\\xe4\\x00 |\n\nUse `Create File` if you want to create a text file using a certain\nencoding. `File Should Not Exist` can be used to avoid overwriting\nexisting files.\n\nNew in Robot Framework 2.8.5.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Create Directory\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Creates the specified directory.\n\nAlso possible intermediate directories are created. Passes if the\ndirectory already exists, but fails if the path exists and is not\na directory.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Create File\">\n<arguments>\n<arg>path</arg>\n<arg>content=</arg>\n<arg>encoding=UTF-8</arg>\n</arguments>\n<doc>Creates a file with the given content and encoding.\n\nIf the directory for the file does not exist, it is created, along\nwith missing intermediate directories.\n\nSee `Get File` for more information about possible ``encoding`` values,\nincluding special values ``SYSTEM`` and ``CONSOLE``.\n\nExamples:\n| Create File | ${dir}/example.txt | Hello, world!      |         |\n| Create File | ${path}            | Hyv\\xe4 esimerkki | Latin-1 |\n| Create File | /tmp/foo.txt       | ${content}         | SYSTEM  |\n\nUse `Append To File` if you want to append to an existing file\nand `Create Binary File` if you need to write bytes without encoding.\n`File Should Not Exist` can be used to avoid overwriting existing\nfiles.\n\nThe support for ``SYSTEM`` and ``CONSOLE`` encodings is new in Robot\nFramework 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Directory Should Be Empty\">\n<arguments>\n<arg>path</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails unless the specified directory is empty.\n\nThe default error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Directory Should Exist\">\n<arguments>\n<arg>path</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails unless the given path points to an existing directory.\n\nThe path can be given as an exact path or as a glob pattern.\nThe pattern matching syntax is explained in `introduction`.\nThe default error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Directory Should Not Be Empty\">\n<arguments>\n<arg>path</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the specified directory is empty.\n\nThe default error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Directory Should Not Exist\">\n<arguments>\n<arg>path</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the given path points to an existing file.\n\nThe path can be given as an exact path or as a glob pattern.\nThe pattern matching syntax is explained in `introduction`.\nThe default error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Empty Directory\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Deletes all the content from the given directory.\n\nDeletes both files and sub-directories, but the specified directory\nitself if not removed. Use `Remove Directory` if you want to remove\nthe whole directory.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Environment Variable Should Be Set\">\n<arguments>\n<arg>name</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the specified environment variable is not set.\n\nThe default error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Environment Variable Should Not Be Set\">\n<arguments>\n<arg>name</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the specified environment variable is set.\n\nThe default error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"File Should Be Empty\">\n<arguments>\n<arg>path</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails unless the specified file is empty.\n\nThe default error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"File Should Exist\">\n<arguments>\n<arg>path</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails unless the given ``path`` points to an existing file.\n\nThe path can be given as an exact path or as a glob pattern.\nThe pattern matching syntax is explained in `introduction`.\nThe default error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"File Should Not Be Empty\">\n<arguments>\n<arg>path</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the specified directory is empty.\n\nThe default error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"File Should Not Exist\">\n<arguments>\n<arg>path</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the given path points to an existing file.\n\nThe path can be given as an exact path or as a glob pattern.\nThe pattern matching syntax is explained in `introduction`.\nThe default error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Binary File\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Returns the contents of a specified file.\n\nThis keyword reads the specified file and returns the contents as is.\nSee also `Get File`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Environment Variable\">\n<arguments>\n<arg>name</arg>\n<arg>default=None</arg>\n</arguments>\n<doc>Returns the value of an environment variable with the given name.\n\nIf no such environment variable is set, returns the default value, if\ngiven. Otherwise fails the test case.\n\nStarting from Robot Framework 2.7, returned variables are automatically\ndecoded to Unicode using the system encoding.\n\nNote that you can also access environment variables directly using\nthe variable syntax ``%{ENV_VAR_NAME}``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Environment Variables\">\n<arguments>\n</arguments>\n<doc>Returns currently available environment variables as a dictionary.\n\nBoth keys and values are decoded to Unicode using the system encoding.\nAltering the returned dictionary has no effect on the actual environment\nvariables.\n\nNew in Robot Framework 2.7.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get File\">\n<arguments>\n<arg>path</arg>\n<arg>encoding=UTF-8</arg>\n<arg>encoding_errors=strict</arg>\n</arguments>\n<doc>Returns the contents of a specified file.\n\nThis keyword reads the specified file and returns the contents.\nLine breaks in content are converted to platform independent form.\nSee also `Get Binary File`.\n\n``encoding`` defines the encoding of the file. The default value is\n``UTF-8``, which means that UTF-8 and ASCII encoded files are read\ncorrectly. In addition to the encodings supported by the underlying\nPython implementation, the following special encoding values can be\nused:\n\n- ``SYSTEM``: Use the default system encoding.\n- ``CONSOLE``: Use the console encoding. Outside Windows this is same\n  as the system encoding.\n\n``encoding_errors`` argument controls what to do if decoding some bytes\nfails. All values accepted by ``decode`` method in Python are valid, but\nin practice the following values are most useful:\n\n- ``strict``: Fail if characters cannot be decoded (default).\n- ``ignore``: Ignore characters that cannot be decoded.\n- ``replace``: Replace characters that cannot be decoded with\n  a replacement character.\n\n``encoding_errors`` argument was added in Robot Framework 2.8.5 and the\nsupport for ``SYSTEM`` and ``CONSOLE`` encodings in Robot Framework 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get File Size\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Returns and logs file size as an integer in bytes.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Modified Time\">\n<arguments>\n<arg>path</arg>\n<arg>format=timestamp</arg>\n</arguments>\n<doc>Returns the last modification time of a file or directory.\n\nHow time is returned is determined based on the given ``format``\nstring as follows. Note that all checks are case-insensitive.\nReturned time is also automatically logged.\n\n1) If ``format`` contains the word ``epoch``, the time is returned\n   in seconds after the UNIX epoch. The return value is always\n   an integer.\n\n2) If ``format`` contains any of the words ``year``, ``month``,\n   ``day``, ``hour``, ``min`` or ``sec``, only the selected parts are\n   returned. The order of the returned parts is always the one\n   in the previous sentence and the order of the words in\n   ``format`` is not significant. The parts are returned as\n   zero-padded strings (e.g. May -&gt; ``05``).\n\n3) Otherwise, and by default, the time is returned as a\n   timestamp string in the format ``2006-02-24 15:08:31``.\n\nExamples (when the modified time of ``${CURDIR}`` is\n2006-03-29 15:06:21):\n| ${time} = | Get Modified Time | ${CURDIR} |\n| ${secs} = | Get Modified Time | ${CURDIR} | epoch |\n| ${year} = | Get Modified Time | ${CURDIR} | return year |\n| ${y} | ${d} = | Get Modified Time | ${CURDIR} | year,day |\n| @{time} = | Get Modified Time | ${CURDIR} | year,month,day,hour,min,sec |\n=&gt;\n- ${time} = '2006-03-29 15:06:21'\n- ${secs} = 1143637581\n- ${year} = '2006'\n- ${y} = '2006' &amp; ${d} = '29'\n- @{time} = ['2006', '03', '29', '15', '06', '21']</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Grep File\">\n<arguments>\n<arg>path</arg>\n<arg>pattern</arg>\n<arg>encoding=UTF-8</arg>\n<arg>encoding_errors=strict</arg>\n</arguments>\n<doc>Returns the lines of the specified file that match the ``pattern``.\n\nThis keyword reads a file from the file system using the defined\n``path``, ``encoding`` and ``encoding_errors`` similarly as `Get File`.\nA difference is that only the lines that match the given ``pattern`` are\nreturned. Lines are returned as a single string catenated back together\nwith newlines and the number of matched lines is automatically logged.\nPossible trailing newline is never returned.\n\nA line matches if it contains the ``pattern`` anywhere in it and\nit *does not need to match the pattern fully*. The pattern\nmatching syntax is explained in `introduction`, and in this\ncase matching is case-sensitive.\n\nExamples:\n| ${errors} = | Grep File | /var/log/myapp.log | ERROR |\n| ${ret} = | Grep File | ${CURDIR}/file.txt | [Ww]ildc??d ex*ple |\n\nIf more complex pattern matching is needed, it is possible to use\n`Get File` in combination with String library keywords like `Get\nLines Matching Regexp`.\n\n``encoding_errors`` argument is new in Robot Framework 2.8.5.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Join Path\">\n<arguments>\n<arg>base</arg>\n<arg>*parts</arg>\n</arguments>\n<doc>Joins the given path part(s) to the given base path.\n\nThe path separator (``/`` or ``\\``) is inserted when needed and\nthe possible absolute paths handled as expected. The resulted\npath is also normalized.\n\nExamples:\n| ${path} = | Join Path | my        | path  |\n| ${p2} =   | Join Path | my/       | path/ |\n| ${p3} =   | Join Path | my        | path  | my | file.txt |\n| ${p4} =   | Join Path | my        | /path |\n| ${p5} =   | Join Path | /my/path/ | ..    | path2 |\n=&gt;\n- ${path} = 'my/path'\n- ${p2} = 'my/path'\n- ${p3} = 'my/path/my/file.txt'\n- ${p4} = '/path'\n- ${p5} = '/my/path2'</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Join Paths\">\n<arguments>\n<arg>base</arg>\n<arg>*paths</arg>\n</arguments>\n<doc>Joins given paths with base and returns resulted paths.\n\nSee `Join Path` for more information.\n\nExamples:\n| @{p1} = | Join Paths | base     | example       | other |          |\n| @{p2} = | Join Paths | /my/base | /example      | other |          |\n| @{p3} = | Join Paths | my/base  | example/path/ | other | one/more |\n=&gt;\n- @{p1} = ['base/example', 'base/other']\n- @{p2} = ['/example', '/my/base/other']\n- @{p3} = ['my/base/example/path', 'my/base/other', 'my/base/one/more']</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Directories In Directory\">\n<arguments>\n<arg>path</arg>\n<arg>pattern=None</arg>\n<arg>absolute=False</arg>\n</arguments>\n<doc>Wrapper for `List Directory` that returns only directories.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Directory\">\n<arguments>\n<arg>path</arg>\n<arg>pattern=None</arg>\n<arg>absolute=False</arg>\n</arguments>\n<doc>Returns and logs items in a directory, optionally filtered with ``pattern``.\n\nFile and directory names are returned in case-sensitive alphabetical\norder, e.g. ``['A Name', 'Second', 'a lower case name', 'one more']``.\nImplicit directories ``.`` and ``..`` are not returned. The returned\nitems are automatically logged.\n\nFile and directory names are returned relative to the given path\n(e.g. ``'file.txt'``) by default. If you want them be returned in\nabsolute format (e.g. ``'/home/robot/file.txt'``), give the ``absolute``\nargument a true value (see `Boolean arguments`).\n\nIf ``pattern`` is given, only items matching it are returned. The pattern\nmatching syntax is explained in `introduction`, and in this case\nmatching is case-sensitive.\n\nExamples (using also other `List Directory` variants):\n| @{items} = | List Directory           | ${TEMPDIR} |\n| @{files} = | List Files In Directory  | /tmp | *.txt | absolute |\n| ${count} = | Count Files In Directory | ${CURDIR} | ??? |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Files In Directory\">\n<arguments>\n<arg>path</arg>\n<arg>pattern=None</arg>\n<arg>absolute=False</arg>\n</arguments>\n<doc>Wrapper for `List Directory` that returns only files.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log Environment Variables\">\n<arguments>\n<arg>level=INFO</arg>\n</arguments>\n<doc>Logs all environment variables using the given log level.\n\nEnvironment variables are also returned the same way as with\n`Get Environment Variables` keyword.\n\nNew in Robot Framework 2.7.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log File\">\n<arguments>\n<arg>path</arg>\n<arg>encoding=UTF-8</arg>\n<arg>encoding_errors=strict</arg>\n</arguments>\n<doc>Wrapper for `Get File` that also logs the returned file.\n\nThe file is logged with the INFO level. If you want something else,\njust use `Get File` and the built-in keyword `Log` with the desired\nlevel.\n\nSee `Get File` for more information about ``encoding`` and\n``encoding_errors`` arguments.\n\n``encoding_errors`` argument is new in Robot Framework 2.8.5.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Move Directory\">\n<arguments>\n<arg>source</arg>\n<arg>destination</arg>\n</arguments>\n<doc>Moves the source directory into a destination.\n\nUses `Copy Directory` keyword internally, and ``source`` and\n``destination`` arguments have exactly same semantics as with\nthat keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Move File\">\n<arguments>\n<arg>source</arg>\n<arg>destination</arg>\n</arguments>\n<doc>Moves the source file into the destination.\n\nArguments have exactly same semantics as with `Copy File` keyword.\nDestination file path is returned since Robot Framework 2.9.2.\n\nIf the source and destination are on the same filesystem, rename\noperation is used. Otherwise file is copied to the destination\nfilesystem and then removed from the original filesystem.\n\nSee also `Move Files`, `Copy File`, and `Copy Files`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Move Files\">\n<arguments>\n<arg>*sources_and_destination</arg>\n</arguments>\n<doc>Moves specified files to the target directory.\n\nArguments have exactly same semantics as with `Copy Files` keyword.\n\nSee also `Move File`, `Copy File`, and `Copy Files`.\n\nNew in Robot Framework 2.8.4.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Normalize Path\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Normalizes the given path.\n\nExamples:\n| ${path} = | Normalize Path | abc        |\n| ${p2} =   | Normalize Path | abc/       |\n| ${p3} =   | Normalize Path | abc/../def |\n| ${p4} =   | Normalize Path | abc/./def  |\n| ${p5} =   | Normalize Path | abc//def   |\n=&gt;\n- ${path} = 'abc'\n- ${p2} = 'abc'\n- ${p3} = 'def'\n- ${p4} = 'abc/def'\n- ${p5} = 'abc/def'</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Directory\">\n<arguments>\n<arg>path</arg>\n<arg>recursive=False</arg>\n</arguments>\n<doc>Removes the directory pointed to by the given ``path``.\n\nIf the second argument ``recursive`` is given a true value (see\n`Boolean arguments`), the directory is removed recursively. Otherwise\nremoving fails if the directory is not empty.\n\nIf the directory pointed to by the ``path`` does not exist, the keyword\npasses, but it fails, if the ``path`` points to a file.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Environment Variable\">\n<arguments>\n<arg>*names</arg>\n</arguments>\n<doc>Deletes the specified environment variable.\n\nDoes nothing if the environment variable is not set.\n\nStarting from Robot Framework 2.7, it is possible to remove multiple\nvariables by passing them to this keyword as separate arguments.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove File\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Removes a file with the given path.\n\nPasses if the file does not exist, but fails if the path does\nnot point to a regular file (e.g. it points to a directory).\n\nThe path can be given as an exact path or as a glob pattern.\nThe pattern matching syntax is explained in `introduction`.\nIf the path is a pattern, all files matching it are removed.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Files\">\n<arguments>\n<arg>*paths</arg>\n</arguments>\n<doc>Uses `Remove File` to remove multiple files one-by-one.\n\nExample:\n| Remove Files | ${TEMPDIR}${/}foo.txt | ${TEMPDIR}${/}bar.txt | ${TEMPDIR}${/}zap.txt |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run\">\n<arguments>\n<arg>command</arg>\n</arguments>\n<doc>Runs the given command in the system and returns the output.\n\nThe execution status of the command *is not checked* by this\nkeyword, and it must be done separately based on the returned\noutput. If the execution return code is needed, either `Run\nAnd Return RC` or `Run And Return RC And Output` can be used.\n\nThe standard error stream is automatically redirected to the standard\noutput stream by adding ``2&gt;&amp;1`` after the executed command. This\nautomatic redirection is done only when the executed command does not\ncontain additional output redirections. You can thus freely forward\nthe standard error somewhere else, for example, like\n``my_command 2&gt;stderr.txt``.\n\nThe returned output contains everything written into the standard\noutput or error streams by the command (unless either of them\nis redirected explicitly). Many commands add an extra newline\n(``\\n``) after the output to make it easier to read in the\nconsole. To ease processing the returned output, this possible\ntrailing newline is stripped by this keyword.\n\nExamples:\n| ${output} =        | Run       | ls -lhF /tmp |\n| Log                | ${output} |\n| ${result} =        | Run       | ${CURDIR}${/}tester.py arg1 arg2 |\n| Should Not Contain | ${result} | FAIL |\n| ${stdout} =        | Run       | /opt/script.sh 2&gt;/tmp/stderr.txt |\n| Should Be Equal    | ${stdout} | TEST PASSED |\n| File Should Be Empty | /tmp/stderr.txt |\n\n*TIP:* `Run Process` keyword provided by the\n[http://robotframework.org/robotframework/latest/libraries/Process.html|\nProcess library] supports better process configuration and is generally\nrecommended as a replacement for this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run And Return Rc\">\n<arguments>\n<arg>command</arg>\n</arguments>\n<doc>Runs the given command in the system and returns the return code.\n\nThe return code (RC) is returned as a positive integer in\nrange from 0 to 255 as returned by the executed command. On\nsome operating systems (notable Windows) original return codes\ncan be something else, but this keyword always maps them to\nthe 0-255 range. Since the RC is an integer, it must be\nchecked e.g. with the keyword `Should Be Equal As Integers`\ninstead of `Should Be Equal` (both are built-in keywords).\n\nExamples:\n| ${rc} = | Run and Return RC | ${CURDIR}${/}script.py arg |\n| Should Be Equal As Integers | ${rc} | 0 |\n| ${rc} = | Run and Return RC | /path/to/example.rb arg1 arg2 |\n| Should Be True | 0 &lt; ${rc} &lt; 42 |\n\nSee `Run` and `Run And Return RC And Output` if you need to get the\noutput of the executed command.\n\n*TIP:* `Run Process` keyword provided by the\n[http://robotframework.org/robotframework/latest/libraries/Process.html|\nProcess library] supports better process configuration and is generally\nrecommended as a replacement for this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run And Return Rc And Output\">\n<arguments>\n<arg>command</arg>\n</arguments>\n<doc>Runs the given command in the system and returns the RC and output.\n\nThe return code (RC) is returned similarly as with `Run And Return RC`\nand the output similarly as with `Run`.\n\nExamples:\n| ${rc} | ${output} =  | Run and Return RC and Output | ${CURDIR}${/}mytool |\n| Should Be Equal As Integers | ${rc}    | 0    |\n| Should Not Contain   | ${output}       | FAIL |\n| ${rc} | ${stdout} =  | Run and Return RC and Output | /opt/script.sh 2&gt;/tmp/stderr.txt |\n| Should Be True       | ${rc} &gt; 42      |\n| Should Be Equal      | ${stdout}       | TEST PASSED |\n| File Should Be Empty | /tmp/stderr.txt |\n\n*TIP:* `Run Process` keyword provided by the\n[http://robotframework.org/robotframework/latest/libraries/Process.html|\nProcess library] supports better process configuration and is generally\nrecommended as a replacement for this keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Environment Variable\">\n<arguments>\n<arg>name</arg>\n<arg>value</arg>\n</arguments>\n<doc>Sets an environment variable to a specified value.\n\nValues are converted to strings automatically. Starting from Robot\nFramework 2.7, set variables are automatically encoded using the system\nencoding.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Modified Time\">\n<arguments>\n<arg>path</arg>\n<arg>mtime</arg>\n</arguments>\n<doc>Sets the file modification and access times.\n\nChanges the modification and access times of the given file to\nthe value determined by ``mtime``. The time can be given in\ndifferent formats described below. Note that all checks\ninvolving strings are case-insensitive. Modified time can only\nbe set to regular files.\n\n1) If ``mtime`` is a number, or a string that can be converted\n   to a number, it is interpreted as seconds since the UNIX\n   epoch (1970-01-01 00:00:00 UTC). This documentation was\n   originally written about 1177654467 seconds after the epoch.\n\n2) If ``mtime`` is a timestamp, that time will be used. Valid\n   timestamp formats are ``YYYY-MM-DD hh:mm:ss`` and\n   ``YYYYMMDD hhmmss``.\n\n3) If ``mtime`` is equal to ``NOW``, the current local time is used.\n   This time is got using Python's ``time.time()`` function.\n\n4) If ``mtime`` is equal to ``UTC``, the current time in\n   [http://en.wikipedia.org/wiki/Coordinated_Universal_Time|UTC]\n   is used. This time is got using ``time.time() + time.altzone``\n   in Python.\n\n5) If ``mtime`` is in the format like ``NOW - 1 day`` or ``UTC + 1\n   hour 30 min``, the current local/UTC time plus/minus the time\n   specified with the time string is used. The time string format\n   is described in an appendix of Robot Framework User Guide.\n\nExamples:\n| Set Modified Time | /path/file | 1177654467         | # Time given as epoch seconds |\n| Set Modified Time | /path/file | 2007-04-27 9:14:27 | # Time given as a timestamp   |\n| Set Modified Time | /path/file | NOW                | # The local time of execution |\n| Set Modified Time | /path/file | NOW - 1 day        | # 1 day subtracted from the local time |\n| Set Modified Time | /path/file | UTC + 1h 2min 3s   | # 1h 2min 3s added to the UTC time |\n\nSupport for UTC time is a new feature in Robot Framework 2.7.5.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Exist\">\n<arguments>\n<arg>path</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails unless the given path (file or directory) exists.\n\nThe path can be given as an exact path or as a glob pattern.\nThe pattern matching syntax is explained in `introduction`.\nThe default error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Exist\">\n<arguments>\n<arg>path</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the given path (file or directory) exists.\n\nThe path can be given as an exact path or as a glob pattern.\nThe pattern matching syntax is explained in `introduction`.\nThe default error message can be overridden with the ``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Split Extension\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Splits the extension from the given path.\n\nThe given path is first normalized (e.g. possible trailing\npath separators removed, special directories ``..`` and ``.``\nremoved). The base path and extension are returned as separate\ncomponents so that the dot used as an extension separator is\nremoved. If the path contains no extension, an empty string is\nreturned for it. Possible leading and trailing dots in the file\nname are never considered to be extension separators.\n\nExamples:\n| ${path} | ${ext} = | Split Extension | file.extension    |\n| ${p2}   | ${e2} =  | Split Extension | path/file.ext     |\n| ${p3}   | ${e3} =  | Split Extension | path/file         |\n| ${p4}   | ${e4} =  | Split Extension | p1/../p2/file.ext |\n| ${p5}   | ${e5} =  | Split Extension | path/.file.ext    |\n| ${p6}   | ${e6} =  | Split Extension | path/.file        |\n=&gt;\n- ${path} = 'file' &amp; ${ext} = 'extension'\n- ${p2} = 'path/file' &amp; ${e2} = 'ext'\n- ${p3} = 'path/file' &amp; ${e3} = ''\n- ${p4} = 'p2/file' &amp; ${e4} = 'ext'\n- ${p5} = 'path/.file' &amp; ${e5} = 'ext'\n- ${p6} = 'path/.file' &amp; ${e6} = ''</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Split Path\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Splits the given path from the last path separator (``/`` or ``\\``).\n\nThe given path is first normalized (e.g. a possible trailing\npath separator is removed, special directories ``..`` and ``.``\nremoved). The parts that are split are returned as separate\ncomponents.\n\nExamples:\n| ${path1} | ${dir} =  | Split Path | abc/def         |\n| ${path2} | ${file} = | Split Path | abc/def/ghi.txt |\n| ${path3} | ${d2}  =  | Split Path | abc/../def/ghi/ |\n=&gt;\n- ${path1} = 'abc' &amp; ${dir} = 'def'\n- ${path2} = 'abc/def' &amp; ${file} = 'ghi.txt'\n- ${path3} = 'def' &amp; ${d2} = 'ghi'</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Touch\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Emulates the UNIX touch command.\n\nCreates a file, if it does not exist. Otherwise changes its access and\nmodification times to the current time.\n\nFails if used with the directories or the parent directory of the given\nfile does not exist.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Created\">\n<arguments>\n<arg>path</arg>\n<arg>timeout=1 minute</arg>\n</arguments>\n<doc>Waits until the given file or directory is created.\n\nThe path can be given as an exact path or as a glob pattern.\nThe pattern matching syntax is explained in `introduction`.\nIf the path is a pattern, the keyword returns when an item matching\nit is created.\n\nThe optional ``timeout`` can be used to control the maximum time of\nwaiting. The timeout is given as a timeout string, e.g. in a format\n``15 seconds``, ``1min 10s`` or just ``10``. The time string format is\ndescribed in an appendix of Robot Framework User Guide.\n\nIf the timeout is negative, the keyword is never timed-out. The keyword\nreturns immediately, if the path already exists.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Removed\">\n<arguments>\n<arg>path</arg>\n<arg>timeout=1 minute</arg>\n</arguments>\n<doc>Waits until the given file or directory is removed.\n\nThe path can be given as an exact path or as a glob pattern.\nThe pattern matching syntax is explained in `introduction`.\nIf the path is a pattern, the keyword waits until all matching\nitems are removed.\n\nThe optional ``timeout`` can be used to control the maximum time of\nwaiting. The timeout is given as a timeout string, e.g. in a format\n``15 seconds``, ``1min 10s`` or just ``10``. The time string format is\ndescribed in an appendix of Robot Framework User Guide.\n\nIf the timeout is negative, the keyword is never timed-out. The keyword\nreturns immediately, if the path does not exist in the first place.</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/Process.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"Process\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:41\">\n<version>3.0.3</version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>Robot Framework test library for running processes.\n\nThis library utilizes Python's\n[http://docs.python.org/2/library/subprocess.html|subprocess]\nmodule and its\n[http://docs.python.org/2/library/subprocess.html#subprocess.Popen|Popen]\nclass.\n\nThe library has following main usages:\n\n- Running processes in system and waiting for their completion using\n  `Run Process` keyword.\n- Starting processes on background using `Start Process`.\n- Waiting started process to complete using `Wait For Process` or\n  stopping them with `Terminate Process` or `Terminate All Processes`.\n\nThis library is new in Robot Framework 2.8.\n\n== Table of contents ==\n\n- `Specifying command and arguments`\n- `Process configuration`\n- `Active process`\n- `Result object`\n- `Boolean arguments`\n- `Example`\n- `Shortcuts`\n- `Keywords`\n\n= Specifying command and arguments =\n\nBoth `Run Process` and `Start Process` accept the command to execute and\nall arguments passed to the command as separate arguments. This makes usage\nconvenient and also allows these keywords to automatically escape possible\nspaces and other special characters in commands and arguments. Notice that\nif a command accepts options that themselves accept values, these options\nand their values must be given as separate arguments.\n\nWhen `running processes in shell`, it is also possible to give the whole\ncommand to execute as a single string. The command can then contain\nmultiple commands to be run together. When using this approach, the caller\nis responsible on escaping.\n\nExamples:\n| `Run Process` | ${tools}${/}prog.py | argument | second arg with spaces |\n| `Run Process` | java | -jar | ${jars}${/}example.jar | --option | value |\n| `Run Process` | prog.py \"one arg\" &amp;&amp; tool.sh | shell=yes | cwd=${tools} |\n\nStarting from Robot Framework 2.8.6, possible non-string arguments are\nconverted to strings automatically.\n\n= Process configuration =\n\n`Run Process` and `Start Process` keywords can be configured using\noptional ``**configuration`` keyword arguments. Configuration arguments\nmust be given after other arguments passed to these keywords and must\nuse syntax like ``name=value``. Available configuration arguments are\nlisted below and discussed further in sections afterwards.\n\n|  = Name =  |                  = Explanation =                      |\n| shell      | Specifies whether to run the command in shell or not. |\n| cwd        | Specifies the working directory.                      |\n| env        | Specifies environment variables given to the process. |\n| env:&lt;name&gt; | Overrides the named environment variable(s) only.     |\n| stdout     | Path of a file where to write standard output.        |\n| stderr     | Path of a file where to write standard error.         |\n| output_encoding | Encoding to use when reading command outputs.    |\n| alias      | Alias given to the process.                           |\n\nNote that because ``**configuration`` is passed using ``name=value`` syntax,\npossible equal signs in other arguments passed to `Run Process` and\n`Start Process` must be escaped with a backslash like ``name\\=value``.\nSee `Run Process` for an example.\n\n== Running processes in shell ==\n\nThe ``shell`` argument specifies whether to run the process in a shell or\nnot. By default shell is not used, which means that shell specific commands,\nlike ``copy`` and ``dir`` on Windows, are not available. You can, however,\nrun shell scripts and batch files without using a shell.\n\nGiving the ``shell`` argument any non-false value, such as ``shell=True``,\nchanges the program to be executed in a shell. It allows using the shell\ncapabilities, but can also make the process invocation operating system\ndependent. Having a shell between the actually started process and this\nlibrary can also interfere communication with the process such as stopping\nit and reading its outputs. Because of these problems, it is recommended\nto use the shell only when absolutely necessary.\n\nWhen using a shell it is possible to give the whole command to execute\nas a single string. See `Specifying command and arguments` section for\nexamples and more details in general.\n\n== Current working directory ==\n\nBy default the child process will be executed in the same directory\nas the parent process, the process running tests, is executed. This\ncan be changed by giving an alternative location using the ``cwd`` argument.\nForward slashes in the given path are automatically converted to\nbackslashes on Windows.\n\n`Standard output and error streams`, when redirected to files,\nare also relative to the current working directory possibly set using\nthe ``cwd`` argument.\n\nExample:\n| `Run Process` | prog.exe | cwd=${ROOT}/directory | stdout=stdout.txt |\n\n== Environment variables ==\n\nBy default the child process will get a copy of the parent process's\nenvironment variables. The ``env`` argument can be used to give the\nchild a custom environment as a Python dictionary. If there is a need\nto specify only certain environment variable, it is possible to use the\n``env:&lt;name&gt;=&lt;value&gt;`` format to set or override only that named variables.\nIt is also possible to use these two approaches together.\n\nExamples:\n| `Run Process` | program | env=${environ} |\n| `Run Process` | program | env:http_proxy=10.144.1.10:8080 | env:PATH=%{PATH}${:}${PROGDIR} |\n| `Run Process` | program | env=${environ} | env:EXTRA=value |\n\n== Standard output and error streams ==\n\nBy default processes are run so that their standard output and standard\nerror streams are kept in the memory. This works fine normally,\nbut if there is a lot of output, the output buffers may get full and\nthe program can hang. Additionally on Jython, everything written to\nthese in-memory buffers can be lost if the process is terminated.\n\nTo avoid the above mentioned problems, it is possible to use ``stdout``\nand ``stderr`` arguments to specify files on the file system where to\nredirect the outputs. This can also be useful if other processes or\nother keywords need to read or manipulate the outputs somehow.\n\nGiven ``stdout`` and ``stderr`` paths are relative to the `current working\ndirectory`. Forward slashes in the given paths are automatically converted\nto backslashes on Windows.\n\nAs a special feature, it is possible to redirect the standard error to\nthe standard output by using ``stderr=STDOUT``.\n\nRegardless are outputs redirected to files or not, they are accessible\nthrough the `result object` returned when the process ends. Commands are\nexpected to write outputs using the console encoding, but `output encoding`\ncan be configured using the ``output_encoding`` argument if needed.\n\nExamples:\n| ${result} = | `Run Process` | program | stdout=${TEMPDIR}/stdout.txt | stderr=${TEMPDIR}/stderr.txt |\n| `Log Many`  | stdout: ${result.stdout} | stderr: ${result.stderr} |\n| ${result} = | `Run Process` | program | stderr=STDOUT |\n| `Log`       | all output: ${result.stdout} |\n\nNote that the created output files are not automatically removed after\nthe test run. The user is responsible to remove them if needed.\n\n== Output encoding ==\n\nExecuted commands are, by default, expected to write outputs to the\n`standard output and error streams` using the encoding used by the\nsystem console. If the command uses some other encoding, that can be\nconfigured using the ``output_encoding`` argument. This is especially\nuseful on Windows where the console uses a different encoding than rest\nof the system, and many commands use the general system encoding instead\nof the console encoding.\n\nThe value used with the ``output_encoding`` argument must be a valid\nencoding and must match the encoding actually used by the command. As a\nconvenience, it is possible to use strings ``CONSOLE`` and ``SYSTEM``\nto specify that the console or system encoding is used, respectively.\nIf produced outputs use different encoding then configured, values got\nthrough the `result object` will be invalid.\n\nExamples:\n| `Start Process` | program | output_encoding=UTF-8 |\n| `Run Process`   | program | stdout=${path} | output_encoding=SYSTEM |\n\nThe support to set output encoding is new in Robot Framework 3.0.\n\n== Alias ==\n\nA custom name given to the process that can be used when selecting the\n`active process`.\n\nExamples:\n| `Start Process` | program | alias=example |\n| `Run Process`   | python  | -c | print 'hello' | alias=hello |\n\n= Active process =\n\nThe test library keeps record which of the started processes is currently\nactive. By default it is latest process started with `Start Process`,\nbut `Switch Process` can be used to select a different one. Using\n`Run Process` does not affect the active process.\n\nThe keywords that operate on started processes will use the active process\nby default, but it is possible to explicitly select a different process\nusing the ``handle`` argument. The handle can be the identifier returned by\n`Start Process` or an ``alias`` explicitly given to `Start Process` or\n`Run Process`.\n\n= Result object =\n\n`Run Process`, `Wait For Process` and `Terminate Process` keywords return a\nresult object that contains information about the process execution as its\nattributes. The same result object, or some of its attributes, can also\nbe get using `Get Process Result` keyword. Attributes available in the\nobject are documented in the table below.\n\n| = Attribute = |             = Explanation =               |\n| rc            | Return code of the process as an integer. |\n| stdout        | Contents of the standard output stream.   |\n| stderr        | Contents of the standard error stream.    |\n| stdout_path   | Path where stdout was redirected or ``None`` if not redirected. |\n| stderr_path   | Path where stderr was redirected or ``None`` if not redirected. |\n\nExample:\n| ${result} =            | `Run Process`         | program               |\n| `Should Be Equal As Integers` | ${result.rc}   | 0                     |\n| `Should Match`         | ${result.stdout}      | Some t?xt*            |\n| `Should Be Empty`      | ${result.stderr}      |                       |\n| ${stdout} =            | `Get File`            | ${result.stdout_path} |\n| `Should Be Equal`      | ${stdout}             | ${result.stdout}      |\n| `File Should Be Empty` | ${result.stderr_path} |                       |\n\n= Boolean arguments =\n\nSome keywords accept arguments that are handled as Boolean values true or\nfalse. If such an argument is given as a string, it is considered false if\nit is either an empty string or case-insensitively equal to ``false``,\n``none`` or ``no``. Other strings are considered true regardless\ntheir value, and other argument types are tested using the same\n[http://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules\nas in Python].\n\nTrue examples:\n| `Terminate Process` | kill=True     | # Strings are generally true.    |\n| `Terminate Process` | kill=yes      | # Same as the above.             |\n| `Terminate Process` | kill=${TRUE}  | # Python ``True`` is true.       |\n| `Terminate Process` | kill=${42}    | # Numbers other than 0 are true. |\n\nFalse examples:\n| `Terminate Process` | kill=False    | # String ``false`` is false.   |\n| `Terminate Process` | kill=no       | # Also string ``no`` is false. |\n| `Terminate Process` | kill=${EMPTY} | # Empty string is false.       |\n| `Terminate Process` | kill=${FALSE} | # Python ``False`` is false.   |\n\nPrior to Robot Framework 2.9, all non-empty strings, including ``false``\nand ``no``, were considered to be true. Considering ``none`` false is\nnew in Robot Framework 3.0.3.\n\n= Example =\n\n| ***** Settings *****\n| Library           Process\n| Suite Teardown    `Terminate All Processes`    kill=True\n|\n| ***** Test Cases *****\n| Example\n|     `Start Process`    program    arg1    arg2    alias=First\n|     ${handle} =    `Start Process`    command.sh arg | command2.sh    shell=True    cwd=/path\n|     ${result} =    `Run Process`    ${CURDIR}/script.py\n|     `Should Not Contain`    ${result.stdout}    FAIL\n|     `Terminate Process`    ${handle}\n|     ${result} =    `Wait For Process`    First\n|     `Should Be Equal As Integers`    ${result.rc}    0</doc>\n<kw name=\"Get Process Id\">\n<arguments>\n<arg>handle=None</arg>\n</arguments>\n<doc>Returns the process ID (pid) of the process as an integer.\n\nIf ``handle`` is not given, uses the current `active process`.\n\nNotice that the pid is not the same as the handle returned by\n`Start Process` that is used internally by this library.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Process Object\">\n<arguments>\n<arg>handle=None</arg>\n</arguments>\n<doc>Return the underlying ``subprocess.Popen`` object.\n\nIf ``handle`` is not given, uses the current `active process`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Process Result\">\n<arguments>\n<arg>handle=None</arg>\n<arg>rc=False</arg>\n<arg>stdout=False</arg>\n<arg>stderr=False</arg>\n<arg>stdout_path=False</arg>\n<arg>stderr_path=False</arg>\n</arguments>\n<doc>Returns the specified `result object` or some of its attributes.\n\nThe given ``handle`` specifies the process whose results should be\nreturned. If no ``handle`` is given, results of the current `active\nprocess` are returned. In either case, the process must have been\nfinishes before this keyword can be used. In practice this means\nthat processes started with `Start Process` must be finished either\nwith `Wait For Process` or `Terminate Process` before using this\nkeyword.\n\nIf no other arguments than the optional ``handle`` are given, a whole\n`result object` is returned. If one or more of the other arguments\nare given any true value, only the specified attributes of the\n`result object` are returned. These attributes are always returned\nin the same order as arguments are specified in the keyword signature.\nSee `Boolean arguments` section for more details about true and false\nvalues.\n\nExamples:\n| Run Process           | python             | -c            | print 'Hello, world!' | alias=myproc |\n| # Get result object   |                    |               |\n| ${result} =           | Get Process Result | myproc        |\n| Should Be Equal       | ${result.rc}       | ${0}          |\n| Should Be Equal       | ${result.stdout}   | Hello, world! |\n| Should Be Empty       | ${result.stderr}   |               |\n| # Get one attribute   |                    |               |\n| ${stdout} =           | Get Process Result | myproc        | stdout=true |\n| Should Be Equal       | ${stdout}          | Hello, world! |\n| # Multiple attributes |                    |               |\n| ${stdout}             | ${stderr} =        | Get Process Result |  myproc | stdout=yes | stderr=yes |\n| Should Be Equal       | ${stdout}          | Hello, world! |\n| Should Be Empty       | ${stderr}          |               |\n\nAlthough getting results of a previously executed process can be handy\nin general, the main use case for this keyword is returning results\nover the remote library interface. The remote interface does not\nsupport returning the whole result object, but individual attributes\ncan be returned without problems.\n\nNew in Robot Framework 2.8.2.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Is Process Running\">\n<arguments>\n<arg>handle=None</arg>\n</arguments>\n<doc>Checks is the process running or not.\n\nIf ``handle`` is not given, uses the current `active process`.\n\nReturns ``True`` if the process is still running and ``False`` otherwise.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Join Command Line\">\n<arguments>\n<arg>*args</arg>\n</arguments>\n<doc>Joins arguments into one command line string.\n\nIn resulting command line string arguments are delimited with a space,\narguments containing spaces are surrounded with quotes, and possible\nquotes are escaped with a backslash.\n\nIf this keyword is given only one argument and that is a list like\nobject, then the values of that list are joined instead.\n\nExample:\n| ${cmd} = | Join Command Line | --option | value with spaces |\n| Should Be Equal | ${cmd} | --option \"value with spaces\" |\n\nNew in Robot Framework 2.9.2.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Process Should Be Running\">\n<arguments>\n<arg>handle=None</arg>\n<arg>error_message=Process is not running.</arg>\n</arguments>\n<doc>Verifies that the process is running.\n\nIf ``handle`` is not given, uses the current `active process`.\n\nFails if the process has stopped.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Process Should Be Stopped\">\n<arguments>\n<arg>handle=None</arg>\n<arg>error_message=Process is running.</arg>\n</arguments>\n<doc>Verifies that the process is not running.\n\nIf ``handle`` is not given, uses the current `active process`.\n\nFails if the process is still running.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Run Process\">\n<arguments>\n<arg>command</arg>\n<arg>*arguments</arg>\n<arg>**configuration</arg>\n</arguments>\n<doc>Runs a process and waits for it to complete.\n\n``command`` and ``*arguments`` specify the command to execute and\narguments passed to it. See `Specifying command and arguments` for\nmore details.\n\n``**configuration`` contains additional configuration related to\nstarting processes and waiting for them to finish. See `Process\nconfiguration` for more details about configuration related to starting\nprocesses. Configuration related to waiting for processes consists of\n``timeout`` and ``on_timeout`` arguments that have same semantics as\nwith `Wait For Process` keyword. By default there is no timeout, and\nif timeout is defined the default action on timeout is ``terminate``.\n\nReturns a `result object` containing information about the execution.\n\nNote that possible equal signs in ``*arguments`` must be escaped\nwith a backslash (e.g. ``name\\=value``) to avoid them to be passed in\nas ``**configuration``.\n\nExamples:\n| ${result} = | Run Process | python | -c | print 'Hello, world!' |\n| Should Be Equal | ${result.stdout} | Hello, world! |\n| ${result} = | Run Process | ${command} | stderr=STDOUT | timeout=10s |\n| ${result} = | Run Process | ${command} | timeout=1min | on_timeout=continue |\n| ${result} = | Run Process | java -Dname\\=value Example | shell=True | cwd=${EXAMPLE} |\n\nThis keyword does not change the `active process`.\n\n``timeout`` and ``on_timeout`` arguments are new in Robot Framework\n2.8.4.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Send Signal To Process\">\n<arguments>\n<arg>signal</arg>\n<arg>handle=None</arg>\n<arg>group=False</arg>\n</arguments>\n<doc>Sends the given ``signal`` to the specified process.\n\nIf ``handle`` is not given, uses the current `active process`.\n\nSignal can be specified either as an integer as a signal name. In the\nlatter case it is possible to give the name both with or without ``SIG``\nprefix, but names are case-sensitive. For example, all the examples\nbelow send signal ``INT (2)``:\n\n| Send Signal To Process | 2      |        | # Send to active process |\n| Send Signal To Process | INT    |        |                          |\n| Send Signal To Process | SIGINT | myproc | # Send to named process  |\n\nThis keyword is only supported on Unix-like machines, not on Windows.\nWhat signals are supported depends on the system. For a list of\nexisting signals on your system, see the Unix man pages related to\nsignal handling (typically ``man signal`` or ``man 7 signal``).\n\nBy default sends the signal only to the parent process, not to possible\nchild processes started by it. Notice that when `running processes in\nshell`, the shell is the parent process and it depends on the system\ndoes the shell propagate the signal to the actual started process.\n\nTo send the signal to the whole process group, ``group`` argument can\nbe set to any true value (see `Boolean arguments`). This is not\nsupported by Jython, however.\n\nNew in Robot Framework 2.8.2. Support for ``group`` argument is new\nin Robot Framework 2.8.5.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Split Command Line\">\n<arguments>\n<arg>args</arg>\n<arg>escaping=False</arg>\n</arguments>\n<doc>Splits command line string into a list of arguments.\n\nString is split from spaces, but argument surrounded in quotes may\ncontain spaces in them. If ``escaping`` is given a true value, then\nbackslash is treated as an escape character. It can escape unquoted\nspaces, quotes inside quotes, and so on, but it also requires using\ndouble backslashes when using Windows paths.\n\nExamples:\n| @{cmd} = | Split Command Line | --option \"value with spaces\" |\n| Should Be True | $cmd == ['--option', 'value with spaces'] |\n\nNew in Robot Framework 2.9.2.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Start Process\">\n<arguments>\n<arg>command</arg>\n<arg>*arguments</arg>\n<arg>**configuration</arg>\n</arguments>\n<doc>Starts a new process on background.\n\nSee `Specifying command and arguments` and `Process configuration`\nfor more information about the arguments, and `Run Process` keyword\nfor related examples.\n\nMakes the started process new `active process`. Returns an identifier\nthat can be used as a handle to activate the started process if needed.\n\nStarting from Robot Framework 2.8.5, processes are started so that\nthey create a new process group. This allows sending signals to and\nterminating also possible child processes. This is not supported by\nJython in general nor by Python versions prior to 2.7 on Windows.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Switch Process\">\n<arguments>\n<arg>handle</arg>\n</arguments>\n<doc>Makes the specified process the current `active process`.\n\nThe handle can be an identifier returned by `Start Process` or\nthe ``alias`` given to it explicitly.\n\nExample:\n| Start Process  | prog1    | alias=process1 |\n| Start Process  | prog2    | alias=process2 |\n| # currently active process is process2 |\n| Switch Process | process1 |\n| # now active process is process1 |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Terminate All Processes\">\n<arguments>\n<arg>kill=False</arg>\n</arguments>\n<doc>Terminates all still running processes started by this library.\n\nThis keyword can be used in suite teardown or elsewhere to make\nsure that all processes are stopped,\n\nBy default tries to terminate processes gracefully, but can be\nconfigured to forcefully kill them immediately. See `Terminate Process`\nthat this keyword uses internally for more details.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Terminate Process\">\n<arguments>\n<arg>handle=None</arg>\n<arg>kill=False</arg>\n</arguments>\n<doc>Stops the process gracefully or forcefully.\n\nIf ``handle`` is not given, uses the current `active process`.\n\nBy default first tries to stop the process gracefully. If the process\ndoes not stop in 30 seconds, or ``kill`` argument is given a true value,\n(see `Boolean arguments`) kills the process forcefully. Stops also all\nthe child processes of the originally started process.\n\nWaits for the process to stop after terminating it. Returns a `result\nobject` containing information about the execution similarly as `Wait\nFor Process`.\n\nOn Unix-like machines graceful termination is done using ``TERM (15)``\nsignal and killing using ``KILL (9)``. Use `Send Signal To Process`\ninstead if you just want to send either of these signals without\nwaiting for the process to stop.\n\nOn Windows graceful termination is done using ``CTRL_BREAK_EVENT``\nevent and killing using Win32 API function ``TerminateProcess()``.\n\nExamples:\n| ${result} =                 | Terminate Process |     |\n| Should Be Equal As Integers | ${result.rc}      | -15 | # On Unixes |\n| Terminate Process           | myproc            | kill=true |\n\nLimitations:\n- Graceful termination is not supported on Windows by Jython nor by\n  Python versions prior to 2.7. Process is killed instead.\n- Stopping the whole process group is not supported by Jython at all\n  nor by Python versions prior to 2.7 on Windows.\n- On Windows forceful kill only stops the main process, not possible\n  child processes.\n\nAutomatically killing the process if termination fails as well as\nreturning a result object are new features in Robot Framework 2.8.2.\nTerminating also possible child processes, including using\n``CTRL_BREAK_EVENT`` on Windows, is new in Robot Framework 2.8.5.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait For Process\">\n<arguments>\n<arg>handle=None</arg>\n<arg>timeout=None</arg>\n<arg>on_timeout=continue</arg>\n</arguments>\n<doc>Waits for the process to complete or to reach the given timeout.\n\nThe process to wait for must have been started earlier with\n`Start Process`. If ``handle`` is not given, uses the current\n`active process`.\n\n``timeout`` defines the maximum time to wait for the process. It can be\ngiven in\n[http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#time-format|\nvarious time formats] supported by Robot Framework, for example, ``42``,\n``42 s``, or ``1 minute 30 seconds``.\n\n``on_timeout`` defines what to do if the timeout occurs. Possible values\nand corresponding actions are explained in the table below. Notice\nthat reaching the timeout never fails the test.\n\n| = Value = |               = Action =               |\n| continue  | The process is left running (default). |\n| terminate | The process is gracefully terminated.  |\n| kill      | The process is forcefully stopped.     |\n\nSee `Terminate Process` keyword for more details how processes are\nterminated and killed.\n\nIf the process ends before the timeout or it is terminated or killed,\nthis keyword returns a `result object` containing information about\nthe execution. If the process is left running, Python ``None`` is\nreturned instead.\n\nExamples:\n| # Process ends cleanly      |                  |                  |\n| ${result} =                 | Wait For Process | example          |\n| Process Should Be Stopped   | example          |                  |\n| Should Be Equal As Integers | ${result.rc}     | 0                |\n| # Process does not end      |                  |                  |\n| ${result} =                 | Wait For Process | timeout=42 secs  |\n| Process Should Be Running   |                  |                  |\n| Should Be Equal             | ${result}        | ${NONE}          |\n| # Kill non-ending process   |                  |                  |\n| ${result} =                 | Wait For Process | timeout=1min 30s | on_timeout=kill |\n| Process Should Be Stopped   |                  |                  |\n| Should Be Equal As Integers | ${result.rc}     | -9               |\n\n``timeout`` and ``on_timeout`` are new in Robot Framework 2.8.2.</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/RequestsLibrary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"RequestsLibrary\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:43\">\n<version></version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>RequestsLibrary is a HTTP client keyword library that uses\nthe requests module from Kenneth Reitz\nhttps://github.com/kennethreitz/requests\n\n\n    Examples:\n    | Create Session | google | http://www.google.com |\n    | Create Session | github  | http://github.com/api/v2/json |\n    | ${resp} | Get  google  |  / |\n    | Should Be Equal As Strings |  ${resp.status_code} | 200 |\n    | ${resp} | Get  github  | /user/search/bulkan |\n    | Should Be Equal As Strings  |  ${resp.status_code} | 200 |\n    | ${jsondata}  | To Json |  ${resp.content} |\n    | Dictionary Should Contain Value | ${jsondata['users'][0]} | Bulkan Savun Evcimen |</doc>\n<kw name=\"Create Digest Session\">\n<arguments>\n<arg>alias</arg>\n<arg>url</arg>\n<arg>auth</arg>\n<arg>headers={}</arg>\n<arg>cookies=None</arg>\n<arg>timeout=None</arg>\n<arg>proxies=None</arg>\n<arg>verify=False</arg>\n<arg>debug=0</arg>\n<arg>max_retries=3</arg>\n<arg>backoff_factor=0.1</arg>\n<arg>disable_warnings=0</arg>\n</arguments>\n<doc>Create Session: create a HTTP session to a server\n\n``url`` Base url of the server\n\n``alias`` Robot Framework alias to identify the session\n\n``headers`` Dictionary of default headers\n\n``auth`` ['DOMAIN', 'username', 'password'] for NTLM Authentication\n\n``timeout`` Connection timeout\n\n``proxies`` Dictionary that contains proxy urls for HTTP and HTTPS communication\n\n``verify`` Whether the SSL cert will be verified. A CA_BUNDLE path can also be provided.\n         Defaults to False.\n\n``debug`` Enable http verbosity option more information\n        https://docs.python.org/2/library/httplib.html#httplib.HTTPConnection.set_debuglevel\n\n``max_retries`` The maximum number of retries each connection should attempt.\n\n``backoff_factor`` The pause between for each retry\n\n``disable_warnings`` Disable requests warning useful when you have large number of testcases</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Create Ntlm Session\">\n<arguments>\n<arg>alias</arg>\n<arg>url</arg>\n<arg>auth</arg>\n<arg>headers={}</arg>\n<arg>cookies=None</arg>\n<arg>timeout=None</arg>\n<arg>proxies=None</arg>\n<arg>verify=False</arg>\n<arg>debug=0</arg>\n<arg>max_retries=3</arg>\n<arg>backoff_factor=0.1</arg>\n<arg>disable_warnings=0</arg>\n</arguments>\n<doc>Create Session: create a HTTP session to a server\n\n``url`` Base url of the server\n\n``alias`` Robot Framework alias to identify the session\n\n``headers`` Dictionary of default headers\n\n``auth`` ['DOMAIN', 'username', 'password'] for NTLM Authentication\n\n``timeout`` Connection timeout\n\n``proxies`` Dictionary that contains proxy urls for HTTP and HTTPS communication\n\n``verify`` Whether the SSL cert will be verified. A CA_BUNDLE path can also be provided.\n         Defaults to False.\n\n``debug`` Enable http verbosity option more information\n        https://docs.python.org/2/library/httplib.html#httplib.HTTPConnection.set_debuglevel\n\n``max_retries`` The maximum number of retries each connection should attempt.\n\n``backoff_factor`` The pause between for each retry\n\n``disable_warnings`` Disable requests warning useful when you have large number of testcases</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Create Session\">\n<arguments>\n<arg>alias</arg>\n<arg>url</arg>\n<arg>headers={}</arg>\n<arg>cookies=None</arg>\n<arg>auth=None</arg>\n<arg>timeout=None</arg>\n<arg>proxies=None</arg>\n<arg>verify=False</arg>\n<arg>debug=0</arg>\n<arg>max_retries=3</arg>\n<arg>backoff_factor=0.1</arg>\n<arg>disable_warnings=0</arg>\n</arguments>\n<doc>Create Session: create a HTTP session to a server\n\n``url`` Base url of the server\n\n``alias`` Robot Framework alias to identify the session\n\n``headers`` Dictionary of default headers\n\n``auth`` List of username &amp; password for HTTP Basic Auth\n\n``timeout`` Connection timeout\n\n``proxies`` Dictionary that contains proxy urls for HTTP and HTTPS communication\n\n``verify`` Whether the SSL cert will be verified. A CA_BUNDLE path can also be provided.\n         Defaults to False.\n\n``debug`` Enable http verbosity option more information\n        https://docs.python.org/2/library/httplib.html#httplib.HTTPConnection.set_debuglevel\n\n``max_retries`` The maximum number of retries each connection should attempt.\n\n``backoff_factor`` The pause between for each retry\n\n``disable_warnings`` Disable requests warning useful when you have large number of testcases</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Delete\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>data=()</arg>\n<arg>headers=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>* * *   Deprecated- See Delete Request now   * * *\n\nSend a DELETE request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the DELETE request to\n\n``headers`` a dictionary of headers to use with the request\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``timeout`` connection timeout</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Delete All Sessions\">\n<arguments>\n</arguments>\n<doc>Removes all the session objects</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Delete Request\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>data=()</arg>\n<arg>params=None</arg>\n<arg>headers=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>Send a DELETE request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the DELETE request to\n\n``headers`` a dictionary of headers to use with the request\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``timeout`` connection timeout</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>params=None</arg>\n<arg>headers=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>**Deprecated- See Get Request now**\n\nSend a GET request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the GET request to\n\n``headers`` a dictionary of headers to use with the request\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``timeout`` connection timeout</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Request\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>headers=None</arg>\n<arg>json=None</arg>\n<arg>params=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>Send a GET request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the GET request to\n\n``params`` url parameters to append to the uri\n\n``headers`` a dictionary of headers to use with the request\n\n``json`` json data to send in the body of the :class:`Request`.\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``timeout`` connection timeout</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Head\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>headers=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>**Deprecated- See Head Request now**\n\nSend a HEAD request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the HEAD request to\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``headers`` a dictionary of headers to use with the request</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Head Request\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>headers=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>Send a HEAD request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the HEAD request to\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``headers`` a dictionary of headers to use with the request</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Options\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>headers=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>**Deprecated- See Options Request now**\n\nSend an OPTIONS request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the OPTIONS request to\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``headers`` a dictionary of headers to use with the request</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Options Request\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>headers=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>Send an OPTIONS request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the OPTIONS request to\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``headers`` a dictionary of headers to use with the request</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Patch\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>data={}</arg>\n<arg>headers=None</arg>\n<arg>files={}</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>**Deprecated- See Patch Request now**\n\nSend a PATCH request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the PATCH request to\n\n``data`` a dictionary of key-value pairs that will be urlencoded\n       and sent as PATCH data\n       or binary data that is sent as the raw body content\n\n``headers`` a dictionary of headers to use with the request\n\n``files`` a dictionary of file names containing file data to PATCH to the server\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``timeout`` connection timeout</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Patch Request\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>data=None</arg>\n<arg>params=None</arg>\n<arg>headers=None</arg>\n<arg>files=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>Send a PATCH request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the PATCH request to\n\n``data`` a dictionary of key-value pairs that will be urlencoded\n       and sent as PATCH data\n       or binary data that is sent as the raw body content\n\n``headers`` a dictionary of headers to use with the request\n\n``files`` a dictionary of file names containing file data to PATCH to the server\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``params`` url parameters to append to the uri\n\n``timeout`` connection timeout</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Post\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>data={}</arg>\n<arg>headers=None</arg>\n<arg>files=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>**Deprecated- See Post Request now**\n\nSend a POST request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the GET request to\n\n``data`` a dictionary of key-value pairs that will be urlencoded\n       and sent as POST data\n       or binary data that is sent as the raw body content\n\n``headers`` a dictionary of headers to use with the request\n\n``files`` a dictionary of file names containing file data to POST to the server\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``timeout`` connection timeout</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Post Request\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>data=None</arg>\n<arg>params=None</arg>\n<arg>headers=None</arg>\n<arg>files=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>Send a POST request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the POST request to\n\n``data`` a dictionary of key-value pairs that will be urlencoded\n       and sent as POST data\n       or binary data that is sent as the raw body content\n       or passed as such for multipart form data if ``files`` is also\n          defined\n\n``params`` url parameters to append to the uri\n\n``headers`` a dictionary of headers to use with the request\n\n``files`` a dictionary of file names containing file data to POST to the server\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``timeout`` connection timeout</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Put\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>data=None</arg>\n<arg>headers=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>**Deprecated- See Put Request now**\n\nSend a PUT request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the PUT request to\n\n``headers`` a dictionary of headers to use with the request\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``timeout`` connection timeout</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Put Request\">\n<arguments>\n<arg>alias</arg>\n<arg>uri</arg>\n<arg>data=None</arg>\n<arg>params=None</arg>\n<arg>files=None</arg>\n<arg>headers=None</arg>\n<arg>allow_redirects=None</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>Send a PUT request on the session object found using the\ngiven `alias`\n\n``alias`` that will be used to identify the Session object in the cache\n\n``uri`` to send the PUT request to\n\n``headers`` a dictionary of headers to use with the request\n\n``allow_redirects`` Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.\n\n``params`` url parameters to append to the uri\n\n``timeout`` connection timeout</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"To Json\">\n<arguments>\n<arg>content</arg>\n<arg>pretty_print=False</arg>\n</arguments>\n<doc>Convert a string to a JSON object\n\n``content`` String content to convert into JSON\n\n``pretty_print`` If defined, will output JSON is pretty print format</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/SSHLibrary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"SSHLibrary\" type=\"library\" format=\"ROBOT\" generated=\"20180817 16:07:53\">\n<version>3.1.1</version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>SSHLibrary is a Robot Framework test library for SSH and SFTP.\n\n This document explains how to use keywords provided by SSHLibrary.\n For information about installation, support, and more please visit the\n [https://github.com/robotframework/SSHLibrary|project page].\n For more information about Robot Framework, see http://robotframework.org.\n\nThe library has the following main usages:\n- Executing commands on the remote machine, either with blocking or\n  non-blocking behaviour (see `Execute Command` and `Start Command`,\n  respectively).\n- Writing and reading in an interactive shell (e.g. `Read` and `Write`).\n- Transferring files and directories over SFTP (e.g. `Get File` and\n  `Put Directory`).\n- Ensuring that files or directories exist on the remote machine\n  (e.g. `File Should Exist` and `Directory Should Not Exist`).\n\nThis library works both with Python and Jython, but uses different\nSSH modules internally depending on the interpreter. See\n[http://robotframework.org/SSHLibrary/#installation|installation instructions]\nfor more details about the dependencies. IronPython is unfortunately\nnot supported. Python 3 is supported starting from SSHLibrary 3.0.0.\n\n== Table of contents ==\n\n- `Connections and login`\n- `Configuration`\n- `Executing commands`\n- `Interactive shells`\n- `Pattern matching`\n- `Example`\n- `Importing`\n- `Time format`\n- `Boolean arguments`\n- `Shortcuts`\n- `Keywords`\n\n= Connections and login =\n\nSSHLibrary supports multiple connections to different hosts.\nNew connections are opened with `Open Connection`.\n\nLogin into the host is done either with username and password\n(`Login`) or with public/private key pair (`Login With Public key`).\n\nOnly one connection can be active at a time. This means that most of the\nkeywords only affect the active connection. Active connection can be\nchanged with `Switch Connection`.\n\n= Configuration =\n\nDefault settings for all the upcoming connections can be configured on\n`library importing` or later with `Set Default Configuration`.\n\nUsing `Set Default Configuration` does not affect the already open\nconnections. Settings of the current connection can be configured\nwith `Set Client Configuration`. Settings of another, non-active connection,\ncan be configured by first using `Switch Connection` and then\n`Set Client Configuration`.\n\nMost of the defaults can be overridden per connection by defining them\nas arguments to `Open Connection`. Otherwise the defaults are used.\n\n== Configurable per connection ==\n\n=== Prompt ===\n\nArgument ``prompt`` defines the character sequence used by `Read Until Prompt`\nand must be set before that keyword can be used.\n\nIf you know the prompt on the remote machine, it is recommended to set it\nto ease reading output from the server after using `Write`. In addition to\nthat, `Login` and `Login With Public Key` can read the server output more\nefficiently when the prompt is set.\n\nPrompt can be specified either as a normal string or as a regular expression.\nThe latter is especially useful if the prompt changes as a result of\nthe executed commands. Prompt can be set to be a regular expression by\ngiving the prompt argument a value starting with ``REGEXP:`` followed by\nthe actual regular expression like ``prompt=REGEXP:[$#]``. See the\n`Regular expressions` section for more details about the syntax.\n\nThe support for regular expressions is new in SSHLibrary 3.0.0.\n\n=== Encoding ===\n\nArgument ``encoding`` defines the\n[https://docs.python.org/3/library/codecs.html#standard-encodings|\ncharacter encoding] of input and output sequences. The default encoding\nis UTF-8.\n\n=== Path separator ===\n\nArgument ``path_separator`` must be set to the one known by the operating\nsystem and the SSH server on the remote machine. The path separator is\nused by keywords `Get File`, `Put File`, `Get Directory` and\n`Put Directory` for joining paths correctly on the remote host.\n\nThe default path separator is forward slash ``/`` which works on\nUnix-like machines. On Windows the path separator to use depends on\nthe SSH server. Some servers use forward slash and others backslash,\nand users need to configure the ``path_separator`` accordingly. Notice\nthat using a backslash in Robot Framework test data requires doubling\nit like ``\\\\``.\n\nThe path separator can be configured on `library importing` or later,\nusing `Set Default Configuration`, `Set Client Configuration` and `Open\nConnection`.\n\n=== Timeout ===\n\nArgument ``timeout`` is used by `Read Until` variants. The default value\nis ``3 seconds``. See `time format` below for supported timeout syntax.\n\n=== Newline ===\n\nArgument ``newline`` is the line break sequence used by `Write` keyword\nand must be set according to the operating system on the remote machine.\nThe default value is ``LF`` (same as ``\\n``) which is used on Unix-like\noperating systems. With Windows remote machines, you need to set this to\n``CRLF`` (``\\r\\n``).\n\n=== Terminal settings ===\n\nArgument ``term_type`` defines the virtual terminal type, and arguments\n``width`` and ``height`` can be used to control its  virtual size.\n\n== Not configurable per connection ==\n\n=== Loglevel ===\n\nArgument ``loglevel`` sets the log level used to log the output read by\n`Read`, `Read Until`, `Read Until Prompt`, `Read Until Regexp`, `Write`,\n`Write Until Expected Output`, `Login` and `Login With Public Key`.\nThe default level is ``INFO``.\n\n``loglevel`` is not configurable per connection but can be overridden by\npassing it as an argument to the most of the aforementioned keywords.\nPossible argument values are ``TRACE``, ``DEBUG``, ``INFO``, ``WARN``\nand ``NONE`` (no logging).\n\n= Executing commands =\n\nFor executing commands on the remote machine, there are two possibilities:\n\n- `Execute Command` and `Start Command`.\n   The command is executed in a new shell on the remote machine,\n   which means that possible changes to the environment\n   (e.g. changing working directory, setting environment variables, etc.)\n   are not visible to the subsequent keywords.\n\n- `Write`, `Write Bare`, `Write Until Expected Output`, `Read`,\n  `Read Until`, `Read Until Prompt` and `Read Until Regexp`.\n   These keywords operate in an interactive shell, which means that changes\n   to the environment are visible to the subsequent keywords.\n\n= Interactive shells =\n\n`Write`, `Write Bare`, `Write Until Expected Output`, `Read`,\n`Read Until`, `Read Until Prompt` and `Read Until Regexp` can be used\nto interact with the server within the same shell.\n\n== Consumed output ==\n\nAll of these keywords, except `Write Bare`, consume the read or the written\ntext from the server output before returning. In practice this means that\nthe text is removed from the server output, i.e. subsequent calls to\n`Read` keywords do not return text that was already read. This is\nillustrated by the example below.\n\n| `Write`              | echo hello   |       | # Consumes written ``echo hello``                  |\n| ${stdout}=           | `Read Until` | hello | # Consumes read ``hello`` and everything before it |\n| `Should Contain`     | ${stdout}    | hello |\n| ${stdout}=           | `Read`       |       | # Consumes everything available                    |\n| `Should Not Contain` | ${stdout}    | hello | # ``hello`` was already consumed earlier           |\n\nThe consumed text is logged by the keywords and their argument\n``loglevel`` can be used to override the default `log level`.\n\n`Login` and `Login With Public Key` consume everything on the server\noutput or if the `prompt` is set, everything until the prompt.\n\n== Reading ==\n\n`Read`, `Read Until`, `Read Until Prompt` and `Read Until Regexp` can be\nused to read from the server. The read text is also consumed from\nthe server output.\n\n`Read` reads everything available on the server output, thus clearing it.\n\n`Read Until` variants read output up until and *including* ``expected``\ntext. These keywords will fail if the `timeout` expires before\n``expected`` is found.\n\n== Writing ==\n\n`Write` and `Write Until Expected Output` consume the written text\nfrom the server output while `Write Bare` does not.\n\nThese keywords do not return any output triggered by the written text.\nTo get the output, one of the `Read` keywords must be explicitly used.\n\n= Pattern matching =\n\n== Glob patterns ==\n\nSome keywords allow their arguments to be specified as _glob patterns_\nwhere:\n| *        | matches anything, even an empty string |\n| ?        | matches any single character |\n| [chars]  | matches any character inside square brackets (e.g. ``[abc]`` matches either ``a``, ``b`` or ``c``) |\n| [!chars] | matches any character not inside square brackets |\n\nPattern matching is case-sensitive regardless the local or remote\noperating system. Matching is implemented using Python's\n[https://docs.python.org/3/library/fnmatch.html|fnmatch module].\n\n== Regular expressions ==\n\nSome keywords support pattern matching using regular expressions, which\nare more powerful but also more complicated than `glob patterns`. This\nlibrary uses Python's regular expressions, which are introduced in the\n[https://docs.python.org/3/howto/regex.html|Regular Expression HOWTO].\n\nRemember that in Robot Framework data the backslash that is used a lot\nin regular expressions is an escape character and needs to be doubled\nto get a literal backslash. For example, ``\\\\d\\\\d\\\\s`` matches\ntwo digits followed by a whitespace character.\n\nPossible flags altering how the expression is parsed (e.g.\n``re.IGNORECASE``, ``re.MULTILINE``) can be set by prefixing the pattern\nwith the ``(?iLmsux)`` group. The available flags are ``IGNORECASE``:\n``i``, ``MULTILINE``: ``m``, ``DOTALL``: ``s``, ``VERBOSE``: ``x``,\n``UNICODE``: ``u``, and ``LOCALE``: ``L``. For example, ``(?is)pat.ern``\nuses ``IGNORECASE`` and ``DOTALL`` flags.\n\n= Example =\n\n| ***** Settings *****\n| Documentation          This example demonstrates executing commands on a remote machine\n| ...                    and getting their output and the return code.\n| ...\n| ...                    Notice how connections are handled as part of the suite setup and\n| ...                    teardown. This saves some time when executing several test cases.\n|\n| Library                `SSHLibrary`\n| Suite Setup            `Open Connection And Log In`\n| Suite Teardown         `Close All Connections`\n|\n| ***** Variables *****\n| ${HOST}                localhost\n| ${USERNAME}            test\n| ${PASSWORD}            test\n|\n| ***** Test Cases *****\n| Execute Command And Verify Output\n|     [Documentation]    Execute Command can be used to ran commands on the remote machine.\n|     ...                The keyword returns the standard output by default.\n|     ${output}=         `Execute Command`  echo Hello SSHLibrary!\n|     `Should Be Equal`    ${output}        Hello SSHLibrary!\n|\n| Execute Command And Verify Return Code\n|     [Documentation]    Often getting the return code of the command is enough.\n|     ...                This behaviour can be adjusted as Execute Command arguments.\n|     ${rc}=             `Execute Command`  echo Success guaranteed.    return_stdout=False    return_rc=True\n|     `Should Be Equal`    ${rc}            ${0}\n|\n| Executing Commands In An Interactive Session\n|     [Documentation]    Execute Command always executes the command in a new shell.\n|     ...                This means that changes to the environment are not persisted\n|     ...                between subsequent Execute Command keyword calls.\n|     ...                Write and Read Until variants can be used to operate in the same shell.\n|     `Write`              cd ..\n|     `Write`              echo Hello from the parent directory!\n|     ${output}=         `Read Until`       directory!\n|     `Should End With`    ${output}        Hello from the parent directory!\n|\n| ***** Keywords *****\n| Open Connection And Log In\n|    `Open Connection`     ${HOST}\n|    `Login`               ${USERNAME}        ${PASSWORD}\n\nSave the content as file ``executing_command.txt`` and run:\n\n``robot executing_commands.txt``\n\nYou may want to override the variables from commandline to try this out on\nyour remote machine:\n\n``robot -v HOST:my.server.com -v USERNAME:johndoe -v PASSWORD:secretpasswd executing_commands.txt``\n\n== Time format ==\n\nAll timeouts, delays or retry intervals can be given as numbers considered seconds\n(e.g. ``0.5`` or ``42``) or in Robot Framework's time syntax\n(e.g. ``1.5 seconds`` or ``1 min 30 s``). For more information about\nthe time syntax see the\n[http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#time-format|Robot Framework User Guide].\n\n= Boolean arguments =\n\nSome keywords accept arguments that are handled as Boolean values true or\nfalse. If such an argument is given as a string, it is considered false if\nit is either an empty string or case-insensitively equal to ``false``,\n``none`` or ``no``. Other strings are considered true regardless\ntheir value, and other argument types are tested using the same\n[http://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules\nas in Python].\n\nTrue examples:\n| `List Directory` | ${path} | recursive=True    | # Strings are generally true.    |\n| `List Directory` | ${path} | recursive=yes     | # Same as the above.             |\n| `List Directory` | ${path} | recursive=${TRUE} | # Python ``True`` is true.       |\n| `List Directory` | ${path} | recursive=${42}   | # Numbers other than 0 are true. |\nFalse examples:\n| `List Directory` | ${path} | recursive=False    | # String ``false`` is false.   |\n| `List Directory` | ${path} | recursive=no       | # Also string ``no`` is false. |\n| `List Directory` | ${path} | recursive=${EMPTY} | # Empty string is false.       |\n| `List Directory` | ${path} | recursive=${FALSE} | # Python ``False`` is false.   |\n\nPrior to SSHLibrary 3.1.0, all non-empty strings, including ``no`` and ``none``\nwere considered to be true. Considering ``none`` false is new in Robot Framework 3.0.3.</doc>\n<init>\n<arguments>\n<arg>timeout=3 seconds</arg>\n<arg>newline=LF</arg>\n<arg>prompt=None</arg>\n<arg>loglevel=INFO</arg>\n<arg>term_type=vt100</arg>\n<arg>width=80</arg>\n<arg>height=24</arg>\n<arg>path_separator=/</arg>\n<arg>encoding=UTF-8</arg>\n</arguments>\n<doc>SSHLibrary allows some import time `configuration`.\n\nIf the library is imported without any arguments, the library\ndefaults are used:\n\n| Library | SSHLibrary |\n\nOnly arguments that are given are changed. In this example the\n`timeout` is changed to ``10 seconds`` but  other settings are left\nto the library defaults:\n\n| Library | SSHLibrary | 10 seconds |\n\nThe `prompt` does not have a default value and\nmust be explicitly set to be able to use `Read Until Prompt`.\nSince SSHLibrary 3.0.0, the prompt can also be a regular expression:\n\n| Library | SSHLibrary | prompt=REGEXP:[$#] |\n\nMultiple settings are also possible. In the example below, the library\nis brought into use with `newline` and `path separator` known by\nWindows:\n\n| Library | SSHLibrary | newline=CRLF | path_separator=\\\\ |</doc>\n<tags>\n</tags>\n</init>\n<kw name=\"Close All Connections\">\n<arguments>\n</arguments>\n<doc>Closes all open connections.\n\nThis keyword is ought to be used either in test or suite teardown to\nmake sure all the connections are closed before the test execution\nfinishes.\n\nAfter this keyword, the connection indices returned by\n`Open Connection` are reset and start from ``1``.\n\nExample:\n| `Open Connection` | my.server.com           |\n| `Open Connection` | build.local.net         |\n| # Do something with the connections         |\n| [Teardown]        | `Close all connections` |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Close Connection\">\n<arguments>\n</arguments>\n<doc>Closes the current connection.\n\nNo other connection is made active by this keyword. Manually use\n`Switch Connection` to switch to another connection.\n\nExample:\n| `Open Connection`  | my.server.com  |\n| `Login`            | johndoe        | secretpasswd |\n| `Get File`         | results.txt    | /tmp         |\n| `Close Connection` |\n| # Do something with /tmp/results.txt               |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Create Local Ssh Tunnel\">\n<arguments>\n<arg>local_port</arg>\n<arg>remote_host</arg>\n<arg>remote_port</arg>\n</arguments>\n<doc>The keyword uses the existing connection to set up local port forwarding\n(the openssh -L option) from a local port through a tunneled\nconnection to a destination reachable from the SSH server machine.\n\nThe example below illustrates the forwarding from the local machine, of\nthe connection on port 80 of an inaccessible server (secure.server.com)\nby connecting to a remote SSH server (remote.server.com) that has access\nto the secure server, and makes it available locally, on the port 9191:\n\n| `Open Connection`         | remote.server.com | prompt=$          |\n| `Login`                   | johndoe           | secretpasswd      |\n| `Create Local SSH Tunnel` | 9191              | secure.server.com | 80 |\n\nThe tunnel is active as long as the connection is open.\n\nNew in SSHLibrary 3.1.0</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Directory Should Exist\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Fails if the given ``path`` does not point to an existing directory.\n\nExample:\n| `Directory Should Exist` | /usr/share/man |\n\nNote that symlinks are followed:\n| `Directory Should Exist` | /usr/local/man | # Points to /usr/share/man/ |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Directory Should Not Exist\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Fails if the given ``path`` points to an existing directory.\n\nExample:\n| `Directory Should Not Exist` | /non/existing |\n\nNote that this keyword follows symlinks. Thus the example fails if\n``/non/existing`` is a link that points to an existing directory.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Enable Ssh Logging\">\n<arguments>\n<arg>logfile</arg>\n</arguments>\n<doc>Enables logging of SSH protocol output to given ``logfile``.\n\nAll the existing and upcoming connections are logged onwards from\nthe moment the keyword was called.\n\n``logfile`` is path to a file that is writable by the current local\nuser. If the file already exists, it will be overwritten.\n\nExample:\n| `Open Connection`    | my.server.com   | # Not logged |\n| `Enable SSH Logging` | myserver.log    |\n| `Login`              | johndoe         | secretpasswd |\n| `Open Connection`    | build.local.net | # Logged     |\n| # Do something with the connections    |\n| # Check myserver.log for detailed debug information   |\n\n*Note:* This keyword does not work when using Jython.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Execute Command\">\n<arguments>\n<arg>command</arg>\n<arg>return_stdout=True</arg>\n<arg>return_stderr=False</arg>\n<arg>return_rc=False</arg>\n<arg>sudo=False</arg>\n<arg>sudo_password=None</arg>\n</arguments>\n<doc>Executes ``command`` on the remote machine and returns its outputs.\n\nThis keyword executes the ``command`` and returns after the execution\nhas been finished. Use `Start Command` if the command should be\nstarted in the background.\n\nBy default, only the standard output is returned:\n\n| ${stdout}=       | `Execute Command` | echo 'Hello John!' |\n| `Should Contain` | ${stdout}         | Hello John!        |\n\nArguments ``return_stdout``, ``return_stderr`` and ``return_rc`` are\nused to specify, what is returned by this keyword.\nIf several arguments evaluate to a true value (see `Boolean arguments`),\nmultiple values are returned.\n\nIf errors are needed as well, set the respective argument value to\ntrue:\n\n| ${stdout}         | ${stderr}= | `Execute Command` | echo 'Hello John!' | return_stderr=True |\n| `Should Be Empty` | ${stderr}  |\n\nOften checking the return code is enough:\n\n| ${rc}=                        | `Execute Command` | echo 'Hello John!' | return_stdout=False | return_rc=True |\n| `Should Be Equal As Integers` | ${rc}             | 0                  | # succeeded         |\n\nArguments ``sudo`` and ``sudo_password`` are used for executing\ncommands within a sudo session. Due to different permission elevation\nin Cygwin, these two arguments will not work when using it.\n\n| `Execute Command`  | pwd | sudo=True |  sudo_password=test |\n\nThe ``command`` is always executed in a new shell. Thus possible\nchanges to the environment (e.g. changing working directory) are not\nvisible to the later keywords:\n\n| ${pwd}=           | `Execute Command` | pwd           |\n| `Should Be Equal` | ${pwd}            | /home/johndoe |\n| `Execute Command` | cd /tmp           |\n| ${pwd}=           | `Execute Command` | pwd           |\n| `Should Be Equal` | ${pwd}            | /home/johndoe |\n\n`Write` and `Read` can be used for running multiple commands in the\nsame shell. See `interactive shells` section for more information.\n\nThis keyword logs the executed command and its exit status with\nlog level ``INFO``.\n\n``sudo`` and ``sudo_password`` arguments are new in SSHLibrary 3.0.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"File Should Exist\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Fails if the given ``path`` does NOT point to an existing file.\n\nExample:\n| `File Should Exist` | /boot/initrd.img |\n\nNote that symlinks are followed:\n| `File Should Exist` | /initrd.img | # Points to /boot/initrd.img |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"File Should Not Exist\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Fails if the given ``path`` points to an existing file.\n\nExample:\n| `File Should Not Exist` | /non/existing |\n\nNote that this keyword follows symlinks. Thus the example fails if\n``/non/existing`` is a link that points an existing file.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Connection\">\n<arguments>\n<arg>index_or_alias=None</arg>\n<arg>index=False</arg>\n<arg>host=False</arg>\n<arg>alias=False</arg>\n<arg>port=False</arg>\n<arg>timeout=False</arg>\n<arg>newline=False</arg>\n<arg>prompt=False</arg>\n<arg>term_type=False</arg>\n<arg>width=False</arg>\n<arg>height=False</arg>\n<arg>encoding=False</arg>\n</arguments>\n<doc>Returns information about the connection.\n\nConnection is not changed by this keyword, use `Switch Connection` to\nchange the active connection.\n\nIf ``index_or_alias`` is not given, the information of the current\nconnection is returned.\n\nThis keyword returns an object that has the following attributes:\n\n| = Name =       | = Type = | = Explanation = |\n| index          | integer  | Number of the connection. Numbering starts from ``1``. |\n| host           | string   | Destination hostname. |\n| alias          | string   | An optional alias given when creating the connection.  |\n| port           | integer  | Destination port. |\n| timeout        | string   | `Timeout` length in textual representation. |\n| newline        | string   | The line break sequence used by `Write` keyword. See `newline`. |\n| prompt         | string   | `Prompt` character sequence for `Read Until Prompt`. |\n| term_type      | string   | Type of the virtual terminal. See `terminal settings`. |\n| width          | integer  | Width of the virtual terminal. See `terminal settings`. |\n| height         | integer  | Height of the virtual terminal. See `terminal settings`. |\n| path_separator | string   | The `path separator` used on the remote host. |\n| encoding       | string   | The `encoding` used for inputs and outputs. |\n\nIf there is no connection, an object having ``index`` and ``host``\nas ``None`` is returned, rest of its attributes having their values\nas configuration defaults.\n\nIf you want the information for all the open connections, use\n`Get Connections`.\n\nGetting connection information of the current connection:\n\n| `Open Connection` | far.server.com        |\n| `Open Connection` | near.server.com       | prompt=&gt;&gt;       | # Current connection |\n| ${nearhost}=      | `Get Connection`      |                 |\n| `Should Be Equal` | ${nearhost.host}      | near.server.com |\n| `Should Be Equal` | ${nearhost.index}     | 2               |\n| `Should Be Equal` | ${nearhost.prompt}    | &gt;&gt;              |\n| `Should Be Equal` | ${nearhost.term_type} | vt100           | # From defaults      |\n\nGetting connection information using an index:\n\n| `Open Connection` | far.server.com   |\n| `Open Connection` | near.server.com  | # Current connection |\n| ${farhost}=       | `Get Connection` | 1                    |\n| `Should Be Equal` | ${farhost.host}  | far.server.com       |\n\nGetting connection information using an alias:\n\n| `Open Connection` | far.server.com   | alias=far            |\n| `Open Connection` | near.server.com  | # Current connection |\n| ${farhost}=       | `Get Connection` | far                  |\n| `Should Be Equal` | ${farhost.host}  | far.server.com       |\n| `Should Be Equal` | ${farhost.alias} | far                  |\n\nThis keyword can also return plain connection attributes instead of\nthe whole connection object. This can be adjusted using the boolean\narguments ``index``, ``host``, ``alias``, and so on, that correspond\nto the attribute names of the object. If such arguments are given, and\nthey evaluate to true (see `Boolean arguments`), only the respective\nconnection attributes are returned. Note that attributes are always\nreturned in the same order arguments are specified in the signature.\n\n| `Open Connection` | my.server.com    | alias=example    |\n| ${host}=          | `Get Connection` | host=True        |\n| `Should Be Equal` | ${host}          | my.server.com    |\n| ${host}           | ${alias}=        | `Get Connection` | host=yes | alias=please |\n| `Should Be Equal` | ${host}          | my.server.com    |\n| `Should Be Equal` | ${alias}         | example          |\n\nGetting only certain attributes is especially useful when using this\nlibrary via the Remote library interface. This interface does not\nsupport returning custom objects, but individual attributes can be\nreturned just fine.\n\nThis keyword logs the connection information with log level ``INFO``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Connections\">\n<arguments>\n</arguments>\n<doc>Returns information about all the open connections.\n\nThis keyword returns a list of objects that are identical to the ones\nreturned by `Get Connection`.\n\nExample:\n| `Open Connection`             | near.server.com     | timeout=10s       |\n| `Open Connection`             | far.server.com      | timeout=5s        |\n| ${nearhost}                   | ${farhost}=         | `Get Connections` |\n| `Should Be Equal`             | ${nearhost.host}    | near.server.com   |\n| `Should Be Equal As Integers` | ${nearhost.timeout} | 10                |\n| `Should Be Equal As Integers` | ${farhost.port}     | 22                |\n| `Should Be Equal As Integers` | ${farhost.timeout}  | 5                 |\n\nThis keyword logs the information of connections with log level\n``INFO``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Directory\">\n<arguments>\n<arg>source</arg>\n<arg>destination=.</arg>\n<arg>recursive=False</arg>\n</arguments>\n<doc>Downloads a directory, including its content, from the remote machine to the local machine.\n\n``source`` is a path on the remote machine. Both absolute paths and\npaths relative to the current working directory are supported.\n\n``destination`` is the target path on the local machine.  Both\nabsolute paths and paths relative to the current working directory\nare supported.\n\n``recursive`` specifies whether to recursively download all\nsubdirectories inside ``source``. Subdirectories are downloaded if\nthe argument value evaluates to true (see `Boolean arguments`).\n\nExamples:\n| `Get Directory` | /var/logs      | /tmp                |\n| `Get Directory` | /var/logs      | /tmp/non/existing   |\n| `Get Directory` | /var/logs      |\n| `Get Directory` | /var/logs      | recursive=True      |\n\nThe local ``destination`` is created as following:\n\n1. If ``destination`` is an existing path on the local machine,\n   ``source`` directory is downloaded into it.\n\n2. If ``destination`` does not exist on the local machine, it is\n   created and the content of ``source`` directory is downloaded\n   into it.\n\n3. If ``destination`` is not given, ``source`` directory is\n   downloaded into the current working directory on the local\n   &lt;machine. This is typically the directory where the test execution\n   was started and thus accessible using the built-in ``${EXECDIR}``\n   variable.\n\nSee also `Get File`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get File\">\n<arguments>\n<arg>source</arg>\n<arg>destination=.</arg>\n</arguments>\n<doc>Downloads file(s) from the remote machine to the local machine.\n\n``source`` is a path on the remote machine. Both absolute paths and\npaths relative to the current working directory are supported.\nIf the source contains wildcards explained in `glob patterns`,\nall files matching it are downloaded. In this case ``destination``\nmust always be a directory.\n\n``destination`` is the target path on the local machine. Both\nabsolute paths and paths relative to the current working directory\nare supported.\n\nExamples:\n| `Get File` | /var/log/auth.log | /tmp/                      |\n| `Get File` | /tmp/example.txt  | C:\\\\temp\\\\new_name.txt |\n| `Get File` | /path/to/*.txt    |\n\nThe local ``destination`` is created using the rules explained below:\n\n1. If the ``destination`` is an existing file, the ``source`` file is\n   downloaded over it.\n\n2. If the ``destination`` is an existing directory, the ``source``\n   file is downloaded into it. Possible file with the same name is\n   overwritten.\n\n3. If the ``destination`` does not exist and it ends with the path\n   separator of the local operating system, it is considered a\n   directory. The directory is then created and the ``source`` file\n   is downloaded into it. Possible missing intermediate directories\n   are also created.\n\n4. If the ``destination`` does not exist and does not end with the\n   local path separator, it is considered a file. The ``source`` file\n   is downloaded and saved using that file name, and possible missing\n   intermediate directories are also created.\n\n5. If ``destination`` is not given, the current working directory on\n   the local machine is used as the destination. This is typically\n   the directory where the test execution was started and thus\n   accessible using built-in ``${EXECDIR}`` variable.\n\nSee also `Get Directory`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Pre Login Banner\">\n<arguments>\n<arg>host=None</arg>\n<arg>port=22</arg>\n</arguments>\n<doc>Returns the banner supplied by the server upon connect.\n\nThere are 2 ways of getting banner information.\n\n1. Independent of any connection:\n\n| ${banner} =       | `Get Pre Login Banner` | ${HOST}                   |\n| `Should Be Equal` | ${banner}              | Testing pre-login banner  |\n\nThe argument ``host`` is mandatory for getting banner key without\nan open connection.\n\n2. From the current connection:\n\n| `Open Connection`  | ${HOST}                | prompt=${PROMPT}         |\n| `Login`            | ${USERNAME}            | ${PASSWORD}              |\n| ${banner} =        | `Get Pre Login Banner` |\n| `Should Be Equal`  | ${banner}              | Testing pre-login banner |\n\nNew in SSHLibrary 3.0.0.\n\n*Note:* This keyword does not work when using Jython.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Directories In Directory\">\n<arguments>\n<arg>path</arg>\n<arg>pattern=None</arg>\n<arg>absolute=False</arg>\n</arguments>\n<doc>A wrapper for `List Directory` that returns only directories.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Directory\">\n<arguments>\n<arg>path</arg>\n<arg>pattern=None</arg>\n<arg>absolute=False</arg>\n</arguments>\n<doc>Returns and logs items in the remote ``path``, optionally filtered with ``pattern``.\n\n``path`` is a path on the remote machine. Both absolute paths and\npaths relative to the current working directory are supported.\nIf ``path`` is a symlink, it is followed.\n\nItem names are returned in case-sensitive alphabetical order,\ne.g. ``['A Name', 'Second', 'a lower case name', 'one more']``.\nImplicit directories ``.`` and ``..`` are not returned. The returned\nitems are automatically logged.\n\nBy default, the item names are returned relative to the given\nremote path (e.g. ``file.txt``). If you want them be returned in the\nabsolute format (e.g. ``/home/johndoe/file.txt``), set the\n``absolute`` argument to any non-empty string.\n\nIf ``pattern`` is given, only items matching it are returned. The\npattern is a glob pattern and its syntax is explained in the\n`Pattern matching` section.\n\nExamples (using also other `List Directory` variants):\n| @{items}= | `List Directory`          | /home/johndoe |\n| @{files}= | `List Files In Directory` | /tmp          | *.txt | absolute=True |\n\nIf you are only interested in directories or files,\nuse `List Files In Directory` or `List Directories In Directory`,\nrespectively.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Files In Directory\">\n<arguments>\n<arg>path</arg>\n<arg>pattern=None</arg>\n<arg>absolute=False</arg>\n</arguments>\n<doc>A wrapper for `List Directory` that returns only files.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Login\">\n<arguments>\n<arg>username</arg>\n<arg>password</arg>\n<arg>delay=0.5 seconds</arg>\n</arguments>\n<doc>Logs into the SSH server with the given ``username`` and ``password``.\n\nConnection must be opened before using this keyword.\n\nThis keyword reads, returns and logs the server output after logging\nin. If the `prompt` is set, everything until the prompt is read.\nOtherwise the output is read using the `Read` keyword with the given\n``delay``. The output is logged using the default `log level`.\n\nExample that logs in and returns the output:\n\n| `Open Connection` | linux.server.com |\n| ${output}=        | `Login`          | johndoe       | secretpasswd |\n| `Should Contain`  | ${output}        | Last login at |\n\nExample that logs in and returns everything until the prompt:\n\n| `Open Connection` | linux.server.com | prompt=$         |\n| ${output}=        | `Login`          | johndoe          | secretpasswd |\n| `Should Contain`  | ${output}        | johndoe@linux:~$ |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Login With Public Key\">\n<arguments>\n<arg>username</arg>\n<arg>keyfile</arg>\n<arg>password=</arg>\n<arg>allow_agent=False</arg>\n<arg>look_for_keys=False</arg>\n<arg>delay=0.5 seconds</arg>\n</arguments>\n<doc>Logs into the SSH server using key-based authentication.\n\nConnection must be opened before using this keyword.\n\n``username`` is the username on the remote machine.\n\n``keyfile`` is a path to a valid OpenSSH private key file on the local\nfilesystem.\n\n``password`` is used to unlock the ``keyfile`` if needed.\n\nThis keyword reads, returns and logs the server output after logging\nin. If the `prompt` is set, everything until the prompt is read.\nOtherwise the output is read using the `Read` keyword with the given\n``delay``. The output is logged using the default `log level`.\n\nExample that logs in using a private key and returns the output:\n\n| `Open Connection` | linux.server.com        |\n| ${output}=        | `Login With Public Key` | johndoe       | /home/johndoe/.ssh/id_rsa |\n| `Should Contain`  | ${motd}                 | Last login at |\n\nWith locked private keys, the keyring ``password`` is required:\n\n| `Open Connection`       | linux.server.com |\n| `Login With Public Key` | johndoe          | /home/johndoe/.ssh/id_dsa | keyringpasswd |\n\n``allow_agent`` enables the connection to the SSH agent.\n``look_for_keys`` enables the searching for discoverable private key\nfiles in ``~/.ssh/``.\n\n``allow_agent`` and ``look_for_keys`` arguments are new in SSHLibrary\n3.0.0.\n\n*Note:* ``allow_agent`` and ``look_for_keys`` do not work when using Jython.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Open Connection\">\n<arguments>\n<arg>host</arg>\n<arg>alias=None</arg>\n<arg>port=22</arg>\n<arg>timeout=None</arg>\n<arg>newline=None</arg>\n<arg>prompt=None</arg>\n<arg>term_type=None</arg>\n<arg>width=None</arg>\n<arg>height=None</arg>\n<arg>path_separator=None</arg>\n<arg>encoding=None</arg>\n</arguments>\n<doc>Opens a new SSH connection to the given ``host`` and ``port``.\n\nThe new connection is made active. Possible existing connections\nare left open in the background.\n\nNote that on Jython this keyword actually opens a connection and\nwill fail immediately on unreachable hosts. On Python the actual\nconnection attempt will not be done until `Login` is called.\n\nThis keyword returns the index of the new connection which can be used\nlater to switch back to it. Indices start from ``1`` and are reset\nwhen `Close All Connections` is used.\n\nOptional ``alias`` can be given for the connection and can be used for\nswitching between connections, similarly as the index.\nSee `Switch Connection` for more details.\n\nConnection parameters, like `timeout` and `newline` are documented in\n`configuration`. If they are not defined as arguments, the library\ndefaults are used for the connection.\n\nAll the arguments, except ``host``, ``alias`` and ``port``\ncan be later updated with `Set Client Configuration`.\n\nPort ``22`` is assumed by default:\n\n| ${index}= | `Open Connection` | my.server.com |\n\nNon-standard port may be given as an argument:\n\n| ${index}= | `Open Connection` | 192.168.1.1 | port=23 |\n\nAliases are handy, if you need to switch back to the connection later:\n\n| `Open Connection`   | my.server.com | alias=myserver |\n| # Do something with my.server.com   |\n| `Open Connection`   | 192.168.1.1   |\n| `Switch Connection` | myserver      |                | # Back to my.server.com |\n\nSettings can be overridden per connection, otherwise the ones set on\n`library importing` or with `Set Default Configuration` are used:\n\n| Open Connection | 192.168.1.1     | timeout=1 hour    | newline=CRLF          |\n| # Do something with the connection                    |\n| `Open Connection` | my.server.com | # Default timeout | # Default line breaks |\n\nThe `terminal settings` are also configurable per connection:\n\n| `Open Connection` | 192.168.1.1  | term_type=ansi | width=40 |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Put Directory\">\n<arguments>\n<arg>source</arg>\n<arg>destination=.</arg>\n<arg>mode=0744</arg>\n<arg>newline=</arg>\n<arg>recursive=False</arg>\n</arguments>\n<doc>Uploads a directory, including its content, from the local machine to the remote machine.\n\n``source`` is the path on the local machine. Both absolute paths and\npaths relative to the current working directory are supported.\n\n``destination`` is the target path on the remote machine. Both\nabsolute paths and paths relative to the current working directory\nare supported.\n\n``mode`` can be used to set the target file permission.\nNumeric values are accepted. The default value is ``0744``\n(``-rwxr--r--``).\n\n``newline`` can be used to force the line break characters that are\nwritten to the remote files. Valid values are ``LF`` and ``CRLF``.\n\n``recursive`` specifies whether to recursively upload all\nsubdirectories inside ``source``. Subdirectories are uploaded if the\nargument value evaluates to true (see `Boolean arguments`).\n\nExamples:\n| `Put Directory` | /var/logs | /tmp               |\n| `Put Directory` | /var/logs | /tmp/non/existing  |\n| `Put Directory` | /var/logs |\n| `Put Directory` | /var/logs | recursive=True     |\n| `Put Directory` | /var/logs | /home/groups/robot | mode=0770 |\n| `Put Directory` | /var/logs | newline=CRLF       |\n\nThe remote ``destination`` is created as following:\n\n1. If ``destination`` is an existing path on the remote machine,\n   ``source`` directory is uploaded into it.\n\n2. If ``destination`` does not exist on the remote machine, it is\n   created and the content of ``source`` directory is uploaded into\n   it.\n\n3. If ``destination`` is not given, ``source`` directory is typically\n   uploaded to user's home directory on the remote machine.\n\nSee also `Put File`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Put File\">\n<arguments>\n<arg>source</arg>\n<arg>destination=.</arg>\n<arg>mode=0744</arg>\n<arg>newline=</arg>\n</arguments>\n<doc>Uploads file(s) from the local machine to the remote machine.\n\n``source`` is the path on the local machine. Both absolute paths and\npaths relative to the current working directory are supported.\nIf the source contains wildcards explained in `glob patterns`,\nall files matching it are uploaded. In this case ``destination``\nmust always be a directory.\n\n``destination`` is the target path on the remote machine. Both\nabsolute paths and paths relative to the current working directory\nare supported.\n\n``mode`` can be used to set the target file permission.\nNumeric values are accepted. The default value is ``0744``\n(``-rwxr--r--``).\n\n``newline`` can be used to force the line break characters that are\nwritten to the remote files. Valid values are ``LF`` and ``CRLF``.\n\nExamples:\n| `Put File` | /path/to/*.txt          |\n| `Put File` | /path/to/*.txt          | /home/groups/robot | mode=0770 |\n| `Put File` | /path/to/*.txt          | newline=CRLF       |\n\nThe remote ``destination`` is created as following:\n\n1. If ``destination`` is an existing file, ``source`` file is uploaded\n   over it.\n\n2. If ``destination`` is an existing directory, ``source`` file is\n   uploaded into it. Possible file with same name is overwritten.\n\n3. If ``destination`` does not exist and it ends with the\n   `path separator`, it is considered a directory. The directory is\n   then created and ``source`` file uploaded into it.\n   Possibly missing intermediate directories are also created.\n\n4. If ``destination`` does not exist and it does not end with\n   the `path separator`, it is considered a file.\n   If the path to the file does not exist, it is created.\n\n5. If ``destination`` is not given, the user's home directory\n   on the remote machine is used as the destination.\n\nSee also `Put Directory`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Read\">\n<arguments>\n<arg>loglevel=None</arg>\n<arg>delay=None</arg>\n</arguments>\n<doc>Consumes and returns everything available on the server output.\n\nIf ``delay`` is given, this keyword waits that amount of time and\nreads output again. This wait-read cycle is repeated as long as\nfurther reads return more output or the default `timeout` expires.\n``delay`` must be given in Robot Framework's `time format`.\n\nThis keyword is most useful for reading everything from\nthe server output, thus clearing it.\n\nThe read output is logged. ``loglevel`` can be used to override\nthe default `log level`.\n\nExample:\n| `Open Connection` | my.server.com |\n| `Login`           | johndoe       | secretpasswd                 |\n| `Write`           | sudo su -     |                              |\n| ${output}=        | `Read`        | delay=0.5s                   |\n| `Should Contain`  | ${output}     | [sudo] password for johndoe: |\n| `Write`           | secretpasswd  |                              |\n| ${output}=        | `Read`        | loglevel=WARN | # Shown in the console due to loglevel |\n| `Should Contain`  | ${output}     | root@                        |\n\nSee `interactive shells` for more information about writing and\nreading in general.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Read Command Output\">\n<arguments>\n<arg>return_stdout=True</arg>\n<arg>return_stderr=False</arg>\n<arg>return_rc=False</arg>\n</arguments>\n<doc>Returns outputs of the most recent started command.\n\nAt least one command must have been started using `Start Command`\nbefore this keyword can be used.\n\nBy default, only the standard output of the started command is\nreturned:\n\n| `Start Command`  | echo 'Hello John!'    |\n| ${stdout}=       | `Read Command Output` |\n| `Should Contain` | ${stdout}             | Hello John! |\n\nArguments ``return_stdout``, ``return_stderr`` and ``return_rc`` are\nused to specify, what is returned by this keyword.\nIf several arguments evaluate to a true value (see `Boolean arguments`),\nmultiple values are returned.\n\nIf errors are needed as well, set the argument value to true:\n\n| `Start Command`   | echo 'Hello John!' |\n| ${stdout}         | ${stderr}=         | `Read Command Output` | return_stderr=True |\n| `Should Be Empty` | ${stderr}          |\n\nOften checking the return code is enough:\n\n| `Start Command`               | echo 'Hello John!'    |\n| ${rc}=                        | `Read Command Output` | return_stdout=False | return_rc=True |\n| `Should Be Equal As Integers` | ${rc}                 | 0                   | # succeeded    |\n\nUsing `Start Command` and `Read Command Output` follows\nLIFO (last in, first out) policy, meaning that `Read Command Output`\noperates on the most recent started command, after which that command\nis discarded and its output cannot be read again.\n\nIf several commands have been started, the output of the last started\ncommand is returned. After that, a subsequent call will return the\noutput of the new last (originally the second last) command:\n\n| `Start Command`  | echo 'HELLO'          |\n| `Start Command`  | echo 'SECOND'         |\n| ${stdout}=       | `Read Command Output` |\n| `Should Contain` | ${stdout}             | 'SECOND' |\n| ${stdout}=       | `Read Command Output` |\n| `Should Contain` | ${stdout}             | 'HELLO'  |\n\nThis keyword logs the read command with log level ``INFO``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Read Until\">\n<arguments>\n<arg>expected</arg>\n<arg>loglevel=None</arg>\n</arguments>\n<doc>Consumes and returns the server output until ``expected`` is encountered.\n\nText up until and including the ``expected`` will be returned.\n\nIf the `timeout` expires before the match is found, this keyword fails.\n\nThe read output is logged. ``loglevel`` can be used to override\nthe default `log level`.\n\nExample:\n| `Open Connection` | my.server.com |\n| `Login`           | johndoe       | ${PASSWORD}                  |\n| `Write`           | sudo su -     |                              |\n| ${output}=        | `Read Until`  | :                            |\n| `Should Contain`  | ${output}     | [sudo] password for johndoe: |\n| `Write`           | ${PASSWORD}   |                              |\n| ${output}=        | `Read Until`  | @                            |\n| `Should End With` | ${output}     | root@                        |\n\nSee also `Read Until Prompt` and `Read Until Regexp` keywords. For\nmore details about reading and writing in general, see the\n`Interactive shells` section.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Read Until Prompt\">\n<arguments>\n<arg>loglevel=None</arg>\n</arguments>\n<doc>Consumes and returns the server output until the prompt is found.\n\nText up and until prompt is returned. The `prompt` must be set before\nthis keyword is used.\n\nIf the `timeout` expires before the match is found, this keyword fails.\n\nThis keyword is useful for reading output of a single command when\noutput of previous command has been read and that command does not\nproduce prompt characters in its output.\n\nThe read output is logged. ``loglevel`` can be used to override\nthe default `log level`.\n\nExample:\n| `Open Connection`          | my.server.com       | prompt=$         |\n| `Login`                    | johndoe             | ${PASSWORD}      |\n| `Write`                    | sudo su -           |                  |\n| `Write`                    | ${PASSWORD}         |                  |\n| `Set Client Configuration` | prompt=#            | # For root, the prompt is # |\n| ${output}=                 | `Read Until Prompt` |                  |\n| `Should End With`          | ${output}           | root@myserver:~# |\n\nSee also `Read Until` and `Read Until Regexp` keywords. For more\ndetails about reading and writing in general, see the `Interactive\nshells` section.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Read Until Regexp\">\n<arguments>\n<arg>regexp</arg>\n<arg>loglevel=None</arg>\n</arguments>\n<doc>Consumes and returns the server output until a match to ``regexp`` is found.\n\n``regexp`` can be a regular expression pattern or a compiled regular\nexpression object. See the `Regular expressions` section for more\ndetails about the syntax.\n\nText up until and including the ``regexp`` will be returned.\n\nIf the `timeout` expires before the match is found, this keyword fails.\n\nThe read output is logged. ``loglevel`` can be used to override\nthe default `log level`.\n\nExample:\n| `Open Connection` | my.server.com       |\n| `Login`           | johndoe             | ${PASSWORD}                  |\n| `Write`           | sudo su -           |                              |\n| ${output}=        | `Read Until Regexp` | \\\\[.*\\\\].*:              |\n| `Should Contain`  | ${output}           | [sudo] password for johndoe: |\n| `Write`           | ${PASSWORD}         |                              |\n| ${output}=        | `Read Until Regexp` | .*@                          |\n| `Should Contain`  | ${output}           | root@                        |\n\nSee also `Read Until` and `Read Until Prompt` keywords. For more\ndetails about reading and writing in general, see the `Interactive\nshells` section.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Client Configuration\">\n<arguments>\n<arg>timeout=None</arg>\n<arg>newline=None</arg>\n<arg>prompt=None</arg>\n<arg>term_type=None</arg>\n<arg>width=None</arg>\n<arg>height=None</arg>\n<arg>path_separator=None</arg>\n<arg>encoding=None</arg>\n</arguments>\n<doc>Update the `configuration` of the current connection.\n\nOnly parameters whose value is other than ``None`` are updated.\n\nIn the following example, `prompt` is set for\nthe current connection. Other settings are left intact:\n\n| `Open Connection    `      | my.server.com      |\n| `Set Client Configuration` | prompt=$           |\n| ${myserver}=               | `Get Connection`   |\n| `Should Be Equal`          | ${myserver.prompt} | $ |\n\nUsing keyword does not affect the other connections:\n\n| `Open Connection`          | linux.server.com   |                   |\n| `Set Client Configuration` | prompt=$           |                   | # Only linux.server.com affected   |\n| `Open Connection    `      | windows.server.com |                   |\n| `Set Client Configuration` | prompt=&gt;           |                   | # Only windows.server.com affected |\n| ${linux}                   | ${windows}=        | `Get Connections` |\n| `Should Be Equal`          | ${linux.prompt}    | $                 |\n| `Should Be Equal`          | ${windows.prompt}  | &gt;                 |\n\nMultiple settings are possible. This example updates the\n`terminal settings` of the current connection:\n\n| `Open Connection`          | 192.168.1.1    |\n| `Set Client Configuration` | term_type=ansi | width=40 |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Default Configuration\">\n<arguments>\n<arg>timeout=None</arg>\n<arg>newline=None</arg>\n<arg>prompt=None</arg>\n<arg>loglevel=None</arg>\n<arg>term_type=None</arg>\n<arg>width=None</arg>\n<arg>height=None</arg>\n<arg>path_separator=None</arg>\n<arg>encoding=None</arg>\n</arguments>\n<doc>Update the default `configuration`.\n\nPlease note that using this keyword does not affect the already\nopened connections. Use `Set Client Configuration` to configure the\nactive connection.\n\nOnly parameters whose value is other than ``None`` are updated.\n\nThis example sets `prompt` to ``$``:\n\n| `Set Default Configuration` | prompt=$ |\n\nThis example sets `newline` and `path separator` to the ones known\nby Windows:\n\n| `Set Default Configuration` | newline=CRLF | path_separator=\\\\ |\n\nSometimes you might want to use longer `timeout` for all the\nsubsequent connections without affecting the existing ones:\n\n| `Set Default Configuration`   | timeout=5 seconds  |\n| `Open Connection       `      | local.server.com   |\n| `Set Default Configuration`   | timeout=20 seconds |\n| `Open Connection`             | emea.server.com    |\n| `Open Connection`             | apac.server.com    |\n| ${local}                      | ${emea}            | ${apac}= | `Get Connections` |\n| `Should Be Equal As Integers` | ${local.timeout}   | 5        |\n| `Should Be Equal As Integers` | ${emea.timeout}    | 20       |\n| `Should Be Equal As Integers` | ${apac.timeout}    | 20       |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Start Command\">\n<arguments>\n<arg>command</arg>\n<arg>sudo=False</arg>\n<arg>sudo_password=None</arg>\n</arguments>\n<doc>Starts execution of the ``command`` on the remote machine and returns immediately.\n\nThis keyword returns nothing and does not wait for the ``command``\nexecution to be finished. If waiting for the output is required,\nuse `Execute Command` instead.\n\nThis keyword does not return any output generated by the started\n``command``. Use `Read Command Output` to read the output:\n\n| `Start Command`   | echo 'Hello John!'    |\n| ${stdout}=        | `Read Command Output` |\n| `Should Contain`  | ${stdout}             | Hello John! |\n\nThe ``command`` is always executed in a new shell, similarly as with\n`Execute Command`. Thus possible changes to the environment (e.g.\nchanging working directory) are not visible to the later keywords:\n\n| `Start Command`   | pwd                   |\n| ${pwd}=           | `Read Command Output` |\n| `Should Be Equal` | ${pwd}                | /home/johndoe |\n| `Start Command`   | cd /tmp               |\n| `Start Command`   | pwd                   |\n| ${pwd}=           | `Read Command Output` |\n| `Should Be Equal` | ${pwd}                | /home/johndoe |\n\nArguments ``sudo`` and ``sudo_password`` are used for executing\ncommands within a sudo session. Due to different permission elevation\nin Cygwin, these two arguments will not when using it.\n\n| `Start Command`   | pwd                 | sudo=True     |  sudo_password=test |\n\n`Write` and `Read` can be used for running multiple commands in the\nsame shell. See `interactive shells` section for more information.\n\nThis keyword logs the started command with log level ``INFO``.\n\n``sudo`` and ``sudo_password`` arguments are new in SSHLibrary 3.0.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Switch Connection\">\n<arguments>\n<arg>index_or_alias</arg>\n</arguments>\n<doc>Switches the active connection by index or alias.\n\n``index_or_alias`` is either connection index (an integer) or alias\n(a string). Index is got as the return value of `Open Connection`.\nAlternatively, both index and alias can queried as attributes\nof the object returned by `Get Connection`.\n\nThis keyword returns the index of the previous active connection,\nwhich can be used to switch back to that connection later.\n\nExample:\n| ${myserver}=        | `Open Connection` | my.server.com |\n| `Login`             | johndoe           | secretpasswd  |\n| `Open Connection`   | build.local.net   | alias=Build   |\n| `Login`             | jenkins           | jenkins       |\n| `Switch Connection` | ${myserver}       |               | # Switch using index          |\n| ${username}=        | `Execute Command` | whoami        | # Executed on my.server.com   |\n| `Should Be Equal`   | ${username}       | johndoe       |\n| `Switch Connection` | Build             |               | # Switch using alias          |\n| ${username}=        | `Execute Command` | whoami        | # Executed on build.local.net |\n| `Should Be Equal`   | ${username}       | jenkins       |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Write\">\n<arguments>\n<arg>text</arg>\n<arg>loglevel=None</arg>\n</arguments>\n<doc>Writes the given ``text`` on the remote machine and appends a newline.\n\nAppended `newline` can be configured.\n\nThis keyword returns and consumes the written ``text``\n(including the appended newline) from the server output. See the\n`Interactive shells` section for more information.\n\nThe written ``text`` is logged. ``loglevel`` can be used to override\nthe default `log level`.\n\nExample:\n| ${written}=          | `Write`         | su                         |\n| `Should Contain`     | ${written}      | su                         | # Returns the consumed output  |\n| ${output}=           | `Read`          |\n| `Should Not Contain` | ${output}       | ${written}                 | # Was consumed from the output |\n| `Should Contain`     | ${output}       | Password:                  |\n| `Write`              | invalidpasswd   |\n| ${output}=           | `Read`          |\n| `Should Contain`     | ${output}       | su: Authentication failure |\n\nSee also `Write Bare`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Write Bare\">\n<arguments>\n<arg>text</arg>\n</arguments>\n<doc>Writes the given ``text`` on the remote machine without appending a newline.\n\nUnlike `Write`, this keyword returns and consumes nothing. See the\n`Interactive shells` section for more information.\n\nExample:\n| `Write Bare`     | su\\n            |\n| ${output}=       | `Read`           |\n| `Should Contain` | ${output}        | su                         | # Was not consumed from output |\n| `Should Contain` | ${output}        | Password:                  |\n| `Write Bare`     | invalidpasswd\\n |\n| ${output}=       | `Read`           |\n| `Should Contain` | ${output}        | su: Authentication failure |\n\nSee also `Write`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Write Until Expected Output\">\n<arguments>\n<arg>text</arg>\n<arg>expected</arg>\n<arg>timeout</arg>\n<arg>retry_interval</arg>\n<arg>loglevel=None</arg>\n</arguments>\n<doc>Writes the given ``text`` repeatedly until ``expected`` appears in the server output.\n\nThis keyword returns nothing.\n\n``text`` is written without appending a newline and is consumed from\nthe server output before ``expected`` is read. See more information\non the `Interactive shells` section.\n\nIf ``expected`` does not appear in output within ``timeout``, this\nkeyword fails. ``retry_interval`` defines the time before writing\n``text`` again. Both ``timeout`` and ``retry_interval`` must be given\nin Robot Framework's `time format`.\n\nThe written ``text`` is logged. ``loglevel`` can be used to override\nthe default `log level`.\n\nThis example will write ``lsof -c python27\\n`` (list all files\ncurrently opened by Python 2.7), until ``myscript.py`` appears in the\noutput. The command is written every 0.5 seconds. The keyword fails if\n``myscript.py`` does not appear in the server output in 5 seconds:\n\n| `Write Until Expected Output` | lsof -c python27\\n | expected=myscript.py | timeout=5s | retry_interval=0.5s |</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/Screenshot.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"Screenshot\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:42\">\n<version>3.0.3</version>\n<scope>test suite</scope>\n<namedargs>yes</namedargs>\n<doc>Test library for taking screenshots on the machine where tests are run.\n\nNotice that successfully taking screenshots requires tests to be run with\na physical or virtual display.\n\n= Using with Python =\n\nHow screenshots are taken when using Python depends on the operating\nsystem. On OSX screenshots are taken using the built-in ``screencapture``\nutility. On other operating systems you need to have one of the following\ntools or Python modules installed. You can specify the tool/module to use\nwhen `importing` the library. If no tool or module is specified, the first\none found will be used.\n\n- wxPython :: http://wxpython.org :: Required also by RIDE so many Robot\n  Framework users already have this module installed.\n- PyGTK :: http://pygtk.org :: This module is available by default on most\n  Linux distributions.\n- Pillow :: https://python-pillow.github.io ::\n  Only works on Windows. Also the original PIL package is supported.\n- Scrot :: https://en.wikipedia.org/wiki/Scrot :: Not used on Windows.\n  Install with ``apt-get install scrot`` or similar.\n\nUsing ``screencapture`` on OSX and specifying explicit screenshot module\nare new in Robot Framework 2.9.2. The support for using ``scrot`` is new\nin Robot Framework 3.0.\n\n= Using with Jython and IronPython =\n\nWith Jython and IronPython this library uses APIs provided by JVM and .NET\nplatforms, respectively. These APIs are always available and thus no\nexternal modules are needed.\n\n= Where screenshots are saved =\n\nBy default screenshots are saved into the same directory where the Robot\nFramework log file is written. If no log is created, screenshots are saved\ninto the directory where the XML output file is written.\n\nIt is possible to specify a custom location for screenshots using\n``screenshot_directory`` argument when `importing` the library and\nusing `Set Screenshot Directory` keyword during execution. It is also\npossible to save screenshots using an absolute path.</doc>\n<init>\n<arguments>\n<arg>screenshot_directory=None</arg>\n<arg>screenshot_module=None</arg>\n</arguments>\n<doc>Configure where screenshots are saved.\n\nIf ``screenshot_directory`` is not given, screenshots are saved into\nsame directory as the log file. The directory can also be set using\n`Set Screenshot Directory` keyword.\n\n``screenshot_module`` specifies the module or tool to use when using\nthis library on Python outside OSX. Possible values are ``wxPython``,\n``PyGTK``, ``PIL`` and ``scrot``, case-insensitively. If no value is\ngiven, the first module/tool found is used in that order. See `Using\nwith Python` for more information.\n\nExamples (use only one of these):\n| =Setting= |  =Value=   |  =Value=   |\n| Library   | Screenshot |            |\n| Library   | Screenshot | ${TEMPDIR} |\n| Library   | Screenshot | screenshot_module=PyGTK |\n\nSpecifying explicit screenshot module is new in Robot Framework 2.9.2.</doc>\n<tags>\n</tags>\n</init>\n<kw name=\"Set Screenshot Directory\">\n<arguments>\n<arg>path</arg>\n</arguments>\n<doc>Sets the directory where screenshots are saved.\n\nIt is possible to use ``/`` as a path separator in all operating\nsystems. Path to the old directory is returned.\n\nThe directory can also be set in `importing`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Take Screenshot\">\n<arguments>\n<arg>name=screenshot</arg>\n<arg>width=800px</arg>\n</arguments>\n<doc>Takes a screenshot in JPEG format and embeds it into the log file.\n\nName of the file where the screenshot is stored is derived from the\ngiven ``name``. If the ``name`` ends with extension ``.jpg`` or\n``.jpeg``, the screenshot will be stored with that exact name.\nOtherwise a unique name is created by adding an underscore, a running\nindex and an extension to the ``name``.\n\nThe name will be interpreted to be relative to the directory where\nthe log file is written. It is also possible to use absolute paths.\nUsing ``/`` as a path separator works in all operating systems.\n\n``width`` specifies the size of the screenshot in the log file.\n\nExamples: (LOGDIR is determined automatically by the library)\n| Take Screenshot |                  |     | # LOGDIR/screenshot_1.jpg (index automatically incremented) |\n| Take Screenshot | mypic            |     | # LOGDIR/mypic_1.jpg (index automatically incremented) |\n| Take Screenshot | ${TEMPDIR}/mypic |     | # /tmp/mypic_1.jpg (index automatically incremented) |\n| Take Screenshot | pic.jpg          |     | # LOGDIR/pic.jpg (always uses this file) |\n| Take Screenshot | images/login.jpg | 80% | # Specify both name and width. |\n| Take Screenshot | width=550px      |     | # Specify only width. |\n\nThe path where the screenshot is saved is returned.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Take Screenshot Without Embedding\">\n<arguments>\n<arg>name=screenshot</arg>\n</arguments>\n<doc>Takes a screenshot and links it from the log file.\n\nThis keyword is otherwise identical to `Take Screenshot` but the saved\nscreenshot is not embedded into the log file. The screenshot is linked\nso it is nevertheless easily available.</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/SeleniumLibrary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"SeleniumLibrary\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:44\">\n<version>3.1.1</version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>SeleniumLibrary is a web testing library for Robot Framework.\n\nThis document explains how to use keywords provided by SeleniumLibrary.\nFor information about installation, support, and more, please visit the\n[https://github.com/robotframework/SeleniumLibrary|project pages].\nFor more information about Robot Framework, see http://robotframework.org.\n\nSeleniumLibrary uses the Selenium WebDriver modules internally to\ncontrol a web browser. See http://seleniumhq.org for more information\nabout Selenium in general.\n\n== Table of contents ==\n\n- `Locating elements`\n- `Timeouts, waits and delays`\n- `Run-on-failure functionality`\n- `Boolean arguments`\n- `Importing`\n- `Shortcuts`\n- `Keywords`\n\n= Locating elements =\n\nAll keywords in SeleniumLibrary that need to interact with an element\non a web page take an argument typically named ``locator`` that specifies\nhow to find the element. Most often the locator is given as a string\nusing the locator syntax described below, but `using WebElements` is\npossible too.\n\n== Locator syntax ==\n\nSeleniumLibrary supports finding elements based on different strategies\nsuch as the element id, XPath expressions, or CSS selectors. The strategy\ncan either be explicitly specified with a prefix or the strategy can be\nimplicit.\n\n=== Default locator strategy ===\n\nBy default locators are considered to use the keyword specific default\nlocator strategy. All keywords support finding elements based on ``id``\nand ``name`` attributes, but some keywords support additional attributes\nor other values that make sense in their context. For example, `Click\nLink` supports the ``href`` attribute and the link text and addition\nto the normal ``id`` and ``name``.\n\nExamples:\n\n| `Click Element` | example | # Match based on ``id`` or ``name``.            |\n| `Click Link`    | example | # Match also based on link text and ``href``.   |\n| `Click Button`  | example | # Match based on ``id``, ``name`` or ``value``. |\n\nIf a locator accidentally starts with a prefix recognized as `explicit\nlocator strategy` or `implicit XPath strategy`, it is possible to use\nthe explicit ``default`` prefix to enable the default strategy.\n\nExamples:\n\n| `Click Element` | name:foo         | # Find element with name ``foo``.               |\n| `Click Element` | default:name:foo | # Use default strategy with value ``name:foo``. |\n| `Click Element` | //foo            | # Find element using XPath ``//foo``.           |\n| `Click Element` | default: //foo   | # Use default strategy with value ``//foo``.    |\n\n=== Explicit locator strategy ===\n\nThe explicit locator strategy is specified with a prefix using either\nsyntax ``strategy:value`` or ``strategy=value``. The former syntax\nis preferred, because the latter is identical to Robot Framework's\n[http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#named-argument-syntax|\nnamed argument syntax] and that can cause problems. Spaces around\nthe separator are ignored, so ``id:foo``, ``id: foo`` and ``id : foo``\nare all equivalent.\n\nLocator strategies that are supported by default are listed in the table\nbelow. In addition to them, it is possible to register `custom locators`.\n\n| = Strategy = |          = Match based on =         |         = Example =            |\n| id           | Element ``id``.                     | ``id:example``                 |\n| name         | ``name`` attribute.                 | ``name:example``               |\n| identifier   | Either ``id`` or ``name``.          | ``identifier:example``         |\n| class        | Element ``class``.                  | ``class:example``              |\n| tag          | Tag name.                           | ``tag:div``                    |\n| xpath        | XPath expression.                   | ``xpath://div[@id=\"example\"]`` |\n| css          | CSS selector.                       | ``css:div#example``            |\n| dom          | DOM expression.                     | ``dom:document.images[5]``     |\n| link         | Exact text a link has.              | ``link:The example``           |\n| partial link | Partial link text.                  | ``partial link:he ex``         |\n| sizzle       | Sizzle selector provided by jQuery. | ``sizzle:div.example``         |\n| jquery       | Same as the above.                  | ``jquery:div.example``         |\n| default      | Keyword specific default behavior.  | ``default:example``            |\n\nSee the `Default locator strategy` section below for more information\nabout how the default strategy works. Using the explicit ``default``\nprefix is only necessary if the locator value itself accidentally\nmatches some of the explicit strategies.\n\nDifferent locator strategies have different pros and cons. Using ids,\neither explicitly like ``id:foo`` or by using the `default locator\nstrategy` simply like ``foo``, is recommended when possible, because\nthe syntax is simple and locating elements by an id is fast for browsers.\nIf an element does not have an id or the id is not stable, other\nsolutions need to be used. If an element has a unique tag name or class,\nusing ``tag``, ``class`` or ``css`` strategy like ``tag:h1``,\n``class:example`` or ``css:h1.example`` is often an easy solution. In\nmore complex cases using XPath expressions is typically the best\napproach. They are very powerful but a downside is that they can also\nget complex.\n\nExamples:\n\n| `Click Element` | id:foo                      | # Element with id 'foo'. |\n| `Click Element` | css:div#foo h1              | # h1 element under div with id 'foo'. |\n| `Click Element` | xpath: //div[@id=\"foo\"]//h1 | # Same as the above using XPath, not CSS. |\n| `Click Element` | xpath: //*[contains(text(), \"example\")] | # Element containing text 'example'. |\n\n*NOTE:*\n\n- The ``strategy:value`` syntax is only supported by SeleniumLibrary 3.0\n  and newer.\n- Using the ``sizzle`` strategy or its alias ``jquery`` requires that\n  the system under test contains the jQuery library.\n- Prior to SeleniumLibrary 3.0, table related keywords only supported\n  ``xpath``, ``css`` and ``sizzle/jquery`` strategies.\n\n=== Implicit XPath strategy ===\n\nIf the locator starts with ``//`` or ``(//``, the locator is considered\nto be an XPath expression. In other words, using ``//div`` is equivalent\nto using explicit ``xpath://div``.\n\nExamples:\n\n| `Click Element` | //div[@id=\"foo\"]//h1 |\n| `Click Element` | (//div)[2]           |\n\nThe support for the ``(//`` prefix is new in SeleniumLibrary 3.0.\n\n== Using WebElements ==\n\nIn addition to specifying a locator as a string, it is possible to use\nSelenium's WebElement objects. This requires first getting a WebElement,\nfor example, by using the `Get WebElement` keyword.\n\n| ${elem} =       | `Get WebElement` | id:example |\n| `Click Element` | ${elem}          |            |\n\n== Custom locators ==\n\nIf more complex lookups are required than what is provided through the\ndefault locators, custom lookup strategies can be created. Using custom\nlocators is a two part process. First, create a keyword that returns\na WebElement that should be acted on:\n\n| Custom Locator Strategy | [Arguments] | ${browser} | ${strategy} | ${tag} | ${constraints} |\n|   | ${element}= | Execute Javascript | return window.document.getElementById('${criteria}'); |\n|   | [Return] | ${element} |\n\nThis keyword is a reimplementation of the basic functionality of the\n``id`` locator where ``${browser}`` is a reference to a WebDriver\ninstance and ``${strategy}`` is name of the locator strategy. To use\nthis locator it must first be registered by using the\n`Add Location Strategy` keyword:\n\n| `Add Location Strategy` | custom | Custom Locator Strategy |\n\nThe first argument of `Add Location Strategy` specifies the name of\nthe strategy and it must be unique. After registering the strategy,\nthe usage is the same as with other locators:\n\n| `Click Element` | custom:example |\n\nSee the `Add Location Strategy` keyword for more details.\n\n= Timeouts, waits and delays =\n\nThis section discusses different ways how to wait for elements to\nappear on web pages and to slow down execution speed otherwise.\nIt also explains the `time format` that can be used when setting various\ntimeouts, waits and delays.\n\n== Timeout ==\n\nSeleniumLibrary contains various keywords that have an optional\n``timeout`` argument that specifies how long these keywords should\nwait for certain events or actions. These keywords include, for example,\n``Wait ...`` keywords and keywords related to alerts.\n\nThe default timeout these keywords use can be set globally either by\nusing the `Set Selenium Timeout` keyword or with the ``timeout`` argument\nwhen `importing` the library. See `time format` below for supported\ntimeout syntax.\n\n== Implicit wait ==\n\nImplicit wait specifies the maximum time how long Selenium waits when\nsearching for elements. It can be set by using the `Set Selenium Implicit\nWait` keyword or with the ``implicit_wait`` argument when `importing`\nthe library. See [http://seleniumhq.org/docs/04_webdriver_advanced.html|\nSelenium documentation] for more information about this functionality.\n\nSee `time format` below for supported syntax.\n\n== Selenium speed ==\n\nSelenium execution speed can be slowed down globally by using `Set\nSelenium speed` keyword. This functionality is designed to be used for\ndemonstrating or debugging purposes. Using it to make sure that elements\nappear on a page is not a good idea, and the above explained timeouts\nand waits should be used instead.\n\nSee `time format` below for supported syntax.\n\n== Time format ==\n\nAll timeouts and waits can be given as numbers considered seconds\n(e.g. ``0.5`` or ``42``) or in Robot Framework's time syntax\n(e.g. ``1.5 seconds`` or ``1 min 30 s``). For more information about\nthe time syntax see the\n[http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#time-format|Robot Framework User Guide].\n\n= Run-on-failure functionality =\n\nSeleniumLibrary has a handy feature that it can automatically execute\na keyword if any of its own keywords fails. By default it uses the\n`Capture Page Screenshot` keyword, but this can be changed either by\nusing the `Register Keyword To Run On Failure` keyword or with the\n``run_on_failure`` argument when `importing` the library. It is\npossible to use any keyword from any imported library or resource file.\n\nThe run-on-failure functionality can be disabled by using a special\nvalue ``NOTHING`` or anything considered false (see `Boolean arguments`)\nsuch as ``NONE``.\n\n= Boolean arguments =\n\nSome keywords accept arguments that are handled as Boolean values true or\nfalse. If such an argument is given as a string, it is considered false if\nit is either empty or case-insensitively equal to ``false``, ``no`` or\n``none``. Other strings are considered true regardless their value, and\nother argument types are tested using same\n[https://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules as in Python].\n\nTrue examples:\n\n| `Set Screenshot Directory` | ${RESULTS} | persist=True    | # Strings are generally true.    |\n| `Set Screenshot Directory` | ${RESULTS} | persist=yes     | # Same as the above.             |\n| `Set Screenshot Directory` | ${RESULTS} | persist=${TRUE} | # Python True is true.           |\n| `Set Screenshot Directory` | ${RESULTS} | persist=${42}   | # Numbers other than 0 are true. |\n\nFalse examples:\n\n| `Set Screenshot Directory` | ${RESULTS} | persist=False    | # String false is false.        |\n| `Set Screenshot Directory` | ${RESULTS} | persist=no       | # Also string no is false.      |\n| `Set Screenshot Directory` | ${RESULTS} | persist=NONE     | # String NONE is false.         |\n| `Set Screenshot Directory` | ${RESULTS} | persist=${EMPTY} | # Empty string is false.        |\n| `Set Screenshot Directory` | ${RESULTS} | persist=${FALSE} | # Python False is false.        |\n| `Set Screenshot Directory` | ${RESULTS} | persist=${NONE}  | # Python None is false.         |\n\nNote that prior to SeleniumLibrary 3.0, all non-empty strings, including\n``false``, ``no`` and ``none``, were considered true.</doc>\n<init>\n<arguments>\n<arg>timeout=5.0</arg>\n<arg>implicit_wait=0.0</arg>\n<arg>run_on_failure=Capture Page Screenshot</arg>\n<arg>screenshot_root_directory=None</arg>\n</arguments>\n<doc>SeleniumLibrary can be imported with several optional arguments.\n\n- ``timeout``:\n  Default value for `timeouts` used with ``Wait ...`` keywords.\n- ``implicit_wait``:\n  Default value for `implicit wait` used when locating elements.\n- ``run_on_failure``:\n  Default action for the `run-on-failure functionality`.\n- ``screenshot_root_directory``:\n  Location where possible screenshots are created. If not given,\n  the directory where the log file is written is used.</doc>\n<tags>\n</tags>\n</init>\n<kw name=\"Add Cookie\">\n<arguments>\n<arg>name</arg>\n<arg>value</arg>\n<arg>path=None</arg>\n<arg>domain=None</arg>\n<arg>secure=None</arg>\n<arg>expiry=None</arg>\n</arguments>\n<doc>Adds a cookie to your current session.\n\n``name`` and ``value`` are required, ``path``, ``domain``, ``secure``\nand ``expiry`` are optional.  Expiry supports the same formats as\nthe [http://robotframework.org/robotframework/latest/libraries/DateTime.html|DateTime]\nlibrary or an epoch time stamp.\n\nExample:\n| `Add Cookie` | foo | bar |                            |\n| `Add Cookie` | foo | bar | domain=example.com         |\n| `Add Cookie` | foo | bar | expiry=2027-09-28 16:21:35 | # Expiry as timestamp.     |\n| `Add Cookie` | foo | bar | expiry=1822137695          | # Expiry as epoch seconds. |\n\nPrior to SeleniumLibrary 3.0 setting expiry did not work.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Add Location Strategy\">\n<arguments>\n<arg>strategy_name</arg>\n<arg>strategy_keyword</arg>\n<arg>persist=False</arg>\n</arguments>\n<doc>Adds a custom location strategy.\n\nSee `Custom locators` for information how to create and use\ncustom strategies. `Remove Location Strategy` can be used to\nremove a registered strategy.\n\nLocation strategies are automatically removed after leaving the\ncurrent scope by default. Setting ``persist`` to a true value (see\n`Boolean arguments`) will cause the location strategy to stay\nregistered throughout the life of the test.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Alert Should Be Present\">\n<arguments>\n<arg>text=</arg>\n<arg>action=ACCEPT</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>Verifies that an alert is present and, by default, accepts it.\n\nFails if no alert is present. If ``text`` is a non-empty string,\nthen it is used to verify alert's message. The alert is accepted\nby default, but that behavior can be controlled by using the\n``action`` argument same way as with `Handle Alert`.\n\n``timeout`` specifies how long to wait for the alert to appear.\nIf it is not given, the global default `timeout` is used instead.\n\n``action`` and ``timeout`` arguments are new in SeleniumLibrary 3.0.\nIn earlier versions the alert was always accepted and timeout was\nhard coded to one second.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Alert Should Not Be Present\">\n<arguments>\n<arg>action=ACCEPT</arg>\n<arg>timeout=0</arg>\n</arguments>\n<doc>Verifies that no alert is present.\n\nIf the alert actually exists, the ``action`` argument determines\nhow it should be handled. By default the alert is accepted, but\nit can be also dismissed or left open the same way as with the\n`Handle Alert` keyword.\n\n``timeout`` specifies how long to wait for the alert to appear.\nBy default the alert is not waited at all, but a custom time can\nbe given if alert may be delayed. See the `time format` section\nfor information about the syntax.\n\nNew in SeleniumLibrary 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Assign Id To Element\">\n<arguments>\n<arg>locator</arg>\n<arg>id</arg>\n</arguments>\n<doc>Assigns temporary ``id`` to element specified by ``locator``.\n\nThis is mainly useful if the locator is complicated and/or slow XPath\nexpression and it is needed multiple times. Identifier expires when\nthe page is reloaded.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nExample:\n| `Assign ID to Element` | //ul[@class='example' and ./li[contains(., 'Stuff')]] | my id |\n| `Page Should Contain Element` | my id |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Capture Page Screenshot\">\n<arguments>\n<arg>filename=selenium-screenshot-{index}.png</arg>\n</arguments>\n<doc>Takes screenshot of the current page and embeds it into log file.\n\n``filename`` argument specifies the name of the file to write the\nscreenshot into. The directory where screenshots are saved can be\nset when `importing` the library or by using the `Set Screenshot\nDirectory` keyword. If the directory is not configured, screenshots\nare saved to the same directory where Robot Framework's log file is\nwritten.\n\nStarting from SeleniumLibrary 1.8, if ``filename`` contains marker\n``{index}``, it will be automatically replaced with unique running\nindex preventing files to be overwritten. Indices start from 1,\nand how they are represented can be customized using Python's\n[https://docs.python.org/2/library/string.html#formatstrings|\nformat string syntax].\n\nAn absolute path to the created screenshot file is returned.\n\nExamples:\n| `Capture Page Screenshot` |                                        |\n| `File Should Exist`       | ${OUTPUTDIR}/selenium-screenshot-1.png |\n| ${path} =                 | `Capture Page Screenshot`              |\n| `File Should Exist`       | ${OUTPUTDIR}/selenium-screenshot-2.png |\n| `File Should Exist`       | ${path}                                |\n| `Capture Page Screenshot` | custom_name.png                        |\n| `File Should Exist`       | ${OUTPUTDIR}/custom_name.png           |\n| `Capture Page Screenshot` | custom_with_index_{index}.png          |\n| `File Should Exist`       | ${OUTPUTDIR}/custom_with_index_1.png   |\n| `Capture Page Screenshot` | formatted_index_{index:03}.png         |\n| `File Should Exist`       | ${OUTPUTDIR}/formatted_index_001.png   |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Checkbox Should Be Selected\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Verifies checkbox ``locator`` is selected/checked.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Checkbox Should Not Be Selected\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Verifies checkbox ``locator`` is not selected/checked.\n\nSee the `Locating elements` section for details about the locator\nsyntax..</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Choose Cancel On Next Confirmation\">\n<arguments>\n</arguments>\n<doc>Deprecated. Use `Handle Alert` directly instead.\n\nIn versions prior to SeleniumLibrary 3.0, the alert handling\napproach needed to be set separately before using the `Confirm\nAction` keyword. New `Handle Alert` keyword accepts the action how\nto handle the alert as a normal argument and should be used instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Choose File\">\n<arguments>\n<arg>locator</arg>\n<arg>file_path</arg>\n</arguments>\n<doc>Inputs the ``file_path`` into file input field ``locator``.\n\nThis keyword is most often used to input files into upload forms.\nThe file specified with ``file_path`` must be available on machine\nwhere tests are executed.\n\nExample:\n| `Choose File` | my_upload_field | ${CURDIR}/trades.csv |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Choose Ok On Next Confirmation\">\n<arguments>\n</arguments>\n<doc>Deprecated. Use `Handle Alert` directly instead.\n\nIn versions prior to SeleniumLibrary 3.0, the alert handling\napproach needed to be set separately before using the `Confirm\nAction` keyword. New `Handle Alert` keyword accepts the action how\nto handle the alert as a normal argument and should be used instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Clear Element Text\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Clears the value of text entry element identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Click Button\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Clicks button identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, buttons are\nsearched using ``id``, ``name`` and ``value``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Click Element\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Click element identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Click Element At Coordinates\">\n<arguments>\n<arg>locator</arg>\n<arg>xoffset</arg>\n<arg>yoffset</arg>\n</arguments>\n<doc>Click element ``locator`` at ``xoffset/yoffset``.\n\nCursor is moved and the center of the element and x/y coordinates are\ncalculated from that point.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Click Image\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Clicks an image identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, images are searched\nusing ``id``, ``name``, ``src`` and ``alt``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Click Link\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Clicks a link identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, links are searched\nusing ``id``, ``name``, ``href`` and the link text.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Close All Browsers\">\n<arguments>\n</arguments>\n<doc>Closes all open browsers and resets the browser cache.\n\nAfter this keyword new indexes returned from `Open Browser` keyword\nare reset to 1.\n\nThis keyword should be used in test or suite teardown to make sure\nall browsers are closed.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Close Browser\">\n<arguments>\n</arguments>\n<doc>Closes the current browser.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Close Window\">\n<arguments>\n</arguments>\n<doc>Closes currently opened pop-up window.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Confirm Action\">\n<arguments>\n</arguments>\n<doc>Deprecated. Use `Handle Alert` instead.\n\nBy default accepts an alert, but this behavior can be altered\nwith `Choose Cancel On Next Confirmation` and `Choose Ok On Next\nConfirmation` keywords. New `Handle Alert` keyword accepts the action\nhow to handle the alert as a normal argument and should be used\ninstead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Create Webdriver\">\n<arguments>\n<arg>driver_name</arg>\n<arg>alias=None</arg>\n<arg>kwargs={}</arg>\n<arg>**init_kwargs</arg>\n</arguments>\n<doc>Creates an instance of Selenium WebDriver.\n\nLike `Open Browser`, but allows passing arguments to the created\nWebDriver instance directly. This keyword should only be used if\nfunctionality provided by `Open Browser` is not adequate.\n\n``driver_name`` must be an WebDriver implementation name like Firefox,\nChrome, Ie, Opera, Safari, PhantomJS, or Remote.\n\nThe initialized WebDriver can be configured either with a Python\ndictionary ``kwargs`` or by using keyword arguments ``**init_kwargs``.\nThese arguments are passed directly to WebDriver without any\nprocessing. See [https://seleniumhq.github.io/selenium/docs/api/py/api.html|\nSelenium API documentation] for details about the supported arguments.\n\nExamples:\n| # Use proxy with Firefox   |                |                                           |                         |\n| ${proxy}=                  | `Evaluate`     | sys.modules['selenium.webdriver'].Proxy() | sys, selenium.webdriver |\n| ${proxy.http_proxy}=       | `Set Variable` | localhost:8888                            |                         |\n| `Create Webdriver`         | Firefox        | proxy=${proxy}                            |                         |\n| # Use proxy with PhantomJS |                |                                           |                         |\n| ${service args}=           | `Create List`  | --proxy=192.168.132.104:8888              |                         |\n| `Create Webdriver`         | PhantomJS      | service_args=${service args}              |                         |\n\nReturns the index of this browser instance which can be used later to\nswitch back to it. Index starts from 1 and is reset back to it when\n`Close All Browsers` keyword is used. See `Switch Browser` for an\nexample.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Current Frame Contains\">\n<arguments>\n<arg>text</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Deprecated. Use `Current Frame Should Contain` instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Current Frame Should Contain\">\n<arguments>\n<arg>text</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that current frame contains ``text``.\n\nSee `Page Should Contain` for explanation about the ``loglevel``\nargument.\n\nPrior to SeleniumLibrary 3.0 this keyword was named\n`Current Frame Contains`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Current Frame Should Not Contain\">\n<arguments>\n<arg>text</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that current frame does not contains ``text``.\n\nSee `Page Should Contain` for explanation about the ``loglevel``\nargument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Delete All Cookies\">\n<arguments>\n</arguments>\n<doc>Deletes all cookies.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Delete Cookie\">\n<arguments>\n<arg>name</arg>\n</arguments>\n<doc>Deletes cookie matching ``name``.\n\nIf the cookie is not found, nothing happens.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Dismiss Alert\">\n<arguments>\n<arg>accept=True</arg>\n</arguments>\n<doc>Deprecated. Use `Handle Alert` instead.\n\nContrary to its name, this keyword accepts the alert by default\n(i.e. presses ``Ok``). ``accept`` can be set to a false value\nto dismiss the alert (i.e. to press ``Cancel``).\n\n`Handle Alert` has better support for controlling should the alert\nbe accepted, dismissed, or left open.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Double Click Element\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Double click element identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Drag And Drop\">\n<arguments>\n<arg>locator</arg>\n<arg>target</arg>\n</arguments>\n<doc>Drags element identified by ``locator`` into ``target`` element.\n\nThe ``locator`` argument is the locator of the dragged element\nand the ``target`` is the locator of the target. See the\n`Locating elements` section for details about the locator syntax.\n\nExample:\n| `Drag And Drop` | css:div#element | css:div.target |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Drag And Drop By Offset\">\n<arguments>\n<arg>locator</arg>\n<arg>xoffset</arg>\n<arg>yoffset</arg>\n</arguments>\n<doc>Drags element identified with ``locator`` by ``xoffset/yoffset``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nElement will be moved by ``xoffset`` and ``yoffset``, each of which\nis a negative or positive number specifying the offset.\n\nExample:\n| `Drag And Drop By Offset` | myElem | 50 | -35 | # Move myElem 50px right and 35px down |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Be Disabled\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Verifies that element identified with ``locator`` is disabled.\n\nThis keyword considers also elements that are read-only to be\ndisabled.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Be Enabled\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Verifies that element identified with ``locator`` is enabled.\n\nThis keyword considers also elements that are read-only to be\ndisabled.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Be Focused\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Verifies that element identified with ``locator`` is focused.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nNew in SeleniumLibrary 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Be Visible\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies that the element identified by ``locator`` is visible.\n\nHerein, visible means that the element is logically visible, not\noptically visible in the current browser viewport. For example,\nan element that carries ``display:none`` is not logically visible,\nso using this keyword on that element would fail.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nThe ``message`` argument can be used to override the default error\nmessage.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Contain\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>message=None</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Verifies that element ``locator`` contains text ``expected``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nThe ``message`` argument can be used to override the default error\nmessage.\n\nThe ``ignore_case`` argument can be set to True to compare case\ninsensitive, default is False. New in SeleniumLibrary 3.1.\n\n``ignore_case`` argument new in SeleniumLibrary 3.1.\n\nUse `Element Text Should Be` if you want to match the exact text,\nnot a substring.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Not Be Visible\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies that the element identified by ``locator`` is NOT visible.\n\nPasses if element does not exists. See `Element Should Be Visible`\nfor more information about visibility and supported arguments.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Not Contain\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>message=None</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Verifies that element ``locator`` does not contains text ``expected``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nThe ``message`` argument can be used to override the default error\nmessage.\n\nThe ``ignore_case`` argument can be set to True to compare case\ninsensitive, default is False.\n\n``ignore_case`` argument new in SeleniumLibrary 3.1.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Text Should Be\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>message=None</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Verifies that element ``locator`` contains exact text ``expected``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nThe ``message`` argument can be used to override the default error\nmessage.\n\nThe ``ignore_case`` argument can be set to True to compare case\ninsensitive, default is False.\n\n``ignore_case`` argument new in SeleniumLibrary 3.1.\n\nUse `Element Should Contain` if a substring match is desired.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Text Should Not Be\">\n<arguments>\n<arg>locator</arg>\n<arg>not_expected</arg>\n<arg>message=None</arg>\n<arg>ignore_case=False</arg>\n</arguments>\n<doc>Verifies that element ``locator`` does not contain exact text ``not_expected``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nThe ``message`` argument can be used to override the default error\nmessage.\n\nThe ``ignore_case`` argument can be set to True to compare case\ninsensitive, default is False.\n\nNew in SeleniumLibrary 3.1.1</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Execute Async Javascript\">\n<arguments>\n<arg>*code</arg>\n</arguments>\n<doc>Executes asynchronous JavaScript code.\n\nSimilar to `Execute Javascript` except that scripts executed with\nthis keyword must explicitly signal they are finished by invoking the\nprovided callback. This callback is always injected into the executed\nfunction as the last argument.\n\nScripts must complete within the script timeout or this keyword will\nfail. See the `Timeouts` section for more information.\n\nExamples:\n| `Execute Async JavaScript` | var callback = arguments[arguments.length - 1]; window.setTimeout(callback, 2000); |\n| `Execute Async JavaScript` | ${CURDIR}/async_js_to_execute.js |\n| ${result} = | `Execute Async JavaScript`                      |\n| ...         | var callback = arguments[arguments.length - 1]; |\n| ...         | function answer(){callback(\"text\");};           |\n| ...         | window.setTimeout(answer, 2000);                |\n| `Should Be Equal` | ${result} | text |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Execute Javascript\">\n<arguments>\n<arg>*code</arg>\n</arguments>\n<doc>Executes the given JavaScript code.\n\n``code`` may contain multiple lines of code and may be divided into\nmultiple cells in the test data. In that case, the parts are\nconcatenated together without adding spaces.\n\nIf ``code`` is an absolute path to an existing file, the JavaScript\nto execute will be read from that file. Forward slashes work as\na path separator on all operating systems.\n\nThe JavaScript executes in the context of the currently selected\nframe or window as the body of an anonymous function. Use ``window``\nto refer to the window of your application and ``document`` to refer\nto the document object of the current frame or window, e.g.\n``document.getElementById('example')``.\n\nThis keyword returns whatever the executed JavaScript code returns.\nReturn values are converted to the appropriate Python types.\n\nExamples:\n| `Execute JavaScript` | window.myFunc('arg1', 'arg2') |\n| `Execute JavaScript` | ${CURDIR}/js_to_execute.js    |\n| ${sum} =             | `Execute JavaScript` | return 1 + 1; |\n| `Should Be Equal`    | ${sum}               | ${2}          |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Focus\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Deprecated. Use `Set Focus To Element` instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Frame Should Contain\">\n<arguments>\n<arg>locator</arg>\n<arg>text</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that frame identified by ``locator`` contains ``text``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nSee `Page Should Contain` for explanation about the ``loglevel``\nargument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Alert Message\">\n<arguments>\n<arg>dismiss=True</arg>\n</arguments>\n<doc>Deprecated. Use `Handle Alert` instead.\n\nReturns the message the alert has. Dismisses the alert by default\n(i.e. presses ``Cancel``) and setting ``dismiss`` to false leaves\nthe alert open. There is no support to accept the alert (i.e. to\npress ``Ok``).\n\n`Handle Alert` has better support for controlling should the alert\nbe accepted, dismissed, or left open.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get All Links\">\n<arguments>\n</arguments>\n<doc>Returns a list containing ids of all links found in current page.\n\nIf a link has no id, an empty string will be in the list instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Cookie\">\n<arguments>\n<arg>name</arg>\n</arguments>\n<doc>Returns information of cookie with ``name`` as an object.\n\nIf no cookie is found with ``name``, keyword fails. The cookie object\ncontains details about the cookie. Attributes available in the object\nare documented in the table below.\n\n| = Attribute = |             = Explanation =                                |\n| name          | The name of a cookie.                                      |\n| value         | Value of the cookie.                                       |\n| path          | Indicates a URL path, for example ``/``.                   |\n| domain        | The domain the cookie is visible to.                       |\n| secure        | When true, cookie is only used with HTTPS connections.     |\n| httpOnly      | When true, cookie is not accessible via JavaScript.        |\n| expiry        | Python datetime object indicating when the cookie expires. |\n\nSee the\n[https://w3c.github.io/webdriver/webdriver-spec.html#cookies|WebDriver specification]\nfor details about the cookie information.\nNotice that ``expiry`` is specified as a\n[https://docs.python.org/3/library/datetime.html#datetime.datetime|datetime object],\nnot as seconds since Unix Epoch like WebDriver natively does.\n\nExample:\n| `Add Cookie`      | foo             | bar |\n| ${cookie} =       | `Get Cookie`    | foo |\n| `Should Be Equal` | ${cookie.name}  | bar |\n| `Should Be Equal` | ${cookie.value} | foo |\n| `Should Be True`  | ${cookie.expiry.year} &gt; 2017 |\n\nNew in SeleniumLibrary 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Cookie Value\">\n<arguments>\n<arg>name</arg>\n</arguments>\n<doc>Deprecated. Use `Get Cookie` instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Cookies\">\n<arguments>\n</arguments>\n<doc>Returns all cookies of the current page.\n\nThe cookie information is returned as a single string in format\n``name1=value1; name2=value2; name3=value3``. It can be used,\nfor example, for logging purposes or in headers when sending\nHTTP requests.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Element Attribute\">\n<arguments>\n<arg>locator</arg>\n<arg>attribute=None</arg>\n</arguments>\n<doc>Returns value of ``attribute`` from element ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nExample:\n| ${id}= | `Get Element Attribute` | css:h1 | id |\n\nPassing attribute name as part of the ``locator`` is deprecated\nsince SeleniumLibrary 3.0. The explicit ``attribute`` argument\nshould be used instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Element Count\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns number of elements matching ``locator``.\n\nIf you wish to assert the number of matching elements, use\n`Page Should Contain Element` with ``limit`` argument. Keyword will\nalways return an integer.\n\nExample:\n| ${count} =       | `Get Element Count` | name:div_name  |\n| `Should Be True` | ${count} &gt; 2        |                |\n\nNew in SeleniumLibrary 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Element Size\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns width and height of element identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nBoth width and height are returned as integers.\n\nExample:\n| ${width} | ${height} = | `Get Element Size` | css:div#container |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Horizontal Position\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns horizontal position of element identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nThe position is returned in pixels off the left side of the page,\nas an integer.\n\nSee also `Get Vertical Position`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get List Items\">\n<arguments>\n<arg>locator</arg>\n<arg>values=False</arg>\n</arguments>\n<doc>Returns all labels or values of selection list ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nReturns visible labels by default, but values can be returned by\nsetting the ``values`` argument to a true value (see `Boolean\narguments`).\n\nExample:\n| ${labels} = | `Get List Items` | mylist              |             |\n| ${values} = | `Get List Items` | css:#example select | values=True |\n\nSupport to return values is new in SeleniumLibrary 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Location\">\n<arguments>\n</arguments>\n<doc>Returns the current browser URL.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Locations\">\n<arguments>\n</arguments>\n<doc>Returns and logs URLs of all known browser windows.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Matching Xpath Count\">\n<arguments>\n<arg>xpath</arg>\n<arg>return_str=True</arg>\n</arguments>\n<doc>Deprecated. Use `Get Element Count` instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Selected List Label\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns label of selected option from selection list ``locator``.\n\nIf there are multiple selected options, label of the first option\nis returned.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Selected List Labels\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns labels of selected options from selection list ``locator``.\n\nStarting from SeleniumLibrary 3.0, returns an empty list if there\nare no selections. In earlier versions this caused an error.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Selected List Value\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns value of selected option from selection list ``locator``.\n\nIf there are multiple selected options, value of the first option\nis returned.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Selected List Values\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns values of selected options from selection list ``locator``.\n\nStarting from SeleniumLibrary 3.0, returns an empty list if there\nare no selections. In earlier versions this caused an error.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Selenium Implicit Wait\">\n<arguments>\n</arguments>\n<doc>Gets the implicit wait value used by Selenium.\n\nThe value is returned as a human readable string like ``1 second``.\n\nSee the `Implicit wait` section above for more information.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Selenium Speed\">\n<arguments>\n</arguments>\n<doc>Gets the delay that is waited after each Selenium command.\n\nThe value is returned as a human readable string like ``1 second``.\n\nSee the `Selenium Speed` section above for more information.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Selenium Timeout\">\n<arguments>\n</arguments>\n<doc>Gets the timeout that is used by various keywords.\n\nThe value is returned as a human readable string like ``1 second``.\n\nSee the `Timeout` section above for more information.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Source\">\n<arguments>\n</arguments>\n<doc>Returns the entire HTML source of the current page or frame.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Table Cell\">\n<arguments>\n<arg>locator</arg>\n<arg>row</arg>\n<arg>column</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Returns contents of table cell.\n\nThe table is located using the ``locator`` argument and its cell\nfound using ``row`` and ``column``. See the `Locating elements`\nsection for details about the locator syntax.\n\nBoth row and column indexes start from 1, and header and footer\nrows are included in the count. It is possible to refer to rows\nand columns from the end by using negative indexes so that -1\nis the last row/column, -2 is the second last, and so on.\n\nAll ``&lt;th&gt;`` and ``&lt;td&gt;`` elements anywhere in the table are\nconsidered to be cells.\n\nSee `Page Should Contain` for explanation about the ``loglevel``\nargument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Text\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns the text value of element identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Title\">\n<arguments>\n</arguments>\n<doc>Returns the title of current page.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Value\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns the value attribute of element identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Vertical Position\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns vertical position of element identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nThe position is returned in pixels off the top of the page,\nas an integer.\n\nSee also `Get Horizontal Position`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get WebElement\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns the first WebElement matching the given ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get WebElements\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Returns list of WebElement objects matching the ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nStarting from SeleniumLibrary 3.0, the keyword returns an empty\nlist if there are no matching elements. In previous releases the\nkeyword failed in this case.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Window Handles\">\n<arguments>\n</arguments>\n<doc>Return all current window handles as a list.\n\nCan be used as a list of windows to exclude with `Select Window`.\n\nPrior to SeleniumLibrary 3.0, this keyword was named `List Windows`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Window Identifiers\">\n<arguments>\n</arguments>\n<doc>Returns and logs id attributes of all known browser windows.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Window Names\">\n<arguments>\n</arguments>\n<doc>Returns and logs names of all known browser windows.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Window Position\">\n<arguments>\n</arguments>\n<doc>Returns current window position.\n\nPosition is relative to the top left corner of the screen. Returned\nvalues are integers. See also `Set Window Position`.\n\nExample:\n| ${x} | ${y}= | `Get Window Position` |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Window Size\">\n<arguments>\n</arguments>\n<doc>Returns current window width and height as integers.\n\nSee also `Set Window Size`.\n\nExample:\n| ${width} | ${height}= | `Get Window Size` |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Window Titles\">\n<arguments>\n</arguments>\n<doc>Returns and logs titles of all known browser windows.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Go Back\">\n<arguments>\n</arguments>\n<doc>Simulates the user clicking the back button on their browser.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Go To\">\n<arguments>\n<arg>url</arg>\n</arguments>\n<doc>Navigates the active browser instance to the provided ``url``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Handle Alert\">\n<arguments>\n<arg>action=ACCEPT</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>Handles the current alert and returns its message.\n\nBy default the alert is accepted, but this can be controlled\nwith the ``action`` argument that supports the following\ncase-insensitive values:\n\n- ``ACCEPT``: Accept the alert i.e. press ``Ok``. Default.\n- ``DISMISS``: Dismiss the alert i.e. press ``Cancel``.\n- ``LEAVE``: Leave the alert open.\n\nThe ``timeout`` argument specifies how long to wait for the alert\nto appear. If it is not given, the global default `timeout` is used\ninstead.\n\nExamples:\n| Handle Alert |                |       | # Accept alert.  |\n| Handle Alert | action=DISMISS |       | # Dismiss alert. |\n| Handle Alert | timeout=10 s   |       | # Use custom timeout and accept alert.  |\n| Handle Alert | DISMISS        | 1 min | # Use custom timeout and dismiss alert. |\n| ${message} = | Handle Alert   |       | # Accept alert and get its message.     |\n| ${message} = | Handle Alert   | LEAVE | # Leave alert open and get its message. |\n\nNew in SeleniumLibrary 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Input Password\">\n<arguments>\n<arg>locator</arg>\n<arg>password</arg>\n</arguments>\n<doc>Types the given password into text field identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nDifference compared to `Input Text` is that this keyword does not\nlog the given password on the INFO level. Notice that if you use\nthe keyword like\n\n| Input Password | password_field | password |\n\nthe password is shown as a normal keyword argument. A way to avoid\nthat is using variables like\n\n| Input Password | password_field | ${PASSWORD} |\n\nNotice also that SeleniumLibrary logs all the communication with\nbrowser drivers using the DEBUG level, and the actual password can\nbe seen there. Additionally Robot Framework logs all arguments using\nthe TRACE level. Tests must thus not be executed using level below\nINFO if password should not be logged in any format.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Input Text\">\n<arguments>\n<arg>locator</arg>\n<arg>text</arg>\n</arguments>\n<doc>Types the given ``text`` into text field identified by ``locator``.\n\nUse `Input Password` if you do not want the given ``text`` to be\nlogged.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Input Text Into Alert\">\n<arguments>\n<arg>text</arg>\n<arg>action=ACCEPT</arg>\n<arg>timeout=None</arg>\n</arguments>\n<doc>Types the given ``text`` into an input field in an alert.\n\nThe alert is accepted by default, but that behavior can be controlled\nby using the ``action`` argument same way as with `Handle Alert`.\n\n``timeout`` specifies how long to wait for the alert to appear.\nIf it is not given, the global default `timeout` is used instead.\n\nNew in SeleniumLibrary 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Input Text Into Prompt\">\n<arguments>\n<arg>text</arg>\n</arguments>\n<doc>Deprecated. Use `Input Text Into Alert` instead.\n\nTypes the given ``text`` into an input field in an alert.\nLeaves the alert open.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Selection Should Be\">\n<arguments>\n<arg>locator</arg>\n<arg>*expected</arg>\n</arguments>\n<doc>Verifies selection list ``locator`` has ``expected`` options selected.\n\nIt is possible to give expected options both as visible labels and\nas values. Starting from SeleniumLibrary 3.0, mixing labels and\nvalues is not possible. Order of the selected options is not\nvalidated.\n\nIf no expected options are given, validates that the list has\nno selections. A more explicit alternative is using `List Should\nHave No Selections`.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nExamples:\n| `List Selection Should Be` | gender    | Female          |        |\n| `List Selection Should Be` | interests | Test Automation | Python |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Should Have No Selections\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Verifies selection list ``locator`` has no options selected.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"List Windows\">\n<arguments>\n</arguments>\n<doc>Deprecated. Use `Get Window Handles` instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Location Should Be\">\n<arguments>\n<arg>url</arg>\n</arguments>\n<doc>Verifies that current URL is exactly ``url``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Location Should Contain\">\n<arguments>\n<arg>expected</arg>\n</arguments>\n<doc>Verifies that current URL contains ``expected``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Locator Should Match X Times\">\n<arguments>\n<arg>locator</arg>\n<arg>x</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Deprecated, use `Page Should Contain Element` with ``limit`` argument instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log Location\">\n<arguments>\n</arguments>\n<doc>Logs and returns the current URL.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log Source\">\n<arguments>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Logs and returns the HTML source of the current page or frame.\n\nThe ``loglevel`` argument defines the used log level. Valid log\nlevels are ``WARN``, ``INFO`` (default), ``DEBUG``, and ``NONE``\n(no logging).</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log Title\">\n<arguments>\n</arguments>\n<doc>Logs and returns the title of current page.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Maximize Browser Window\">\n<arguments>\n</arguments>\n<doc>Maximizes current browser window.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Mouse Down\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Simulates pressing the left mouse button on the element ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nThe element is pressed without releasing the mouse button.\n\nSee also the more specific keywords `Mouse Down On Image` and\n`Mouse Down On Link`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Mouse Down On Image\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Simulates a mouse down event on an image identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, images are searched\nusing ``id``, ``name``, ``src`` and ``alt``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Mouse Down On Link\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Simulates a mouse down event on a link identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, links are searched\nusing ``id``, ``name``, ``href`` and the link text.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Mouse Out\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Simulates moving mouse away from the element ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Mouse Over\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Simulates hovering mouse over the element ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Mouse Up\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Simulates releasing the left mouse button on the element ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Open Browser\">\n<arguments>\n<arg>url</arg>\n<arg>browser=firefox</arg>\n<arg>alias=None</arg>\n<arg>remote_url=False</arg>\n<arg>desired_capabilities=None</arg>\n<arg>ff_profile_dir=None</arg>\n</arguments>\n<doc>Opens a new browser instance to the given ``url``.\n\nThe ``browser`` argument specifies which browser to use, and the\nsupported browser are listed in the table below. The browser names\nare case-insensitive and some browsers have multiple supported names.\n\n|    = Browser =    |        = Name(s) =       |\n| Firefox           | firefox, ff              |\n| Google Chrome     | googlechrome, chrome, gc |\n| Headless Firefox  | headlessfirefox          |\n| Headless Chrome   | headlesschrome           |\n| Internet Explorer | internetexplorer, ie     |\n| Edge              | edge                     |\n| Safari            | safari                   |\n| Opera             | opera                    |\n| Android           | android                  |\n| Iphone            | iphone                   |\n| PhantomJS         | phantomjs                |\n| HTMLUnit          | htmlunit                 |\n| HTMLUnit with Javascript | htmlunitwithjs    |\n\nTo be able to actually use one of these browsers, you need to have\na matching Selenium browser driver available. See the\n[https://github.com/robotframework/SeleniumLibrary#browser-drivers|\nproject documentation] for more details. Headless Firefox and\nHeadless Chrome are new additions in SeleniumLibrary 3.1.0\nand require Selenium 3.8.0 or newer.\n\nOptional ``alias`` is an alias given for this browser instance and\nit can be used for switching between browsers. An alternative\napproach for switching is using an index returned by this keyword.\nThese indices start from 1, are incremented when new browsers are\nopened, and reset back to 1 when `Close All Browsers` is called.\nSee `Switch Browser` for more information and examples.\n\nOptional ``remote_url`` is the URL for a\n[https://github.com/SeleniumHQ/selenium/wiki/Grid2|Selenium Grid].\n\nOptional ``desired_capabilities`` can be used to configure, for example,\nlogging preferences for a browser or a browser and operating system\nwhen using [http://saucelabs.com|Sauce Labs]. Desired capabilities can\nbe given either as a Python dictionary or as a string in format\n``key1:value1,key2:value2``.\n[https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities|\nSelenium documentation] lists possible capabilities that can be\nenabled.\n\nOptional ``ff_profile_dir`` is the path to the Firefox profile\ndirectory if you wish to overwrite the default profile Selenium\nuses. Notice that prior to SeleniumLibrary 3.0, the library\ncontained its own profile that was used by default.\n\nExamples:\n| `Open Browser` | http://example.com | Chrome  |\n| `Open Browser` | http://example.com | Firefox | alias=Firefox |\n| `Open Browser` | http://example.com | Edge    | remote_url=http://127.0.0.1:4444/wd/hub |\n\nIf the provided configuration options are not enough, it is possible\nto use `Create Webdriver` to customize browser initialization even\nmore.\n\nApplying ``desired_capabilities`` argument also for local browser is\nnew in SeleniumLibrary 3.1.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Open Context Menu\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Opens context menu on element identified by ``locator``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Contain\">\n<arguments>\n<arg>text</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that current page contains ``text``.\n\nIf this keyword fails, it automatically logs the page source\nusing the log level specified with the optional ``loglevel``\nargument. Valid log levels are ``DEBUG``, ``INFO`` (default),\n``WARN``, and ``NONE``. If the log level is ``NONE`` or below\nthe current active log level the source will not be logged.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Contain Button\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies button ``locator`` is found from current page.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, buttons are\nsearched using ``id``, ``name`` and ``value``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Contain Checkbox\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies checkbox ``locator`` is found from current page.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Contain Element\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n<arg>limit=None</arg>\n</arguments>\n<doc>Verifies that element ``locator`` is found on the current page.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nThe ``message`` argument can be used to override the default error\nmessage.\n\nThe ``limit`` argument can used to define how many elements the\npage should contain. When ``limit`` is ``None`` (default) page can\ncontain one or more elements. When limit is a number, page must\ncontain same number of elements.\n\nSee `Page Should Contain` for explanation about the ``loglevel``\nargument.\n\nExamples assumes that locator matches to two elements.\n| `Page Should Contain Element` | div_name | limit=1    | # Keyword fails.                  |\n| `Page Should Contain Element` | div_name | limit=2    | # Keyword passes.                 |\n| `Page Should Contain Element` | div_name | limit=none | # None is considered one or more. |\n| `Page Should Contain Element` | div_name |            | # Same as above.                  |\n\nThe ``limit`` argument is new in SeleniumLibrary 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Contain Image\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies image identified by ``locator`` is found from current page.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, images are searched\nusing ``id``, ``name``, ``src`` and ``alt``.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Contain Link\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies link identified by ``locator`` is found from current page.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, links are searched\nusing ``id``, ``name``, ``href`` and the link text.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Contain List\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies selection list ``locator`` is found from current page.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Contain Radio Button\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies radio button ``locator`` is found from current page.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, radio buttons are\nsearched using ``id``, ``name`` and ``value``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Contain Textfield\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies text field ``locator`` is found from current page.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Not Contain\">\n<arguments>\n<arg>text</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies the current page does not contain ``text``.\n\nSee `Page Should Contain` for explanation about the ``loglevel``\nargument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Not Contain Button\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies button ``locator`` is not found from current page.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, buttons are\nsearched using ``id``, ``name`` and ``value``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Not Contain Checkbox\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies checkbox ``locator`` is not found from current page.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Not Contain Element\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that element ``locator`` is found on the current page.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nSee `Page Should Contain` for explanation about ``message`` and\n``loglevel`` arguments.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Not Contain Image\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies image identified by ``locator`` is found from current page.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, images are searched\nusing ``id``, ``name``, ``src`` and ``alt``.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Not Contain Link\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies link identified by ``locator`` is not found from current page.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, links are searched\nusing ``id``, ``name``, ``href`` and the link text.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Not Contain List\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies selection list ``locator`` is not found from current page.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Not Contain Radio Button\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies radio button ``locator`` is not found from current page.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.\n\nSee the `Locating elements` section for details about the locator\nsyntax. When using the default locator strategy, radio buttons are\nsearched using ``id``, ``name`` and ``value``.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Page Should Not Contain Textfield\">\n<arguments>\n<arg>locator</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies text field ``locator`` is not found from current page.\n\nSee `Page Should Contain Element` for explanation about ``message``\nand ``loglevel`` arguments.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Press Key\">\n<arguments>\n<arg>locator</arg>\n<arg>key</arg>\n</arguments>\n<doc>Simulates user pressing key on element identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\n``key`` is either a single character, a string, or a numerical ASCII\ncode of the key lead by '\\\\'.\n\nExamples:\n| `Press Key` | text_field   | q     |\n| `Press Key` | text_field   | abcde |\n| `Press Key` | login_button | \\\\13  | # ASCII code for enter key |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Radio Button Should Be Set To\">\n<arguments>\n<arg>group_name</arg>\n<arg>value</arg>\n</arguments>\n<doc>Verifies radio button group ``group_name`` is set to ``value``.\n\n``group_name`` is the ``name`` of the radio button group.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Radio Button Should Not Be Selected\">\n<arguments>\n<arg>group_name</arg>\n</arguments>\n<doc>Verifies radio button group ``group_name`` has no selection.\n\n``group_name`` is the ``name`` of the radio button group.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Register Keyword To Run On Failure\">\n<arguments>\n<arg>keyword</arg>\n</arguments>\n<doc>Sets the keyword to execute when a SeleniumLibrary keyword fails.\n\n``keyword`` is the name of a keyword that will be executed if a\nSeleniumLibrary keyword fails. It is possible to use any available\nkeyword, including user keywords or keywords from other libraries,\nbut the keyword must not take any arguments.\n\nThe initial keyword to use is set when `importing` the library, and\nthe keyword that is used by default is `Capture Page Screenshot`.\nTaking a screenshot when something failed is a very useful\nfeature, but notice that it can slow down the execution.\n\nIt is possible to use string ``NOTHING`` or ``NONE``,\ncase-insensitively, as well as Python ``None`` to disable this\nfeature altogether.\n\nThis keyword returns the name of the previously registered\nfailure keyword or Python ``None`` if this functionality was\npreviously disabled. The return value can be always used to\nrestore the original value later.\n\nExample:\n| `Register Keyword To Run On Failure`  | Log Source |\n| ${previous kw}= | `Register Keyword To Run On Failure`  | NONE |\n| `Register Keyword To Run On Failure`  | ${previous kw} |\n\nChanges in SeleniumLibrary 3.0:\n- Possible to use string ``NONE`` or Python ``None`` to disable the\n  functionality.\n- Return Python ``None`` when the functionality was disabled earlier.\n  In previous versions special value ``No Keyword`` was returned and\n  it could not be used to restore the original state.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Reload Page\">\n<arguments>\n</arguments>\n<doc>Simulates user reloading page.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Location Strategy\">\n<arguments>\n<arg>strategy_name</arg>\n</arguments>\n<doc>Removes a previously added custom location strategy.\n\nSee `Custom locators` for information how to create and use\ncustom strategies.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Select All From List\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Selects all options from multi-selection list ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Select Checkbox\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Selects checkbox identified by ``locator``.\n\nDoes nothing if checkbox is already selected.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Select Frame\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Sets frame identified by ``locator`` as the current frame.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nWorks both with frames and iframes. Use `Unselect Frame` to cancel\nthe frame selection and return to the main frame.\n\nExample:\n| `Select Frame`   | top-frame | # Select frame with id or name 'top-frame'   |\n| `Click Link`     | example   | # Click link 'example' in the selected frame |\n| `Unselect Frame` |           | # Back to main frame.                        |\n| `Select Frame`   | //iframe[@name='xxx'] | # Select frame using xpath       |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Select From List\">\n<arguments>\n<arg>locator</arg>\n<arg>*options</arg>\n</arguments>\n<doc>Deprecated. Use `Select From List By Label/Value/Index` instead.\n\nThis keyword selects options based on labels or values, which makes\nit very complicated and slow. It has been deprecated in\nSeleniumLibrary 3.0, and dedicated keywords `Select From List By\nLabel`, `Select From List By Value` and `Select From List By Index`\nshould be used instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Select From List By Index\">\n<arguments>\n<arg>locator</arg>\n<arg>*indexes</arg>\n</arguments>\n<doc>Selects options from selection list ``locator`` by ``indexes``.\n\nIndexes of list options start from 0.\n\nIf more than one option is given for a single-selection list,\nthe last value will be selected. With multi-selection lists all\nspecified options are selected, but possible old selections are\nnot cleared.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Select From List By Label\">\n<arguments>\n<arg>locator</arg>\n<arg>*labels</arg>\n</arguments>\n<doc>Selects options from selection list ``locator`` by ``labels``.\n\nIf more than one option is given for a single-selection list,\nthe last value will be selected. With multi-selection lists all\nspecified options are selected, but possible old selections are\nnot cleared.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Select From List By Value\">\n<arguments>\n<arg>locator</arg>\n<arg>*values</arg>\n</arguments>\n<doc>Selects options from selection list ``locator`` by ``values``.\n\nIf more than one option is given for a single-selection list,\nthe last value will be selected. With multi-selection lists all\nspecified options are selected, but possible old selections are\nnot cleared.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Select Radio Button\">\n<arguments>\n<arg>group_name</arg>\n<arg>value</arg>\n</arguments>\n<doc>Sets radio button group ``group_name`` to ``value``.\n\nThe radio button to be selected is located by two arguments:\n- ``group_name`` is the name of the radio button group.\n- ``value`` is the ``id`` or ``value`` attribute of the actual\n  radio button.\n\nExamples:\n| `Select Radio Button` | size    | XL    |\n| `Select Radio Button` | contact | email |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Select Window\">\n<arguments>\n<arg>locator=MAIN</arg>\n</arguments>\n<doc>Selects browser window matching ``locator``.\n\nIf the window is found, all subsequent commands use the selected\nwindow, until this keyword is used again. If the window is not\nfound, this keyword fails. The previous window handle is returned,\nand can be used to return back to it later.\n\nNotice that in this context _window_ means a pop-up window opened\nwhen doing something on an existing window. It is not possible to\nselect windows opened with `Open Browser`, `Switch Browser` must\nbe used instead. Notice also that alerts should be handled with\n`Handle Alert` or other alert related keywords.\n\nThe ``locator`` can be specified using different strategies somewhat\nsimilarly as when `locating elements` on pages.\n\n- By default the ``locator`` is matched against window handle, name,\n  title, and URL. Matching is done in that order and the the first\n  matching window is selected.\n\n- The ``locator`` can specify an explicit strategy by using format\n  ``strategy:value`` (recommended) or ``strategy=value``. Supported\n  strategies are ``name``, ``title`` and ``url``, which match windows\n  using name, title, and URL, respectively. Additionally, ``default``\n  can be used to explicitly use the default strategy explained above.\n\n- If the ``locator`` is ``NEW`` (case-insensitive), the latest\n  opened window is selected. It is an error if this is the same\n  as the current window.\n\n- If the ``locator`` is ``MAIN`` (default, case-insensitive),\n  the main window is selected.\n\n- If the ``locator`` is ``CURRENT`` (case-insensitive), nothing is\n  done. This effectively just returns the current window handle.\n\n- If the ``locator`` is not a string, it is expected to be a list\n  of window handles _to exclude_. Such a list of excluded windows\n  can be get from `Get Window Handles` prior to doing an action that\n  opens a new window.\n\nExample:\n| `Click Link`      | popup1      |      | # Open new window |\n| `Select Window`   | example     |      | # Select window using default strategy |\n| `Title Should Be` | Pop-up 1    |      |\n| `Click Button`    | popup2      |      | # Open another window |\n| ${handle} = | `Select Window`   | NEW  | # Select latest opened window |\n| `Title Should Be` | Pop-up 2    |      |\n| `Select Window`   | ${handle}   |      | # Select window using handle |\n| `Title Should Be` | Pop-up 1    |      |\n| `Select Window`   | MAIN        |      | # Select the main window |\n| `Title Should Be` | Main        |      |\n| ${excludes} = | `Get Window Handles` | | # Get list of current windows |\n| `Click Link`      | popup3      |      | # Open one more window |\n| `Select Window`   | ${excludes} |      | # Select window using excludes |\n| `Title Should Be` | Pop-up 3    |      |\n\n*NOTE:*\n\n- The ``strategy:value`` syntax is only supported by SeleniumLibrary\n  3.0 and newer.\n- Earlier versions supported aliases ``None``, ``null`` and the\n  empty string for selecting the main window, and alias ``self``\n  for selecting the current window. These aliases were deprecated\n  in SeleniumLibrary 3.0.\n- Prior to SeleniumLibrary 3.0 matching windows by name, title\n  and URL was case-insensitive.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Browser Implicit Wait\">\n<arguments>\n<arg>value</arg>\n</arguments>\n<doc>Sets the implicit wait value used by Selenium.\n\nSame as `Set Selenium Implicit Wait` but only affects the current\nbrowser.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Focus To Element\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Sets focus to element identified by ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nPrior to SeleniumLibrary 3.0 this keyword was named `Focus`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Screenshot Directory\">\n<arguments>\n<arg>path</arg>\n<arg>persist=DEPRECATED</arg>\n</arguments>\n<doc>Sets the directory for captured screenshots.\n\n``path`` argument specifies the absolute path to a directory where\nthe screenshots should be written to. If the directory does not\nexist, it will be created. The directory can also be set when\n`importing` the library. If it is not configured anywhere,\nscreenshots are saved to the same directory where Robot Framework's\nlog file is written.\n\n``persist`` argument is deprecated and has no effect.\n\nThe previous value is returned and can be used to restore\nthe original value later if needed.\n\nDeprecating ``persist`` and returning the previous value are new\nin SeleniumLibrary 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Selenium Implicit Wait\">\n<arguments>\n<arg>value</arg>\n</arguments>\n<doc>Sets the implicit wait value used by Selenium.\n\nThe value can be given as a number that is considered to be\nseconds or as a human readable string like ``1 second``.\nThe previous value is returned and can be used to restore\nthe original value later if needed.\n\nThis keyword sets the implicit wait for all opened browsers.\nUse `Set Browser Implicit Wait` to set it only to the current\nbrowser.\n\nSee the `Implicit wait` section above for more information.\n\nExample:\n| ${orig wait} = | `Set Selenium Implicit Wait` | 10 seconds |\n| `Perform AJAX call that is slow` |\n| `Set Selenium Implicit Wait` | ${orig wait} |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Selenium Speed\">\n<arguments>\n<arg>value</arg>\n</arguments>\n<doc>Sets the delay that is waited after each Selenium command.\n\nThe value can be given as a number that is considered to be\nseconds or as a human readable string like ``1 second``.\nThe previous value is returned and can be used to restore\nthe original value later if needed.\n\nSee the `Selenium Speed` section above for more information.\n\nExample:\n| `Set Selenium Speed` | 0.5 seconds |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Selenium Timeout\">\n<arguments>\n<arg>value</arg>\n</arguments>\n<doc>Sets the timeout that is used by various keywords.\n\nThe value can be given as a number that is considered to be\nseconds or as a human readable string like ``1 second``.\nThe previous value is returned and can be used to restore\nthe original value later if needed.\n\nSee the `Timeout` section above for more information.\n\nExample:\n| ${orig timeout} = | `Set Selenium Timeout` | 15 seconds |\n| `Open page that loads slowly` |\n| `Set Selenium Timeout` | ${orig timeout} |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Window Position\">\n<arguments>\n<arg>x</arg>\n<arg>y</arg>\n</arguments>\n<doc>Sets window position using ``x`` and ``y`` coordinates.\n\nThe position is relative to the top left corner of the screen,\nbut some browsers exclude possible task bar set by the operating\nsystem from the calculation. The actual position may thus be\ndifferent with different browsers.\n\nValues can be given using strings containing numbers or by using\nactual numbers. See also `Get Window Position`.\n\nExample:\n| `Set Window Position` | 100 | 200 |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Window Size\">\n<arguments>\n<arg>width</arg>\n<arg>height</arg>\n</arguments>\n<doc>Sets current windows size to given ``width`` and ``height``.\n\nValues can be given using strings containing numbers or by using\nactual numbers. See also `Get Window Size`.\n\nBrowsers have a limit how small they can be set. Trying to set them\nsmaller will cause the actual size to be bigger than the requested\nsize.\n\nExample:\n| `Set Window Size` | 800 | 600 |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Simulate\">\n<arguments>\n<arg>locator</arg>\n<arg>event</arg>\n</arguments>\n<doc>Deprecated. Use `Simulate Event` instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Simulate Event\">\n<arguments>\n<arg>locator</arg>\n<arg>event</arg>\n</arguments>\n<doc>Simulates ``event`` on element identified by ``locator``.\n\nThis keyword is useful if element has ``OnEvent`` handler that\nneeds to be explicitly invoked.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nPrior to SeleniumLibrary 3.0 this keyword was named `Simulate`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Submit Form\">\n<arguments>\n<arg>locator=None</arg>\n</arguments>\n<doc>Submits a form identified by ``locator``.\n\nIf ``locator`` is not given, first form on the page is submitted.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Switch Browser\">\n<arguments>\n<arg>index_or_alias</arg>\n</arguments>\n<doc>Switches between active browsers using ``index_or_alias``.\n\nIndices are returned by the `Open Browser` keyword and aliases can\nbe given to it explicitly. Indices start from 1.\n\nExample:\n| `Open Browser`        | http://google.com | ff       |\n| `Location Should Be`  | http://google.com |          |\n| `Open Browser`        | http://yahoo.com  | ie       | alias=second |\n| `Location Should Be`  | http://yahoo.com  |          |\n| `Switch Browser`      | 1                 | # index  |\n| `Page Should Contain` | I'm feeling lucky |          |\n| `Switch Browser`      | second            | # alias  |\n| `Page Should Contain` | More Yahoo!       |          |\n| `Close All Browsers`  |                   |          |\n\nAbove example expects that there was no other open browsers when\nopening the first one because it used index ``1`` when switching to\nit later. If you are not sure about that, you can store the index\ninto a variable as below.\n\n| ${index} =         | `Open Browser` | http://google.com |\n| # Do something ... |                |                   |\n| `Switch Browser`   | ${index}       |                   |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Table Cell Should Contain\">\n<arguments>\n<arg>locator</arg>\n<arg>row</arg>\n<arg>column</arg>\n<arg>expected</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies table cell contains text ``expected``.\n\nSee `Get Table Cell` that this keyword uses internally for\nexplanation about accepted arguments.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Table Column Should Contain\">\n<arguments>\n<arg>locator</arg>\n<arg>column</arg>\n<arg>expected</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies table column contains text ``expected``.\n\nThe table is located using the ``locator`` argument and its column\nfound using ``column``. See the `Locating elements` section for\ndetails about the locator syntax.\n\nColumn indexes start from 1. It is possible to refer to columns\nfrom the end by using negative indexes so that -1 is the last column,\n-2 is the second last, and so on.\n\nIf a table contains cells that span multiple columns, those merged\ncells count as a single column.\n\nSee `Page Should Contain Element` for explanation about the\n``loglevel`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Table Footer Should Contain\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies table footer contains text ``expected``.\n\nAny ``&lt;td&gt;`` element inside ``&lt;tfoot&gt;`` element is considered to\nbe part of the footer.\n\nThe table is located using the ``locator`` argument. See the\n`Locating elements` section for details about the locator syntax.\n\nSee `Page Should Contain Element` for explanation about the\n``loglevel`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Table Header Should Contain\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies table header contains text ``expected``.\n\nAny ``&lt;th&gt;`` element anywhere in the table is considered to be\npart of the header.\n\nThe table is located using the ``locator`` argument. See the\n`Locating elements` section for details about the locator syntax.\n\nSee `Page Should Contain Element` for explanation about the\n``loglevel`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Table Row Should Contain\">\n<arguments>\n<arg>locator</arg>\n<arg>row</arg>\n<arg>expected</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies that table row contains text ``expected``.\n\nThe table is located using the ``locator`` argument and its column\nfound using ``column``. See the `Locating elements` section for\ndetails about the locator syntax.\n\nRow indexes start from 1. It is possible to refer to rows\nfrom the end by using negative indexes so that -1 is the last row,\n-2 is the second last, and so on.\n\nIf a table contains cells that span multiple rows, a match\nonly occurs for the uppermost row of those merged cells.\n\nSee `Page Should Contain Element` for explanation about the\n``loglevel`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Table Should Contain\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Verifies table contains text ``expected``.\n\nThe table is located using the ``locator`` argument. See the\n`Locating elements` section for details about the locator syntax.\n\nSee `Page Should Contain Element` for explanation about the\n``loglevel`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Textarea Should Contain\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies text area ``locator`` contains text ``expected``.\n\n``message`` can be used to override default error message.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Textarea Value Should Be\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies text area ``locator`` has exactly text ``expected``.\n\n``message`` can be used to override default error message.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Textfield Should Contain\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies text field ``locator`` contains text ``expected``.\n\n``message`` can be used to override the default error message.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Textfield Value Should Be\">\n<arguments>\n<arg>locator</arg>\n<arg>expected</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies text field ``locator`` has exactly text ``expected``.\n\n``message`` can be used to override default error message.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Title Should Be\">\n<arguments>\n<arg>title</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies that current page title equals ``title``.\n\nThe ``message`` argument can be used to override the default error\nmessage.\n\n``message`` argument is new in SeleniumLibrary 3.1.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Unselect All From List\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Unselects all options from multi-selection list ``locator``.\n\nSee the `Locating elements` section for details about the locator\nsyntax.\n\nNew in SeleniumLibrary 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Unselect Checkbox\">\n<arguments>\n<arg>locator</arg>\n</arguments>\n<doc>Removes selection of checkbox identified by ``locator``.\n\nDoes nothing if the checkbox is not selected.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Unselect Frame\">\n<arguments>\n</arguments>\n<doc>Sets the main frame as the current frame.\n\nIn practice cancels the previous `Select Frame` call.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Unselect From List\">\n<arguments>\n<arg>locator</arg>\n<arg>*items</arg>\n</arguments>\n<doc>Deprecated. Use `Unselect From List By Label/Value/Index` instead.\n\nThis keyword unselects options based on labels or values, which makes\nit very complicated and slow. It has been deprecated in\nSeleniumLibrary 3.0, and dedicated keywords `Unselect From List By\nLabel`, `Unselect From List By Value` and `Unselect From List By\nIndex` should be used instead.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Unselect From List By Index\">\n<arguments>\n<arg>locator</arg>\n<arg>*indexes</arg>\n</arguments>\n<doc>Unselects options from selection list ``locator`` by ``indexes``.\n\nIndexes of list options start from 0. This keyword works only with\nmulti-selection lists.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Unselect From List By Label\">\n<arguments>\n<arg>locator</arg>\n<arg>*labels</arg>\n</arguments>\n<doc>Unselects options from selection list ``locator`` by ``labels``.\n\nThis keyword works only with multi-selection lists.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Unselect From List By Value\">\n<arguments>\n<arg>locator</arg>\n<arg>*values</arg>\n</arguments>\n<doc>Unselects options from selection list ``locator`` by ``values``.\n\nThis keyword works only with multi-selection lists.\n\nSee the `Locating elements` section for details about the locator\nsyntax.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait For Condition\">\n<arguments>\n<arg>condition</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until ``condition`` is true or ``timeout`` expires.\n\nThe condition can be arbitrary JavaScript expression but it\nmust return a value to be evaluated. See `Execute JavaScript` for\ninformation about accessing content on pages.\n\nFails if the timeout expires before the condition becomes true. See\nthe `Timeouts` section for more information about using timeouts\nand their default value.\n\n``error`` can be used to override the default error message.\n\nExamples:\n| `Wait For Condition` | return document.title == \"New Title\" |\n| `Wait For Condition` | return jQuery.active == 0            |\n| `Wait For Condition` | style = document.querySelector('h1').style; return style.background == \"red\" &amp;&amp; style.color == \"white\" |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Element Contains\">\n<arguments>\n<arg>locator</arg>\n<arg>text</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until element ``locator`` contains ``text``.\n\nFails if ``timeout`` expires before the text appears. See\nthe `Timeouts` section for more information about using timeouts and\ntheir default value and the `Locating elements` section for details\nabout the locator syntax.\n\n``error`` can be used to override the default error message.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Element Does Not Contain\">\n<arguments>\n<arg>locator</arg>\n<arg>text</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until element ``locator`` does not contain ``text``.\n\nFails if ``timeout`` expires before the text disappears. See\nthe `Timeouts` section for more information about using timeouts and\ntheir default value and the `Locating elements` section for details\nabout the locator syntax.\n\n``error`` can be used to override the default error message.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Element Is Enabled\">\n<arguments>\n<arg>locator</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until element ``locator`` is enabled.\n\nElement is considered enabled if it is not disabled nor read-only.\n\nFails if ``timeout`` expires before the element is enabled. See\nthe `Timeouts` section for more information about using timeouts and\ntheir default value and the `Locating elements` section for details\nabout the locator syntax.\n\n``error`` can be used to override the default error message.\n\nConsidering read-only elements to be disabled is a new feature\nin SeleniumLibrary 3.0.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Element Is Not Visible\">\n<arguments>\n<arg>locator</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until element ``locator`` is not visible.\n\nFails if ``timeout`` expires before the element is not visible. See\nthe `Timeouts` section for more information about using timeouts and\ntheir default value and the `Locating elements` section for details\nabout the locator syntax.\n\n``error`` can be used to override the default error message.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Element Is Visible\">\n<arguments>\n<arg>locator</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until element ``locator`` is visible.\n\nFails if ``timeout`` expires before the element is visible. See\nthe `Timeouts` section for more information about using timeouts and\ntheir default value and the `Locating elements` section for details\nabout the locator syntax.\n\n``error`` can be used to override the default error message.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Page Contains\">\n<arguments>\n<arg>text</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until ``text`` appears on current page.\n\nFails if ``timeout`` expires before the text appears. See\nthe `Timeouts` section for more information about using timeouts\nand their default value.\n\n``error`` can be used to override the default error message.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Page Contains Element\">\n<arguments>\n<arg>locator</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until element ``locator`` appears on current page.\n\nFails if ``timeout`` expires before the element appears. See\nthe `Timeouts` section for more information about using timeouts and\ntheir default value and the `Locating elements` section for details\nabout the locator syntax.\n\n``error`` can be used to override the default error message.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Page Does Not Contain\">\n<arguments>\n<arg>text</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until ``text`` disappears from current page.\n\nFails if ``timeout`` expires before the text disappears. See\nthe `Timeouts` section for more information about using timeouts\nand their default value.\n\n``error`` can be used to override the default error message.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Wait Until Page Does Not Contain Element\">\n<arguments>\n<arg>locator</arg>\n<arg>timeout=None</arg>\n<arg>error=None</arg>\n</arguments>\n<doc>Waits until element ``locator`` disappears from current page.\n\nFails if ``timeout`` expires before the element disappears. See\nthe `Timeouts` section for more information about using timeouts and\ntheir default value and the `Locating elements` section for details\nabout the locator syntax.\n\n``error`` can be used to override the default error message.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Xpath Should Match X Times\">\n<arguments>\n<arg>xpath</arg>\n<arg>x</arg>\n<arg>message=None</arg>\n<arg>loglevel=INFO</arg>\n</arguments>\n<doc>Deprecated, use `Page Should Contain Element` with ``limit`` argument instead.</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/String.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"String\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:42\">\n<version>3.0.3</version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>A test library for string manipulation and verification.\n\n``String`` is Robot Framework's standard library for manipulating\nstrings (e.g. `Replace String Using Regexp`, `Split To Lines`) and\nverifying their contents (e.g. `Should Be String`).\n\nFollowing keywords from ``BuiltIn`` library can also be used with strings:\n\n- `Catenate`\n- `Get Length`\n- `Length Should Be`\n- `Should (Not) Be Empty`\n- `Should (Not) Be Equal (As Strings/Integers/Numbers)`\n- `Should (Not) Match (Regexp)`\n- `Should (Not) Contain`\n- `Should (Not) Start With`\n- `Should (Not) End With`\n- `Convert To String`\n- `Convert To Bytes`</doc>\n<kw name=\"Convert To Lowercase\">\n<arguments>\n<arg>string</arg>\n</arguments>\n<doc>Converts string to lowercase.\n\nExamples:\n| ${str1} = | Convert To Lowercase | ABC |\n| ${str2} = | Convert To Lowercase | 1A2c3D |\n| Should Be Equal | ${str1} | abc |\n| Should Be Equal | ${str2} | 1a2c3d |\n\nNew in Robot Framework 2.8.6.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Convert To Uppercase\">\n<arguments>\n<arg>string</arg>\n</arguments>\n<doc>Converts string to uppercase.\n\nExamples:\n| ${str1} = | Convert To Uppercase | abc |\n| ${str2} = | Convert To Uppercase | 1a2C3d |\n| Should Be Equal | ${str1} | ABC |\n| Should Be Equal | ${str2} | 1A2C3D |\n\nNew in Robot Framework 2.8.6.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Decode Bytes To String\">\n<arguments>\n<arg>bytes</arg>\n<arg>encoding</arg>\n<arg>errors=strict</arg>\n</arguments>\n<doc>Decodes the given ``bytes`` to a Unicode string using the given ``encoding``.\n\n``errors`` argument controls what to do if decoding some bytes fails.\nAll values accepted by ``decode`` method in Python are valid, but in\npractice the following values are most useful:\n\n- ``strict``: fail if characters cannot be decoded (default)\n- ``ignore``: ignore characters that cannot be decoded\n- ``replace``: replace characters that cannot be decoded with\n  a replacement character\n\nExamples:\n| ${string} = | Decode Bytes To String | ${bytes} | UTF-8 |\n| ${string} = | Decode Bytes To String | ${bytes} | ASCII | errors=ignore |\n\nUse `Encode String To Bytes` if you need to convert Unicode strings to\nbyte strings, and `Convert To String` in ``BuiltIn`` if you need to\nconvert arbitrary objects to Unicode strings.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Encode String To Bytes\">\n<arguments>\n<arg>string</arg>\n<arg>encoding</arg>\n<arg>errors=strict</arg>\n</arguments>\n<doc>Encodes the given Unicode ``string`` to bytes using the given ``encoding``.\n\n``errors`` argument controls what to do if encoding some characters fails.\nAll values accepted by ``encode`` method in Python are valid, but in\npractice the following values are most useful:\n\n- ``strict``: fail if characters cannot be encoded (default)\n- ``ignore``: ignore characters that cannot be encoded\n- ``replace``: replace characters that cannot be encoded with\n  a replacement character\n\nExamples:\n| ${bytes} = | Encode String To Bytes | ${string} | UTF-8 |\n| ${bytes} = | Encode String To Bytes | ${string} | ASCII | errors=ignore |\n\nUse `Convert To Bytes` in ``BuiltIn`` if you want to create bytes based\non character or integer sequences. Use `Decode Bytes To String` if you\nneed to convert byte strings to Unicode strings and `Convert To String`\nin ``BuiltIn`` if you need to convert arbitrary objects to Unicode.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Fetch From Left\">\n<arguments>\n<arg>string</arg>\n<arg>marker</arg>\n</arguments>\n<doc>Returns contents of the ``string`` before the first occurrence of ``marker``.\n\nIf the ``marker`` is not found, whole string is returned.\n\nSee also `Fetch From Right`, `Split String` and `Split String\nFrom Right`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Fetch From Right\">\n<arguments>\n<arg>string</arg>\n<arg>marker</arg>\n</arguments>\n<doc>Returns contents of the ``string`` after the last occurrence of ``marker``.\n\nIf the ``marker`` is not found, whole string is returned.\n\nSee also `Fetch From Left`, `Split String` and `Split String\nFrom Right`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Generate Random String\">\n<arguments>\n<arg>length=8</arg>\n<arg>chars=[LETTERS][NUMBERS]</arg>\n</arguments>\n<doc>Generates a string with a desired ``length`` from the given ``chars``.\n\nThe population sequence ``chars`` contains the characters to use\nwhen generating the random string. It can contain any\ncharacters, and it is possible to use special markers\nexplained in the table below:\n\n|  = Marker =   |               = Explanation =                   |\n| ``[LOWER]``   | Lowercase ASCII characters from ``a`` to ``z``. |\n| ``[UPPER]``   | Uppercase ASCII characters from ``A`` to ``Z``. |\n| ``[LETTERS]`` | Lowercase and uppercase ASCII characters.       |\n| ``[NUMBERS]`` | Numbers from 0 to 9.                            |\n\nExamples:\n| ${ret} = | Generate Random String |\n| ${low} = | Generate Random String | 12 | [LOWER]         |\n| ${bin} = | Generate Random String | 8  | 01              |\n| ${hex} = | Generate Random String | 4  | [NUMBERS]abcdef |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Line\">\n<arguments>\n<arg>string</arg>\n<arg>line_number</arg>\n</arguments>\n<doc>Returns the specified line from the given ``string``.\n\nLine numbering starts from 0 and it is possible to use\nnegative indices to refer to lines from the end. The line is\nreturned without the newline character.\n\nExamples:\n| ${first} =    | Get Line | ${string} | 0  |\n| ${2nd last} = | Get Line | ${string} | -2 |\n\nUse `Split To Lines` if all lines are needed.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Line Count\">\n<arguments>\n<arg>string</arg>\n</arguments>\n<doc>Returns and logs the number of lines in the given string.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Lines Containing String\">\n<arguments>\n<arg>string</arg>\n<arg>pattern</arg>\n<arg>case_insensitive=False</arg>\n</arguments>\n<doc>Returns lines of the given ``string`` that contain the ``pattern``.\n\nThe ``pattern`` is always considered to be a normal string, not a glob\nor regexp pattern. A line matches if the ``pattern`` is found anywhere\non it.\n\nThe match is case-sensitive by default, but giving ``case_insensitive``\na true value makes it case-insensitive. The value is considered true\nif it is a non-empty string that is not equal to ``false``, ``none`` or\n``no``. If the value is not a string, its truth value is got directly\nin Python. Considering ``none`` false is new in RF 3.0.3.\n\nLines are returned as one string catenated back together with\nnewlines. Possible trailing newline is never returned. The\nnumber of matching lines is automatically logged.\n\nExamples:\n| ${lines} = | Get Lines Containing String | ${result} | An example |\n| ${ret} =   | Get Lines Containing String | ${ret} | FAIL | case-insensitive |\n\nSee `Get Lines Matching Pattern` and `Get Lines Matching Regexp`\nif you need more complex pattern matching.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Lines Matching Pattern\">\n<arguments>\n<arg>string</arg>\n<arg>pattern</arg>\n<arg>case_insensitive=False</arg>\n</arguments>\n<doc>Returns lines of the given ``string`` that match the ``pattern``.\n\nThe ``pattern`` is a _glob pattern_ where:\n| ``*``        | matches everything |\n| ``?``        | matches any single character |\n| ``[chars]``  | matches any character inside square brackets (e.g. ``[abc]`` matches either ``a``, ``b`` or ``c``) |\n| ``[!chars]`` | matches any character not inside square brackets |\n\nA line matches only if it matches the ``pattern`` fully.\n\nThe match is case-sensitive by default, but giving ``case_insensitive``\na true value makes it case-insensitive. The value is considered true\nif it is a non-empty string that is not equal to ``false``, ``none`` or\n``no``. If the value is not a string, its truth value is got directly\nin Python. Considering ``none`` false is new in RF 3.0.3.\n\nLines are returned as one string catenated back together with\nnewlines. Possible trailing newline is never returned. The\nnumber of matching lines is automatically logged.\n\nExamples:\n| ${lines} = | Get Lines Matching Pattern | ${result} | Wild???? example |\n| ${ret} = | Get Lines Matching Pattern | ${ret} | FAIL: * | case_insensitive=true |\n\nSee `Get Lines Matching Regexp` if you need more complex\npatterns and `Get Lines Containing String` if searching\nliteral strings is enough.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Lines Matching Regexp\">\n<arguments>\n<arg>string</arg>\n<arg>pattern</arg>\n<arg>partial_match=False</arg>\n</arguments>\n<doc>Returns lines of the given ``string`` that match the regexp ``pattern``.\n\nSee `BuiltIn.Should Match Regexp` for more information about\nPython regular expression syntax in general and how to use it\nin Robot Framework test data in particular.\n\nBy default lines match only if they match the pattern fully, but\npartial matching can be enabled by giving the ``partial_match``\nargument a true value. The value is considered true\nif it is a non-empty string that is not equal to ``false``, ``none`` or\n``no``. If the value is not a string, its truth value is got directly\nin Python. Considering ``none`` false is new in RF 3.0.3.\n\nIf the pattern is empty, it matches only empty lines by default.\nWhen partial matching is enabled, empty pattern matches all lines.\n\nNotice that to make the match case-insensitive, you need to prefix\nthe pattern with case-insensitive flag ``(?i)``.\n\nLines are returned as one string concatenated back together with\nnewlines. Possible trailing newline is never returned. The\nnumber of matching lines is automatically logged.\n\nExamples:\n| ${lines} = | Get Lines Matching Regexp | ${result} | Reg\\\\w{3} example |\n| ${lines} = | Get Lines Matching Regexp | ${result} | Reg\\\\w{3} example | partial_match=true |\n| ${ret} =   | Get Lines Matching Regexp | ${ret}    | (?i)FAIL: .* |\n\nSee `Get Lines Matching Pattern` and `Get Lines Containing\nString` if you do not need full regular expression powers (and\ncomplexity).\n\n``partial_match`` argument is new in Robot Framework 2.9. In earlier\n versions exact match was always required.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Regexp Matches\">\n<arguments>\n<arg>string</arg>\n<arg>pattern</arg>\n<arg>*groups</arg>\n</arguments>\n<doc>Returns a list of all non-overlapping matches in the given string.\n\n``string`` is the string to find matches from and ``pattern`` is the\nregular expression. See `BuiltIn.Should Match Regexp` for more\ninformation about Python regular expression syntax in general and how\nto use it in Robot Framework test data in particular.\n\nIf no groups are used, the returned list contains full matches. If one\ngroup is used, the list contains only contents of that group. If\nmultiple groups are used, the list contains tuples that contain\nindividual group contents. All groups can be given as indexes (starting\nfrom 1) and named groups also as names.\n\nExamples:\n| ${no match} =    | Get Regexp Matches | the string | xxx     |\n| ${matches} =     | Get Regexp Matches | the string | t..     |\n| ${one group} =   | Get Regexp Matches | the string | t(..)   | 1 |\n| ${named group} = | Get Regexp Matches | the string | t(?P&lt;name&gt;..) | name |\n| ${two groups} =  | Get Regexp Matches | the string | t(.)(.) | 1 | 2 |\n=&gt;\n| ${no match} = []\n| ${matches} = ['the', 'tri']\n| ${one group} = ['he', 'ri']\n| ${named group} = ['he', 'ri']\n| ${two groups} = [('h', 'e'), ('r', 'i')]\n\nNew in Robot Framework 2.9.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Substring\">\n<arguments>\n<arg>string</arg>\n<arg>start</arg>\n<arg>end=None</arg>\n</arguments>\n<doc>Returns a substring from ``start`` index to ``end`` index.\n\nThe ``start`` index is inclusive and ``end`` is exclusive.\nIndexing starts from 0, and it is possible to use\nnegative indices to refer to characters from the end.\n\nExamples:\n| ${ignore first} = | Get Substring | ${string} | 1  |    |\n| ${ignore last} =  | Get Substring | ${string} |    | -1 |\n| ${5th to 10th} =  | Get Substring | ${string} | 4  | 10 |\n| ${first two} =    | Get Substring | ${string} |    | 1  |\n| ${last two} =     | Get Substring | ${string} | -2 |    |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove String\">\n<arguments>\n<arg>string</arg>\n<arg>*removables</arg>\n</arguments>\n<doc>Removes all ``removables`` from the given ``string``.\n\n``removables`` are used as literal strings. Each removable will be\nmatched to a temporary string from which preceding removables have\nbeen already removed. See second example below.\n\nUse `Remove String Using Regexp` if more powerful pattern matching is\nneeded. If only a certain number of matches should be removed,\n`Replace String` or `Replace String Using Regexp` can be used.\n\nA modified version of the string is returned and the original\nstring is not altered.\n\nExamples:\n| ${str} =        | Remove String | Robot Framework | work   |\n| Should Be Equal | ${str}        | Robot Frame     |\n| ${str} =        | Remove String | Robot Framework | o | bt |\n| Should Be Equal | ${str}        | R Framewrk      |\n\nNew in Robot Framework 2.8.2.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove String Using Regexp\">\n<arguments>\n<arg>string</arg>\n<arg>*patterns</arg>\n</arguments>\n<doc>Removes ``patterns`` from the given ``string``.\n\nThis keyword is otherwise identical to `Remove String`, but\nthe ``patterns`` to search for are considered to be a regular\nexpression. See `Replace String Using Regexp` for more information\nabout the regular expression syntax. That keyword can also be\nused if there is a need to remove only a certain number of\noccurrences.\n\nNew in Robot Framework 2.8.2.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Replace String\">\n<arguments>\n<arg>string</arg>\n<arg>search_for</arg>\n<arg>replace_with</arg>\n<arg>count=-1</arg>\n</arguments>\n<doc>Replaces ``search_for`` in the given ``string`` with ``replace_with``.\n\n``search_for`` is used as a literal string. See `Replace String\nUsing Regexp` if more powerful pattern matching is needed.\nIf you need to just remove a string see `Remove String`.\n\nIf the optional argument ``count`` is given, only that many\noccurrences from left are replaced. Negative ``count`` means\nthat all occurrences are replaced (default behaviour) and zero\nmeans that nothing is done.\n\nA modified version of the string is returned and the original\nstring is not altered.\n\nExamples:\n| ${str} =        | Replace String | Hello, world!  | world | tellus   |\n| Should Be Equal | ${str}         | Hello, tellus! |       |          |\n| ${str} =        | Replace String | Hello, world!  | l     | ${EMPTY} | count=1 |\n| Should Be Equal | ${str}         | Helo, world!   |       |          |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Replace String Using Regexp\">\n<arguments>\n<arg>string</arg>\n<arg>pattern</arg>\n<arg>replace_with</arg>\n<arg>count=-1</arg>\n</arguments>\n<doc>Replaces ``pattern`` in the given ``string`` with ``replace_with``.\n\nThis keyword is otherwise identical to `Replace String`, but\nthe ``pattern`` to search for is considered to be a regular\nexpression.  See `BuiltIn.Should Match Regexp` for more\ninformation about Python regular expression syntax in general\nand how to use it in Robot Framework test data in particular.\n\nIf you need to just remove a string see `Remove String Using Regexp`.\n\nExamples:\n| ${str} = | Replace String Using Regexp | ${str} | 20\\\\d\\\\d-\\\\d\\\\d-\\\\d\\\\d | &lt;DATE&gt; |\n| ${str} = | Replace String Using Regexp | ${str} | (Hello|Hi) | ${EMPTY} | count=1 |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Be Byte String\">\n<arguments>\n<arg>item</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the given ``item`` is not a byte string.\n\nUse `Should Be Unicode String` if you want to verify the ``item`` is a\nUnicode string, or `Should Be String` if both Unicode and byte strings\nare fine. See `Should Be String` for more details about Unicode strings\nand byte strings.\n\nThe default error message can be overridden with the optional\n``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Be Lowercase\">\n<arguments>\n<arg>string</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the given ``string`` is not in lowercase.\n\nFor example, ``'string'`` and ``'with specials!'`` would pass, and\n``'String'``, ``''`` and ``' '`` would fail.\n\nThe default error message can be overridden with the optional\n``msg`` argument.\n\nSee also `Should Be Uppercase` and `Should Be Titlecase`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Be String\">\n<arguments>\n<arg>item</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the given ``item`` is not a string.\n\nWith Python 2, except with IronPython, this keyword passes regardless\nis the ``item`` a Unicode string or a byte string. Use `Should Be\nUnicode String` or `Should Be Byte String` if you want to restrict\nthe string type. Notice that with Python 2, except with IronPython,\n``'string'`` creates a byte string and ``u'unicode'`` must be used to\ncreate a Unicode string.\n\nWith Python 3 and IronPython, this keyword passes if the string is\na Unicode string but fails if it is bytes. Notice that with both\nPython 3 and IronPython, ``'string'`` creates a Unicode string, and\n``b'bytes'`` must be used to create a byte string.\n\nThe default error message can be overridden with the optional\n``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Be Titlecase\">\n<arguments>\n<arg>string</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if given ``string`` is not title.\n\n``string`` is a titlecased string if there is at least one\ncharacter in it, uppercase characters only follow uncased\ncharacters and lowercase characters only cased ones.\n\nFor example, ``'This Is Title'`` would pass, and ``'Word In UPPER'``,\n``'Word In lower'``, ``''`` and ``' '`` would fail.\n\nThe default error message can be overridden with the optional\n``msg`` argument.\n\nSee also `Should Be Uppercase` and `Should Be Lowercase`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Be Unicode String\">\n<arguments>\n<arg>item</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the given ``item`` is not a Unicode string.\n\nUse `Should Be Byte String` if you want to verify the ``item`` is a\nbyte string, or `Should Be String` if both Unicode and byte strings\nare fine. See `Should Be String` for more details about Unicode\nstrings and byte strings.\n\nThe default error message can be overridden with the optional\n``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Be Uppercase\">\n<arguments>\n<arg>string</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the given ``string`` is not in uppercase.\n\nFor example, ``'STRING'`` and ``'WITH SPECIALS!'`` would pass, and\n``'String'``, ``''`` and ``' '`` would fail.\n\nThe default error message can be overridden with the optional\n``msg`` argument.\n\nSee also `Should Be Titlecase` and `Should Be Lowercase`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Should Not Be String\">\n<arguments>\n<arg>item</arg>\n<arg>msg=None</arg>\n</arguments>\n<doc>Fails if the given ``item`` is a string.\n\nSee `Should Be String` for more details about Unicode strings and byte\nstrings.\n\nThe default error message can be overridden with the optional\n``msg`` argument.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Split String\">\n<arguments>\n<arg>string</arg>\n<arg>separator=None</arg>\n<arg>max_split=-1</arg>\n</arguments>\n<doc>Splits the ``string`` using ``separator`` as a delimiter string.\n\nIf a ``separator`` is not given, any whitespace string is a\nseparator. In that case also possible consecutive whitespace\nas well as leading and trailing whitespace is ignored.\n\nSplit words are returned as a list. If the optional\n``max_split`` is given, at most ``max_split`` splits are done, and\nthe returned list will have maximum ``max_split + 1`` elements.\n\nExamples:\n| @{words} =         | Split String | ${string} |\n| @{words} =         | Split String | ${string} | ,${SPACE} |\n| ${pre} | ${post} = | Split String | ${string} | ::    | 1 |\n\nSee `Split String From Right` if you want to start splitting\nfrom right, and `Fetch From Left` and `Fetch From Right` if\nyou only want to get first/last part of the string.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Split String From Right\">\n<arguments>\n<arg>string</arg>\n<arg>separator=None</arg>\n<arg>max_split=-1</arg>\n</arguments>\n<doc>Splits the ``string`` using ``separator`` starting from right.\n\nSame as `Split String`, but splitting is started from right. This has\nan effect only when ``max_split`` is given.\n\nExamples:\n| ${first} | ${rest} = | Split String            | ${string} | - | 1 |\n| ${rest}  | ${last} = | Split String From Right | ${string} | - | 1 |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Split String To Characters\">\n<arguments>\n<arg>string</arg>\n</arguments>\n<doc>Splits the given ``string`` to characters.\n\nExample:\n| @{characters} = | Split String To Characters | ${string} |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Split To Lines\">\n<arguments>\n<arg>string</arg>\n<arg>start=0</arg>\n<arg>end=None</arg>\n</arguments>\n<doc>Splits the given string to lines.\n\nIt is possible to get only a selection of lines from ``start``\nto ``end`` so that ``start`` index is inclusive and ``end`` is\nexclusive. Line numbering starts from 0, and it is possible to\nuse negative indices to refer to lines from the end.\n\nLines are returned without the newlines. The number of\nreturned lines is automatically logged.\n\nExamples:\n| @{lines} =        | Split To Lines | ${manylines} |    |    |\n| @{ignore first} = | Split To Lines | ${manylines} | 1  |    |\n| @{ignore last} =  | Split To Lines | ${manylines} |    | -1 |\n| @{5th to 10th} =  | Split To Lines | ${manylines} | 4  | 10 |\n| @{first two} =    | Split To Lines | ${manylines} |    | 1  |\n| @{last two} =     | Split To Lines | ${manylines} | -2 |    |\n\nUse `Get Line` if you only need to get a single line.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Strip String\">\n<arguments>\n<arg>string</arg>\n<arg>mode=both</arg>\n<arg>characters=None</arg>\n</arguments>\n<doc>Remove leading and/or trailing whitespaces from the given string.\n\n``mode`` is either ``left`` to remove leading characters, ``right`` to\nremove trailing characters, ``both`` (default) to remove the\ncharacters from both sides of the string or ``none`` to return the\nunmodified string.\n\nIf the optional ``characters`` is given, it must be a string and the\ncharacters in the string will be stripped in the string. Please note,\nthat this is not a substring to be removed but a list of characters,\nsee the example below.\n\nExamples:\n| ${stripped}=  | Strip String | ${SPACE}Hello${SPACE} | |\n| Should Be Equal | ${stripped} | Hello | |\n| ${stripped}=  | Strip String | ${SPACE}Hello${SPACE} | mode=left |\n| Should Be Equal | ${stripped} | Hello${SPACE} | |\n| ${stripped}=  | Strip String | aabaHelloeee | characters=abe |\n| Should Be Equal | ${stripped} | Hello | |\n\nNew in Robot Framework 3.0.</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/Telnet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"Telnet\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:42\">\n<version>3.0.3</version>\n<scope>test suite</scope>\n<namedargs>yes</namedargs>\n<doc>A test library providing communication over Telnet connections.\n\n``Telnet`` is Robot Framework's standard library that makes it possible to\nconnect to Telnet servers and execute commands on the opened connections.\n\n== Table of contents ==\n\n- `Connections`\n- `Writing and reading`\n- `Configuration`\n- `Terminal emulation`\n- `Logging`\n- `Time string format`\n- `Boolean arguments`\n- `Importing`\n- `Shortcuts`\n- `Keywords`\n\n= Connections =\n\nThe first step of using ``Telnet`` is opening a connection with `Open\nConnection` keyword. Typically the next step is logging in with `Login`\nkeyword, and in the end the opened connection can be closed with `Close\nConnection`.\n\nIt is possible to open multiple connections and switch the active one\nusing `Switch Connection`. `Close All Connections` can be used to close\nall the connections, which is especially useful in suite teardowns to\nguarantee that all connections are always closed.\n\n= Writing and reading =\n\nAfter opening a connection and possibly logging in, commands can be\nexecuted or text written to the connection for other reasons using `Write`\nand `Write Bare` keywords. The main difference between these two is that\nthe former adds a [#Configuration|configurable newline] after the text\nautomatically.\n\nAfter writing something to the connection, the resulting output can be\nread using `Read`, `Read Until`, `Read Until Regexp`, and `Read Until\nPrompt` keywords. Which one to use depends on the context, but the latest\none is often the most convenient.\n\nAs a convenience when running a command, it is possible to use `Execute\nCommand` that simply uses `Write` and `Read Until Prompt` internally.\n`Write Until Expected Output` is useful if you need to wait until writing\nsomething produces a desired output.\n\nWritten and read text is automatically encoded/decoded using a\n[#Configuration|configured encoding].\n\nThe ANSI escape codes, like cursor movement and color codes, are\nnormally returned as part of the read operation. If an escape code occurs\nin middle of a search pattern it may also prevent finding the searched\nstring. `Terminal emulation` can be used to process these\nescape codes as they would be if a real terminal would be in use.\n\n= Configuration =\n\nMany aspects related the connections can be easily configured either\nglobally or per connection basis. Global configuration is done when\n[#Importing|library is imported], and these values can be overridden per\nconnection by `Open Connection` or with setting specific keywords\n`Set Timeout`, `Set Newline`, `Set Prompt`, `Set Encoding`,\n`Set Default Log Level` and `Set Telnetlib Log Level`.\n\nValues of ``environ_user``, ``window_size``, ``terminal_emulation``, and\n``terminal_type`` can not be changed after opening the connection.\n\n== Timeout ==\n\nTimeout defines how long is the maximum time to wait when reading\noutput. It is used internally by `Read Until`, `Read Until Regexp`,\n`Read Until Prompt`, and `Login` keywords. The default value is 3 seconds.\n\n== Connection Timeout ==\n\nConnection Timeout defines how long is the maximum time to wait when\nopening the telnet connection. It is used internally by `Open Connection`.\nThe default value is the system global default timeout.\n\nNew in Robot Framework 2.9.2.\n\n== Newline ==\n\nNewline defines which line separator `Write` keyword should use. The\ndefault value is ``CRLF`` that is typically used by Telnet connections.\n\nNewline can be given either in escaped format using ``\\n`` and ``\\r`` or\nwith special ``LF`` and ``CR`` syntax.\n\nExamples:\n| `Set Newline` | \\n  |\n| `Set Newline` | CRLF |\n\n== Prompt ==\n\nOften the easiest way to read the output of a command is reading all\nthe output until the next prompt with `Read Until Prompt`. It also makes\nit easier, and faster, to verify did `Login` succeed.\n\nPrompt can be specified either as a normal string or a regular expression.\nThe latter is especially useful if the prompt changes as a result of\nthe executed commands. Prompt can be set to be a regular expression\nby giving ``prompt_is_regexp`` argument a true value (see `Boolean\narguments`).\n\nExamples:\n| `Open Connection` | lolcathost | prompt=$              |\n| `Set Prompt`      | (&gt; |# )    | prompt_is_regexp=true |\n\n== Encoding ==\n\nTo ease handling text containing non-ASCII characters, all written text is\nencoded and read text decoded by default. The default encoding is UTF-8\nthat works also with ASCII. Encoding can be disabled by using a special\nencoding value ``NONE``. This is mainly useful if you need to get the bytes\nreceived from the connection as-is.\n\nNotice that when writing to the connection, only Unicode strings are\nencoded using the defined encoding. Byte strings are expected to be already\nencoded correctly. Notice also that normal text in test data is passed to\nthe library as Unicode and you need to use variables to use bytes.\n\nIt is also possible to configure the error handler to use if encoding or\ndecoding characters fails. Accepted values are the same that encode/decode\nfunctions in Python strings accept. In practice the following values are\nthe most useful:\n\n- ``ignore``: ignore characters that cannot be encoded (default)\n- ``strict``: fail if characters cannot be encoded\n- ``replace``: replace characters that cannot be encoded with a replacement\n  character\n\nExamples:\n| `Open Connection` | lolcathost | encoding=Latin1 | encoding_errors=strict |\n| `Set Encoding` | ISO-8859-15 |\n| `Set Encoding` | errors=ignore |\n\nUsing UTF-8 encoding by default and being able to configure the encoding\nare new features in Robot Framework 2.7.6. In earlier versions only ASCII\nwas supported and encoding errors were silently ignored. Robot Framework\n2.7.7 added a possibility to specify the error handler, changed the\ndefault behavior back to ignoring encoding errors, and added the\npossibility to disable encoding.\n\n== Default log level ==\n\nDefault log level specifies the log level keywords use for `logging` unless\nthey are given an explicit log level. The default value is ``INFO``, and\nchanging it, for example, to ``DEBUG`` can be a good idea if there is lot\nof unnecessary output that makes log files big.\n\nConfiguring default log level in `importing` and with `Open Connection`\nare new features in Robot Framework 2.7.6. In earlier versions only\n`Set Default Log Level` could be used.\n\n== Terminal type ==\n\nBy default the Telnet library does not negotiate any specific terminal type\nwith the server. If a specific terminal type, for example ``vt100``, is\ndesired, the terminal type can be configured in `importing` and with\n`Open Connection`.\n\nNew in Robot Framework 2.8.2.\n\n== Window size ==\n\nWindow size for negotiation with the server can be configured when\n`importing` the library and with `Open Connection`.\n\nNew in Robot Framework 2.8.2.\n\n== USER environment variable ==\n\nTelnet protocol allows the ``USER`` environment variable to be sent when\nconnecting to the server. On some servers it may happen that there is no\nlogin prompt, and on those cases this configuration option will allow still\nto define the desired username. The option ``environ_user`` can be used in\n`importing` and with `Open Connection`.\n\nNew in Robot Framework 2.8.2.\n\n= Terminal emulation =\n\nStarting from Robot Framework 2.8.2, Telnet library supports terminal\nemulation with [https://pyte.readthedocs.io|Pyte]. Terminal emulation\nwill process the output in a virtual screen. This means that ANSI escape\ncodes, like cursor movements, and also control characters, like\ncarriage returns and backspaces, have the same effect on the result as they\nwould have on a normal terminal screen. For example the sequence\n``acdc\\x1b[3Dbba`` will result in output ``abba``.\n\nTerminal emulation is taken into use by giving ``terminal_emulation``\nargument a true value (see `Boolean arguments`) either in the library\ninitialization or with `Open Connection`.\n\nAs Pyte approximates vt-style terminal, you may also want to set the\nterminal type as ``vt100``. We also recommend that you increase the window\nsize, as the terminal emulation will break all lines that are longer than\nthe window row length.\n\nWhen terminal emulation is used, the `newline` and `encoding` can not be\nchanged anymore after opening the connection.\n\nExamples:\n| `Open Connection` | lolcathost | terminal_emulation=True | terminal_type=vt100 | window_size=400x100 |\n\nAs a prerequisite for using terminal emulation, you need to have Pyte\ninstalled. Due to backwards incompatible changes in Pyte, different\nRobot Framework versions support different Pyte versions:\n\n- Pyte 0.6 and newer are supported by Robot Framework 3.0.3.\n  Latest Pyte version can be installed (or upgraded) with\n  ``pip install --upgrade pyte``.\n- Pyte 0.5.2 and older are supported by Robot Framework 3.0.2 and earlier.\n  Pyte 0.5.2 can be installed with ``pip install pyte==0.5.2``.\n\n= Logging =\n\nAll keywords that read something log the output. These keywords take the\nlog level to use as an optional argument, and if no log level is specified\nthey use the [#Configuration|configured] default value.\n\nThe valid log levels to use are ``TRACE``, ``DEBUG``, ``INFO`` (default),\nand ``WARN``. Levels below ``INFO`` are not shown in log files by default\nwhereas warnings are shown more prominently.\n\nThe [http://docs.python.org/2/library/telnetlib.html|telnetlib module]\nused by this library has a custom logging system for logging content it\nsends and receives. By default these messages are written using ``TRACE``\nlevel. Starting with Robot Framework 2.8.7 the level is configurable\nwith the ``telnetlib_log_level`` option either in the library initialization,\nto the `Open Connection` or by using the `Set Telnetlib Log Level`\nkeyword to the active connection. Special level ``NONE`` con be used to\ndisable the logging altogether.\n\n= Time string format =\n\nTimeouts and other times used must be given as a time string using format\nlike ``15 seconds`` or ``1min 10s``. If the timeout is given as just\na number, for example, ``10`` or ``1.5``, it is considered to be seconds.\nThe time string format is described in more detail in an appendix of\n[http://robotframework.org/robotframework/#user-guide|Robot Framework User Guide].\n\n= Boolean arguments =\n\nSome keywords accept arguments that are handled as Boolean values true or\nfalse. If such an argument is given as a string, it is considered false if\nit is either an empty string or case-insensitively equal to ``false``,\n``none`` or ``no``. Other strings are considered true regardless\ntheir value, and other argument types are tested using the same\n[http://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules\nas in Python].\n\nTrue examples:\n| `Open Connection` | lolcathost | terminal_emulation=True    | # Strings are generally true.    |\n| `Open Connection` | lolcathost | terminal_emulation=yes     | # Same as the above.             |\n| `Open Connection` | lolcathost | terminal_emulation=${TRUE} | # Python ``True`` is true.       |\n| `Open Connection` | lolcathost | terminal_emulation=${42}   | # Numbers other than 0 are true. |\n\nFalse examples:\n| `Open Connection` | lolcathost | terminal_emulation=False    | # String ``false`` is false.   |\n| `Open Connection` | lolcathost | terminal_emulation=no       | # Also string ``no`` is false. |\n| `Open Connection` | lolcathost | terminal_emulation=${EMPTY} | # Empty string is false.       |\n| `Open Connection` | lolcathost | terminal_emulation=${FALSE} | # Python ``False`` is false.   |\n\nPrior to Robot Framework 2.9, all non-empty strings, including ``false``\nand ``no``, were considered to be true. Considering ``none`` false is\nnew in Robot Framework 3.0.3.</doc>\n<init>\n<arguments>\n<arg>timeout=3 seconds</arg>\n<arg>newline=CRLF</arg>\n<arg>prompt=None</arg>\n<arg>prompt_is_regexp=False</arg>\n<arg>encoding=UTF-8</arg>\n<arg>encoding_errors=ignore</arg>\n<arg>default_log_level=INFO</arg>\n<arg>window_size=None</arg>\n<arg>environ_user=None</arg>\n<arg>terminal_emulation=False</arg>\n<arg>terminal_type=None</arg>\n<arg>telnetlib_log_level=TRACE</arg>\n<arg>connection_timeout=None</arg>\n</arguments>\n<doc>Telnet library can be imported with optional configuration parameters.\n\nConfiguration parameters are used as default values when new\nconnections are opened with `Open Connection` keyword. They can also be\noverridden after opening the connection using the `Set ...` `keywords`.\nSee these keywords as well as `Configuration`, `Terminal emulation` and\n`Logging` sections above for more information about these parameters\nand their possible values.\n\nSee `Time string format` and `Boolean arguments` sections for\ninformation about using arguments accepting times and Boolean values,\nrespectively.\n\nExamples (use only one of these):\n| = Setting = | = Value = | = Value =                | = Value =            | = Value =           | = Comment = |\n| Library     | Telnet    |                          |                      |                     | # default values |\n| Library     | Telnet    | 5 seconds                |                      |                     | # set only timeout |\n| Library     | Telnet    | newline=LF               | encoding=ISO-8859-1  |                     | # set newline and encoding using named arguments |\n| Library     | Telnet    | prompt=$                 |                      |                     | # set prompt |\n| Library     | Telnet    | prompt=(&gt; |# )           | prompt_is_regexp=yes |                     | # set prompt as a regular expression |\n| Library     | Telnet    | terminal_emulation=True  | terminal_type=vt100  | window_size=400x100 | # use terminal emulation with defined window size and terminal type |\n| Library     | Telnet    | telnetlib_log_level=NONE |                      |                     | # disable logging messages from the underlying telnetlib |</doc>\n<tags>\n</tags>\n</init>\n<kw name=\"Close All Connections\">\n<arguments>\n</arguments>\n<doc>Closes all open connections and empties the connection cache.\n\nIf multiple connections are opened, this keyword should be used in\na test or suite teardown to make sure that all connections are closed.\nIt is not an error is some of the connections have already been closed\nby `Close Connection`.\n\nAfter this keyword, new indexes returned by `Open Connection`\nkeyword are reset to 1.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Close Connection\">\n<arguments>\n<arg>loglevel=None</arg>\n</arguments>\n<doc>Closes the current Telnet connection.\n\nRemaining output in the connection is read, logged, and returned.\nIt is not an error to close an already closed connection.\n\nUse `Close All Connections` if you want to make sure all opened\nconnections are closed.\n\nSee `Logging` section for more information about log levels.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Execute Command\">\n<arguments>\n<arg>command</arg>\n<arg>loglevel=None</arg>\n<arg>strip_prompt=False</arg>\n</arguments>\n<doc>Executes the given ``command`` and reads, logs, and returns everything until the prompt.\n\nThis keyword requires the prompt to be [#Configuration|configured]\neither in `importing` or with `Open Connection` or `Set Prompt` keyword.\n\nThis is a convenience keyword that uses `Write` and `Read Until Prompt`\ninternally. Following two examples are thus functionally identical:\n\n| ${out} = | `Execute Command`   | pwd |\n\n| `Write`  | pwd                 |\n| ${out} = | `Read Until Prompt` |\n\nSee `Logging` section for more information about log levels and `Read\nUntil Prompt` for more information about the ``strip_prompt`` parameter.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Login\">\n<arguments>\n<arg>username</arg>\n<arg>password</arg>\n<arg>login_prompt=login: </arg>\n<arg>password_prompt=Password: </arg>\n<arg>login_timeout=1 second</arg>\n<arg>login_incorrect=Login incorrect</arg>\n</arguments>\n<doc>Logs in to the Telnet server with the given user information.\n\nThis keyword reads from the connection until the ``login_prompt`` is\nencountered and then types the given ``username``. Then it reads until\nthe ``password_prompt`` and types the given ``password``. In both cases\na newline is appended automatically and the connection specific\ntimeout used when waiting for outputs.\n\nHow logging status is verified depends on whether a prompt is set for\nthis connection or not:\n\n1) If the prompt is set, this keyword reads the output until the prompt\nis found using the normal timeout. If no prompt is found, login is\nconsidered failed and also this keyword fails. Note that in this case\nboth ``login_timeout`` and ``login_incorrect`` arguments are ignored.\n\n2) If the prompt is not set, this keywords sleeps until ``login_timeout``\nand then reads all the output available on the connection. If the\noutput contains ``login_incorrect`` text, login is considered failed\nand also this keyword fails. Both of these configuration parameters\nwere added in Robot Framework 2.7.6. In earlier versions they were\nhard coded.\n\nSee `Configuration` section for more information about setting\nnewline, timeout, and prompt.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Open Connection\">\n<arguments>\n<arg>host</arg>\n<arg>alias=None</arg>\n<arg>port=23</arg>\n<arg>timeout=None</arg>\n<arg>newline=None</arg>\n<arg>prompt=None</arg>\n<arg>prompt_is_regexp=False</arg>\n<arg>encoding=None</arg>\n<arg>encoding_errors=None</arg>\n<arg>default_log_level=None</arg>\n<arg>window_size=None</arg>\n<arg>environ_user=None</arg>\n<arg>terminal_emulation=None</arg>\n<arg>terminal_type=None</arg>\n<arg>telnetlib_log_level=None</arg>\n<arg>connection_timeout=None</arg>\n</arguments>\n<doc>Opens a new Telnet connection to the given host and port.\n\nThe ``timeout``, ``newline``, ``prompt``, ``prompt_is_regexp``,\n``encoding``, ``default_log_level``, ``window_size``, ``environ_user``,\n``terminal_emulation``, ``terminal_type`` and ``telnetlib_log_level``\narguments get default values when the library is [#Importing|imported].\nSetting them here overrides those values for the opened connection.\nSee `Configuration`, `Terminal emulation` and `Logging` sections for\nmore information about these parameters and their possible values.\n\nPossible already opened connections are cached and it is possible to\nswitch back to them using `Switch Connection` keyword. It is possible to\nswitch either using explicitly given ``alias`` or using index returned\nby this keyword. Indexing starts from 1 and is reset back to it by\n`Close All Connections` keyword.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Read\">\n<arguments>\n<arg>loglevel=None</arg>\n</arguments>\n<doc>Reads everything that is currently available in the output.\n\nRead output is both returned and logged. See `Logging` section for more\ninformation about log levels.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Read Until\">\n<arguments>\n<arg>expected</arg>\n<arg>loglevel=None</arg>\n</arguments>\n<doc>Reads output until ``expected`` text is encountered.\n\nText up to and including the match is returned and logged. If no match\nis found, this keyword fails. How much to wait for the output depends\non the [#Configuration|configured timeout].\n\nSee `Logging` section for more information about log levels. Use\n`Read Until Regexp` if more complex matching is needed.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Read Until Prompt\">\n<arguments>\n<arg>loglevel=None</arg>\n<arg>strip_prompt=False</arg>\n</arguments>\n<doc>Reads output until the prompt is encountered.\n\nThis keyword requires the prompt to be [#Configuration|configured]\neither in `importing` or with `Open Connection` or `Set Prompt` keyword.\n\nBy default, text up to and including the prompt is returned and logged.\nIf no prompt is found, this keyword fails. How much to wait for the\noutput depends on the [#Configuration|configured timeout].\n\nIf you want to exclude the prompt from the returned output, set\n``strip_prompt`` to a true value (see `Boolean arguments`). If your\nprompt is a regular expression, make sure that the expression spans the\nwhole prompt, because only the part of the output that matches the\nregular expression is stripped away.\n\nSee `Logging` section for more information about log levels.\n\nOptionally stripping prompt is a new feature in Robot Framework 2.8.7.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Read Until Regexp\">\n<arguments>\n<arg>*expected</arg>\n</arguments>\n<doc>Reads output until any of the ``expected`` regular expressions match.\n\nThis keyword accepts any number of regular expressions patterns or\ncompiled Python regular expression objects as arguments. Text up to\nand including the first match to any of the regular expressions is\nreturned and logged. If no match is found, this keyword fails. How much\nto wait for the output depends on the [#Configuration|configured timeout].\n\nIf the last given argument is a [#Logging|valid log level], it is used\nas ``loglevel`` similarly as with `Read Until` keyword.\n\nSee the documentation of\n[http://docs.python.org/2/library/re.html|Python re module]\nfor more information about the supported regular expression syntax.\nNotice that possible backslashes need to be escaped in Robot Framework\ntest data.\n\nExamples:\n| `Read Until Regexp` | (#|$) |\n| `Read Until Regexp` | first_regexp | second_regexp |\n| `Read Until Regexp` | \\\\d{4}-\\\\d{2}-\\\\d{2} | DEBUG |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Default Log Level\">\n<arguments>\n<arg>level</arg>\n</arguments>\n<doc>Sets the default log level used for `logging` in the current connection.\n\nThe old default log level is returned and can be used to restore the\nlog level later.\n\nSee `Configuration` section for more information about global and\nconnection specific configuration.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Encoding\">\n<arguments>\n<arg>encoding=None</arg>\n<arg>errors=None</arg>\n</arguments>\n<doc>Sets the encoding to use for `writing and reading` in the current connection.\n\nThe given ``encoding`` specifies the encoding to use when written/read\ntext is encoded/decoded, and ``errors`` specifies the error handler to\nuse if encoding/decoding fails. Either of these can be omitted and in\nthat case the old value is not affected. Use string ``NONE`` to disable\nencoding altogether.\n\nSee `Configuration` section for more information about encoding and\nerror handlers, as well as global and connection specific configuration\nin general.\n\nThe old values are returned and can be used to restore the encoding\nand the error handler later. See `Set Prompt` for a similar example.\n\nIf terminal emulation is used, the encoding can not be changed on an open\nconnection.\n\nSetting encoding in general is a new feature in Robot Framework 2.7.6.\nSpecifying the error handler and disabling encoding were added in 2.7.7.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Newline\">\n<arguments>\n<arg>newline</arg>\n</arguments>\n<doc>Sets the newline used by `Write` keyword in the current connection.\n\nThe old newline is returned and can be used to restore the newline later.\nSee `Set Timeout` for a similar example.\n\nIf terminal emulation is used, the newline can not be changed on an open\nconnection.\n\nSee `Configuration` section for more information about global and\nconnection specific configuration.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Prompt\">\n<arguments>\n<arg>prompt</arg>\n<arg>prompt_is_regexp=False</arg>\n</arguments>\n<doc>Sets the prompt used by `Read Until Prompt` and `Login` in the current connection.\n\nIf ``prompt_is_regexp`` is given a true value (see `Boolean arguments`),\nthe given ``prompt`` is considered to be a regular expression.\n\nThe old prompt is returned and can be used to restore the prompt later.\n\nExample:\n| ${prompt} | ${regexp} = | `Set Prompt` | $ |\n| `Do Something` |\n| `Set Prompt` | ${prompt} | ${regexp} |\n\nSee the documentation of\n[http://docs.python.org/2/library/re.html|Python re module]\nfor more information about the supported regular expression syntax.\nNotice that possible backslashes need to be escaped in Robot Framework\ntest data.\n\nSee `Configuration` section for more information about global and\nconnection specific configuration.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Telnetlib Log Level\">\n<arguments>\n<arg>level</arg>\n</arguments>\n<doc>Sets the log level used for `logging` in the underlying ``telnetlib``.\n\nNote that ``telnetlib`` can be very noisy thus using the level ``NONE``\ncan shutdown the messages generated by this library.\n\nNew in Robot Framework 2.8.7.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Timeout\">\n<arguments>\n<arg>timeout</arg>\n</arguments>\n<doc>Sets the timeout used for waiting output in the current connection.\n\nRead operations that expect some output to appear (`Read Until`, `Read\nUntil Regexp`, `Read Until Prompt`, `Login`) use this timeout and fail\nif the expected output does not appear before this timeout expires.\n\nThe ``timeout`` must be given in `time string format`. The old timeout\nis returned and can be used to restore the timeout later.\n\nExample:\n| ${old} =       | `Set Timeout` | 2 minute 30 seconds |\n| `Do Something` |\n| `Set Timeout`  | ${old}  |\n\nSee `Configuration` section for more information about global and\nconnection specific configuration.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Switch Connection\">\n<arguments>\n<arg>index_or_alias</arg>\n</arguments>\n<doc>Switches between active connections using an index or an alias.\n\nAliases can be given to `Open Connection` keyword which also always\nreturns the connection index.\n\nThis keyword returns the index of previous active connection.\n\nExample:\n| `Open Connection`   | myhost.net              |          |           |\n| `Login`             | john                    | secret   |           |\n| `Write`             | some command            |          |           |\n| `Open Connection`   | yourhost.com            | 2nd conn |           |\n| `Login`             | root                    | password |           |\n| `Write`             | another cmd             |          |           |\n| ${old index}=       | `Switch Connection`     | 1        | # index   |\n| `Write`             | something               |          |           |\n| `Switch Connection` | 2nd conn                |          | # alias   |\n| `Write`             | whatever                |          |           |\n| `Switch Connection` | ${old index}            | | # back to original |\n| [Teardown]          | `Close All Connections` |          |           |\n\nThe example above expects that there were no other open\nconnections when opening the first one, because it used index\n``1`` when switching to the connection later. If you are not\nsure about that, you can store the index into a variable as\nshown below.\n\n| ${index} =          | `Open Connection` | myhost.net |\n| `Do Something`      |                   |            |\n| `Switch Connection` | ${index}          |            |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Write\">\n<arguments>\n<arg>text</arg>\n<arg>loglevel=None</arg>\n</arguments>\n<doc>Writes the given text plus a newline into the connection.\n\nThe newline character sequence to use can be [#Configuration|configured]\nboth globally and per connection basis. The default value is ``CRLF``.\n\nThis keyword consumes the written text, until the added newline, from\nthe output and logs and returns it. The given text itself must not\ncontain newlines. Use `Write Bare` instead if either of these features\ncauses a problem.\n\n*Note:* This keyword does not return the possible output of the executed\ncommand. To get the output, one of the `Read ...` `keywords` must be\nused. See `Writing and reading` section for more details.\n\nSee `Logging` section for more information about log levels.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Write Bare\">\n<arguments>\n<arg>text</arg>\n</arguments>\n<doc>Writes the given text, and nothing else, into the connection.\n\nThis keyword does not append a newline nor consume the written text.\nUse `Write` if these features are needed.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Write Control Character\">\n<arguments>\n<arg>character</arg>\n</arguments>\n<doc>Writes the given control character into the connection.\n\nThe control character is prepended with an IAC (interpret as command)\ncharacter.\n\nThe following control character names are supported: BRK, IP, AO, AYT,\nEC, EL, NOP. Additionally, you can use arbitrary numbers to send any\ncontrol character.\n\nExample:\n| Write Control Character | BRK | # Send Break command |\n| Write Control Character | 241 | # Send No operation command |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Write Until Expected Output\">\n<arguments>\n<arg>text</arg>\n<arg>expected</arg>\n<arg>timeout</arg>\n<arg>retry_interval</arg>\n<arg>loglevel=None</arg>\n</arguments>\n<doc>Writes the given ``text`` repeatedly, until ``expected`` appears in the output.\n\n``text`` is written without appending a newline and it is consumed from\nthe output before trying to find ``expected``. If ``expected`` does not\nappear in the output within ``timeout``, this keyword fails.\n\n``retry_interval`` defines the time to wait ``expected`` to appear before\nwriting the ``text`` again. Consuming the written ``text`` is subject to\nthe normal [#Configuration|configured timeout].\n\nBoth ``timeout`` and ``retry_interval`` must be given in `time string\nformat`. See `Logging` section for more information about log levels.\n\nExample:\n| Write Until Expected Output | ps -ef| grep myprocess\\r\\n | myprocess |\n| ...                         | 5 s                          | 0.5 s     |\n\nThe above example writes command ``ps -ef | grep myprocess\\r\\n`` until\n``myprocess`` appears in the output. The command is written every 0.5\nseconds and the keyword fails if ``myprocess`` does not appear in\nthe output in 5 seconds.</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "keyword/XML.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<keywordspec name=\"XML\" type=\"library\" format=\"ROBOT\" generated=\"20180425 10:46:43\">\n<version>3.0.3</version>\n<scope>global</scope>\n<namedargs>yes</namedargs>\n<doc>Robot Framework test library for verifying and modifying XML documents.\n\nAs the name implies, _XML_ is a test library for verifying contents of XML\nfiles. In practice it is a pretty thin wrapper on top of Python's\n[https://docs.python.org/2/library/xml.etree.elementtree.html|ElementTree XML API].\n\nThe library has the following main usages:\n\n- Parsing an XML file, or a string containing XML, into an XML element\n  structure and finding certain elements from it for for further analysis\n  (e.g. `Parse XML` and `Get Element` keywords).\n- Getting text or attributes of elements\n  (e.g. `Get Element Text` and `Get Element Attribute`).\n- Directly verifying text, attributes, or whole elements\n  (e.g `Element Text Should Be` and `Elements Should Be Equal`).\n- Modifying XML and saving it (e.g. `Set Element Text`, `Add Element`\n  and `Save XML`).\n\n== Table of contents ==\n\n- `Parsing XML`\n- `Using lxml`\n- `Example`\n- `Finding elements with xpath`\n- `Element attributes`\n- `Handling XML namespaces`\n- `Boolean arguments`\n- `Shortcuts`\n- `Keywords`\n\n= Parsing XML =\n\nXML can be parsed into an element structure using `Parse XML` keyword.\nIt accepts both paths to XML files and strings that contain XML. The\nkeyword returns the root element of the structure, which then contains\nother elements as its children and their children. Possible comments and\nprocessing instructions in the source XML are removed.\n\nXML is not validated during parsing even if has a schema defined. How\npossible doctype elements are handled otherwise depends on the used XML\nmodule and on the platform. The standard ElementTree strips doctypes\naltogether but when `using lxml` they are preserved when XML is saved.\nWith IronPython parsing XML with a doctype is not supported at all.\n\nThe element structure returned by `Parse XML`, as well as elements\nreturned by keywords such as `Get Element`, can be used as the ``source``\nargument with other keywords. In addition to an already parsed XML\nstructure, other keywords also accept paths to XML files and strings\ncontaining XML similarly as `Parse XML`. Notice that keywords that modify\nXML do not write those changes back to disk even if the source would be\ngiven as a path to a file. Changes must always saved explicitly using\n`Save XML` keyword.\n\nWhen the source is given as a path to a file, the forward slash character\n(``/``) can be used as the path separator regardless the operating system.\nOn Windows also the backslash works, but it the test data it needs to be\nescaped by doubling it (``\\\\``). Using the built-in variable ``${/}``\nnaturally works too.\n\n= Using lxml =\n\nBy default this library uses Python's standard\n[https://docs.python.org/2/library/xml.etree.elementtree.html|ElementTree]\nmodule for parsing XML, but it can be configured to use\n[http://lxml.de|lxml] module instead when `importing` the library.\nThe resulting element structure has same API regardless which module\nis used for parsing.\n\nThe main benefits of using lxml is that it supports richer xpath syntax\nthan the standard ElementTree and enables using `Evaluate Xpath` keyword.\nIt also preserves the doctype and possible namespace prefixes saving XML.\n\nThe lxml support is new in Robot Framework 2.8.5.\n\n= Example =\n\nThe following simple example demonstrates parsing XML and verifying its\ncontents both using keywords in this library and in _BuiltIn_ and\n_Collections_ libraries. How to use xpath expressions to find elements\nand what attributes the returned elements contain are discussed, with\nmore examples, in `Finding elements with xpath` and `Element attributes`\nsections.\n\nIn this example, as well as in many other examples in this documentation,\n``${XML}`` refers to the following example XML document. In practice\n``${XML}`` could either be a path to an XML file or it could contain the XML\nitself.\n\n| &lt;example&gt;\n|   &lt;first id=\"1\"&gt;text&lt;/first&gt;\n|   &lt;second id=\"2\"&gt;\n|     &lt;child/&gt;\n|   &lt;/second&gt;\n|   &lt;third&gt;\n|     &lt;child&gt;more text&lt;/child&gt;\n|     &lt;second id=\"child\"/&gt;\n|     &lt;child&gt;&lt;grandchild/&gt;&lt;/child&gt;\n|   &lt;/third&gt;\n|   &lt;html&gt;\n|     &lt;p&gt;\n|       Text with &lt;b&gt;bold&lt;/b&gt; and &lt;i&gt;italics&lt;/i&gt;.\n|     &lt;/p&gt;\n|   &lt;/html&gt;\n| &lt;/example&gt;\n\n| ${root} =                | `Parse XML`   | ${XML}  |       |             |\n| `Should Be Equal`        | ${root.tag}   | example |       |             |\n| ${first} =               | `Get Element` | ${root} | first |             |\n| `Should Be Equal`        | ${first.text} | text    |       |             |\n| `Dictionary Should Contain Key` | ${first.attrib}  | id    |             |\n| `Element Text Should Be` | ${first}      | text    |       |             |\n| `Element Attribute Should Be` | ${first} | id      | 1     |             |\n| `Element Attribute Should Be` | ${root}  | id      | 1     | xpath=first |\n| `Element Attribute Should Be` | ${XML}   | id      | 1     | xpath=first |\n\nNotice that in the example three last lines are equivalent. Which one to\nuse in practice depends on which other elements you need to get or verify.\nIf you only need to do one verification, using the last line alone would\nsuffice. If more verifications are needed, parsing the XML with `Parse XML`\nonly once would be more efficient.\n\n= Finding elements with xpath =\n\nElementTree, and thus also this library, supports finding elements using\nxpath expressions. ElementTree does not, however, support the full xpath\nsyntax, and what is supported depends on its version. ElementTree 1.3 that\nis distributed with Python 2.7 supports richer syntax than earlier versions.\n\nThe supported xpath syntax is explained below and\n[http://effbot.org/zone/element-xpath.htm|ElementTree documentation]\nprovides more details. In the examples ``${XML}`` refers to the same XML\nstructure as in the earlier example.\n\nIf lxml support is enabled when `importing` the library, the whole\n[http://www.w3.org/TR/xpath/|xpath 1.0 standard] is supported.\nThat includes everything listed below but also lot of other useful\nconstructs.\n\n== Tag names ==\n\nWhen just a single tag name is used, xpath matches all direct child\nelements that have that tag name.\n\n| ${elem} =          | `Get Element`  | ${XML}      | third |\n| `Should Be Equal`  | ${elem.tag}    | third       |       |\n| @{children} =      | `Get Elements` | ${elem}     | child |\n| `Length Should Be` | ${children}    | 2           |       |\n\n== Paths ==\n\nPaths are created by combining tag names with a forward slash (``/``). For\nexample, ``parent/child`` matches all ``child`` elements under ``parent``\nelement. Notice that if there are multiple ``parent`` elements that all\nhave ``child`` elements, ``parent/child`` xpath will match all these\n``child`` elements.\n\n| ${elem} =         | `Get Element` | ${XML}     | second/child            |\n| `Should Be Equal` | ${elem.tag}   | child      |                         |\n| ${elem} =         | `Get Element` | ${XML}     | third/child/grandchild  |\n| `Should Be Equal` | ${elem.tag}   | grandchild |                         |\n\n== Wildcards ==\n\nAn asterisk (``*``) can be used in paths instead of a tag name to denote\nany element.\n\n| @{children} =      | `Get Elements` | ${XML} | */child |\n| `Length Should Be` | ${children}    | 3      |         |\n\n== Current element ==\n\nThe current element is denoted with a dot (``.``). Normally the current\nelement is implicit and does not need to be included in the xpath.\n\n== Parent element ==\n\nThe parent element of another element is denoted with two dots (``..``).\nNotice that it is not possible to refer to the parent of the current\nelement. This syntax is supported only in ElementTree 1.3 (i.e.\nPython/Jython 2.7 and newer).\n\n| ${elem} =         | `Get Element` | ${XML} | */second/.. |\n| `Should Be Equal` | ${elem.tag}   | third  |             |\n\n== Search all sub elements ==\n\nTwo forward slashes (``//``) mean that all sub elements, not only the\ndirect children, are searched. If the search is started from the current\nelement, an explicit dot is required.\n\n| @{elements} =      | `Get Elements` | ${XML} | .//second |\n| `Length Should Be` | ${elements}    | 2      |           |\n| ${b} =             | `Get Element`  | ${XML} | html//b   |\n| `Should Be Equal`  | ${b.text}      | bold   |           |\n\n== Predicates ==\n\nPredicates allow selecting elements using also other criteria than tag\nnames, for example, attributes or position. They are specified after the\nnormal tag name or path using syntax ``path[predicate]``. The path can have\nwildcards and other special syntax explained above.\n\nWhat predicates ElementTree supports is explained in the table below.\nNotice that predicates in general are supported only in ElementTree 1.3\n(i.e. Python/Jython 2.7 and newer).\n\n|  = Predicate =  |             = Matches =           |    = Example =     |\n| @attrib         | Elements with attribute ``attrib``. | second[@id]        |\n| @attrib=\"value\" | Elements with attribute ``attrib`` having value ``value``. | *[@id=\"2\"] |\n| position        | Elements at the specified position. Position can be an integer (starting from 1), expression ``last()``, or relative expression like ``last() - 1``. | third/child[1] |\n| tag             | Elements with a child element named ``tag``. | third/child[grandchild] |\n\nPredicates can also be stacked like ``path[predicate1][predicate2]``.\nA limitation is that possible position predicate must always be first.\n\n= Element attributes =\n\nAll keywords returning elements, such as `Parse XML`, and `Get Element`,\nreturn ElementTree's\n[http://docs.python.org/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element|Element objects].\nThese elements can be used as inputs for other keywords, but they also\ncontain several useful attributes that can be accessed directly using\nthe extended variable syntax.\n\nThe attributes that are both useful and convenient to use in the test\ndata are explained below. Also other attributes, including methods, can\nbe accessed, but that is typically better to do in custom libraries than\ndirectly in the test data.\n\nThe examples use the same ``${XML}`` structure as the earlier examples.\n\n== tag ==\n\nThe tag of the element.\n\n| ${root} =         | `Parse XML` | ${XML}  |\n| `Should Be Equal` | ${root.tag} | example |\n\n== text ==\n\nThe text that the element contains or Python ``None`` if the element has no\ntext. Notice that the text _does not_ contain texts of possible child\nelements nor text after or between children. Notice also that in XML\nwhitespace is significant, so the text contains also possible indentation\nand newlines. To get also text of the possible children, optionally\nwhitespace normalized, use `Get Element Text` keyword.\n\n| ${1st} =          | `Get Element` | ${XML}  | first        |\n| `Should Be Equal` | ${1st.text}   | text    |              |\n| ${2nd} =          | `Get Element` | ${XML}  | second/child |\n| `Should Be Equal` | ${2nd.text}   | ${NONE} |              |\n| ${p} =            | `Get Element` | ${XML}  | html/p       |\n| `Should Be Equal` | ${p.text}     | \\n${SPACE*6}Text with${SPACE} |\n\n== tail ==\n\nThe text after the element before the next opening or closing tag. Python\n``None`` if the element has no tail. Similarly as with ``text``, also\n``tail`` contains possible indentation and newlines.\n\n| ${b} =            | `Get Element` | ${XML}  | html/p/b  |\n| `Should Be Equal` | ${b.tail}     | ${SPACE}and${SPACE} |\n\n== attrib ==\n\nA Python dictionary containing attributes of the element.\n\n| ${2nd} =          | `Get Element`       | ${XML} | second |\n| `Should Be Equal` | ${2nd.attrib['id']} | 2      |        |\n| ${3rd} =          | `Get Element`       | ${XML} | third  |\n| `Should Be Empty` | ${3rd.attrib}       |        |        |\n\n= Handling XML namespaces =\n\nElementTree and lxml handle possible namespaces in XML documents by adding\nthe namespace URI to tag names in so called Clark Notation. That is\ninconvenient especially with xpaths, and by default this library strips\nthose namespaces away and moves them to ``xmlns`` attribute instead. That\ncan be avoided by passing ``keep_clark_notation`` argument to `Parse XML`\nkeyword. Alternatively `Parse XML` supports stripping namespace information\naltogether by using ``strip_namespaces`` argument. The pros and cons of\ndifferent approaches are discussed in more detail below.\n\n== How ElementTree handles namespaces ==\n\nIf an XML document has namespaces, ElementTree adds namespace information\nto tag names in [http://www.jclark.com/xml/xmlns.htm|Clark Notation]\n(e.g. ``{http://ns.uri}tag``) and removes original ``xmlns`` attributes.\nThis is done both with default namespaces and with namespaces with a prefix.\nHow it works in practice is illustrated by the following example, where\n``${NS}`` variable contains this XML document:\n\n| &lt;xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n|                 xmlns=\"http://www.w3.org/1999/xhtml\"&gt;\n|   &lt;xsl:template match=\"/\"&gt;\n|     &lt;html&gt;&lt;/html&gt;\n|   &lt;/xsl:template&gt;\n| &lt;/xsl:stylesheet&gt;\n\n| ${root} = | `Parse XML` | ${NS} | keep_clark_notation=yes |\n| `Should Be Equal` | ${root.tag} | {http://www.w3.org/1999/XSL/Transform}stylesheet |\n| `Element Should Exist` | ${root} | {http://www.w3.org/1999/XSL/Transform}template/{http://www.w3.org/1999/xhtml}html |\n| `Should Be Empty` | ${root.attrib} |\n\nAs you can see, including the namespace URI in tag names makes xpaths\nreally long and complex.\n\nIf you save the XML, ElementTree moves namespace information back to\n``xmlns`` attributes. Unfortunately it does not restore the original\nprefixes:\n\n| &lt;ns0:stylesheet xmlns:ns0=\"http://www.w3.org/1999/XSL/Transform\"&gt;\n|   &lt;ns0:template match=\"/\"&gt;\n|     &lt;ns1:html xmlns:ns1=\"http://www.w3.org/1999/xhtml\"&gt;&lt;/ns1:html&gt;\n|   &lt;/ns0:template&gt;\n| &lt;/ns0:stylesheet&gt;\n\nThe resulting output is semantically same as the original, but mangling\nprefixes like this may still not be desirable. Notice also that the actual\noutput depends slightly on ElementTree version.\n\n== Default namespace handling ==\n\nBecause the way ElementTree handles namespaces makes xpaths so complicated,\nthis library, by default, strips namespaces from tag names and moves that\ninformation back to ``xmlns`` attributes. How this works in practice is\nshown by the example below, where ``${NS}`` variable contains the same XML\ndocument as in the previous example.\n\n| ${root} = | `Parse XML` | ${NS} |\n| `Should Be Equal` | ${root.tag} | stylesheet |\n| `Element Should Exist` | ${root} | template/html |\n| `Element Attribute Should Be` | ${root} | xmlns | http://www.w3.org/1999/XSL/Transform |\n| `Element Attribute Should Be` | ${root} | xmlns | http://www.w3.org/1999/xhtml | xpath=template/html |\n\nNow that tags do not contain namespace information, xpaths are simple again.\n\nA minor limitation of this approach is that namespace prefixes are lost.\nAs a result the saved output is not exactly same as the original one in\nthis case either:\n\n| &lt;stylesheet xmlns=\"http://www.w3.org/1999/XSL/Transform\"&gt;\n|   &lt;template match=\"/\"&gt;\n|     &lt;html xmlns=\"http://www.w3.org/1999/xhtml\"&gt;&lt;/html&gt;\n|   &lt;/template&gt;\n| &lt;/stylesheet&gt;\n\nAlso this output is semantically same as the original. If the original XML\nhad only default namespaces, the output would also look identical.\n\n== Namespaces when using lxml ==\n\nThis library handles namespaces same way both when `using lxml` and when\nnot using it. There are, however, differences how lxml internally handles\nnamespaces compared to the standard ElementTree. The main difference is\nthat lxml stores information about namespace prefixes and they are thus\npreserved if XML is saved. Another visible difference is that lxml includes\nnamespace information in child elements got with `Get Element` if the\nparent element has namespaces.\n\n== Stripping namespaces altogether ==\n\nBecause namespaces often add unnecessary complexity, `Parse XML` supports\nstripping them altogether by using ``strip_namespaces=True``. When this\noption is enabled, namespaces are not shown anywhere nor are they included\nif XML is saved.\n\n== Attribute namespaces ==\n\nAttributes in XML documents are, by default, in the same namespaces as\nthe element they belong to. It is possible to use different namespaces\nby using prefixes, but this is pretty rare.\n\nIf an attribute has a namespace prefix, ElementTree will replace it with\nClark Notation the same way it handles elements. Because stripping\nnamespaces from attributes could cause attribute conflicts, this library\ndoes not handle attribute namespaces at all. Thus the following example\nworks the same way regardless how namespaces are handled.\n\n| ${root} = | `Parse XML` | &lt;root id=\"1\" ns:id=\"2\" xmlns:ns=\"http://my.ns\"/&gt; |\n| `Element Attribute Should Be` | ${root} | id | 1 |\n| `Element Attribute Should Be` | ${root} | {http://my.ns}id | 2 |\n\n= Boolean arguments =\n\nSome keywords accept arguments that are handled as Boolean values true or\nfalse. If such an argument is given as a string, it is considered false if\nit is either an empty string or case-insensitively equal to ``false``,\n``none`` or ``no``. Other strings are considered true regardless\ntheir value, and other argument types are tested using the same\n[http://docs.python.org/2/library/stdtypes.html#truth-value-testing|rules\nas in Python].\n\nTrue examples:\n| `Parse XML` | ${XML} | keep_clark_notation=True    | # Strings are generally true.    |\n| `Parse XML` | ${XML} | keep_clark_notation=yes     | # Same as the above.             |\n| `Parse XML` | ${XML} | keep_clark_notation=${TRUE} | # Python ``True`` is true.       |\n| `Parse XML` | ${XML} | keep_clark_notation=${42}   | # Numbers other than 0 are true. |\n\nFalse examples:\n| `Parse XML` | ${XML} | keep_clark_notation=False    | # String ``false`` is false.   |\n| `Parse XML` | ${XML} | keep_clark_notation=no       | # Also string ``no`` is false. |\n| `Parse XML` | ${XML} | keep_clark_notation=${EMPTY} | # Empty string is false.       |\n| `Parse XML` | ${XML} | keep_clark_notation=${FALSE} | # Python ``False`` is false.   |\n\nPrior to Robot Framework 2.9, all non-empty strings, including ``false``\nand ``no``, were considered to be true. Considering ``none`` false is\nnew in Robot Framework 3.0.3.</doc>\n<init>\n<arguments>\n<arg>use_lxml=False</arg>\n</arguments>\n<doc>Import library with optionally lxml mode enabled.\n\nBy default this library uses Python's standard\n[https://docs.python.org/2/library/xml.etree.elementtree.html|ElementTree]\nmodule for parsing XML. If ``use_lxml`` argument is given a true value\n(see `Boolean arguments`), the library will use [http://lxml.de|lxml]\nmodule instead. See `Using lxml` section for benefits provided by lxml.\n\nUsing lxml requires that the lxml module is installed on the system.\nIf lxml mode is enabled but the module is not installed, this library\nwill emit a warning and revert back to using the standard ElementTree.\n\nThe support for lxml is new in Robot Framework 2.8.5.</doc>\n<tags>\n</tags>\n</init>\n<kw name=\"Add Element\">\n<arguments>\n<arg>source</arg>\n<arg>element</arg>\n<arg>index=None</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Adds a child element to the specified element.\n\nThe element to whom to add the new element is specified using ``source``\nand ``xpath``. They have exactly the same semantics as with `Get Element`\nkeyword. The resulting XML structure is returned, and if the ``source``\nis an already parsed XML structure, it is also modified in place.\n\nThe ``element`` to add can be specified as a path to an XML file or\nas a string containing XML, or it can be an already parsed XML element.\nThe element is copied before adding so modifying either the original\nor the added element has no effect on the other\n.\nThe element is added as the last child by default, but a custom index\ncan be used to alter the position. Indices start from zero (0 = first\nposition, 1 = second position, etc.), and negative numbers refer to\npositions at the end (-1 = second last position, -2 = third last, etc.).\n\nExamples using ``${XML}`` structure from `Example`:\n| Add Element | ${XML} | &lt;new id=\"x\"&gt;&lt;c1/&gt;&lt;/new&gt; |\n| Add Element | ${XML} | &lt;c2/&gt; | xpath=new |\n| Add Element | ${XML} | &lt;c3/&gt; | index=1 | xpath=new |\n| ${new} = | Get Element | ${XML} | new |\n| Elements Should Be Equal | ${new} | &lt;new id=\"x\"&gt;&lt;c1/&gt;&lt;c3/&gt;&lt;c2/&gt;&lt;/new&gt; |\n\nUse `Remove Element` or `Remove Elements` to remove elements.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Clear Element\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=.</arg>\n<arg>clear_tail=False</arg>\n</arguments>\n<doc>Clears the contents of the specified element.\n\nThe element to clear is specified using ``source`` and ``xpath``. They\nhave exactly the same semantics as with `Get Element` keyword.\nThe resulting XML structure is returned, and if the ``source`` is\nan already parsed XML structure, it is also modified in place.\n\nClearing the element means removing its text, attributes, and children.\nElement's tail text is not removed by default, but that can be changed\nby giving ``clear_tail`` a true value (see `Boolean arguments`). See\n`Element attributes` section for more information about tail in\ngeneral.\n\nExamples using ``${XML}`` structure from `Example`:\n| Clear Element            | ${XML}   | xpath=first |\n| ${first} = | Get Element | ${XML}   | xpath=first |\n| Elements Should Be Equal | ${first} | &lt;first/&gt;    |\n| Clear Element            | ${XML}   | xpath=html/p/b | clear_tail=yes |\n| Element Text Should Be   | ${XML}   | Text with italics. | xpath=html/p | normalize_whitespace=yes |\n| Clear Element            | ${XML}   |\n| Elements Should Be Equal | ${XML}   | &lt;example/&gt; |\n\nUse `Remove Element` to remove the whole element.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Copy Element\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Returns a copy of the specified element.\n\nThe element to copy is specified using ``source`` and ``xpath``. They\nhave exactly the same semantics as with `Get Element` keyword.\n\nIf the copy or the original element is modified afterwards, the changes\nhave no effect on the other.\n\nExamples using ``${XML}`` structure from `Example`:\n| ${elem} =  | Get Element  | ${XML}  | xpath=first |\n| ${copy1} = | Copy Element | ${elem} |\n| ${copy2} = | Copy Element | ${XML}  | xpath=first |\n| Set Element Text         | ${XML}   | new text    | xpath=first      |\n| Set Element Attribute    | ${copy1} | id          | new              |\n| Elements Should Be Equal | ${elem}  | &lt;first id=\"1\"&gt;new text&lt;/first&gt; |\n| Elements Should Be Equal | ${copy1} | &lt;first id=\"new\"&gt;text&lt;/first&gt;   |\n| Elements Should Be Equal | ${copy2} | &lt;first id=\"1\"&gt;text&lt;/first&gt;     |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Attribute Should Be\">\n<arguments>\n<arg>source</arg>\n<arg>name</arg>\n<arg>expected</arg>\n<arg>xpath=.</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies that the specified attribute is ``expected``.\n\nThe element whose attribute is verified is specified using ``source``\nand ``xpath``. They have exactly the same semantics as with\n`Get Element` keyword.\n\nThe keyword passes if the attribute ``name`` of the element is equal to\nthe ``expected`` value, and otherwise it fails. The default error\nmessage can be overridden with the ``message`` argument.\n\nTo test that the element does not have a certain attribute, Python\n``None`` (i.e. variable ``${NONE}``) can be used as the expected value.\nA cleaner alternative is using `Element Should Not Have Attribute`.\n\nExamples using ``${XML}`` structure from `Example`:\n| Element Attribute Should Be | ${XML} | id | 1       | xpath=first |\n| Element Attribute Should Be | ${XML} | id | ${NONE} |             |\n\nSee also `Element Attribute Should Match` and `Get Element Attribute`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Attribute Should Match\">\n<arguments>\n<arg>source</arg>\n<arg>name</arg>\n<arg>pattern</arg>\n<arg>xpath=.</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies that the specified attribute matches ``expected``.\n\nThis keyword works exactly like `Element Attribute Should Be` except\nthat the expected value can be given as a pattern that the attribute of\nthe element must match.\n\nPattern matching is similar as matching files in a shell, and it is\nalways case-sensitive. In the pattern, '*' matches anything and '?'\nmatches any single character.\n\nExamples using ``${XML}`` structure from `Example`:\n| Element Attribute Should Match | ${XML} | id | ?   | xpath=first |\n| Element Attribute Should Match | ${XML} | id | c*d | xpath=third/second |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Exist\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=.</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies that one or more element match the given ``xpath``.\n\nArguments ``source`` and ``xpath`` have exactly the same semantics as\nwith `Get Elements` keyword. Keyword passes if the ``xpath`` matches\none or more elements in the ``source``. The default error message can\nbe overridden with the ``message`` argument.\n\nSee also `Element Should Not Exist` as well as `Get Element Count`\nthat this keyword uses internally.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Not Exist\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=.</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies that no element match the given ``xpath``.\n\nArguments ``source`` and ``xpath`` have exactly the same semantics as\nwith `Get Elements` keyword. Keyword fails if the ``xpath`` matches any\nelement in the ``source``. The default error message can be overridden\nwith the ``message`` argument.\n\nSee also `Element Should Exist` as well as `Get Element Count`\nthat this keyword uses internally.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Should Not Have Attribute\">\n<arguments>\n<arg>source</arg>\n<arg>name</arg>\n<arg>xpath=.</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies that the specified element does not have  attribute ``name``.\n\nThe element whose attribute is verified is specified using ``source``\nand ``xpath``. They have exactly the same semantics as with\n`Get Element` keyword.\n\nThe keyword fails if the specified element has attribute ``name``. The\ndefault error message can be overridden with the ``message`` argument.\n\nExamples using ``${XML}`` structure from `Example`:\n| Element Should Not Have Attribute | ${XML} | id  |\n| Element Should Not Have Attribute | ${XML} | xxx | xpath=first |\n\nSee also `Get Element Attribute`, `Get Element Attributes`,\n`Element Text Should Be` and `Element Text Should Match`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Text Should Be\">\n<arguments>\n<arg>source</arg>\n<arg>expected</arg>\n<arg>xpath=.</arg>\n<arg>normalize_whitespace=False</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies that the text of the specified element is ``expected``.\n\nThe element whose text is verified is specified using ``source`` and\n``xpath``. They have exactly the same semantics as with `Get Element`\nkeyword.\n\nThe text to verify is got from the specified element using the same\nlogic as with `Get Element Text`. This includes optional whitespace\nnormalization using the ``normalize_whitespace`` option.\n\nThe keyword passes if the text of the element is equal to the\n``expected`` value, and otherwise it fails. The default error message\ncan be overridden with the ``message`` argument.  Use `Element Text\nShould Match` to verify the text against a pattern instead of an exact\nvalue.\n\nExamples using ``${XML}`` structure from `Example`:\n| Element Text Should Be | ${XML}       | text     | xpath=first      |\n| Element Text Should Be | ${XML}       | ${EMPTY} | xpath=second/child |\n| ${paragraph} =         | Get Element  | ${XML}   | xpath=html/p     |\n| Element Text Should Be | ${paragraph} | Text with bold and italics. | normalize_whitespace=yes |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element Text Should Match\">\n<arguments>\n<arg>source</arg>\n<arg>pattern</arg>\n<arg>xpath=.</arg>\n<arg>normalize_whitespace=False</arg>\n<arg>message=None</arg>\n</arguments>\n<doc>Verifies that the text of the specified element matches ``expected``.\n\nThis keyword works exactly like `Element Text Should Be` except that\nthe expected value can be given as a pattern that the text of the\nelement must match.\n\nPattern matching is similar as matching files in a shell, and it is\nalways case-sensitive. In the pattern, '*' matches anything and '?'\nmatches any single character.\n\nExamples using ``${XML}`` structure from `Example`:\n| Element Text Should Match | ${XML}       | t???   | xpath=first  |\n| ${paragraph} =            | Get Element  | ${XML} | xpath=html/p |\n| Element Text Should Match | ${paragraph} | Text with * and *. | normalize_whitespace=yes |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Element To String\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=.</arg>\n<arg>encoding=None</arg>\n</arguments>\n<doc>Returns the string representation of the specified element.\n\nThe element to convert to a string is specified using ``source`` and\n``xpath``. They have exactly the same semantics as with `Get Element`\nkeyword.\n\nBy default the string is returned as Unicode. If ``encoding`` argument\nis given any value, the string is returned as bytes in the specified\nencoding. The resulting string never contains the XML declaration.\n\nSee also `Log Element` and `Save XML`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Elements Should Be Equal\">\n<arguments>\n<arg>source</arg>\n<arg>expected</arg>\n<arg>exclude_children=False</arg>\n<arg>normalize_whitespace=False</arg>\n</arguments>\n<doc>Verifies that the given ``source`` element is equal to ``expected``.\n\nBoth ``source`` and ``expected`` can be given as a path to an XML file,\nas a string containing XML, or as an already parsed XML element\nstructure. See `introduction` for more information about parsing XML in\ngeneral.\n\nThe keyword passes if the ``source`` element and ``expected`` element\nare equal. This includes testing the tag names, texts, and attributes\nof the elements. By default also child elements are verified the same\nway, but this can be disabled by setting ``exclude_children`` to a\ntrue value (see `Boolean arguments`).\n\nAll texts inside the given elements are verified, but possible text\noutside them is not. By default texts must match exactly, but setting\n``normalize_whitespace`` to a true value makes text verification\nindependent on newlines, tabs, and the amount of spaces. For more\ndetails about handling text see `Get Element Text` keyword and\ndiscussion about elements' `text` and `tail` attributes in the\n`introduction`.\n\nExamples using ``${XML}`` structure from `Example`:\n| ${first} =               | Get Element | ${XML} | first             |\n| Elements Should Be Equal | ${first}    | &lt;first id=\"1\"&gt;text&lt;/first&gt; |\n| ${p} =                   | Get Element | ${XML} | html/p            |\n| Elements Should Be Equal | ${p} | &lt;p&gt;Text with &lt;b&gt;bold&lt;/b&gt; and &lt;i&gt;italics&lt;/i&gt;.&lt;/p&gt; | normalize_whitespace=yes |\n| Elements Should Be Equal | ${p} | &lt;p&gt;Text with&lt;/p&gt; | exclude | normalize |\n\nThe last example may look a bit strange because the ``&lt;p&gt;`` element\nonly has text ``Text with``. The reason is that rest of the text\ninside ``&lt;p&gt;`` actually belongs to the child elements. This includes\nthe ``.`` at the end that is the `tail` text of the ``&lt;i&gt;`` element.\n\nSee also `Elements Should Match`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Elements Should Match\">\n<arguments>\n<arg>source</arg>\n<arg>expected</arg>\n<arg>exclude_children=False</arg>\n<arg>normalize_whitespace=False</arg>\n</arguments>\n<doc>Verifies that the given ``source`` element matches ``expected``.\n\nThis keyword works exactly like `Elements Should Be Equal` except that\ntexts and attribute values in the expected value can be given as\npatterns.\n\nPattern matching is similar as matching files in a shell, and it is\nalways case-sensitive. In the pattern, '*' matches anything and '?'\nmatches any single character.\n\nExamples using ``${XML}`` structure from `Example`:\n| ${first} =            | Get Element | ${XML} | first          |\n| Elements Should Match | ${first}    | &lt;first id=\"?\"&gt;*&lt;/first&gt; |\n\nSee `Elements Should Be Equal` for more examples.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Evaluate Xpath\">\n<arguments>\n<arg>source</arg>\n<arg>expression</arg>\n<arg>context=.</arg>\n</arguments>\n<doc>Evaluates the given xpath expression and returns results.\n\nThe element in which context the expression is executed is specified\nusing ``source`` and ``context`` arguments. They have exactly the same\nsemantics as ``source`` and ``xpath`` arguments have with `Get Element`\nkeyword.\n\nThe xpath expression to evaluate is given as ``expression`` argument.\nThe result of the evaluation is returned as-is.\n\nExamples using ``${XML}`` structure from `Example`:\n| ${count} =      | Evaluate Xpath | ${XML}  | count(third/*) |\n| Should Be Equal | ${count}       | ${3}    |\n| ${text} =       | Evaluate Xpath | ${XML}  | string(descendant::second[last()]/@id) |\n| Should Be Equal | ${text}        | child   |\n| ${bold} =       | Evaluate Xpath | ${XML}  | boolean(preceding-sibling::*[1] = 'bold') | context=html/p/i |\n| Should Be Equal | ${bold}        | ${True} |\n\nThis keyword works only if lxml mode is taken into use when `importing`\nthe library. New in Robot Framework 2.8.5.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Child Elements\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Returns the child elements of the specified element as a list.\n\nThe element whose children to return is specified using ``source`` and\n``xpath``. They have exactly the same semantics as with `Get Element`\nkeyword.\n\nAll the direct child elements of the specified element are returned.\nIf the element has no children, an empty list is returned.\n\nExamples using ``${XML}`` structure from `Example`:\n| ${children} =    | Get Child Elements | ${XML} |             |\n| Length Should Be | ${children}        | 4      |             |\n| ${children} =    | Get Child Elements | ${XML} | xpath=first |\n| Should Be Empty  | ${children}        |        |             |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Element\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Returns an element in the ``source`` matching the ``xpath``.\n\nThe ``source`` can be a path to an XML file, a string containing XML, or\nan already parsed XML element. The ``xpath`` specifies which element to\nfind. See the `introduction` for more details about both the possible\nsources and the supported xpath syntax.\n\nThe keyword fails if more, or less, than one element matches the\n``xpath``. Use `Get Elements` if you want all matching elements to be\nreturned.\n\nExamples using ``${XML}`` structure from `Example`:\n| ${element} = | Get Element | ${XML}     | second |\n| ${child} =   | Get Element | ${element} | child  |\n\n`Parse XML` is recommended for parsing XML when the whole structure\nis needed. It must be used if there is a need to configure how XML\nnamespaces are handled.\n\nMany other keywords use this keyword internally, and keywords modifying\nXML are typically documented to both to modify the given source and\nto return it. Modifying the source does not apply if the source is\ngiven as a string. The XML structure parsed based on the string and\nthen modified is nevertheless returned.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Element Attribute\">\n<arguments>\n<arg>source</arg>\n<arg>name</arg>\n<arg>xpath=.</arg>\n<arg>default=None</arg>\n</arguments>\n<doc>Returns the named attribute of the specified element.\n\nThe element whose attribute to return is specified using ``source`` and\n``xpath``. They have exactly the same semantics as with `Get Element`\nkeyword.\n\nThe value of the attribute ``name`` of the specified element is returned.\nIf the element does not have such element, the ``default`` value is\nreturned instead.\n\nExamples using ``${XML}`` structure from `Example`:\n| ${attribute} =  | Get Element Attribute | ${XML} | id | xpath=first |\n| Should Be Equal | ${attribute}          | 1      |    |             |\n| ${attribute} =  | Get Element Attribute | ${XML} | xx | xpath=first | default=value |\n| Should Be Equal | ${attribute}          | value  |    |             |\n\nSee also `Get Element Attributes`, `Element Attribute Should Be`,\n`Element Attribute Should Match` and `Element Should Not Have Attribute`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Element Attributes\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Returns all attributes of the specified element.\n\nThe element whose attributes to return is specified using ``source`` and\n``xpath``. They have exactly the same semantics as with `Get Element`\nkeyword.\n\nAttributes are returned as a Python dictionary. It is a copy of the\noriginal attributes so modifying it has no effect on the XML structure.\n\nExamples using ``${XML}`` structure from `Example`:\n| ${attributes} = | Get Element Attributes      | ${XML} | first |\n| Dictionary Should Contain Key | ${attributes} | id     |       |\n| ${attributes} = | Get Element Attributes      | ${XML} | third |\n| Should Be Empty | ${attributes}               |        |       |\n\nUse `Get Element Attribute` to get the value of a single attribute.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Element Count\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Returns and logs how many elements the given ``xpath`` matches.\n\nArguments ``source`` and ``xpath`` have exactly the same semantics as\nwith `Get Elements` keyword that this keyword uses internally.\n\nSee also `Element Should Exist` and `Element Should Not Exist`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Element Text\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=.</arg>\n<arg>normalize_whitespace=False</arg>\n</arguments>\n<doc>Returns all text of the element, possibly whitespace normalized.\n\nThe element whose text to return is specified using ``source`` and\n``xpath``. They have exactly the same semantics as with `Get Element`\nkeyword.\n\nThis keyword returns all the text of the specified element, including\nall the text its children and grandchildren contain. If the element\nhas no text, an empty string is returned. The returned text is thus not\nalways the same as the `text` attribute of the element.\n\nBy default all whitespace, including newlines and indentation, inside\nthe element is returned as-is. If ``normalize_whitespace`` is given\na true value (see `Boolean arguments`), then leading and trailing\nwhitespace is stripped, newlines and tabs converted to spaces, and\nmultiple spaces collapsed into one. This is especially useful when\ndealing with HTML data.\n\nExamples using ``${XML}`` structure from `Example`:\n| ${text} =       | Get Element Text | ${XML}       | first        |\n| Should Be Equal | ${text}          | text         |              |\n| ${text} =       | Get Element Text | ${XML}       | second/child |\n| Should Be Empty | ${text}          |              |              |\n| ${paragraph} =  | Get Element      | ${XML}       | html/p       |\n| ${text} =       | Get Element Text | ${paragraph} | normalize_whitespace=yes |\n| Should Be Equal | ${text}          | Text with bold and italics. |\n\nSee also `Get Elements Texts`, `Element Text Should Be` and\n`Element Text Should Match`.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Elements\">\n<arguments>\n<arg>source</arg>\n<arg>xpath</arg>\n</arguments>\n<doc>Returns a list of elements in the ``source`` matching the ``xpath``.\n\nThe ``source`` can be a path to an XML file, a string containing XML, or\nan already parsed XML element. The ``xpath`` specifies which element to\nfind. See the `introduction` for more details.\n\nElements matching the ``xpath`` are returned as a list. If no elements\nmatch, an empty list is returned. Use `Get Element` if you want to get\nexactly one match.\n\nExamples using ``${XML}`` structure from `Example`:\n| ${children} =    | Get Elements | ${XML} | third/child |\n| Length Should Be | ${children}  | 2      |             |\n| ${children} =    | Get Elements | ${XML} | first/child |\n| Should Be Empty  |  ${children} |        |             |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Get Elements Texts\">\n<arguments>\n<arg>source</arg>\n<arg>xpath</arg>\n<arg>normalize_whitespace=False</arg>\n</arguments>\n<doc>Returns text of all elements matching ``xpath`` as a list.\n\nThe elements whose text to return is specified using ``source`` and\n``xpath``. They have exactly the same semantics as with `Get Elements`\nkeyword.\n\nThe text of the matched elements is returned using the same logic\nas with `Get Element Text`. This includes optional whitespace\nnormalization using the ``normalize_whitespace`` option.\n\nExamples using ``${XML}`` structure from `Example`:\n| @{texts} =       | Get Elements Texts | ${XML}    | third/child |\n| Length Should Be | ${texts}           | 2         |             |\n| Should Be Equal  | @{texts}[0]        | more text |             |\n| Should Be Equal  | @{texts}[1]        | ${EMPTY}  |             |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Log Element\">\n<arguments>\n<arg>source</arg>\n<arg>level=INFO</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Logs the string representation of the specified element.\n\nThe element specified with ``source`` and ``xpath`` is first converted\ninto a string using `Element To String` keyword internally. The\nresulting string is then logged using the given ``level``.\n\nThe logged string is also returned.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Parse Xml\">\n<arguments>\n<arg>source</arg>\n<arg>keep_clark_notation=False</arg>\n<arg>strip_namespaces=False</arg>\n</arguments>\n<doc>Parses the given XML file or string into an element structure.\n\nThe ``source`` can either be a path to an XML file or a string\ncontaining XML. In both cases the XML is parsed into ElementTree\n[http://docs.python.org/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element|element structure]\nand the root element is returned. Possible comments and processing\ninstructions in the source XML are removed.\n\nAs discussed in `Handling XML namespaces` section, this keyword, by\ndefault, removes namespace information ElementTree has added to tag\nnames and moves it into ``xmlns`` attributes. This typically eases\nhandling XML documents with namespaces considerably. If you do not\nwant that to happen, or want to avoid the small overhead of going\nthrough the element structure when your XML does not have namespaces,\nyou can disable this feature by giving ``keep_clark_notation`` argument\na true value (see `Boolean arguments`).\n\nIf you want to strip namespace information altogether so that it is\nnot included even if XML is saved, you can give a true value to\n``strip_namespaces`` argument. This functionality is new in Robot\nFramework 3.0.2.\n\nExamples:\n| ${root} = | Parse XML | &lt;root&gt;&lt;child/&gt;&lt;/root&gt; |\n| ${xml} = | Parse XML | ${CURDIR}/test.xml | keep_clark_notation=True |\n| ${xml} = | Parse XML | ${CURDIR}/test.xml | strip_namespaces=True |\n\nUse `Get Element` keyword if you want to get a certain element and not\nthe whole structure. See `Parsing XML` section for more details and\nexamples.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Element\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=</arg>\n<arg>remove_tail=False</arg>\n</arguments>\n<doc>Removes the element matching ``xpath`` from the ``source`` structure.\n\nThe element to remove from the ``source`` is specified with ``xpath``\nusing the same semantics as with `Get Element` keyword. The resulting\nXML structure is returned, and if the ``source`` is an already parsed\nXML structure, it is also modified in place.\n\nThe keyword fails if ``xpath`` does not match exactly one element.\nUse `Remove Elements` to remove all matched elements.\n\nElement's tail text is not removed by default, but that can be changed\nby giving ``remove_tail`` a true value (see `Boolean arguments`). See\n`Element attributes` section for more information about `tail` in\ngeneral.\n\nExamples using ``${XML}`` structure from `Example`:\n| Remove Element           | ${XML} | xpath=second |\n| Element Should Not Exist | ${XML} | xpath=second |\n| Remove Element           | ${XML} | xpath=html/p/b | remove_tail=yes |\n| Element Text Should Be   | ${XML} | Text with italics. | xpath=html/p | normalize_whitespace=yes |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Element Attribute\">\n<arguments>\n<arg>source</arg>\n<arg>name</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Removes attribute ``name`` from the specified element.\n\nThe element whose attribute to remove is specified using ``source`` and\n``xpath``. They have exactly the same semantics as with `Get Element`\nkeyword. The resulting XML structure is returned, and if the ``source``\nis an already parsed XML structure, it is also modified in place.\n\nIt is not a failure to remove a non-existing attribute. Use `Remove\nElement Attributes` to remove all attributes and `Set Element Attribute`\nto set them.\n\nExamples using ``${XML}`` structure from `Example`:\n| Remove Element Attribute          | ${XML} | id | xpath=first |\n| Element Should Not Have Attribute | ${XML} | id | xpath=first |\n\nCan only remove an attribute from a single element. Use `Remove Elements\nAttribute` to remove an attribute of multiple elements in one call.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Element Attributes\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Removes all attributes from the specified element.\n\nThe element whose attributes to remove is specified using ``source`` and\n``xpath``. They have exactly the same semantics as with `Get Element`\nkeyword. The resulting XML structure is returned, and if the ``source``\nis an already parsed XML structure, it is also modified in place.\n\nUse `Remove Element Attribute` to remove a single attribute and\n`Set Element Attribute` to set them.\n\nExamples using ``${XML}`` structure from `Example`:\n| Remove Element Attributes         | ${XML} | xpath=first |\n| Element Should Not Have Attribute | ${XML} | id | xpath=first |\n\nCan only remove attributes from a single element. Use `Remove Elements\nAttributes` to remove all attributes of multiple elements in one call.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Elements\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=</arg>\n<arg>remove_tail=False</arg>\n</arguments>\n<doc>Removes all elements matching ``xpath`` from the ``source`` structure.\n\nThe elements to remove from the ``source`` are specified with ``xpath``\nusing the same semantics as with `Get Elements` keyword. The resulting\nXML structure is returned, and if the ``source`` is an already parsed\nXML structure, it is also modified in place.\n\nIt is not a failure if ``xpath`` matches no elements. Use `Remove\nElement` to remove exactly one element.\n\nElement's tail text is not removed by default, but that can be changed\nby using ``remove_tail`` argument similarly as with `Remove Element`.\n\nExamples using ``${XML}`` structure from `Example`:\n| Remove Elements          | ${XML} | xpath=*/child      |\n| Element Should Not Exist | ${XML} | xpath=second/child |\n| Element Should Not Exist | ${XML} | xpath=third/child  |</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Elements Attribute\">\n<arguments>\n<arg>source</arg>\n<arg>name</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Removes attribute ``name`` from the specified elements.\n\nLike `Remove Element Attribute` but removes the attribute of all\nelements matching the given ``xpath``.\n\nNew in Robot Framework 2.8.6.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Remove Elements Attributes\">\n<arguments>\n<arg>source</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Removes all attributes from the specified elements.\n\nLike `Remove Element Attributes` but removes all attributes of all\nelements matching the given ``xpath``.\n\nNew in Robot Framework 2.8.6.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Save Xml\">\n<arguments>\n<arg>source</arg>\n<arg>path</arg>\n<arg>encoding=UTF-8</arg>\n</arguments>\n<doc>Saves the given element to the specified file.\n\nThe element to save is specified with ``source`` using the same\nsemantics as with `Get Element` keyword.\n\nThe file where the element is saved is denoted with ``path`` and the\nencoding to use with ``encoding``. The resulting file always contains\nthe XML declaration.\n\nThe resulting XML file may not be exactly the same as the original:\n- Comments and processing instructions are always stripped.\n- Possible doctype and namespace prefixes are only preserved when\n  `using lxml`.\n- Other small differences are possible depending on the ElementTree\n  or lxml version.\n\nUse `Element To String` if you just need a string representation of\nthe element.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Element Attribute\">\n<arguments>\n<arg>source</arg>\n<arg>name</arg>\n<arg>value</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Sets attribute ``name`` of the specified element to ``value``.\n\nThe element whose attribute to set is specified using ``source`` and\n``xpath``. They have exactly the same semantics as with `Get Element`\nkeyword. The resulting XML structure is returned, and if the ``source``\nis an already parsed XML structure, it is also modified in place.\n\nIt is possible to both set new attributes and to overwrite existing.\nUse `Remove Element Attribute` or `Remove Element Attributes` for\nremoving them.\n\nExamples using ``${XML}`` structure from `Example`:\n| Set Element Attribute       | ${XML} | attr | value |\n| Element Attribute Should Be | ${XML} | attr | value |\n| Set Element Attribute       | ${XML} | id   | new   | xpath=first |\n| Element Attribute Should Be | ${XML} | id   | new   | xpath=first |\n\nCan only set an attribute of a single element. Use `Set Elements\nAttribute` to set an attribute of multiple elements in one call.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Element Tag\">\n<arguments>\n<arg>source</arg>\n<arg>tag</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Sets the tag of the specified element.\n\nThe element whose tag to set is specified using ``source`` and\n``xpath``. They have exactly the same semantics as with `Get Element`\nkeyword. The resulting XML structure is returned, and if the ``source``\nis an already parsed XML structure, it is also modified in place.\n\nExamples using ``${XML}`` structure from `Example`:\n| Set Element Tag      | ${XML}     | newTag     |\n| Should Be Equal      | ${XML.tag} | newTag     |\n| Set Element Tag      | ${XML}     | xxx        | xpath=second/child |\n| Element Should Exist | ${XML}     | second/xxx |\n| Element Should Not Exist | ${XML} | second/child |\n\nCan only set the tag of a single element. Use `Set Elements Tag` to set\nthe tag of multiple elements in one call.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Element Text\">\n<arguments>\n<arg>source</arg>\n<arg>text=None</arg>\n<arg>tail=None</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Sets text and/or tail text of the specified element.\n\nThe element whose text to set is specified using ``source`` and\n``xpath``. They have exactly the same semantics as with `Get Element`\nkeyword. The resulting XML structure is returned, and if the ``source``\nis an already parsed XML structure, it is also modified in place.\n\nElement's text and tail text are changed only if new ``text`` and/or\n``tail`` values are given. See `Element attributes` section for more\ninformation about `text` and `tail` in general.\n\nExamples using ``${XML}`` structure from `Example`:\n| Set Element Text       | ${XML} | new text | xpath=first    |\n| Element Text Should Be | ${XML} | new text | xpath=first    |\n| Set Element Text       | ${XML} | tail=&amp;   | xpath=html/p/b |\n| Element Text Should Be | ${XML} | Text with bold&amp;italics. | xpath=html/p  | normalize_whitespace=yes |\n| Set Element Text       | ${XML} | slanted  | !! | xpath=html/p/i |\n| Element Text Should Be | ${XML} | Text with bold&amp;slanted!! | xpath=html/p  | normalize_whitespace=yes |\n\nCan only set the text/tail of a single element. Use `Set Elements Text`\nto set the text/tail of multiple elements in one call.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Elements Attribute\">\n<arguments>\n<arg>source</arg>\n<arg>name</arg>\n<arg>value</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Sets attribute ``name`` of the specified elements to ``value``.\n\nLike `Set Element Attribute` but sets the attribute of all elements\nmatching the given ``xpath``.\n\nNew in Robot Framework 2.8.6.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Elements Tag\">\n<arguments>\n<arg>source</arg>\n<arg>tag</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Sets the tag of the specified elements.\n\nLike `Set Element Tag` but sets the tag of all elements matching\nthe given ``xpath``.\n\nNew in Robot Framework 2.8.6.</doc>\n<tags>\n</tags>\n</kw>\n<kw name=\"Set Elements Text\">\n<arguments>\n<arg>source</arg>\n<arg>text=None</arg>\n<arg>tail=None</arg>\n<arg>xpath=.</arg>\n</arguments>\n<doc>Sets text and/or tail text of the specified elements.\n\nLike `Set Element Text` but sets the text or tail of all elements\nmatching the given ``xpath``.\n\nNew in Robot Framework 2.8.6.</doc>\n<tags>\n</tags>\n</kw>\n</keywordspec>\n"
  },
  {
    "path": "licenses/LICENSE-CodeMirror",
    "content": "MIT License\n\nCopyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "licenses/LICENSE-jquery",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2011-2018 Twitter, Inc.\nCopyright (c) 2011-2018 The Bootstrap Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "licenses/LICENSE-wide.html",
    "content": "\n\n\n\n\n\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n  <link rel=\"dns-prefetch\" href=\"https://assets-cdn.github.com\">\n  <link rel=\"dns-prefetch\" href=\"https://avatars0.githubusercontent.com\">\n  <link rel=\"dns-prefetch\" href=\"https://avatars1.githubusercontent.com\">\n  <link rel=\"dns-prefetch\" href=\"https://avatars2.githubusercontent.com\">\n  <link rel=\"dns-prefetch\" href=\"https://avatars3.githubusercontent.com\">\n  <link rel=\"dns-prefetch\" href=\"https://github-cloud.s3.amazonaws.com\">\n  <link rel=\"dns-prefetch\" href=\"https://user-images.githubusercontent.com/\">\n\n\n\n  <link crossorigin=\"anonymous\" media=\"all\" integrity=\"sha512-PkbtxdWDpLChpxtWQ0KbvJoef4XMYPq5pfd/ZmylYZTzXYpCfGwN9d+bsSKcmOJLwTkfjFkfj5wz3poDrhJoSQ==\" rel=\"stylesheet\" href=\"https://assets-cdn.github.com/assets/frameworks-f6e6ce21346c0d2eb22def1e8534afcb.css\" />\n  <link crossorigin=\"anonymous\" media=\"all\" integrity=\"sha512-LHNZGPA72iEyT2UIFOpxTPnfDcJ1Ecx8MKZgMzCJzkqfID/5niECnSBbRtDc4LDgbI3YDHu5dgs5mQiMmum6cA==\" rel=\"stylesheet\" href=\"https://assets-cdn.github.com/assets/github-caf1b1f61473986b3fdfa6e73e76a94f.css\" />\n  \n  \n  \n  \n\n  <meta name=\"viewport\" content=\"width=device-width\">\n  \n  <title>wide/LICENSE at master · b3log/wide</title>\n    <meta name=\"description\" content=\"GitHub is where people build software. More than 28 million people use GitHub to discover, fork, and contribute to over 85 million projects.\">\n    <link rel=\"search\" type=\"application/opensearchdescription+xml\" href=\"/opensearch.xml\" title=\"GitHub\">\n  <link rel=\"fluid-icon\" href=\"https://github.com/fluidicon.png\" title=\"GitHub\">\n  <meta property=\"fb:app_id\" content=\"1401488693436528\">\n\n    \n    <meta property=\"og:image\" content=\"https://avatars0.githubusercontent.com/u/1627618?s=400&amp;v=4\" /><meta property=\"og:site_name\" content=\"GitHub\" /><meta property=\"og:type\" content=\"object\" /><meta property=\"og:title\" content=\"b3log/wide\" /><meta property=\"og:url\" content=\"https://github.com/b3log/wide\" /><meta property=\"og:description\" content=\" :fireworks: 一个基于 Web 的 Go 语言 IDE。A Web-based IDE for teams using Go programming language/Golang. https://wide.b3log.org\" />\n\n  <link rel=\"assets\" href=\"https://assets-cdn.github.com/\">\n  <link rel=\"web-socket\" href=\"wss://live.github.com/_sockets/VjI6Mjc4NDU5MzI0OjUyMGY4ZDAyOWZiZGY1ZTU5NWI4MjdjODhhM2YxN2NkNDczOTIzZThiMTgwYjVjYWE5ZDk3YWM5N2NjOTMyM2Y=--7028d3943e55fb54f81cc258f1d160dfbaf1286d\">\n  <meta name=\"pjax-timeout\" content=\"1000\">\n  <link rel=\"sudo-modal\" href=\"/sessions/sudo_modal\">\n  <meta name=\"request-id\" content=\"6F7A:3189:B29B83:1056360:5B19FA4D\" data-pjax-transient>\n\n\n  \n\n  <meta name=\"selected-link\" value=\"repo_source\" data-pjax-transient>\n\n    <meta name=\"google-site-verification\" content=\"KT5gs8h0wvaagLKAVWq8bbeNwnZZK1r1XQysX3xurLU\">\n  <meta name=\"google-site-verification\" content=\"ZzhVyEFwb7w3e0-uOTltm8Jsck2F5StVihD0exw2fsA\">\n  <meta name=\"google-site-verification\" content=\"GXs5KoUUkNCoaAZn7wPN-t01Pywp9M3sEjnt_3_ZWPc\">\n    <meta name=\"google-analytics\" content=\"UA-3769691-2\">\n\n<meta name=\"octolytics-host\" content=\"collector.githubapp.com\" /><meta name=\"octolytics-app-id\" content=\"github\" /><meta name=\"octolytics-event-url\" content=\"https://collector.githubapp.com/github-external/browser_event\" /><meta name=\"octolytics-dimension-request_id\" content=\"6F7A:3189:B29B83:1056360:5B19FA4D\" /><meta name=\"octolytics-dimension-region_edge\" content=\"ap-southeast-1\" /><meta name=\"octolytics-dimension-region_render\" content=\"iad\" /><meta name=\"octolytics-actor-id\" content=\"10411707\" /><meta name=\"octolytics-actor-login\" content=\"small99\" /><meta name=\"octolytics-actor-hash\" content=\"d2767eee5e405834e9aeb889893b0ca4053f454dc86fe1926de7f69b6061a29e\" />\n<meta name=\"analytics-location\" content=\"/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show\" data-pjax-transient=\"true\" />\n\n\n\n\n  <meta class=\"js-ga-set\" name=\"dimension1\" content=\"Logged In\">\n\n\n  \n\n      <meta name=\"hostname\" content=\"github.com\">\n    <meta name=\"user-login\" content=\"small99\">\n\n      <meta name=\"expected-hostname\" content=\"github.com\">\n    <meta name=\"js-proxy-site-detection-payload\" content=\"ODM5Mjc0ZmM0NWFkZTkyZTdiMTg4M2QyNzlkMDMzNWZmM2YwNTQ5ZjZhMDNjNDEwNDczNDJlMDg3OGQ5NWE1Y3x7InJlbW90ZV9hZGRyZXNzIjoiMTEzLjExOS41OC4zIiwicmVxdWVzdF9pZCI6IjZGN0E6MzE4OTpCMjlCODM6MTA1NjM2MDo1QjE5RkE0RCIsInRpbWVzdGFtcCI6MTUyODQyOTEzOCwiaG9zdCI6ImdpdGh1Yi5jb20ifQ==\">\n\n    <meta name=\"enabled-features\" content=\"UNIVERSE_BANNER,FREE_TRIALS,MARKETPLACE_INSIGHTS,MARKETPLACE_INSIGHTS_CONVERSION_PERCENTAGES\">\n\n  <meta name=\"html-safe-nonce\" content=\"c1c9cc2489665ad2ac50f2e26a973d69dc145c43\">\n\n  <meta http-equiv=\"x-pjax-version\" content=\"474ab0b0581ad949b1ccd2ec62e46bb5\">\n  \n\n      <link href=\"https://github.com/b3log/wide/commits/master.atom\" rel=\"alternate\" title=\"Recent Commits to wide:master\" type=\"application/atom+xml\">\n\n  <meta name=\"description\" content=\" :fireworks: 一个基于 Web 的 Go 语言 IDE。A Web-based IDE for teams using Go programming language/Golang. https://wide.b3log.org\">\n  <meta name=\"go-import\" content=\"github.com/b3log/wide git https://github.com/b3log/wide.git\">\n\n  <meta name=\"octolytics-dimension-user_id\" content=\"1627618\" /><meta name=\"octolytics-dimension-user_login\" content=\"b3log\" /><meta name=\"octolytics-dimension-repository_id\" content=\"23072533\" /><meta name=\"octolytics-dimension-repository_nwo\" content=\"b3log/wide\" /><meta name=\"octolytics-dimension-repository_public\" content=\"true\" /><meta name=\"octolytics-dimension-repository_is_fork\" content=\"false\" /><meta name=\"octolytics-dimension-repository_network_root_id\" content=\"23072533\" /><meta name=\"octolytics-dimension-repository_network_root_nwo\" content=\"b3log/wide\" /><meta name=\"octolytics-dimension-repository_explore_github_marketplace_ci_cta_shown\" content=\"false\" />\n\n\n    <link rel=\"canonical\" href=\"https://github.com/b3log/wide/blob/master/LICENSE\" data-pjax-transient>\n\n\n  <meta name=\"browser-stats-url\" content=\"https://api.github.com/_private/browser/stats\">\n\n  <meta name=\"browser-errors-url\" content=\"https://api.github.com/_private/browser/errors\">\n\n  <link rel=\"mask-icon\" href=\"https://assets-cdn.github.com/pinned-octocat.svg\" color=\"#000000\">\n  <link rel=\"icon\" type=\"image/x-icon\" class=\"js-site-favicon\" href=\"https://assets-cdn.github.com/favicon.ico\">\n\n<meta name=\"theme-color\" content=\"#1e2327\">\n\n\n  <meta name=\"u2f-support\" content=\"true\">\n\n<link rel=\"manifest\" href=\"/manifest.json\" crossOrigin=\"use-credentials\">\n\n  </head>\n\n  <body class=\"logged-in env-production emoji-size-boost page-blob\">\n    \n\n  <div class=\"position-relative js-header-wrapper \">\n    <a href=\"#start-of-content\" tabindex=\"1\" class=\"p-3 bg-blue text-white show-on-focus js-skip-to-content\">Skip to content</a>\n    <div id=\"js-pjax-loader-bar\" class=\"pjax-loader-bar\"><div class=\"progress\"></div></div>\n\n    \n    \n    \n\n\n\n        \n<header class=\"Header  f5\" role=\"banner\">\n  <div class=\"d-flex flex-justify-between px-3 container-lg\">\n    <div class=\"d-flex flex-justify-between \">\n      <div class=\"\">\n        <a class=\"header-logo-invertocat\" href=\"https://github.com/\" data-hotkey=\"g d\" aria-label=\"Homepage\" data-ga-click=\"Header, go to dashboard, icon:logo\">\n  <svg height=\"32\" class=\"octicon octicon-mark-github\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"32\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z\"/></svg>\n</a>\n\n      </div>\n\n    </div>\n\n    <div class=\"HeaderMenu d-flex flex-justify-between flex-auto\">\n      <div class=\"d-flex\">\n            <div class=\"\">\n              <div class=\"header-search scoped-search site-scoped-search js-site-search position-relative\" role=\"search\">\n  <div class=\"position-relative\">\n    <!-- '\"` --><!-- </textarea></xmp> --></option></form><form class=\"js-site-search-form\" data-scope-type=\"Repository\" data-scope-id=\"23072533\" data-scoped-search-url=\"/b3log/wide/search\" data-unscoped-search-url=\"/search\" action=\"/b3log/wide/search\" accept-charset=\"UTF-8\" method=\"get\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" />\n      <label class=\"form-control header-search-wrapper header-search-wrapper-jump-to position-relative d-flex flex-justify-between flex-items-center js-chromeless-input-container\">\n        <input type=\"text\"\n          class=\"form-control header-search-input jump-to-field js-jump-to-field js-site-search-focus js-site-search-field is-clearable\"\n          data-hotkey=\"s,/\"\n          name=\"q\"\n          value=\"\"\n          placeholder=\"Search or jump to…\"\n          aria-label=\"Search this repository\"\n          data-unscoped-placeholder=\"Search or jump to…\"\n          data-scoped-placeholder=\"Search or jump to…\"\n          data-jump-to-suggestions-path=\"/_graphql/GetSuggestedNavigationDestinations#csrf-token=E3MQc3DPFy2TH1envTpDiRCEUC4oz0Cgv5nISBuz5/7pVPbhDkCtOQoCjZ0WaybSbNz7MjqkFGsA+QdiH34naQ==\"\n          spellcheck=\"false\"\n          autocomplete=\"off\"\n          autocapitalize=\"off\"\n          >\n          <input type=\"hidden\" class=\"js-site-search-type-field\" name=\"type\" >\n            <img src=\"https://assets-cdn.github.com/images/search-shortcut-hint.svg\" alt=\"\" class=\"mr-2 header-search-key-slash\">\n\n            <div class=\"Box position-absolute overflow-hidden d-none jump-to-suggestions js-jump-to-suggestions-container\">\n              <ul class=\"d-none js-jump-to-suggestions-template-container\">\n                <li class=\"d-flex flex-justify-start flex-items-center p-0 f5 navigation-item js-navigation-item\">\n                  <a class=\"no-underline d-flex flex-auto flex-items-center p-2 jump-to-suggestions-path js-jump-to-suggestion-path js-navigation-open\" aria-label=\"Jump to...\" href=\"\">\n                    <div class=\"jump-to-octicon js-jump-to-octicon mr-2 text-center d-none\"></div>\n                    <img class=\"avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar\" alt=\"\" src=\"\" width=\"28\" height=\"28\">\n                    <div class=\"jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden no-wrap css-truncate css-truncate-target\">\n                    </div>\n\n                    <div class=\"border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-search\">\n                      In this repository\n                      <span class=\"d-inline-block ml-1 v-align-middle\">↵</span>\n                    </div>\n\n                    <div class=\"border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump\">\n                      Jump to\n                      <span class=\"d-inline-block ml-1 v-align-middle\">↵</span>\n                    </div>\n                  </a>\n                </li>\n                <svg height=\"16\" width=\"16\" class=\"octicon octicon-repo flex-shrink-0 js-jump-to-repo-octicon-template\" title=\"Repository\" viewBox=\"0 0 12 16\" version=\"1.1\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z\"/></svg>\n                <svg height=\"16\" width=\"16\" class=\"octicon octicon-project flex-shrink-0 js-jump-to-project-octicon-template\" title=\"Project\" viewBox=\"0 0 15 16\" version=\"1.1\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M10 12h3V2h-3v10zm-4-2h3V2H6v8zm-4 4h3V2H2v12zm-1 1h13V1H1v14zM14 0H1a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h13a1 1 0 0 0 1-1V1a1 1 0 0 0-1-1z\"/></svg>\n                <svg height=\"16\" width=\"16\" class=\"octicon octicon-search flex-shrink-0 js-jump-to-search-octicon-template\" title=\"Search\" viewBox=\"0 0 16 16\" version=\"1.1\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M15.7 13.3l-3.81-3.83A5.93 5.93 0 0 0 13 6c0-3.31-2.69-6-6-6S1 2.69 1 6s2.69 6 6 6c1.3 0 2.48-.41 3.47-1.11l3.83 3.81c.19.2.45.3.7.3.25 0 .52-.09.7-.3a.996.996 0 0 0 0-1.41v.01zM7 10.7c-2.59 0-4.7-2.11-4.7-4.7 0-2.59 2.11-4.7 4.7-4.7 2.59 0 4.7 2.11 4.7 4.7 0 2.59-2.11 4.7-4.7 4.7z\"/></svg>\n              </ul>\n              <ul class=\"d-none js-jump-to-no-results-template-container\">\n                <li class=\"d-flex flex-justify-center flex-items-center p-3 f5 d-none\">\n                  <span class=\"text-gray\">No suggested jump to results</span>\n                </li> \n              </ul>\n\n              <ul class=\"js-navigation-container jump-to-suggestions-results-container js-jump-to-suggestions-results-container\">\n                <li class=\"d-flex flex-justify-center flex-items-center p-0 f5\">\n                  <img src=\"https://assets-cdn.github.com/images/spinners/octocat-spinner-128.gif\" alt=\"Octocat Spinner Icon\" class=\"m-2\" width=\"28\">\n                </li>\n              </ul>\n            </div>\n      </label>\n</form>  </div>\n</div>\n\n            </div>\n\n          <ul class=\"d-flex pl-2 flex-items-center text-bold list-style-none\" role=\"navigation\">\n            <li>\n              <a class=\"js-selected-navigation-item HeaderNavlink px-2\" data-hotkey=\"g p\" data-ga-click=\"Header, click, Nav menu - item:pulls context:user\" aria-label=\"Pull requests you created\" data-selected-links=\"/pulls /pulls/assigned /pulls/mentioned /pulls\" href=\"/pulls\">\n                Pull requests\n</a>            </li>\n            <li>\n              <a class=\"js-selected-navigation-item HeaderNavlink px-2\" data-hotkey=\"g i\" data-ga-click=\"Header, click, Nav menu - item:issues context:user\" aria-label=\"Issues you created\" data-selected-links=\"/issues /issues/assigned /issues/mentioned /issues\" href=\"/issues\">\n                Issues\n</a>            </li>\n                <li>\n                  <a class=\"js-selected-navigation-item HeaderNavlink px-2\" data-ga-click=\"Header, click, Nav menu - item:marketplace context:user\" data-octo-click=\"marketplace_click\" data-octo-dimensions=\"location:nav_bar, group:\" data-selected-links=\" /marketplace\" href=\"/marketplace\">\n                    Marketplace\n</a>                </li>\n            <li>\n              <a class=\"js-selected-navigation-item HeaderNavlink px-2\" data-ga-click=\"Header, click, Nav menu - item:explore\" data-selected-links=\"/explore /trending /trending/developers /integrations /integrations/feature/code /integrations/feature/collaborate /integrations/feature/ship showcases showcases_search showcases_landing /explore\" href=\"/explore\">\n                Explore\n</a>            </li>\n          </ul>\n      </div>\n\n      <div class=\"d-flex\">\n        \n<ul class=\"user-nav d-flex flex-items-center list-style-none\" id=\"user-links\">\n  <li class=\"dropdown\">\n    <span class=\"d-inline-block  px-2\">\n      \n    <a aria-label=\"You have no unread notifications\" class=\"notification-indicator tooltipped tooltipped-s  js-socket-channel js-notification-indicator\" data-hotkey=\"g n\" data-ga-click=\"Header, go to notifications, icon:read\" data-channel=\"notification-changed:10411707\" href=\"/notifications\">\n        <span class=\"mail-status \"></span>\n        <svg class=\"octicon octicon-bell\" viewBox=\"0 0 14 16\" version=\"1.1\" width=\"14\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M13.99 11.991v1H0v-1l.73-.58c.769-.769.809-2.547 1.189-4.416.77-3.767 4.077-4.996 4.077-4.996 0-.55.45-1 .999-1 .55 0 1 .45 1 1 0 0 3.387 1.229 4.156 4.996.38 1.879.42 3.657 1.19 4.417l.659.58h-.01zM6.995 15.99c1.11 0 1.999-.89 1.999-1.999H4.996c0 1.11.89 1.999 1.999 1.999z\"/></svg>\n</a>\n    </span>\n  </li>\n\n  <li class=\"dropdown\">\n    <details class=\"details-expanded details-reset js-dropdown-details d-flex px-2 flex-items-center\">\n      <summary class=\"HeaderNavlink\"\n         aria-label=\"Create new…\"\n         data-ga-click=\"Header, create new, icon:add\">\n        <svg class=\"octicon octicon-plus float-left mr-1 mt-1\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 9H7v5H5V9H0V7h5V2h2v5h5v2z\"/></svg>\n        <span class=\"dropdown-caret mt-1\"></span>\n      </summary>\n\n      <ul class=\"dropdown-menu dropdown-menu-sw\">\n        \n<a class=\"dropdown-item\" href=\"/new\" data-ga-click=\"Header, create new repository\">\n  New repository\n</a>\n\n  <a class=\"dropdown-item\" href=\"/new/import\" data-ga-click=\"Header, import a repository\">\n    Import repository\n  </a>\n\n<a class=\"dropdown-item\" href=\"https://gist.github.com/\" data-ga-click=\"Header, create new gist\">\n  New gist\n</a>\n\n  <a class=\"dropdown-item\" href=\"/organizations/new\" data-ga-click=\"Header, create new organization\">\n    New organization\n  </a>\n\n\n\n  <div class=\"dropdown-divider\"></div>\n  <div class=\"dropdown-header\">\n    <span title=\"b3log/wide\">This repository</span>\n  </div>\n    <a class=\"dropdown-item\" href=\"/b3log/wide/issues/new\" data-ga-click=\"Header, create new issue\">\n      New issue\n    </a>\n\n      </ul>\n    </details>\n  </li>\n\n  <li class=\"dropdown\">\n\n    <details class=\"details-expanded details-reset js-dropdown-details d-flex pl-2 flex-items-center\">\n      <summary class=\"HeaderNavlink name mt-1\"\n        aria-label=\"View profile and more\"\n        data-ga-click=\"Header, show menu, icon:avatar\">\n        <img alt=\"@small99\" class=\"avatar float-left mr-1\" src=\"https://avatars0.githubusercontent.com/u/10411707?s=40&amp;v=4\" height=\"20\" width=\"20\">\n        <span class=\"dropdown-caret\"></span>\n      </summary>\n\n      <ul class=\"dropdown-menu dropdown-menu-sw\">\n        <li class=\"dropdown-header header-nav-current-user css-truncate\">\n          Signed in as <strong class=\"css-truncate-target\">small99</strong>\n        </li>\n\n        <li class=\"dropdown-divider\"></li>\n\n        <li><a class=\"dropdown-item\" href=\"/small99\" data-ga-click=\"Header, go to profile, text:your profile\">\n          Your profile\n        </a></li>\n        <li><a class=\"dropdown-item\" href=\"/small99?tab=stars\" data-ga-click=\"Header, go to starred repos, text:your stars\">\n          Your stars\n        </a></li>\n          <li><a class=\"dropdown-item\" href=\"https://gist.github.com/\" data-ga-click=\"Header, your gists, text:your gists\">Your gists</a></li>\n\n        <li class=\"dropdown-divider\"></li>\n\n        <li><a class=\"dropdown-item\" href=\"https://help.github.com\" data-ga-click=\"Header, go to help, text:help\">\n          Help\n        </a></li>\n\n        <li><a class=\"dropdown-item\" href=\"/settings/profile\" data-ga-click=\"Header, go to settings, icon:settings\">\n          Settings\n        </a></li>\n\n        <li><!-- '\"` --><!-- </textarea></xmp> --></option></form><form class=\"logout-form\" action=\"/logout\" accept-charset=\"UTF-8\" method=\"post\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /><input type=\"hidden\" name=\"authenticity_token\" value=\"zC/U8Yw/aMq2wuv8Uoo72duqFJpamKvt6o2Yl8bCZMbMRbwLUKb5uWMQrrhp2nZb6G6E7YaFLRuUrKKFIL+pWg==\" />\n          <button type=\"submit\" class=\"dropdown-item dropdown-signout\" data-ga-click=\"Header, sign out, icon:logout\">\n            Sign out\n          </button>\n        </form></li>\n      </ul>\n    </details>\n  </li>\n</ul>\n\n\n\n        <!-- '\"` --><!-- </textarea></xmp> --></option></form><form class=\"sr-only right-0\" action=\"/logout\" accept-charset=\"UTF-8\" method=\"post\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /><input type=\"hidden\" name=\"authenticity_token\" value=\"BoBXPs3t0Z33PEb4AFAAaB0EWpiMSgd8Uj2FlVm9VVAG6j/EEXRA7iLuA7w7AE3qLsDK71BXgYosHL+Hv8CYzA==\" />\n          <button type=\"submit\" class=\"dropdown-item dropdown-signout\" data-ga-click=\"Header, sign out, icon:logout\">\n            Sign out\n          </button>\n</form>      </div>\n    </div>\n  </div>\n</header>\n\n      \n\n  </div>\n\n  <div id=\"start-of-content\" class=\"show-on-focus\"></div>\n\n    <div id=\"js-flash-container\">\n</div>\n\n\n\n  <div role=\"main\" class=\"application-main \">\n        <div itemscope itemtype=\"http://schema.org/SoftwareSourceCode\" class=\"\">\n    <div id=\"js-repo-pjax-container\" data-pjax-container >\n      \n\n\n\n\n\n  \n\n\n\n  <div class=\"pagehead repohead instapaper_ignore readability-menu experiment-repo-nav  \">\n    <div class=\"repohead-details-container clearfix container\">\n\n      <ul class=\"pagehead-actions\">\n  <li>\n        <!-- '\"` --><!-- </textarea></xmp> --></option></form><form data-autosubmit=\"true\" data-remote=\"true\" class=\"js-social-container\" action=\"/notifications/subscribe\" accept-charset=\"UTF-8\" method=\"post\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /><input type=\"hidden\" name=\"authenticity_token\" value=\"fzLD00fKkiPRbEJl8MUmvgcd86/zfmFOPyuADpoUPbX1+FxRfrP+1FIuA2CLpbfbGTWyihgGxPHFMg1q4vy45A==\" />      <input type=\"hidden\" name=\"repository_id\" id=\"repository_id\" value=\"23072533\" class=\"form-control\" />\n\n        <div class=\"select-menu js-menu-container js-select-menu\">\n          <a href=\"/b3log/wide/subscription\"\n            class=\"btn btn-sm btn-with-count select-menu-button js-menu-target\"\n            role=\"button\"\n            aria-haspopup=\"true\"\n            aria-expanded=\"false\"\n            aria-label=\"Toggle repository notifications menu\"\n            data-ga-click=\"Repository, click Watch settings, action:blob#show\">\n            <span class=\"js-select-button\">\n                <svg class=\"octicon octicon-eye\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M8.06 2C3 2 0 8 0 8s3 6 8.06 6C13 14 16 8 16 8s-3-6-7.94-6zM8 12c-2.2 0-4-1.78-4-4 0-2.2 1.8-4 4-4 2.22 0 4 1.8 4 4 0 2.22-1.78 4-4 4zm2-4c0 1.11-.89 2-2 2-1.11 0-2-.89-2-2 0-1.11.89-2 2-2 1.11 0 2 .89 2 2z\"/></svg>\n                Watch\n            </span>\n          </a>\n          <a class=\"social-count js-social-count\"\n            href=\"/b3log/wide/watchers\"\n            aria-label=\"190 users are watching this repository\">\n            190\n          </a>\n\n        <div class=\"select-menu-modal-holder\">\n          <div class=\"select-menu-modal subscription-menu-modal js-menu-content\">\n            <div class=\"select-menu-header js-navigation-enable\" tabindex=\"-1\">\n              <svg class=\"octicon octicon-x js-menu-close\" role=\"img\" aria-label=\"Close\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z\"/></svg>\n              <span class=\"select-menu-title\">Notifications</span>\n            </div>\n\n              <div class=\"select-menu-list js-navigation-container\" role=\"menu\">\n\n                <div class=\"select-menu-item js-navigation-item selected\" role=\"menuitem\" tabindex=\"0\">\n                  <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n                  <div class=\"select-menu-item-text\">\n                    <input type=\"radio\" name=\"do\" id=\"do_included\" value=\"included\" checked=\"checked\" />\n                    <span class=\"select-menu-item-heading\">Not watching</span>\n                    <span class=\"description\">Be notified when participating or @mentioned.</span>\n                    <span class=\"js-select-button-text hidden-select-button-text\">\n                      <svg class=\"octicon octicon-eye\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M8.06 2C3 2 0 8 0 8s3 6 8.06 6C13 14 16 8 16 8s-3-6-7.94-6zM8 12c-2.2 0-4-1.78-4-4 0-2.2 1.8-4 4-4 2.22 0 4 1.8 4 4 0 2.22-1.78 4-4 4zm2-4c0 1.11-.89 2-2 2-1.11 0-2-.89-2-2 0-1.11.89-2 2-2 1.11 0 2 .89 2 2z\"/></svg>\n                      Watch\n                    </span>\n                  </div>\n                </div>\n\n                <div class=\"select-menu-item js-navigation-item \" role=\"menuitem\" tabindex=\"0\">\n                  <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n                  <div class=\"select-menu-item-text\">\n                    <input type=\"radio\" name=\"do\" id=\"do_subscribed\" value=\"subscribed\" />\n                    <span class=\"select-menu-item-heading\">Watching</span>\n                    <span class=\"description\">Be notified of all conversations.</span>\n                    <span class=\"js-select-button-text hidden-select-button-text\">\n                      <svg class=\"octicon octicon-eye\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M8.06 2C3 2 0 8 0 8s3 6 8.06 6C13 14 16 8 16 8s-3-6-7.94-6zM8 12c-2.2 0-4-1.78-4-4 0-2.2 1.8-4 4-4 2.22 0 4 1.8 4 4 0 2.22-1.78 4-4 4zm2-4c0 1.11-.89 2-2 2-1.11 0-2-.89-2-2 0-1.11.89-2 2-2 1.11 0 2 .89 2 2z\"/></svg>\n                        Unwatch\n                    </span>\n                  </div>\n                </div>\n\n                <div class=\"select-menu-item js-navigation-item \" role=\"menuitem\" tabindex=\"0\">\n                  <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n                  <div class=\"select-menu-item-text\">\n                    <input type=\"radio\" name=\"do\" id=\"do_ignore\" value=\"ignore\" />\n                    <span class=\"select-menu-item-heading\">Ignoring</span>\n                    <span class=\"description\">Never be notified.</span>\n                    <span class=\"js-select-button-text hidden-select-button-text\">\n                      <svg class=\"octicon octicon-mute\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M8 2.81v10.38c0 .67-.81 1-1.28.53L3 10H1c-.55 0-1-.45-1-1V7c0-.55.45-1 1-1h2l3.72-3.72C7.19 1.81 8 2.14 8 2.81zm7.53 3.22l-1.06-1.06-1.97 1.97-1.97-1.97-1.06 1.06L11.44 8 9.47 9.97l1.06 1.06 1.97-1.97 1.97 1.97 1.06-1.06L13.56 8l1.97-1.97z\"/></svg>\n                        Stop ignoring\n                    </span>\n                  </div>\n                </div>\n\n              </div>\n\n            </div>\n          </div>\n        </div>\n</form>\n  </li>\n\n  <li>\n    \n  <div class=\"js-toggler-container js-social-container starring-container \">\n    <!-- '\"` --><!-- </textarea></xmp> --></option></form><form class=\"starred js-social-form\" action=\"/b3log/wide/unstar\" accept-charset=\"UTF-8\" method=\"post\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /><input type=\"hidden\" name=\"authenticity_token\" value=\"Etw2O0+aGGq/KiFb+140TQrqnrsJt/MVl23VPomnnRFvg7XdVEeiFwBPhyvYiH7wBarimp2J/EkxEaxdOjrhEA==\" />\n      <input type=\"hidden\" name=\"context\" value=\"repository\"></input>\n      <button\n        type=\"submit\"\n        class=\"btn btn-sm btn-with-count js-toggler-target\"\n        aria-label=\"Unstar this repository\" title=\"Unstar b3log/wide\"\n        data-ga-click=\"Repository, click unstar button, action:blob#show; text:Unstar\">\n        <svg class=\"octicon octicon-star\" viewBox=\"0 0 14 16\" version=\"1.1\" width=\"14\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M14 6l-4.9-.64L7 1 4.9 5.36 0 6l3.6 3.26L2.67 14 7 11.67 11.33 14l-.93-4.74L14 6z\"/></svg>\n        Unstar\n      </button>\n        <a class=\"social-count js-social-count\" href=\"/b3log/wide/stargazers\"\n           aria-label=\"2418 users starred this repository\">\n          2,418\n        </a>\n</form>\n    <!-- '\"` --><!-- </textarea></xmp> --></option></form><form class=\"unstarred js-social-form\" action=\"/b3log/wide/star\" accept-charset=\"UTF-8\" method=\"post\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /><input type=\"hidden\" name=\"authenticity_token\" value=\"a5duPulgM5rRxGakX8VkiU+uIH4YVlJPclFuXq8JWZJKU83UxWZaFt8E9a+WdMcwyIw1uZPolOhr7+cUw6AWKg==\" />\n      <input type=\"hidden\" name=\"context\" value=\"repository\"></input>\n      <button\n        type=\"submit\"\n        class=\"btn btn-sm btn-with-count js-toggler-target\"\n        aria-label=\"Star this repository\" title=\"Star b3log/wide\"\n        data-ga-click=\"Repository, click star button, action:blob#show; text:Star\">\n        <svg class=\"octicon octicon-star\" viewBox=\"0 0 14 16\" version=\"1.1\" width=\"14\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M14 6l-4.9-.64L7 1 4.9 5.36 0 6l3.6 3.26L2.67 14 7 11.67 11.33 14l-.93-4.74L14 6z\"/></svg>\n        Star\n      </button>\n        <a class=\"social-count js-social-count\" href=\"/b3log/wide/stargazers\"\n           aria-label=\"2418 users starred this repository\">\n          2,418\n        </a>\n</form>  </div>\n\n  </li>\n\n  <li>\n          <!-- '\"` --><!-- </textarea></xmp> --></option></form><form class=\"btn-with-count\" action=\"/b3log/wide/fork\" accept-charset=\"UTF-8\" method=\"post\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /><input type=\"hidden\" name=\"authenticity_token\" value=\"52zYSFJQs1iBZ8VLnH0yaHRslwJriXUxFBD/Pm5pKCirXpCL2zLo8+o3ksbeMzM6cYzh5vTvu9dv3zw3/xzgHg==\" />\n            <button\n                type=\"submit\"\n                class=\"btn btn-sm btn-with-count\"\n                data-ga-click=\"Repository, show fork modal, action:blob#show; text:Fork\"\n                title=\"Fork your own copy of b3log/wide to your account\"\n                aria-label=\"Fork your own copy of b3log/wide to your account\">\n              <svg class=\"octicon octicon-repo-forked\" viewBox=\"0 0 10 16\" version=\"1.1\" width=\"10\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M8 1a1.993 1.993 0 0 0-1 3.72V6L5 8 3 6V4.72A1.993 1.993 0 0 0 2 1a1.993 1.993 0 0 0-1 3.72V6.5l3 3v1.78A1.993 1.993 0 0 0 5 15a1.993 1.993 0 0 0 1-3.72V9.5l3-3V4.72A1.993 1.993 0 0 0 8 1zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3 10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3-10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z\"/></svg>\n              Fork\n            </button>\n</form>\n    <a href=\"/b3log/wide/network\" class=\"social-count\"\n       aria-label=\"398 users forked this repository\">\n      398\n    </a>\n  </li>\n</ul>\n\n      <h1 class=\"public \">\n  <svg class=\"octicon octicon-repo\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z\"/></svg>\n  <span class=\"author\" itemprop=\"author\"><a class=\"url fn\" rel=\"author\" href=\"/b3log\">b3log</a></span><!--\n--><span class=\"path-divider\">/</span><!--\n--><strong itemprop=\"name\"><a data-pjax=\"#js-repo-pjax-container\" href=\"/b3log/wide\">wide</a></strong>\n\n</h1>\n\n    </div>\n    \n<nav class=\"reponav js-repo-nav js-sidenav-container-pjax container\"\n     itemscope\n     itemtype=\"http://schema.org/BreadcrumbList\"\n     role=\"navigation\"\n     data-pjax=\"#js-repo-pjax-container\">\n\n  <span itemscope itemtype=\"http://schema.org/ListItem\" itemprop=\"itemListElement\">\n    <a class=\"js-selected-navigation-item selected reponav-item\" itemprop=\"url\" data-hotkey=\"g c\" data-selected-links=\"repo_source repo_downloads repo_commits repo_releases repo_tags repo_branches repo_packages /b3log/wide\" href=\"/b3log/wide\">\n      <svg class=\"octicon octicon-code\" viewBox=\"0 0 14 16\" version=\"1.1\" width=\"14\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M9.5 3L8 4.5 11.5 8 8 11.5 9.5 13 14 8 9.5 3zm-5 0L0 8l4.5 5L6 11.5 2.5 8 6 4.5 4.5 3z\"/></svg>\n      <span itemprop=\"name\">Code</span>\n      <meta itemprop=\"position\" content=\"1\">\n</a>  </span>\n\n    <span itemscope itemtype=\"http://schema.org/ListItem\" itemprop=\"itemListElement\">\n      <a itemprop=\"url\" data-hotkey=\"g i\" class=\"js-selected-navigation-item reponav-item\" data-selected-links=\"repo_issues repo_labels repo_milestones /b3log/wide/issues\" href=\"/b3log/wide/issues\">\n        <svg class=\"octicon octicon-issue-opened\" viewBox=\"0 0 14 16\" version=\"1.1\" width=\"14\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"/></svg>\n        <span itemprop=\"name\">Issues</span>\n        <span class=\"Counter\">25</span>\n        <meta itemprop=\"position\" content=\"2\">\n</a>    </span>\n\n  <span itemscope itemtype=\"http://schema.org/ListItem\" itemprop=\"itemListElement\">\n    <a data-hotkey=\"g p\" itemprop=\"url\" class=\"js-selected-navigation-item reponav-item\" data-selected-links=\"repo_pulls checks /b3log/wide/pulls\" href=\"/b3log/wide/pulls\">\n      <svg class=\"octicon octicon-git-pull-request\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M11 11.28V5c-.03-.78-.34-1.47-.94-2.06C9.46 2.35 8.78 2.03 8 2H7V0L4 3l3 3V4h1c.27.02.48.11.69.31.21.2.3.42.31.69v6.28A1.993 1.993 0 0 0 10 15a1.993 1.993 0 0 0 1-3.72zm-1 2.92c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zM4 3c0-1.11-.89-2-2-2a1.993 1.993 0 0 0-1 3.72v6.56A1.993 1.993 0 0 0 2 15a1.993 1.993 0 0 0 1-3.72V4.72c.59-.34 1-.98 1-1.72zm-.8 10c0 .66-.55 1.2-1.2 1.2-.65 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z\"/></svg>\n      <span itemprop=\"name\">Pull requests</span>\n      <span class=\"Counter\">0</span>\n      <meta itemprop=\"position\" content=\"3\">\n</a>  </span>\n\n\n    <a class=\"js-selected-navigation-item reponav-item\" data-hotkey=\"g w\" data-selected-links=\"repo_wiki /b3log/wide/wiki\" href=\"/b3log/wide/wiki\">\n      <svg class=\"octicon octicon-book\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M3 5h4v1H3V5zm0 3h4V7H3v1zm0 2h4V9H3v1zm11-5h-4v1h4V5zm0 2h-4v1h4V7zm0 2h-4v1h4V9zm2-6v9c0 .55-.45 1-1 1H9.5l-1 1-1-1H2c-.55 0-1-.45-1-1V3c0-.55.45-1 1-1h5.5l1 1 1-1H15c.55 0 1 .45 1 1zm-8 .5L7.5 3H2v9h6V3.5zm7-.5H9.5l-.5.5V12h6V3z\"/></svg>\n      Wiki\n</a>\n\n  <a class=\"js-selected-navigation-item reponav-item\" data-selected-links=\"repo_graphs repo_contributors dependency_graph pulse /b3log/wide/pulse\" href=\"/b3log/wide/pulse\">\n    <svg class=\"octicon octicon-graph\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M16 14v1H0V0h1v14h15zM5 13H3V8h2v5zm4 0H7V3h2v10zm4 0h-2V6h2v7z\"/></svg>\n    Insights\n</a>\n\n</nav>\n\n\n  </div>\n\n<div class=\"container new-discussion-timeline experiment-repo-nav  \">\n  <div class=\"repository-content \">\n\n    \n  <a class=\"d-none js-permalink-shortcut\" data-hotkey=\"y\" href=\"/b3log/wide/blob/8c7d2636b22131b101f54e5d597c41b9cc4d8bcb/LICENSE\">Permalink</a>\n\n  <!-- blob contrib key: blob_contributors:v21:5a1c747998c0b1e59443b786c549fd19 -->\n\n  <div class=\"file-navigation\">\n    \n<div class=\"select-menu branch-select-menu js-menu-container js-select-menu float-left\">\n  <button class=\" btn btn-sm select-menu-button js-menu-target css-truncate\" data-hotkey=\"w\"\n    \n    type=\"button\" aria-label=\"Switch branches or tags\" aria-expanded=\"false\" aria-haspopup=\"true\">\n      <i>Branch:</i>\n      <span class=\"js-select-button css-truncate-target\">master</span>\n  </button>\n\n  <div class=\"select-menu-modal-holder js-menu-content js-navigation-container\" data-pjax>\n\n    <div class=\"select-menu-modal\">\n      <div class=\"select-menu-header\">\n        <svg class=\"octicon octicon-x js-menu-close\" role=\"img\" aria-label=\"Close\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z\"/></svg>\n        <span class=\"select-menu-title\">Switch branches/tags</span>\n      </div>\n\n      <div class=\"select-menu-filters\">\n        <div class=\"select-menu-text-filter\">\n          <input type=\"text\" aria-label=\"Filter branches/tags\" id=\"context-commitish-filter-field\" class=\"form-control js-filterable-field js-navigation-enable\" placeholder=\"Filter branches/tags\">\n        </div>\n        <div class=\"select-menu-tabs\">\n          <ul>\n            <li class=\"select-menu-tab\">\n              <a href=\"#\" data-tab-filter=\"branches\" data-filter-placeholder=\"Filter branches/tags\" class=\"js-select-menu-tab\" role=\"tab\">Branches</a>\n            </li>\n            <li class=\"select-menu-tab\">\n              <a href=\"#\" data-tab-filter=\"tags\" data-filter-placeholder=\"Find a tag…\" class=\"js-select-menu-tab\" role=\"tab\">Tags</a>\n            </li>\n          </ul>\n        </div>\n      </div>\n\n      <div class=\"select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket\" data-tab-filter=\"branches\" role=\"menu\">\n\n        <div data-filterable-for=\"context-commitish-filter-field\" data-filterable-type=\"substring\">\n\n\n            <a class=\"select-menu-item js-navigation-item js-navigation-open \"\n               href=\"/b3log/wide/blob/1.6.0-dev/LICENSE\"\n               data-name=\"1.6.0-dev\"\n               data-skip-pjax=\"true\"\n               rel=\"nofollow\">\n              <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"select-menu-item-text css-truncate-target js-select-menu-filter-text\">\n                1.6.0-dev\n              </span>\n            </a>\n            <a class=\"select-menu-item js-navigation-item js-navigation-open selected\"\n               href=\"/b3log/wide/blob/master/LICENSE\"\n               data-name=\"master\"\n               data-skip-pjax=\"true\"\n               rel=\"nofollow\">\n              <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"select-menu-item-text css-truncate-target js-select-menu-filter-text\">\n                master\n              </span>\n            </a>\n        </div>\n\n          <div class=\"select-menu-no-results\">Nothing to show</div>\n      </div>\n\n      <div class=\"select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket\" data-tab-filter=\"tags\">\n        <div data-filterable-for=\"context-commitish-filter-field\" data-filterable-type=\"substring\">\n\n\n            <a class=\"select-menu-item js-navigation-item js-navigation-open \"\n              href=\"/b3log/wide/tree/1.5.3/LICENSE\"\n              data-name=\"1.5.3\"\n              data-skip-pjax=\"true\"\n              rel=\"nofollow\">\n              <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"select-menu-item-text css-truncate-target\" title=\"1.5.3\">\n                1.5.3\n              </span>\n            </a>\n            <a class=\"select-menu-item js-navigation-item js-navigation-open \"\n              href=\"/b3log/wide/tree/1.5.2/LICENSE\"\n              data-name=\"1.5.2\"\n              data-skip-pjax=\"true\"\n              rel=\"nofollow\">\n              <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"select-menu-item-text css-truncate-target\" title=\"1.5.2\">\n                1.5.2\n              </span>\n            </a>\n            <a class=\"select-menu-item js-navigation-item js-navigation-open \"\n              href=\"/b3log/wide/tree/1.5.1/LICENSE\"\n              data-name=\"1.5.1\"\n              data-skip-pjax=\"true\"\n              rel=\"nofollow\">\n              <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"select-menu-item-text css-truncate-target\" title=\"1.5.1\">\n                1.5.1\n              </span>\n            </a>\n            <a class=\"select-menu-item js-navigation-item js-navigation-open \"\n              href=\"/b3log/wide/tree/1.5.0/LICENSE\"\n              data-name=\"1.5.0\"\n              data-skip-pjax=\"true\"\n              rel=\"nofollow\">\n              <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"select-menu-item-text css-truncate-target\" title=\"1.5.0\">\n                1.5.0\n              </span>\n            </a>\n            <a class=\"select-menu-item js-navigation-item js-navigation-open \"\n              href=\"/b3log/wide/tree/1.4.0/LICENSE\"\n              data-name=\"1.4.0\"\n              data-skip-pjax=\"true\"\n              rel=\"nofollow\">\n              <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"select-menu-item-text css-truncate-target\" title=\"1.4.0\">\n                1.4.0\n              </span>\n            </a>\n            <a class=\"select-menu-item js-navigation-item js-navigation-open \"\n              href=\"/b3log/wide/tree/1.3.0/LICENSE\"\n              data-name=\"1.3.0\"\n              data-skip-pjax=\"true\"\n              rel=\"nofollow\">\n              <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"select-menu-item-text css-truncate-target\" title=\"1.3.0\">\n                1.3.0\n              </span>\n            </a>\n            <a class=\"select-menu-item js-navigation-item js-navigation-open \"\n              href=\"/b3log/wide/tree/1.2.0/LICENSE\"\n              data-name=\"1.2.0\"\n              data-skip-pjax=\"true\"\n              rel=\"nofollow\">\n              <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"select-menu-item-text css-truncate-target\" title=\"1.2.0\">\n                1.2.0\n              </span>\n            </a>\n            <a class=\"select-menu-item js-navigation-item js-navigation-open \"\n              href=\"/b3log/wide/tree/1.1.0/LICENSE\"\n              data-name=\"1.1.0\"\n              data-skip-pjax=\"true\"\n              rel=\"nofollow\">\n              <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"select-menu-item-text css-truncate-target\" title=\"1.1.0\">\n                1.1.0\n              </span>\n            </a>\n            <a class=\"select-menu-item js-navigation-item js-navigation-open \"\n              href=\"/b3log/wide/tree/1.0.0/LICENSE\"\n              data-name=\"1.0.0\"\n              data-skip-pjax=\"true\"\n              rel=\"nofollow\">\n              <svg class=\"octicon octicon-check select-menu-item-icon\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"select-menu-item-text css-truncate-target\" title=\"1.0.0\">\n                1.0.0\n              </span>\n            </a>\n        </div>\n\n        <div class=\"select-menu-no-results\">Nothing to show</div>\n      </div>\n\n    </div>\n  </div>\n</div>\n\n    <div class=\"BtnGroup float-right\">\n      <a href=\"/b3log/wide/find/master\"\n            class=\"js-pjax-capture-input btn btn-sm BtnGroup-item\"\n            data-pjax\n            data-hotkey=\"t\">\n        Find file\n      </a>\n      <clipboard-copy for=\"blob-path\" class=\"btn btn-sm BtnGroup-item\">\n        Copy path\n      </clipboard-copy>\n    </div>\n    <div id=\"blob-path\" class=\"breadcrumb\">\n      <span class=\"repo-root js-repo-root\"><span class=\"js-path-segment\"><a data-pjax=\"true\" href=\"/b3log/wide\"><span>wide</span></a></span></span><span class=\"separator\">/</span><strong class=\"final-path\">LICENSE</strong>\n    </div>\n  </div>\n\n    <div class=\"Box mb-3 clearfix\">\n  <div class=\"col-6 float-left p-2 pt-3\">\n    <svg height=\"32\" class=\"octicon octicon-law license-summary-octicon float-left mr-2\" viewBox=\"0 0 14 16\" version=\"1.1\" width=\"28\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M7 4c-.83 0-1.5-.67-1.5-1.5S6.17 1 7 1s1.5.67 1.5 1.5S7.83 4 7 4zm7 6c0 1.11-.89 2-2 2h-1c-1.11 0-2-.89-2-2l2-4h-1c-.55 0-1-.45-1-1H8v8c.42 0 1 .45 1 1h1c.42 0 1 .45 1 1H3c0-.55.58-1 1-1h1c0-.55.58-1 1-1h.03L6 5H5c0 .55-.45 1-1 1H3l2 4c0 1.11-.89 2-2 2H2c-1.11 0-2-.89-2-2l2-4H1V5h3c0-.55.45-1 1-1h4c.55 0 1 .45 1 1h3v1h-1l2 4zM2.5 7L1 10h3L2.5 7zM13 10l-1.5-3-1.5 3h3z\"/></svg>\n    <p class=\"text-small text-gray mb-0 lh-condensed-ultra\">\n      b3log/wide is licensed under the\n    </p>\n    <h4 class=\"mt-0 mb-2\">Apache License 2.0</h4>\n    <p class=\"mb-0 text-gray text-small pr-2\">A permissive license whose main conditions require preservation of copyright and license notices. Contributors provide an express grant of patent rights. Licensed works, modifications, and larger works may be distributed under different terms and without source code.</p>\n  </div>\n\n  <div class=\"col-6 float-left p-2\">\n      <div class=\"one-third column\">\n        <h5 class=\"mt-1 mb-2\">Permissions</h5>\n        <ul class=\"list-style-none\">\n            <li class=\"text-small pl-3\">\n              <svg width=\"13\" class=\"octicon octicon-check rule-type-permissions ml-n3 v-align-middle\" viewBox=\"0 0 12 16\" version=\"1.1\" height=\"17\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"tooltipped tooltipped-se tooltipped-multiline v-align-middle\"\n                aria-label=\"This software and derivatives may be used for commercial purposes.\">\n                Commercial use\n              </span>\n            </li>\n            <li class=\"text-small pl-3\">\n              <svg width=\"13\" class=\"octicon octicon-check rule-type-permissions ml-n3 v-align-middle\" viewBox=\"0 0 12 16\" version=\"1.1\" height=\"17\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"tooltipped tooltipped-se tooltipped-multiline v-align-middle\"\n                aria-label=\"This software may be modified.\">\n                Modification\n              </span>\n            </li>\n            <li class=\"text-small pl-3\">\n              <svg width=\"13\" class=\"octicon octicon-check rule-type-permissions ml-n3 v-align-middle\" viewBox=\"0 0 12 16\" version=\"1.1\" height=\"17\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"tooltipped tooltipped-se tooltipped-multiline v-align-middle\"\n                aria-label=\"This software may be distributed.\">\n                Distribution\n              </span>\n            </li>\n            <li class=\"text-small pl-3\">\n              <svg width=\"13\" class=\"octicon octicon-check rule-type-permissions ml-n3 v-align-middle\" viewBox=\"0 0 12 16\" version=\"1.1\" height=\"17\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"tooltipped tooltipped-se tooltipped-multiline v-align-middle\"\n                aria-label=\"This license provides an express grant of patent rights from contributors.\">\n                Patent use\n              </span>\n            </li>\n            <li class=\"text-small pl-3\">\n              <svg width=\"13\" class=\"octicon octicon-check rule-type-permissions ml-n3 v-align-middle\" viewBox=\"0 0 12 16\" version=\"1.1\" height=\"17\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5L12 5z\"/></svg>\n              <span class=\"tooltipped tooltipped-se tooltipped-multiline v-align-middle\"\n                aria-label=\"This software may be used and modified in private.\">\n                Private use\n              </span>\n            </li>\n        </ul>\n      </div>\n      <div class=\"one-third column\">\n        <h5 class=\"mt-1 mb-2\">Limitations</h5>\n        <ul class=\"list-style-none\">\n            <li class=\"text-small pl-3\">\n              <svg width=\"13\" class=\"octicon octicon-x rule-type-limitations ml-n3 v-align-middle\" viewBox=\"0 0 12 16\" version=\"1.1\" height=\"17\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z\"/></svg>\n              <span class=\"tooltipped tooltipped-s tooltipped-multiline v-align-middle\"\n                aria-label=\"This license explicitly states that it does NOT grant trademark rights, even though licenses without such a statement probably do not grant any implicit trademark rights.\">\n                Trademark use\n              </span>\n            </li>\n            <li class=\"text-small pl-3\">\n              <svg width=\"13\" class=\"octicon octicon-x rule-type-limitations ml-n3 v-align-middle\" viewBox=\"0 0 12 16\" version=\"1.1\" height=\"17\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z\"/></svg>\n              <span class=\"tooltipped tooltipped-s tooltipped-multiline v-align-middle\"\n                aria-label=\"This license includes a limitation of liability.\">\n                Liability\n              </span>\n            </li>\n            <li class=\"text-small pl-3\">\n              <svg width=\"13\" class=\"octicon octicon-x rule-type-limitations ml-n3 v-align-middle\" viewBox=\"0 0 12 16\" version=\"1.1\" height=\"17\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z\"/></svg>\n              <span class=\"tooltipped tooltipped-s tooltipped-multiline v-align-middle\"\n                aria-label=\"The license explicitly states that it does NOT provide any warranty.\">\n                Warranty\n              </span>\n            </li>\n        </ul>\n      </div>\n      <div class=\"one-third column\">\n        <h5 class=\"mt-1 mb-2\">Conditions</h5>\n        <ul class=\"list-style-none\">\n            <li class=\"text-small pl-3\">\n              <svg width=\"13\" class=\"octicon octicon-info rule-type-conditions ml-n3 v-align-middle\" viewBox=\"0 0 14 16\" version=\"1.1\" height=\"14\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"/></svg>\n              <span class=\"tooltipped tooltipped-sw tooltipped-multiline v-align-middle\"\n                aria-label=\"A copy of the license and copyright notice must be included with the software.\">\n                License and copyright notice\n              </span>\n            </li>\n            <li class=\"text-small pl-3\">\n              <svg width=\"13\" class=\"octicon octicon-info rule-type-conditions ml-n3 v-align-middle\" viewBox=\"0 0 14 16\" version=\"1.1\" height=\"14\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"/></svg>\n              <span class=\"tooltipped tooltipped-sw tooltipped-multiline v-align-middle\"\n                aria-label=\"Changes made to the code must be documented.\">\n                State changes\n              </span>\n            </li>\n        </ul>\n      </div>\n  </div>\n\n  <p class=\"text-gray text-small mb-0 border-top col-12 float-left p-2\">\n    This is not legal advice.\n    <a href=\"https://help.github.com/articles/licensing-a-repository/#disclaimer\">Learn more about repository licenses</a>.\n  </p>\n</div>\n\n\n  <include-fragment src=\"/b3log/wide/contributors/master/LICENSE\" class=\"commit-tease\">\n    <div>\n      Fetching contributors&hellip;\n    </div>\n\n    <div class=\"commit-tease-contributors\">\n      <img alt=\"\" class=\"loader-loading float-left\" src=\"https://assets-cdn.github.com/images/spinners/octocat-spinner-32-EAF2F5.gif\" width=\"16\" height=\"16\" />\n      <span class=\"loader-error\">Cannot retrieve contributors at this time</span>\n    </div>\n</include-fragment>\n\n\n  <div class=\"file\">\n    <div class=\"file-header\">\n  <div class=\"file-actions\">\n\n    <div class=\"BtnGroup\">\n      <a id=\"raw-url\" class=\"btn btn-sm BtnGroup-item\" href=\"/b3log/wide/raw/master/LICENSE\">Raw</a>\n        <a class=\"btn btn-sm js-update-url-with-hash BtnGroup-item\" data-hotkey=\"b\" href=\"/b3log/wide/blame/master/LICENSE\">Blame</a>\n      <a rel=\"nofollow\" class=\"btn btn-sm BtnGroup-item\" href=\"/b3log/wide/commits/master/LICENSE\">History</a>\n    </div>\n\n        <a class=\"btn-octicon tooltipped tooltipped-nw\"\n           href=\"x-github-client://openRepo/https://github.com/b3log/wide?branch=master&amp;filepath=LICENSE\"\n           aria-label=\"Open this file in GitHub Desktop\"\n           data-ga-click=\"Repository, open with desktop, type:mac\">\n            <svg class=\"octicon octicon-device-desktop\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M15 2H1c-.55 0-1 .45-1 1v9c0 .55.45 1 1 1h5.34c-.25.61-.86 1.39-2.34 2h8c-1.48-.61-2.09-1.39-2.34-2H15c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1zm0 9H1V3h14v8z\"/></svg>\n        </a>\n\n          <!-- '\"` --><!-- </textarea></xmp> --></option></form><form class=\"inline-form js-update-url-with-hash\" action=\"/b3log/wide/edit/master/LICENSE\" accept-charset=\"UTF-8\" method=\"post\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /><input type=\"hidden\" name=\"authenticity_token\" value=\"r7fAT8kGI2SFh2I/QrOE26VeurlOX4g0TsHeoVhTk4H6AMtY+M7TeaqWEJ6YAmkjfwt6W+NoASZb5+jT2ujoHQ==\" />\n            <button class=\"btn-octicon tooltipped tooltipped-nw\" type=\"submit\"\n              aria-label=\"Fork this project and edit the file\" data-hotkey=\"e\" data-disable-with>\n              <svg class=\"octicon octicon-pencil\" viewBox=\"0 0 14 16\" version=\"1.1\" width=\"14\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M0 12v3h3l8-8-3-3-8 8zm3 2H1v-2h1v1h1v1zm10.3-9.3L12 6 9 3l1.3-1.3a.996.996 0 0 1 1.41 0l1.59 1.59c.39.39.39 1.02 0 1.41z\"/></svg>\n            </button>\n</form>\n        <!-- '\"` --><!-- </textarea></xmp> --></option></form><form class=\"inline-form\" action=\"/b3log/wide/delete/master/LICENSE\" accept-charset=\"UTF-8\" method=\"post\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /><input type=\"hidden\" name=\"authenticity_token\" value=\"U/WONCoGZhGlqCBFCHSd1HmC6wFPpzektspKxkQ6oP7lqdstQDGrjHi+QDb+64X1AJ/jHsu9QvkKuhdbw7jTXg==\" />\n          <button class=\"btn-octicon btn-octicon-danger tooltipped tooltipped-nw\" type=\"submit\"\n            aria-label=\"Fork this project and delete the file\" data-disable-with>\n            <svg class=\"octicon octicon-trashcan\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M11 2H9c0-.55-.45-1-1-1H5c-.55 0-1 .45-1 1H2c-.55 0-1 .45-1 1v1c0 .55.45 1 1 1v9c0 .55.45 1 1 1h7c.55 0 1-.45 1-1V5c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1zm-1 12H3V5h1v8h1V5h1v8h1V5h1v8h1V5h1v9zm1-10H2V3h9v1z\"/></svg>\n          </button>\n</form>  </div>\n\n  <div class=\"file-info\">\n      202 lines (169 sloc)\n      <span class=\"file-info-divider\"></span>\n    11.1 KB\n  </div>\n</div>\n\n    \n\n  <div itemprop=\"text\" class=\"blob-wrapper data type-text\">\n      <table class=\"highlight tab-size js-file-line-container\" data-tab-size=\"8\">\n      <tr>\n        <td id=\"L1\" class=\"blob-num js-line-number\" data-line-number=\"1\"></td>\n        <td id=\"LC1\" class=\"blob-code blob-code-inner js-file-line\">                                 Apache License</td>\n      </tr>\n      <tr>\n        <td id=\"L2\" class=\"blob-num js-line-number\" data-line-number=\"2\"></td>\n        <td id=\"LC2\" class=\"blob-code blob-code-inner js-file-line\">                           Version 2.0, January 2004</td>\n      </tr>\n      <tr>\n        <td id=\"L3\" class=\"blob-num js-line-number\" data-line-number=\"3\"></td>\n        <td id=\"LC3\" class=\"blob-code blob-code-inner js-file-line\">                        http://www.apache.org/licenses/</td>\n      </tr>\n      <tr>\n        <td id=\"L4\" class=\"blob-num js-line-number\" data-line-number=\"4\"></td>\n        <td id=\"LC4\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L5\" class=\"blob-num js-line-number\" data-line-number=\"5\"></td>\n        <td id=\"LC5\" class=\"blob-code blob-code-inner js-file-line\">   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION</td>\n      </tr>\n      <tr>\n        <td id=\"L6\" class=\"blob-num js-line-number\" data-line-number=\"6\"></td>\n        <td id=\"LC6\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L7\" class=\"blob-num js-line-number\" data-line-number=\"7\"></td>\n        <td id=\"LC7\" class=\"blob-code blob-code-inner js-file-line\">   1. Definitions.</td>\n      </tr>\n      <tr>\n        <td id=\"L8\" class=\"blob-num js-line-number\" data-line-number=\"8\"></td>\n        <td id=\"LC8\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L9\" class=\"blob-num js-line-number\" data-line-number=\"9\"></td>\n        <td id=\"LC9\" class=\"blob-code blob-code-inner js-file-line\">      &quot;License&quot; shall mean the terms and conditions for use, reproduction,</td>\n      </tr>\n      <tr>\n        <td id=\"L10\" class=\"blob-num js-line-number\" data-line-number=\"10\"></td>\n        <td id=\"LC10\" class=\"blob-code blob-code-inner js-file-line\">      and distribution as defined by Sections 1 through 9 of this document.</td>\n      </tr>\n      <tr>\n        <td id=\"L11\" class=\"blob-num js-line-number\" data-line-number=\"11\"></td>\n        <td id=\"LC11\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L12\" class=\"blob-num js-line-number\" data-line-number=\"12\"></td>\n        <td id=\"LC12\" class=\"blob-code blob-code-inner js-file-line\">      &quot;Licensor&quot; shall mean the copyright owner or entity authorized by</td>\n      </tr>\n      <tr>\n        <td id=\"L13\" class=\"blob-num js-line-number\" data-line-number=\"13\"></td>\n        <td id=\"LC13\" class=\"blob-code blob-code-inner js-file-line\">      the copyright owner that is granting the License.</td>\n      </tr>\n      <tr>\n        <td id=\"L14\" class=\"blob-num js-line-number\" data-line-number=\"14\"></td>\n        <td id=\"LC14\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L15\" class=\"blob-num js-line-number\" data-line-number=\"15\"></td>\n        <td id=\"LC15\" class=\"blob-code blob-code-inner js-file-line\">      &quot;Legal Entity&quot; shall mean the union of the acting entity and all</td>\n      </tr>\n      <tr>\n        <td id=\"L16\" class=\"blob-num js-line-number\" data-line-number=\"16\"></td>\n        <td id=\"LC16\" class=\"blob-code blob-code-inner js-file-line\">      other entities that control, are controlled by, or are under common</td>\n      </tr>\n      <tr>\n        <td id=\"L17\" class=\"blob-num js-line-number\" data-line-number=\"17\"></td>\n        <td id=\"LC17\" class=\"blob-code blob-code-inner js-file-line\">      control with that entity. For the purposes of this definition,</td>\n      </tr>\n      <tr>\n        <td id=\"L18\" class=\"blob-num js-line-number\" data-line-number=\"18\"></td>\n        <td id=\"LC18\" class=\"blob-code blob-code-inner js-file-line\">      &quot;control&quot; means (i) the power, direct or indirect, to cause the</td>\n      </tr>\n      <tr>\n        <td id=\"L19\" class=\"blob-num js-line-number\" data-line-number=\"19\"></td>\n        <td id=\"LC19\" class=\"blob-code blob-code-inner js-file-line\">      direction or management of such entity, whether by contract or</td>\n      </tr>\n      <tr>\n        <td id=\"L20\" class=\"blob-num js-line-number\" data-line-number=\"20\"></td>\n        <td id=\"LC20\" class=\"blob-code blob-code-inner js-file-line\">      otherwise, or (ii) ownership of fifty percent (50%) or more of the</td>\n      </tr>\n      <tr>\n        <td id=\"L21\" class=\"blob-num js-line-number\" data-line-number=\"21\"></td>\n        <td id=\"LC21\" class=\"blob-code blob-code-inner js-file-line\">      outstanding shares, or (iii) beneficial ownership of such entity.</td>\n      </tr>\n      <tr>\n        <td id=\"L22\" class=\"blob-num js-line-number\" data-line-number=\"22\"></td>\n        <td id=\"LC22\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L23\" class=\"blob-num js-line-number\" data-line-number=\"23\"></td>\n        <td id=\"LC23\" class=\"blob-code blob-code-inner js-file-line\">      &quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity</td>\n      </tr>\n      <tr>\n        <td id=\"L24\" class=\"blob-num js-line-number\" data-line-number=\"24\"></td>\n        <td id=\"LC24\" class=\"blob-code blob-code-inner js-file-line\">      exercising permissions granted by this License.</td>\n      </tr>\n      <tr>\n        <td id=\"L25\" class=\"blob-num js-line-number\" data-line-number=\"25\"></td>\n        <td id=\"LC25\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L26\" class=\"blob-num js-line-number\" data-line-number=\"26\"></td>\n        <td id=\"LC26\" class=\"blob-code blob-code-inner js-file-line\">      &quot;Source&quot; form shall mean the preferred form for making modifications,</td>\n      </tr>\n      <tr>\n        <td id=\"L27\" class=\"blob-num js-line-number\" data-line-number=\"27\"></td>\n        <td id=\"LC27\" class=\"blob-code blob-code-inner js-file-line\">      including but not limited to software source code, documentation</td>\n      </tr>\n      <tr>\n        <td id=\"L28\" class=\"blob-num js-line-number\" data-line-number=\"28\"></td>\n        <td id=\"LC28\" class=\"blob-code blob-code-inner js-file-line\">      source, and configuration files.</td>\n      </tr>\n      <tr>\n        <td id=\"L29\" class=\"blob-num js-line-number\" data-line-number=\"29\"></td>\n        <td id=\"LC29\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L30\" class=\"blob-num js-line-number\" data-line-number=\"30\"></td>\n        <td id=\"LC30\" class=\"blob-code blob-code-inner js-file-line\">      &quot;Object&quot; form shall mean any form resulting from mechanical</td>\n      </tr>\n      <tr>\n        <td id=\"L31\" class=\"blob-num js-line-number\" data-line-number=\"31\"></td>\n        <td id=\"LC31\" class=\"blob-code blob-code-inner js-file-line\">      transformation or translation of a Source form, including but</td>\n      </tr>\n      <tr>\n        <td id=\"L32\" class=\"blob-num js-line-number\" data-line-number=\"32\"></td>\n        <td id=\"LC32\" class=\"blob-code blob-code-inner js-file-line\">      not limited to compiled object code, generated documentation,</td>\n      </tr>\n      <tr>\n        <td id=\"L33\" class=\"blob-num js-line-number\" data-line-number=\"33\"></td>\n        <td id=\"LC33\" class=\"blob-code blob-code-inner js-file-line\">      and conversions to other media types.</td>\n      </tr>\n      <tr>\n        <td id=\"L34\" class=\"blob-num js-line-number\" data-line-number=\"34\"></td>\n        <td id=\"LC34\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L35\" class=\"blob-num js-line-number\" data-line-number=\"35\"></td>\n        <td id=\"LC35\" class=\"blob-code blob-code-inner js-file-line\">      &quot;Work&quot; shall mean the work of authorship, whether in Source or</td>\n      </tr>\n      <tr>\n        <td id=\"L36\" class=\"blob-num js-line-number\" data-line-number=\"36\"></td>\n        <td id=\"LC36\" class=\"blob-code blob-code-inner js-file-line\">      Object form, made available under the License, as indicated by a</td>\n      </tr>\n      <tr>\n        <td id=\"L37\" class=\"blob-num js-line-number\" data-line-number=\"37\"></td>\n        <td id=\"LC37\" class=\"blob-code blob-code-inner js-file-line\">      copyright notice that is included in or attached to the work</td>\n      </tr>\n      <tr>\n        <td id=\"L38\" class=\"blob-num js-line-number\" data-line-number=\"38\"></td>\n        <td id=\"LC38\" class=\"blob-code blob-code-inner js-file-line\">      (an example is provided in the Appendix below).</td>\n      </tr>\n      <tr>\n        <td id=\"L39\" class=\"blob-num js-line-number\" data-line-number=\"39\"></td>\n        <td id=\"LC39\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L40\" class=\"blob-num js-line-number\" data-line-number=\"40\"></td>\n        <td id=\"LC40\" class=\"blob-code blob-code-inner js-file-line\">      &quot;Derivative Works&quot; shall mean any work, whether in Source or Object</td>\n      </tr>\n      <tr>\n        <td id=\"L41\" class=\"blob-num js-line-number\" data-line-number=\"41\"></td>\n        <td id=\"LC41\" class=\"blob-code blob-code-inner js-file-line\">      form, that is based on (or derived from) the Work and for which the</td>\n      </tr>\n      <tr>\n        <td id=\"L42\" class=\"blob-num js-line-number\" data-line-number=\"42\"></td>\n        <td id=\"LC42\" class=\"blob-code blob-code-inner js-file-line\">      editorial revisions, annotations, elaborations, or other modifications</td>\n      </tr>\n      <tr>\n        <td id=\"L43\" class=\"blob-num js-line-number\" data-line-number=\"43\"></td>\n        <td id=\"LC43\" class=\"blob-code blob-code-inner js-file-line\">      represent, as a whole, an original work of authorship. For the purposes</td>\n      </tr>\n      <tr>\n        <td id=\"L44\" class=\"blob-num js-line-number\" data-line-number=\"44\"></td>\n        <td id=\"LC44\" class=\"blob-code blob-code-inner js-file-line\">      of this License, Derivative Works shall not include works that remain</td>\n      </tr>\n      <tr>\n        <td id=\"L45\" class=\"blob-num js-line-number\" data-line-number=\"45\"></td>\n        <td id=\"LC45\" class=\"blob-code blob-code-inner js-file-line\">      separable from, or merely link (or bind by name) to the interfaces of,</td>\n      </tr>\n      <tr>\n        <td id=\"L46\" class=\"blob-num js-line-number\" data-line-number=\"46\"></td>\n        <td id=\"LC46\" class=\"blob-code blob-code-inner js-file-line\">      the Work and Derivative Works thereof.</td>\n      </tr>\n      <tr>\n        <td id=\"L47\" class=\"blob-num js-line-number\" data-line-number=\"47\"></td>\n        <td id=\"LC47\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L48\" class=\"blob-num js-line-number\" data-line-number=\"48\"></td>\n        <td id=\"LC48\" class=\"blob-code blob-code-inner js-file-line\">      &quot;Contribution&quot; shall mean any work of authorship, including</td>\n      </tr>\n      <tr>\n        <td id=\"L49\" class=\"blob-num js-line-number\" data-line-number=\"49\"></td>\n        <td id=\"LC49\" class=\"blob-code blob-code-inner js-file-line\">      the original version of the Work and any modifications or additions</td>\n      </tr>\n      <tr>\n        <td id=\"L50\" class=\"blob-num js-line-number\" data-line-number=\"50\"></td>\n        <td id=\"LC50\" class=\"blob-code blob-code-inner js-file-line\">      to that Work or Derivative Works thereof, that is intentionally</td>\n      </tr>\n      <tr>\n        <td id=\"L51\" class=\"blob-num js-line-number\" data-line-number=\"51\"></td>\n        <td id=\"LC51\" class=\"blob-code blob-code-inner js-file-line\">      submitted to Licensor for inclusion in the Work by the copyright owner</td>\n      </tr>\n      <tr>\n        <td id=\"L52\" class=\"blob-num js-line-number\" data-line-number=\"52\"></td>\n        <td id=\"LC52\" class=\"blob-code blob-code-inner js-file-line\">      or by an individual or Legal Entity authorized to submit on behalf of</td>\n      </tr>\n      <tr>\n        <td id=\"L53\" class=\"blob-num js-line-number\" data-line-number=\"53\"></td>\n        <td id=\"LC53\" class=\"blob-code blob-code-inner js-file-line\">      the copyright owner. For the purposes of this definition, &quot;submitted&quot;</td>\n      </tr>\n      <tr>\n        <td id=\"L54\" class=\"blob-num js-line-number\" data-line-number=\"54\"></td>\n        <td id=\"LC54\" class=\"blob-code blob-code-inner js-file-line\">      means any form of electronic, verbal, or written communication sent</td>\n      </tr>\n      <tr>\n        <td id=\"L55\" class=\"blob-num js-line-number\" data-line-number=\"55\"></td>\n        <td id=\"LC55\" class=\"blob-code blob-code-inner js-file-line\">      to the Licensor or its representatives, including but not limited to</td>\n      </tr>\n      <tr>\n        <td id=\"L56\" class=\"blob-num js-line-number\" data-line-number=\"56\"></td>\n        <td id=\"LC56\" class=\"blob-code blob-code-inner js-file-line\">      communication on electronic mailing lists, source code control systems,</td>\n      </tr>\n      <tr>\n        <td id=\"L57\" class=\"blob-num js-line-number\" data-line-number=\"57\"></td>\n        <td id=\"LC57\" class=\"blob-code blob-code-inner js-file-line\">      and issue tracking systems that are managed by, or on behalf of, the</td>\n      </tr>\n      <tr>\n        <td id=\"L58\" class=\"blob-num js-line-number\" data-line-number=\"58\"></td>\n        <td id=\"LC58\" class=\"blob-code blob-code-inner js-file-line\">      Licensor for the purpose of discussing and improving the Work, but</td>\n      </tr>\n      <tr>\n        <td id=\"L59\" class=\"blob-num js-line-number\" data-line-number=\"59\"></td>\n        <td id=\"LC59\" class=\"blob-code blob-code-inner js-file-line\">      excluding communication that is conspicuously marked or otherwise</td>\n      </tr>\n      <tr>\n        <td id=\"L60\" class=\"blob-num js-line-number\" data-line-number=\"60\"></td>\n        <td id=\"LC60\" class=\"blob-code blob-code-inner js-file-line\">      designated in writing by the copyright owner as &quot;Not a Contribution.&quot;</td>\n      </tr>\n      <tr>\n        <td id=\"L61\" class=\"blob-num js-line-number\" data-line-number=\"61\"></td>\n        <td id=\"LC61\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L62\" class=\"blob-num js-line-number\" data-line-number=\"62\"></td>\n        <td id=\"LC62\" class=\"blob-code blob-code-inner js-file-line\">      &quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity</td>\n      </tr>\n      <tr>\n        <td id=\"L63\" class=\"blob-num js-line-number\" data-line-number=\"63\"></td>\n        <td id=\"LC63\" class=\"blob-code blob-code-inner js-file-line\">      on behalf of whom a Contribution has been received by Licensor and</td>\n      </tr>\n      <tr>\n        <td id=\"L64\" class=\"blob-num js-line-number\" data-line-number=\"64\"></td>\n        <td id=\"LC64\" class=\"blob-code blob-code-inner js-file-line\">      subsequently incorporated within the Work.</td>\n      </tr>\n      <tr>\n        <td id=\"L65\" class=\"blob-num js-line-number\" data-line-number=\"65\"></td>\n        <td id=\"LC65\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L66\" class=\"blob-num js-line-number\" data-line-number=\"66\"></td>\n        <td id=\"LC66\" class=\"blob-code blob-code-inner js-file-line\">   2. Grant of Copyright License. Subject to the terms and conditions of</td>\n      </tr>\n      <tr>\n        <td id=\"L67\" class=\"blob-num js-line-number\" data-line-number=\"67\"></td>\n        <td id=\"LC67\" class=\"blob-code blob-code-inner js-file-line\">      this License, each Contributor hereby grants to You a perpetual,</td>\n      </tr>\n      <tr>\n        <td id=\"L68\" class=\"blob-num js-line-number\" data-line-number=\"68\"></td>\n        <td id=\"LC68\" class=\"blob-code blob-code-inner js-file-line\">      worldwide, non-exclusive, no-charge, royalty-free, irrevocable</td>\n      </tr>\n      <tr>\n        <td id=\"L69\" class=\"blob-num js-line-number\" data-line-number=\"69\"></td>\n        <td id=\"LC69\" class=\"blob-code blob-code-inner js-file-line\">      copyright license to reproduce, prepare Derivative Works of,</td>\n      </tr>\n      <tr>\n        <td id=\"L70\" class=\"blob-num js-line-number\" data-line-number=\"70\"></td>\n        <td id=\"LC70\" class=\"blob-code blob-code-inner js-file-line\">      publicly display, publicly perform, sublicense, and distribute the</td>\n      </tr>\n      <tr>\n        <td id=\"L71\" class=\"blob-num js-line-number\" data-line-number=\"71\"></td>\n        <td id=\"LC71\" class=\"blob-code blob-code-inner js-file-line\">      Work and such Derivative Works in Source or Object form.</td>\n      </tr>\n      <tr>\n        <td id=\"L72\" class=\"blob-num js-line-number\" data-line-number=\"72\"></td>\n        <td id=\"LC72\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L73\" class=\"blob-num js-line-number\" data-line-number=\"73\"></td>\n        <td id=\"LC73\" class=\"blob-code blob-code-inner js-file-line\">   3. Grant of Patent License. Subject to the terms and conditions of</td>\n      </tr>\n      <tr>\n        <td id=\"L74\" class=\"blob-num js-line-number\" data-line-number=\"74\"></td>\n        <td id=\"LC74\" class=\"blob-code blob-code-inner js-file-line\">      this License, each Contributor hereby grants to You a perpetual,</td>\n      </tr>\n      <tr>\n        <td id=\"L75\" class=\"blob-num js-line-number\" data-line-number=\"75\"></td>\n        <td id=\"LC75\" class=\"blob-code blob-code-inner js-file-line\">      worldwide, non-exclusive, no-charge, royalty-free, irrevocable</td>\n      </tr>\n      <tr>\n        <td id=\"L76\" class=\"blob-num js-line-number\" data-line-number=\"76\"></td>\n        <td id=\"LC76\" class=\"blob-code blob-code-inner js-file-line\">      (except as stated in this section) patent license to make, have made,</td>\n      </tr>\n      <tr>\n        <td id=\"L77\" class=\"blob-num js-line-number\" data-line-number=\"77\"></td>\n        <td id=\"LC77\" class=\"blob-code blob-code-inner js-file-line\">      use, offer to sell, sell, import, and otherwise transfer the Work,</td>\n      </tr>\n      <tr>\n        <td id=\"L78\" class=\"blob-num js-line-number\" data-line-number=\"78\"></td>\n        <td id=\"LC78\" class=\"blob-code blob-code-inner js-file-line\">      where such license applies only to those patent claims licensable</td>\n      </tr>\n      <tr>\n        <td id=\"L79\" class=\"blob-num js-line-number\" data-line-number=\"79\"></td>\n        <td id=\"LC79\" class=\"blob-code blob-code-inner js-file-line\">      by such Contributor that are necessarily infringed by their</td>\n      </tr>\n      <tr>\n        <td id=\"L80\" class=\"blob-num js-line-number\" data-line-number=\"80\"></td>\n        <td id=\"LC80\" class=\"blob-code blob-code-inner js-file-line\">      Contribution(s) alone or by combination of their Contribution(s)</td>\n      </tr>\n      <tr>\n        <td id=\"L81\" class=\"blob-num js-line-number\" data-line-number=\"81\"></td>\n        <td id=\"LC81\" class=\"blob-code blob-code-inner js-file-line\">      with the Work to which such Contribution(s) was submitted. If You</td>\n      </tr>\n      <tr>\n        <td id=\"L82\" class=\"blob-num js-line-number\" data-line-number=\"82\"></td>\n        <td id=\"LC82\" class=\"blob-code blob-code-inner js-file-line\">      institute patent litigation against any entity (including a</td>\n      </tr>\n      <tr>\n        <td id=\"L83\" class=\"blob-num js-line-number\" data-line-number=\"83\"></td>\n        <td id=\"LC83\" class=\"blob-code blob-code-inner js-file-line\">      cross-claim or counterclaim in a lawsuit) alleging that the Work</td>\n      </tr>\n      <tr>\n        <td id=\"L84\" class=\"blob-num js-line-number\" data-line-number=\"84\"></td>\n        <td id=\"LC84\" class=\"blob-code blob-code-inner js-file-line\">      or a Contribution incorporated within the Work constitutes direct</td>\n      </tr>\n      <tr>\n        <td id=\"L85\" class=\"blob-num js-line-number\" data-line-number=\"85\"></td>\n        <td id=\"LC85\" class=\"blob-code blob-code-inner js-file-line\">      or contributory patent infringement, then any patent licenses</td>\n      </tr>\n      <tr>\n        <td id=\"L86\" class=\"blob-num js-line-number\" data-line-number=\"86\"></td>\n        <td id=\"LC86\" class=\"blob-code blob-code-inner js-file-line\">      granted to You under this License for that Work shall terminate</td>\n      </tr>\n      <tr>\n        <td id=\"L87\" class=\"blob-num js-line-number\" data-line-number=\"87\"></td>\n        <td id=\"LC87\" class=\"blob-code blob-code-inner js-file-line\">      as of the date such litigation is filed.</td>\n      </tr>\n      <tr>\n        <td id=\"L88\" class=\"blob-num js-line-number\" data-line-number=\"88\"></td>\n        <td id=\"LC88\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L89\" class=\"blob-num js-line-number\" data-line-number=\"89\"></td>\n        <td id=\"LC89\" class=\"blob-code blob-code-inner js-file-line\">   4. Redistribution. You may reproduce and distribute copies of the</td>\n      </tr>\n      <tr>\n        <td id=\"L90\" class=\"blob-num js-line-number\" data-line-number=\"90\"></td>\n        <td id=\"LC90\" class=\"blob-code blob-code-inner js-file-line\">      Work or Derivative Works thereof in any medium, with or without</td>\n      </tr>\n      <tr>\n        <td id=\"L91\" class=\"blob-num js-line-number\" data-line-number=\"91\"></td>\n        <td id=\"LC91\" class=\"blob-code blob-code-inner js-file-line\">      modifications, and in Source or Object form, provided that You</td>\n      </tr>\n      <tr>\n        <td id=\"L92\" class=\"blob-num js-line-number\" data-line-number=\"92\"></td>\n        <td id=\"LC92\" class=\"blob-code blob-code-inner js-file-line\">      meet the following conditions:</td>\n      </tr>\n      <tr>\n        <td id=\"L93\" class=\"blob-num js-line-number\" data-line-number=\"93\"></td>\n        <td id=\"LC93\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L94\" class=\"blob-num js-line-number\" data-line-number=\"94\"></td>\n        <td id=\"LC94\" class=\"blob-code blob-code-inner js-file-line\">      (a) You must give any other recipients of the Work or</td>\n      </tr>\n      <tr>\n        <td id=\"L95\" class=\"blob-num js-line-number\" data-line-number=\"95\"></td>\n        <td id=\"LC95\" class=\"blob-code blob-code-inner js-file-line\">          Derivative Works a copy of this License; and</td>\n      </tr>\n      <tr>\n        <td id=\"L96\" class=\"blob-num js-line-number\" data-line-number=\"96\"></td>\n        <td id=\"LC96\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L97\" class=\"blob-num js-line-number\" data-line-number=\"97\"></td>\n        <td id=\"LC97\" class=\"blob-code blob-code-inner js-file-line\">      (b) You must cause any modified files to carry prominent notices</td>\n      </tr>\n      <tr>\n        <td id=\"L98\" class=\"blob-num js-line-number\" data-line-number=\"98\"></td>\n        <td id=\"LC98\" class=\"blob-code blob-code-inner js-file-line\">          stating that You changed the files; and</td>\n      </tr>\n      <tr>\n        <td id=\"L99\" class=\"blob-num js-line-number\" data-line-number=\"99\"></td>\n        <td id=\"LC99\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L100\" class=\"blob-num js-line-number\" data-line-number=\"100\"></td>\n        <td id=\"LC100\" class=\"blob-code blob-code-inner js-file-line\">      (c) You must retain, in the Source form of any Derivative Works</td>\n      </tr>\n      <tr>\n        <td id=\"L101\" class=\"blob-num js-line-number\" data-line-number=\"101\"></td>\n        <td id=\"LC101\" class=\"blob-code blob-code-inner js-file-line\">          that You distribute, all copyright, patent, trademark, and</td>\n      </tr>\n      <tr>\n        <td id=\"L102\" class=\"blob-num js-line-number\" data-line-number=\"102\"></td>\n        <td id=\"LC102\" class=\"blob-code blob-code-inner js-file-line\">          attribution notices from the Source form of the Work,</td>\n      </tr>\n      <tr>\n        <td id=\"L103\" class=\"blob-num js-line-number\" data-line-number=\"103\"></td>\n        <td id=\"LC103\" class=\"blob-code blob-code-inner js-file-line\">          excluding those notices that do not pertain to any part of</td>\n      </tr>\n      <tr>\n        <td id=\"L104\" class=\"blob-num js-line-number\" data-line-number=\"104\"></td>\n        <td id=\"LC104\" class=\"blob-code blob-code-inner js-file-line\">          the Derivative Works; and</td>\n      </tr>\n      <tr>\n        <td id=\"L105\" class=\"blob-num js-line-number\" data-line-number=\"105\"></td>\n        <td id=\"LC105\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L106\" class=\"blob-num js-line-number\" data-line-number=\"106\"></td>\n        <td id=\"LC106\" class=\"blob-code blob-code-inner js-file-line\">      (d) If the Work includes a &quot;NOTICE&quot; text file as part of its</td>\n      </tr>\n      <tr>\n        <td id=\"L107\" class=\"blob-num js-line-number\" data-line-number=\"107\"></td>\n        <td id=\"LC107\" class=\"blob-code blob-code-inner js-file-line\">          distribution, then any Derivative Works that You distribute must</td>\n      </tr>\n      <tr>\n        <td id=\"L108\" class=\"blob-num js-line-number\" data-line-number=\"108\"></td>\n        <td id=\"LC108\" class=\"blob-code blob-code-inner js-file-line\">          include a readable copy of the attribution notices contained</td>\n      </tr>\n      <tr>\n        <td id=\"L109\" class=\"blob-num js-line-number\" data-line-number=\"109\"></td>\n        <td id=\"LC109\" class=\"blob-code blob-code-inner js-file-line\">          within such NOTICE file, excluding those notices that do not</td>\n      </tr>\n      <tr>\n        <td id=\"L110\" class=\"blob-num js-line-number\" data-line-number=\"110\"></td>\n        <td id=\"LC110\" class=\"blob-code blob-code-inner js-file-line\">          pertain to any part of the Derivative Works, in at least one</td>\n      </tr>\n      <tr>\n        <td id=\"L111\" class=\"blob-num js-line-number\" data-line-number=\"111\"></td>\n        <td id=\"LC111\" class=\"blob-code blob-code-inner js-file-line\">          of the following places: within a NOTICE text file distributed</td>\n      </tr>\n      <tr>\n        <td id=\"L112\" class=\"blob-num js-line-number\" data-line-number=\"112\"></td>\n        <td id=\"LC112\" class=\"blob-code blob-code-inner js-file-line\">          as part of the Derivative Works; within the Source form or</td>\n      </tr>\n      <tr>\n        <td id=\"L113\" class=\"blob-num js-line-number\" data-line-number=\"113\"></td>\n        <td id=\"LC113\" class=\"blob-code blob-code-inner js-file-line\">          documentation, if provided along with the Derivative Works; or,</td>\n      </tr>\n      <tr>\n        <td id=\"L114\" class=\"blob-num js-line-number\" data-line-number=\"114\"></td>\n        <td id=\"LC114\" class=\"blob-code blob-code-inner js-file-line\">          within a display generated by the Derivative Works, if and</td>\n      </tr>\n      <tr>\n        <td id=\"L115\" class=\"blob-num js-line-number\" data-line-number=\"115\"></td>\n        <td id=\"LC115\" class=\"blob-code blob-code-inner js-file-line\">          wherever such third-party notices normally appear. The contents</td>\n      </tr>\n      <tr>\n        <td id=\"L116\" class=\"blob-num js-line-number\" data-line-number=\"116\"></td>\n        <td id=\"LC116\" class=\"blob-code blob-code-inner js-file-line\">          of the NOTICE file are for informational purposes only and</td>\n      </tr>\n      <tr>\n        <td id=\"L117\" class=\"blob-num js-line-number\" data-line-number=\"117\"></td>\n        <td id=\"LC117\" class=\"blob-code blob-code-inner js-file-line\">          do not modify the License. You may add Your own attribution</td>\n      </tr>\n      <tr>\n        <td id=\"L118\" class=\"blob-num js-line-number\" data-line-number=\"118\"></td>\n        <td id=\"LC118\" class=\"blob-code blob-code-inner js-file-line\">          notices within Derivative Works that You distribute, alongside</td>\n      </tr>\n      <tr>\n        <td id=\"L119\" class=\"blob-num js-line-number\" data-line-number=\"119\"></td>\n        <td id=\"LC119\" class=\"blob-code blob-code-inner js-file-line\">          or as an addendum to the NOTICE text from the Work, provided</td>\n      </tr>\n      <tr>\n        <td id=\"L120\" class=\"blob-num js-line-number\" data-line-number=\"120\"></td>\n        <td id=\"LC120\" class=\"blob-code blob-code-inner js-file-line\">          that such additional attribution notices cannot be construed</td>\n      </tr>\n      <tr>\n        <td id=\"L121\" class=\"blob-num js-line-number\" data-line-number=\"121\"></td>\n        <td id=\"LC121\" class=\"blob-code blob-code-inner js-file-line\">          as modifying the License.</td>\n      </tr>\n      <tr>\n        <td id=\"L122\" class=\"blob-num js-line-number\" data-line-number=\"122\"></td>\n        <td id=\"LC122\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L123\" class=\"blob-num js-line-number\" data-line-number=\"123\"></td>\n        <td id=\"LC123\" class=\"blob-code blob-code-inner js-file-line\">      You may add Your own copyright statement to Your modifications and</td>\n      </tr>\n      <tr>\n        <td id=\"L124\" class=\"blob-num js-line-number\" data-line-number=\"124\"></td>\n        <td id=\"LC124\" class=\"blob-code blob-code-inner js-file-line\">      may provide additional or different license terms and conditions</td>\n      </tr>\n      <tr>\n        <td id=\"L125\" class=\"blob-num js-line-number\" data-line-number=\"125\"></td>\n        <td id=\"LC125\" class=\"blob-code blob-code-inner js-file-line\">      for use, reproduction, or distribution of Your modifications, or</td>\n      </tr>\n      <tr>\n        <td id=\"L126\" class=\"blob-num js-line-number\" data-line-number=\"126\"></td>\n        <td id=\"LC126\" class=\"blob-code blob-code-inner js-file-line\">      for any such Derivative Works as a whole, provided Your use,</td>\n      </tr>\n      <tr>\n        <td id=\"L127\" class=\"blob-num js-line-number\" data-line-number=\"127\"></td>\n        <td id=\"LC127\" class=\"blob-code blob-code-inner js-file-line\">      reproduction, and distribution of the Work otherwise complies with</td>\n      </tr>\n      <tr>\n        <td id=\"L128\" class=\"blob-num js-line-number\" data-line-number=\"128\"></td>\n        <td id=\"LC128\" class=\"blob-code blob-code-inner js-file-line\">      the conditions stated in this License.</td>\n      </tr>\n      <tr>\n        <td id=\"L129\" class=\"blob-num js-line-number\" data-line-number=\"129\"></td>\n        <td id=\"LC129\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L130\" class=\"blob-num js-line-number\" data-line-number=\"130\"></td>\n        <td id=\"LC130\" class=\"blob-code blob-code-inner js-file-line\">   5. Submission of Contributions. Unless You explicitly state otherwise,</td>\n      </tr>\n      <tr>\n        <td id=\"L131\" class=\"blob-num js-line-number\" data-line-number=\"131\"></td>\n        <td id=\"LC131\" class=\"blob-code blob-code-inner js-file-line\">      any Contribution intentionally submitted for inclusion in the Work</td>\n      </tr>\n      <tr>\n        <td id=\"L132\" class=\"blob-num js-line-number\" data-line-number=\"132\"></td>\n        <td id=\"LC132\" class=\"blob-code blob-code-inner js-file-line\">      by You to the Licensor shall be under the terms and conditions of</td>\n      </tr>\n      <tr>\n        <td id=\"L133\" class=\"blob-num js-line-number\" data-line-number=\"133\"></td>\n        <td id=\"LC133\" class=\"blob-code blob-code-inner js-file-line\">      this License, without any additional terms or conditions.</td>\n      </tr>\n      <tr>\n        <td id=\"L134\" class=\"blob-num js-line-number\" data-line-number=\"134\"></td>\n        <td id=\"LC134\" class=\"blob-code blob-code-inner js-file-line\">      Notwithstanding the above, nothing herein shall supersede or modify</td>\n      </tr>\n      <tr>\n        <td id=\"L135\" class=\"blob-num js-line-number\" data-line-number=\"135\"></td>\n        <td id=\"LC135\" class=\"blob-code blob-code-inner js-file-line\">      the terms of any separate license agreement you may have executed</td>\n      </tr>\n      <tr>\n        <td id=\"L136\" class=\"blob-num js-line-number\" data-line-number=\"136\"></td>\n        <td id=\"LC136\" class=\"blob-code blob-code-inner js-file-line\">      with Licensor regarding such Contributions.</td>\n      </tr>\n      <tr>\n        <td id=\"L137\" class=\"blob-num js-line-number\" data-line-number=\"137\"></td>\n        <td id=\"LC137\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L138\" class=\"blob-num js-line-number\" data-line-number=\"138\"></td>\n        <td id=\"LC138\" class=\"blob-code blob-code-inner js-file-line\">   6. Trademarks. This License does not grant permission to use the trade</td>\n      </tr>\n      <tr>\n        <td id=\"L139\" class=\"blob-num js-line-number\" data-line-number=\"139\"></td>\n        <td id=\"LC139\" class=\"blob-code blob-code-inner js-file-line\">      names, trademarks, service marks, or product names of the Licensor,</td>\n      </tr>\n      <tr>\n        <td id=\"L140\" class=\"blob-num js-line-number\" data-line-number=\"140\"></td>\n        <td id=\"LC140\" class=\"blob-code blob-code-inner js-file-line\">      except as required for reasonable and customary use in describing the</td>\n      </tr>\n      <tr>\n        <td id=\"L141\" class=\"blob-num js-line-number\" data-line-number=\"141\"></td>\n        <td id=\"LC141\" class=\"blob-code blob-code-inner js-file-line\">      origin of the Work and reproducing the content of the NOTICE file.</td>\n      </tr>\n      <tr>\n        <td id=\"L142\" class=\"blob-num js-line-number\" data-line-number=\"142\"></td>\n        <td id=\"LC142\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L143\" class=\"blob-num js-line-number\" data-line-number=\"143\"></td>\n        <td id=\"LC143\" class=\"blob-code blob-code-inner js-file-line\">   7. Disclaimer of Warranty. Unless required by applicable law or</td>\n      </tr>\n      <tr>\n        <td id=\"L144\" class=\"blob-num js-line-number\" data-line-number=\"144\"></td>\n        <td id=\"LC144\" class=\"blob-code blob-code-inner js-file-line\">      agreed to in writing, Licensor provides the Work (and each</td>\n      </tr>\n      <tr>\n        <td id=\"L145\" class=\"blob-num js-line-number\" data-line-number=\"145\"></td>\n        <td id=\"LC145\" class=\"blob-code blob-code-inner js-file-line\">      Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS,</td>\n      </tr>\n      <tr>\n        <td id=\"L146\" class=\"blob-num js-line-number\" data-line-number=\"146\"></td>\n        <td id=\"LC146\" class=\"blob-code blob-code-inner js-file-line\">      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or</td>\n      </tr>\n      <tr>\n        <td id=\"L147\" class=\"blob-num js-line-number\" data-line-number=\"147\"></td>\n        <td id=\"LC147\" class=\"blob-code blob-code-inner js-file-line\">      implied, including, without limitation, any warranties or conditions</td>\n      </tr>\n      <tr>\n        <td id=\"L148\" class=\"blob-num js-line-number\" data-line-number=\"148\"></td>\n        <td id=\"LC148\" class=\"blob-code blob-code-inner js-file-line\">      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A</td>\n      </tr>\n      <tr>\n        <td id=\"L149\" class=\"blob-num js-line-number\" data-line-number=\"149\"></td>\n        <td id=\"LC149\" class=\"blob-code blob-code-inner js-file-line\">      PARTICULAR PURPOSE. You are solely responsible for determining the</td>\n      </tr>\n      <tr>\n        <td id=\"L150\" class=\"blob-num js-line-number\" data-line-number=\"150\"></td>\n        <td id=\"LC150\" class=\"blob-code blob-code-inner js-file-line\">      appropriateness of using or redistributing the Work and assume any</td>\n      </tr>\n      <tr>\n        <td id=\"L151\" class=\"blob-num js-line-number\" data-line-number=\"151\"></td>\n        <td id=\"LC151\" class=\"blob-code blob-code-inner js-file-line\">      risks associated with Your exercise of permissions under this License.</td>\n      </tr>\n      <tr>\n        <td id=\"L152\" class=\"blob-num js-line-number\" data-line-number=\"152\"></td>\n        <td id=\"LC152\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L153\" class=\"blob-num js-line-number\" data-line-number=\"153\"></td>\n        <td id=\"LC153\" class=\"blob-code blob-code-inner js-file-line\">   8. Limitation of Liability. In no event and under no legal theory,</td>\n      </tr>\n      <tr>\n        <td id=\"L154\" class=\"blob-num js-line-number\" data-line-number=\"154\"></td>\n        <td id=\"LC154\" class=\"blob-code blob-code-inner js-file-line\">      whether in tort (including negligence), contract, or otherwise,</td>\n      </tr>\n      <tr>\n        <td id=\"L155\" class=\"blob-num js-line-number\" data-line-number=\"155\"></td>\n        <td id=\"LC155\" class=\"blob-code blob-code-inner js-file-line\">      unless required by applicable law (such as deliberate and grossly</td>\n      </tr>\n      <tr>\n        <td id=\"L156\" class=\"blob-num js-line-number\" data-line-number=\"156\"></td>\n        <td id=\"LC156\" class=\"blob-code blob-code-inner js-file-line\">      negligent acts) or agreed to in writing, shall any Contributor be</td>\n      </tr>\n      <tr>\n        <td id=\"L157\" class=\"blob-num js-line-number\" data-line-number=\"157\"></td>\n        <td id=\"LC157\" class=\"blob-code blob-code-inner js-file-line\">      liable to You for damages, including any direct, indirect, special,</td>\n      </tr>\n      <tr>\n        <td id=\"L158\" class=\"blob-num js-line-number\" data-line-number=\"158\"></td>\n        <td id=\"LC158\" class=\"blob-code blob-code-inner js-file-line\">      incidental, or consequential damages of any character arising as a</td>\n      </tr>\n      <tr>\n        <td id=\"L159\" class=\"blob-num js-line-number\" data-line-number=\"159\"></td>\n        <td id=\"LC159\" class=\"blob-code blob-code-inner js-file-line\">      result of this License or out of the use or inability to use the</td>\n      </tr>\n      <tr>\n        <td id=\"L160\" class=\"blob-num js-line-number\" data-line-number=\"160\"></td>\n        <td id=\"LC160\" class=\"blob-code blob-code-inner js-file-line\">      Work (including but not limited to damages for loss of goodwill,</td>\n      </tr>\n      <tr>\n        <td id=\"L161\" class=\"blob-num js-line-number\" data-line-number=\"161\"></td>\n        <td id=\"LC161\" class=\"blob-code blob-code-inner js-file-line\">      work stoppage, computer failure or malfunction, or any and all</td>\n      </tr>\n      <tr>\n        <td id=\"L162\" class=\"blob-num js-line-number\" data-line-number=\"162\"></td>\n        <td id=\"LC162\" class=\"blob-code blob-code-inner js-file-line\">      other commercial damages or losses), even if such Contributor</td>\n      </tr>\n      <tr>\n        <td id=\"L163\" class=\"blob-num js-line-number\" data-line-number=\"163\"></td>\n        <td id=\"LC163\" class=\"blob-code blob-code-inner js-file-line\">      has been advised of the possibility of such damages.</td>\n      </tr>\n      <tr>\n        <td id=\"L164\" class=\"blob-num js-line-number\" data-line-number=\"164\"></td>\n        <td id=\"LC164\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L165\" class=\"blob-num js-line-number\" data-line-number=\"165\"></td>\n        <td id=\"LC165\" class=\"blob-code blob-code-inner js-file-line\">   9. Accepting Warranty or Additional Liability. While redistributing</td>\n      </tr>\n      <tr>\n        <td id=\"L166\" class=\"blob-num js-line-number\" data-line-number=\"166\"></td>\n        <td id=\"LC166\" class=\"blob-code blob-code-inner js-file-line\">      the Work or Derivative Works thereof, You may choose to offer,</td>\n      </tr>\n      <tr>\n        <td id=\"L167\" class=\"blob-num js-line-number\" data-line-number=\"167\"></td>\n        <td id=\"LC167\" class=\"blob-code blob-code-inner js-file-line\">      and charge a fee for, acceptance of support, warranty, indemnity,</td>\n      </tr>\n      <tr>\n        <td id=\"L168\" class=\"blob-num js-line-number\" data-line-number=\"168\"></td>\n        <td id=\"LC168\" class=\"blob-code blob-code-inner js-file-line\">      or other liability obligations and/or rights consistent with this</td>\n      </tr>\n      <tr>\n        <td id=\"L169\" class=\"blob-num js-line-number\" data-line-number=\"169\"></td>\n        <td id=\"LC169\" class=\"blob-code blob-code-inner js-file-line\">      License. However, in accepting such obligations, You may act only</td>\n      </tr>\n      <tr>\n        <td id=\"L170\" class=\"blob-num js-line-number\" data-line-number=\"170\"></td>\n        <td id=\"LC170\" class=\"blob-code blob-code-inner js-file-line\">      on Your own behalf and on Your sole responsibility, not on behalf</td>\n      </tr>\n      <tr>\n        <td id=\"L171\" class=\"blob-num js-line-number\" data-line-number=\"171\"></td>\n        <td id=\"LC171\" class=\"blob-code blob-code-inner js-file-line\">      of any other Contributor, and only if You agree to indemnify,</td>\n      </tr>\n      <tr>\n        <td id=\"L172\" class=\"blob-num js-line-number\" data-line-number=\"172\"></td>\n        <td id=\"LC172\" class=\"blob-code blob-code-inner js-file-line\">      defend, and hold each Contributor harmless for any liability</td>\n      </tr>\n      <tr>\n        <td id=\"L173\" class=\"blob-num js-line-number\" data-line-number=\"173\"></td>\n        <td id=\"LC173\" class=\"blob-code blob-code-inner js-file-line\">      incurred by, or claims asserted against, such Contributor by reason</td>\n      </tr>\n      <tr>\n        <td id=\"L174\" class=\"blob-num js-line-number\" data-line-number=\"174\"></td>\n        <td id=\"LC174\" class=\"blob-code blob-code-inner js-file-line\">      of your accepting any such warranty or additional liability.</td>\n      </tr>\n      <tr>\n        <td id=\"L175\" class=\"blob-num js-line-number\" data-line-number=\"175\"></td>\n        <td id=\"LC175\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L176\" class=\"blob-num js-line-number\" data-line-number=\"176\"></td>\n        <td id=\"LC176\" class=\"blob-code blob-code-inner js-file-line\">   END OF TERMS AND CONDITIONS</td>\n      </tr>\n      <tr>\n        <td id=\"L177\" class=\"blob-num js-line-number\" data-line-number=\"177\"></td>\n        <td id=\"LC177\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L178\" class=\"blob-num js-line-number\" data-line-number=\"178\"></td>\n        <td id=\"LC178\" class=\"blob-code blob-code-inner js-file-line\">   APPENDIX: How to apply the Apache License to your work.</td>\n      </tr>\n      <tr>\n        <td id=\"L179\" class=\"blob-num js-line-number\" data-line-number=\"179\"></td>\n        <td id=\"LC179\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L180\" class=\"blob-num js-line-number\" data-line-number=\"180\"></td>\n        <td id=\"LC180\" class=\"blob-code blob-code-inner js-file-line\">      To apply the Apache License to your work, attach the following</td>\n      </tr>\n      <tr>\n        <td id=\"L181\" class=\"blob-num js-line-number\" data-line-number=\"181\"></td>\n        <td id=\"LC181\" class=\"blob-code blob-code-inner js-file-line\">      boilerplate notice, with the fields enclosed by brackets &quot;[]&quot;</td>\n      </tr>\n      <tr>\n        <td id=\"L182\" class=\"blob-num js-line-number\" data-line-number=\"182\"></td>\n        <td id=\"LC182\" class=\"blob-code blob-code-inner js-file-line\">      replaced with your own identifying information. (Don&#39;t include</td>\n      </tr>\n      <tr>\n        <td id=\"L183\" class=\"blob-num js-line-number\" data-line-number=\"183\"></td>\n        <td id=\"LC183\" class=\"blob-code blob-code-inner js-file-line\">      the brackets!)  The text should be enclosed in the appropriate</td>\n      </tr>\n      <tr>\n        <td id=\"L184\" class=\"blob-num js-line-number\" data-line-number=\"184\"></td>\n        <td id=\"LC184\" class=\"blob-code blob-code-inner js-file-line\">      comment syntax for the file format. We also recommend that a</td>\n      </tr>\n      <tr>\n        <td id=\"L185\" class=\"blob-num js-line-number\" data-line-number=\"185\"></td>\n        <td id=\"LC185\" class=\"blob-code blob-code-inner js-file-line\">      file or class name and description of purpose be included on the</td>\n      </tr>\n      <tr>\n        <td id=\"L186\" class=\"blob-num js-line-number\" data-line-number=\"186\"></td>\n        <td id=\"LC186\" class=\"blob-code blob-code-inner js-file-line\">      same &quot;printed page&quot; as the copyright notice for easier</td>\n      </tr>\n      <tr>\n        <td id=\"L187\" class=\"blob-num js-line-number\" data-line-number=\"187\"></td>\n        <td id=\"LC187\" class=\"blob-code blob-code-inner js-file-line\">      identification within third-party archives.</td>\n      </tr>\n      <tr>\n        <td id=\"L188\" class=\"blob-num js-line-number\" data-line-number=\"188\"></td>\n        <td id=\"LC188\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L189\" class=\"blob-num js-line-number\" data-line-number=\"189\"></td>\n        <td id=\"LC189\" class=\"blob-code blob-code-inner js-file-line\">   Copyright [yyyy] [name of copyright owner]</td>\n      </tr>\n      <tr>\n        <td id=\"L190\" class=\"blob-num js-line-number\" data-line-number=\"190\"></td>\n        <td id=\"LC190\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L191\" class=\"blob-num js-line-number\" data-line-number=\"191\"></td>\n        <td id=\"LC191\" class=\"blob-code blob-code-inner js-file-line\">   Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);</td>\n      </tr>\n      <tr>\n        <td id=\"L192\" class=\"blob-num js-line-number\" data-line-number=\"192\"></td>\n        <td id=\"LC192\" class=\"blob-code blob-code-inner js-file-line\">   you may not use this file except in compliance with the License.</td>\n      </tr>\n      <tr>\n        <td id=\"L193\" class=\"blob-num js-line-number\" data-line-number=\"193\"></td>\n        <td id=\"LC193\" class=\"blob-code blob-code-inner js-file-line\">   You may obtain a copy of the License at</td>\n      </tr>\n      <tr>\n        <td id=\"L194\" class=\"blob-num js-line-number\" data-line-number=\"194\"></td>\n        <td id=\"LC194\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L195\" class=\"blob-num js-line-number\" data-line-number=\"195\"></td>\n        <td id=\"LC195\" class=\"blob-code blob-code-inner js-file-line\">       https://www.apache.org/licenses/LICENSE-2.0</td>\n      </tr>\n      <tr>\n        <td id=\"L196\" class=\"blob-num js-line-number\" data-line-number=\"196\"></td>\n        <td id=\"LC196\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n      </tr>\n      <tr>\n        <td id=\"L197\" class=\"blob-num js-line-number\" data-line-number=\"197\"></td>\n        <td id=\"LC197\" class=\"blob-code blob-code-inner js-file-line\">   Unless required by applicable law or agreed to in writing, software</td>\n      </tr>\n      <tr>\n        <td id=\"L198\" class=\"blob-num js-line-number\" data-line-number=\"198\"></td>\n        <td id=\"LC198\" class=\"blob-code blob-code-inner js-file-line\">   distributed under the License is distributed on an &quot;AS IS&quot; BASIS,</td>\n      </tr>\n      <tr>\n        <td id=\"L199\" class=\"blob-num js-line-number\" data-line-number=\"199\"></td>\n        <td id=\"LC199\" class=\"blob-code blob-code-inner js-file-line\">   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</td>\n      </tr>\n      <tr>\n        <td id=\"L200\" class=\"blob-num js-line-number\" data-line-number=\"200\"></td>\n        <td id=\"LC200\" class=\"blob-code blob-code-inner js-file-line\">   See the License for the specific language governing permissions and</td>\n      </tr>\n      <tr>\n        <td id=\"L201\" class=\"blob-num js-line-number\" data-line-number=\"201\"></td>\n        <td id=\"LC201\" class=\"blob-code blob-code-inner js-file-line\">   limitations under the License.</td>\n      </tr>\n</table>\n\n  <div class=\"BlobToolbar position-absolute js-file-line-actions dropdown js-menu-container js-select-menu d-none\" aria-hidden=\"true\">\n    <button class=\"btn-octicon ml-0 px-2 p-0 bg-white border border-gray-dark rounded-1 dropdown-toggle js-menu-target\" type=\"button\" aria-expanded=\"false\" aria-haspopup=\"true\" aria-label=\"Inline file action toolbar\" aria-controls=\"inline-file-actions\">\n      <svg class=\"octicon octicon-kebab-horizontal\" viewBox=\"0 0 13 16\" version=\"1.1\" width=\"13\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm5 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zM13 7.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z\"/></svg>\n    </button>\n    <div class=\"dropdown-menu-content js-menu-content\" id=\"inline-file-actions\">\n      <ul class=\"BlobToolbar-dropdown dropdown-menu dropdown-menu-se mt-2\">\n        <li><clipboard-copy class=\"dropdown-item\" id=\"js-copy-lines\" style=\"cursor:pointer;\" data-original-text=\"Copy lines\">Copy lines</clipboard-copy></li>\n        <li><clipboard-copy class=\"dropdown-item\" id=\"js-copy-permalink\" style=\"cursor:pointer;\" data-original-text=\"Copy permalink\">Copy permalink</clipboard-copy></li>\n        <li><a class=\"dropdown-item js-update-url-with-hash\" id=\"js-view-git-blame\" href=\"/b3log/wide/blame/8c7d2636b22131b101f54e5d597c41b9cc4d8bcb/LICENSE\">View git blame</a></li>\n          <li><a class=\"dropdown-item\" id=\"js-new-issue\" href=\"/b3log/wide/issues/new\">Open new issue</a></li>\n      </ul>\n    </div>\n  </div>\n\n  </div>\n\n  </div>\n\n  <button type=\"button\" data-facebox=\"#jump-to-line\" data-facebox-class=\"linejump\" data-hotkey=\"l\" class=\"d-none\">Jump to Line</button>\n  <div id=\"jump-to-line\" style=\"display:none\">\n    <!-- '\"` --><!-- </textarea></xmp> --></option></form><form class=\"js-jump-to-line-form\" action=\"\" accept-charset=\"UTF-8\" method=\"get\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" />\n      <input class=\"form-control linejump-input js-jump-to-line-field\" type=\"text\" placeholder=\"Jump to line&hellip;\" aria-label=\"Jump to line\" autofocus>\n      <button type=\"submit\" class=\"btn\">Go</button>\n</form>  </div>\n\n\n  </div>\n  <div class=\"modal-backdrop js-touch-events\"></div>\n</div>\n\n    </div>\n  </div>\n\n  </div>\n\n      \n<div class=\"footer container-lg px-3\" role=\"contentinfo\">\n  <div class=\"position-relative d-flex flex-justify-between pt-6 pb-2 mt-6 f6 text-gray border-top border-gray-light \">\n    <ul class=\"list-style-none d-flex flex-wrap \">\n      <li class=\"mr-3\">&copy; 2018 <span title=\"0.27113s from unicorn-1154236671-81bm8\">GitHub</span>, Inc.</li>\n        <li class=\"mr-3\"><a data-ga-click=\"Footer, go to terms, text:terms\" href=\"https://github.com/site/terms\">Terms</a></li>\n        <li class=\"mr-3\"><a data-ga-click=\"Footer, go to privacy, text:privacy\" href=\"https://github.com/site/privacy\">Privacy</a></li>\n        <li class=\"mr-3\"><a href=\"https://help.github.com/articles/github-security/\" data-ga-click=\"Footer, go to security, text:security\">Security</a></li>\n        <li class=\"mr-3\"><a href=\"https://status.github.com/\" data-ga-click=\"Footer, go to status, text:status\">Status</a></li>\n        <li><a data-ga-click=\"Footer, go to help, text:help\" href=\"https://help.github.com\">Help</a></li>\n    </ul>\n\n    <a aria-label=\"Homepage\" title=\"GitHub\" class=\"footer-octicon\" href=\"https://github.com\">\n      <svg height=\"24\" class=\"octicon octicon-mark-github\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"24\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z\"/></svg>\n</a>\n   <ul class=\"list-style-none d-flex flex-wrap \">\n        <li class=\"mr-3\"><a data-ga-click=\"Footer, go to contact, text:contact\" href=\"https://github.com/contact\">Contact GitHub</a></li>\n      <li class=\"mr-3\"><a href=\"https://developer.github.com\" data-ga-click=\"Footer, go to api, text:api\">API</a></li>\n      <li class=\"mr-3\"><a href=\"https://training.github.com\" data-ga-click=\"Footer, go to training, text:training\">Training</a></li>\n      <li class=\"mr-3\"><a href=\"https://shop.github.com\" data-ga-click=\"Footer, go to shop, text:shop\">Shop</a></li>\n        <li class=\"mr-3\"><a href=\"https://blog.github.com\" data-ga-click=\"Footer, go to blog, text:blog\">Blog</a></li>\n        <li><a data-ga-click=\"Footer, go to about, text:about\" href=\"https://github.com/about\">About</a></li>\n\n    </ul>\n  </div>\n  <div class=\"d-flex flex-justify-center pb-6\">\n    <span class=\"f6 text-gray-light\"></span>\n  </div>\n</div>\n\n\n\n  <div id=\"ajax-error-message\" class=\"ajax-error-message flash flash-error\">\n    <svg class=\"octicon octicon-alert\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z\"/></svg>\n    <button type=\"button\" class=\"flash-close js-ajax-error-dismiss\" aria-label=\"Dismiss error\">\n      <svg class=\"octicon octicon-x\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z\"/></svg>\n    </button>\n    You can’t perform that action at this time.\n  </div>\n\n\n    \n    <script crossorigin=\"anonymous\" integrity=\"sha512-rTmClkyJxuWVxD3HuXtPq/5kD0857EfJF77T++HayvDGIYtxo5/ctJc8dYwYj/wSbAU4pD6iSTbLDMs8uNVtlQ==\" type=\"application/javascript\" src=\"https://assets-cdn.github.com/assets/frameworks-10f86fbecbc74b507bd240fe66202501.js\"></script>\n    \n    <script crossorigin=\"anonymous\" async=\"async\" integrity=\"sha512-VZX5e2PAduVwKFBY5IaJk7Bg7BOI7vzsbneQ+XGdrvg4mXNNu7m1NIKRX7M4eFwjEXS49mZJQraPKwvXmDVzvw==\" type=\"application/javascript\" src=\"https://assets-cdn.github.com/assets/github-5c1954d1915cc1eb4f0d7f0959ad2d35.js\"></script>\n    \n    \n    \n  <div class=\"js-stale-session-flash stale-session-flash flash flash-warn flash-banner d-none\">\n    <svg class=\"octicon octicon-alert\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z\"/></svg>\n    <span class=\"signed-in-tab-flash\">You signed in with another tab or window. <a href=\"\">Reload</a> to refresh your session.</span>\n    <span class=\"signed-out-tab-flash\">You signed out in another tab or window. <a href=\"\">Reload</a> to refresh your session.</span>\n  </div>\n  <div class=\"facebox\" id=\"facebox\" style=\"display:none;\">\n  <div class=\"facebox-popup\">\n    <div class=\"facebox-content\" role=\"dialog\" aria-labelledby=\"facebox-header\" aria-describedby=\"facebox-description\">\n    </div>\n    <button type=\"button\" class=\"facebox-close js-facebox-close\" aria-label=\"Close modal\">\n      <svg class=\"octicon octicon-x\" viewBox=\"0 0 12 16\" version=\"1.1\" width=\"12\" height=\"16\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" d=\"M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z\"/></svg>\n    </button>\n  </div>\n</div>\n\n  <div class=\"Popover js-hovercard-content position-absolute\" style=\"display: none; outline: none;\" tabindex=\"0\">\n  <div class=\"Popover-message Popover-message--bottom-left Popover-message--large Box box-shadow-large\" style=\"width:360px;\">\n  </div>\n</div>\n\n<div id=\"hovercard-aria-description\" class=\"sr-only\">\n  Press h to open a hovercard with more details.\n</div>\n\n\n  </body>\n</html>\n\n"
  },
  {
    "path": "licenses/license_freeware-easyui.txt",
    "content": "This license agreement refers to EasyUI for jQuery software - Freeware License.\r\n\r\nEasyUI Team grants to you a limited, non-transferable and non-exclusive right to use, royalty-free, copy and redistribute the software.\r\n\r\nThe licensee has the right to use the software for a non-profit projects/sites. There are no limitations on the number of non-profit projects/sites you can use the software in, you can use it on any number of non-profit projects/sites you need. There is no time limit, you can use the software for any period of time you need. There is no restriction while you are developing your solution. There are no royalties of any kind involved.\r\n\r\nThe governmental entities are not allowed to use this freeware license.\r\n\r\nThe licensee is allowed to copy and redistribute the software but you may not:\r\na) Distribute the modified software or part(s) of it as a standalone application.\r\nb) Sublicense, rent, lease or lend any portion of the software as a standalone application.\r\nc) Modify or remove any copyright notices from any of the software files.\r\n\r\nEasyUI Team retains all ownership rights to the software.\r\n"
  },
  {
    "path": "requirements.txt",
    "content": "Flask_Login==0.2.11\nFlask==0.11\nFlask_SSLify==0.1.5\nFlask_APScheduler==1.8.0\nFlask_RESTful==0.3.6\nFlask_Script==2.0.6\nWerkzeug==0.14.1\nAPScheduler==3.5.1\npython_dateutil==2.7.3\nAppium-Python-Client==0.26\nJinja2==2.8\nrequests==2.18.4\nrobotframework==3.0.3\nrobotframework-appiumlibrary==1.4.6\nrobotframework-requests==0.4.7\nrobotframework-seleniumlibrary==3.1.1\nrobotframework-sshlibrary==3.1.1\nrobotframework-databaselibrary==1.0.1\nFlask-Mail==0.9.0"
  },
  {
    "path": "utils/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n"
  },
  {
    "path": "utils/file.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\nimport os, stat, codecs\nimport shutil\n\n\ndef mk_dirs(path, mode=0o777):\n    try:\n        os.makedirs(path, mode=mode)\n    except OSError:\n        if not os.path.isdir(path):\n            raise\n\n\ndef walk_dir(path):\n    try:\n        return os.walk(path)\n    except OSError:\n        if not os.path.exists(path):\n            raise\n\n\ndef list_dir(path):\n    try:\n        return os.listdir(path)\n    except OSError:\n        if not os.path.exists(path):\n            raise\n\n\ndef exists_path(path):\n    if not os.path.exists(path):\n        return False\n\n    return True\n\n\ndef rename_file(src, dst):\n    if exists_path(src) and not exists_path(dst):\n        os.rename(src, dst)\n\n        return True\n\n    return False\n\n\ndef remove_readonly(func, path, _):\n    \"\"\"Clear the readonly bit and reattempt the removal\"\"\"\n    os.chmod(path, stat.S_IWRITE)\n    func(path)\n\n\ndef remove_dir(path):\n    shutil.rmtree(path, onerror=remove_readonly)\n\n\ndef remove_file(path):\n    os.remove(path)\n\n\ndef get_splitext(path):\n    return os.path.splitext(path)\n\n\ndef make_nod(path, mode=\"w\", encoding=\"utf-8\"):\n    if exists_path(path):\n        return False\n\n    f = codecs.open(path, mode, encoding)\n\n    f.close()\n\n    return True\n\n\ndef write_file(path, data, mode=\"w\", encoding=\"utf-8\"):\n    if not exists_path(path):\n        return False\n\n    f = codecs.open(path, mode, encoding)\n\n    f.write(data)\n\n    f.close()\n\n    return True\n\n\ndef read_file(path, mode=\"r\", encoding=\"utf-8\"):\n    if not exists_path(path):\n        return {\"status\": False, \"data\": \"\"}\n\n    f = codecs.open(path, mode, encoding)\n\n    data = f.read()\n\n    f.close()\n\n    return {\"status\": True, \"data\": data}\n\n"
  },
  {
    "path": "utils/help.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\nimport codecs\nimport requests\n\n\ndef check_version():\n    f = codecs.open('version.txt', 'r')\n    version = f.readline()\n    s = requests.Session()\n    r_version = s.get(\"https://gitee.com/lym51/AutoLink/raw/master/version.txt\").text\n    if version != r_version:\n        print(\"*\" * 25)\n        print(\"本地版本：v%s\" % version)\n        print(\"github版本: v%s\" % r_version)\n        print(\"AutoLinK开源平台代码已有更新，请到下面的地址更新代码:\")\n        print(\"下载最新代码，直接覆盖本地即可\")\n        print(\"https://github.com/small99/AutoLink\")\n        print(\"*\" * 25)\n        exit(0)\n    f.close()\n"
  },
  {
    "path": "utils/parsing.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\nimport os\nimport codecs\nimport xml.etree.ElementTree as ET\n\n\nUSER_KEYS = {\n    \"web\": [\"BuiltIn\", \"Collections\", \"DateTime\", \"String\", \"Screenshot\", \"SeleniumLibrary\"],\n    \"app\": [\"BuiltIn\", \"Collections\", \"DateTime\", \"String\", \"Screenshot\", \"AppiumLibrary\"],\n    \"http\": [\"BuiltIn\", \"Collections\", \"DateTime\", \"String\", \"RequestsLibrary\"],\n    \"all\": [\"BuiltIn\", \"Collections\", \"DateTime\",\n            \"OperatingSystem\", \"Process\", \"String\", \"Screenshot\", \"Telnet\",\n            \"AppiumLibrary\", \"RequestsLibrary\", \"SeleniumLibrary\", \"SSHLibrary\", \"DatabaseLibrary\"\n            ]\n}\n\n\ndef parser_robot_keyword_list():\n    keyword_list = []\n    cwd = os.getcwd() + \"/keyword\"\n    for k in USER_KEYS['all']:\n        path = cwd + \"/%s.xml\" % k\n        tree = ET.parse(path)\n        root = tree.getroot()\n        name = root.attrib[\"name\"]\n\n        children = []\n        for kw in root.iter(\"kw\"):\n            # 关键字\n            keyword = kw.attrib[\"name\"]\n\n            # 关键字参数\n            params = \"\"\n            doc_params = []\n            for arg in kw.iter(\"arg\"):\n                params += \"\\t[\" + arg.text + \"]\"\n                doc_params.append(arg.text)\n            params += \"\\n\"\n            if len(doc_params) == 0:\n                doc_params = \"无\"\n\n            # 使用说明\n            doc = kw.find(\"doc\").text\n            if doc is not None:\n                doc_help = doc .replace(\"\\n\", \"<br>\").replace(\"\\r\\t\", \"<br>\")\n            else:\n                doc_help = doc\n\n            children.append({\n                \"id\": keyword,\n                \"text\": keyword,\n                \"iconCls\": \"icon-keyword\",\n                \"attributes\": {\n                    \"keyword\": keyword,\n                    \"category\": \"keyword\",\n                    \"params\": params,\n                    \"doc\": \"<p>关键字: %s<br><br/>所属库: %s<br><br>参数: <br>%s<br><br>文档:<br>%s</p>\" % (keyword, k, \" | \".join(doc_params), doc_help)\n                }\n            })\n\n        keyword_list.append({\n            \"id\": name,\n            \"text\": name,\n            \"state\": \"closed\",\n            \"iconCls\": \"icon-keyword-list\",\n            \"attributes\": {\"category\": name},\n            \"children\": children\n        })\n\n    return keyword_list\n\n\ndef parser(doc_dir):\n\n    for k in USER_KEYS[\"all\"]:\n        keyword_list = []\n        path = doc_dir + \"/%s.xml\" % k\n        tree = ET.parse(path)\n        root = tree.getroot()\n        name = root.attrib[\"name\"]\n\n        for kw in root.iter(\"kw\"):\n            # 关键字\n            keyword_list.append(\"'\" + kw.attrib[\"name\"] + \"'\")\n\n        print(\"rf_\" + name + \"=[\" + \",\".join(keyword_list))\n\n\ndef parser_with_args(doc_dir):\n\n    for k in USER_KEYS[\"all\"]:\n        keyword_list = []\n        path = doc_dir + \"/%s.xml\" % k\n        tree = ET.parse(path)\n        root = tree.getroot()\n        name = root.attrib[\"name\"]\n\n        for kw in root.iter(\"kw\"):\n            # 关键字\n            word = \"'\" + kw.attrib[\"name\"]\n\n            # 关键字参数\n            for arg in kw.iter(\"arg\"):\n                word += \"\\t[\" + arg.text + \"]\"\n\n            word += \"'\"\n\n            keyword_list.append(word)\n\n        print(\"rf_\" + name + \"_args = [\" + \",\".join(keyword_list) + \"];\")\n\n\ndef generate_high_light(doc_dir):\n    ff = codecs.open(os.getcwd() + \"/auto/www/static/js/highlight.js\", \"w\", \"utf-8\")\n    keyword_list = []\n    for k in USER_KEYS[\"all\"]:\n        path = doc_dir + \"/%s.xml\" % k\n        tree = ET.parse(path)\n        root = tree.getroot()\n        name = root.attrib[\"name\"]\n\n        for kw in root.iter(\"kw\"):\n            # 关键字\n            keyword_list.append(\"'\" + kw.attrib[\"name\"] + \"'\")\n    kewords = \"var high_light=\" + \"[\" + \",\".join(keyword_list) + \"];\"\n    ff.write(kewords)\n    ff.close()\n\n\ndef generate_auto_complete(doc_dir):\n    ff = codecs.open(os.getcwd() + \"/auto/www/static/js/autocomplete.js\", \"w\", \"utf-8\")\n    keyword_list = []\n    for k in USER_KEYS[\"all\"]:\n        path = doc_dir + \"/%s.xml\" % k\n        tree = ET.parse(path)\n        root = tree.getroot()\n        name = root.attrib[\"name\"]\n\n        for kw in root.iter(\"kw\"):\n            # 关键字\n            word = \"'\" + kw.attrib[\"name\"]\n\n            # 关键字参数\n            for arg in kw.iter(\"arg\"):\n                word += \"\\t[\" + arg.text + \"]\"\n\n            word += \"'\"\n\n            keyword_list.append(word)\n\n    kewords = \"var auto_complete=\" + \"[\" + \",\".join(keyword_list) + \"];\"\n    ff.write(kewords)\n    ff.close()\n\n\nif __name__ == \"__main__\":\n    path = os.getcwd() + \"/keyword\"\n    generate_high_light(path)\n    generate_auto_complete(path)\n"
  },
  {
    "path": "utils/resource.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\n\nICONS = {\n    \".robot\": \"icon-robot\",\n    \".txt\": \"icon-resource\",\n    \".bmp\": \"icon-image\",\n    \".jpg\": \"icon-image\",\n    \".jpeg\": \"icon-image\",\n    \".png\": \"icon-image\",\n    \".gif\": \"icon-image\",\n    \".py\": \"icon-python\",\n    \".xls\": \"icon-excel\",\n    \".xlsx\": \"icon-excel\"\n}"
  },
  {
    "path": "utils/run.py",
    "content": "# -*- coding: utf-8 -*-\n\n__author__ = \"苦叶子\"\n\n\"\"\"\n\n公众号: 开源优测\n\nEmail: lymking@foxmail.com\n\n\"\"\"\nimport sys\nimport os\nimport codecs\nfrom flask import current_app, session, url_for\nfrom flask_mail import Mail, Message\nimport threading\nfrom threading import Thread\nimport multiprocessing\nimport time\nimport smtplib\nfrom email.mime.text import MIMEText\nfrom email.header import Header\nimport json\n\nfrom robot.api import TestSuiteBuilder, ResultWriter, ExecutionResult\n\nfrom utils.file import exists_path, make_nod, write_file, read_file, mk_dirs\n\n\ndef robot_job(app, name, username):\n    with app.app_context():\n        project = app.config[\"AUTO_HOME\"] + \"/workspace/%s/%s\" % (username, name)\n        output = app.config[\"AUTO_HOME\"] + \"/jobs/%s/%s\" % (username, name)\n        if not is_run(app, project):\n            p = multiprocessing.Process(target=robot_run, args=(username, name, project, output))\n            p.start()\n            app.config[\"AUTO_ROBOT\"].append({\"name\": project, \"process\": p})\n            print(\"-+\" * 15)\n            print(app.config[\"AUTO_ROBOT\"])\n            print(\"-+\" * 15)\n\n\ndef robot_run(username, name, project, output):\n    if not exists_path(output):\n        mk_dirs(output)\n\n    suite = TestSuiteBuilder().build(project)\n\n    (out, index) = reset_next_build_numb(output)\n\n    result = suite.run(output_directory=out,\n                       output=out + \"/output.xml\",\n                       debugfile=out + \"/debug.txt\",\n                       loglevel=\"TRACE\")\n\n    # reset_last_status(result, output, index)\n\n    # Report and xUnit files can be generated based on the result object.\n    # ResultWriter(result).write_results(report=out + '/report.html', log=out + '/log.html')\n    detail_result = ExecutionResult(out + \"/output.xml\")\n\n    # detail_result.save(out + \"/output_new.xml\")\n    reset_last_status(detail_result, output, index)\n\n    # Report and xUnit files can be generated based on the result object.\n    ResultWriter(detail_result).write_results(report=out + '/report.html', log=out + '/log.html')\n\n    send_robot_report(username, name, index, detail_result, out)\n\n\ndef reset_next_build_numb(output):\n    next_build_number = output + \"/nextBuildNumber\"\n    index = 1\n    data = \"%d\" % (index + 1)\n    if not exists_path(next_build_number):\n        make_nod(next_build_number)\n    else:\n        index = int(read_file(next_build_number)[\"data\"])\n        data = \"%d\" % (index + 1)\n    write_file(next_build_number, data)\n\n    out = output + \"/%d\" % index\n    if not exists_path(output):\n        mk_dirs(output)\n\n    return (out, index)\n\n\ndef reset_last_status(result, output, index):\n    stats = result.statistics\n    fail = stats.total.critical.failed\n\n    last_fail = output + \"/lastFail\"\n    last_passed = output + \"/lastPassed\"\n    data = \"%d\" % index\n\n    if fail != 0:\n        if not exists_path(last_fail):\n            make_nod(last_fail)\n\n        write_file(last_fail, data)\n    else:\n        if not exists_path(last_passed):\n            make_nod(last_passed)\n        write_file(last_passed, data)\n\n\ndef remove_robot(app):\n    lock = threading.Lock()\n    lock.acquire()\n    for p in app.config[\"AUTO_ROBOT\"]:\n        if not p[\"process\"].is_alive():\n            app.config[\"AUTO_ROBOT\"].remove(p)\n            break\n    lock.release()\n\n\ndef stop_robot(app, name):\n    lock = threading.Lock()\n    lock.acquire()\n    for p in app.config[\"AUTO_ROBOT\"]:\n        if name == p[\"name\"]:\n            if p[\"process\"].is_alive():\n                p[\"process\"].terminate()\n                time.sleep(0.2)\n                app.config[\"AUTO_ROBOT\"].remove(p)\n                break\n\n    lock.release()\n\n    return True\n\n\ndef is_run(app, name):\n    remove_robot(app)\n    for p in app.config[\"AUTO_ROBOT\"]:\n        if name == p[\"name\"]:\n            return True\n\n    return False\n\n\ndef send_robot_report(username, name, task_no, result, output):\n    # app = current_app._get_current_object()\n    build_msg = \"<font color='green'>Success</font>\"\n    if result.statistics.total.critical.failed != 0:\n        build_msg = \"<font color='red'>Failure</font>\"\n\n    #report_url = url_for(\"routes.q_view_report\",\n    #                     _external=True,\n    #                     username=username,\n    #                     project=name,\n    #                     task=task_no)\n    report_url = \"/q_view_report/%s/%s/%s\" % (username, name, task_no)\n    msg = MIMEText(\"\"\"Hello, %s<hr>\n                项目名称：%s<hr>\n                构建编号: %s<hr>\n                构建状态: %s<hr>\n                持续时间: %s毫秒<hr>\n                详细报告: <a href='%s'>%s</a><hr>\n                构建日志: <br>%s<hr><br><br>\n                (本邮件是程序自动下发的，请勿回复！)\"\"\" %\n                   (username,\n                    result.statistics.suite.stat.name,\n                    task_no,\n                    build_msg,\n                    result.suite.elapsedtime,\n                    report_url, report_url,\n                    codecs.open(output + \"/debug.txt\", \"r\", \"utf-8\").read().replace(\"\\n\", \"<br>\")\n                    ),\n                   \"html\", \"utf-8\")\n\n    msg[\"Subject\"] = Header(\"AutoLink通知消息\", \"utf-8\")\n\n    try:\n        auto_home = os.getcwd().replace('\\\\', '/') + '/.beats'\n\n        #user_path = app.config[\"AUTO_HOME\"] + \"/users/%s/config.json\" % session[\"username\"]\n        user_path = auto_home + \"/users/%s/config.json\" % session[\"username\"]\n        user_conf = json.load(codecs.open(user_path, 'r', 'utf-8'))\n        for p in user_conf[\"data\"]:\n            if p[\"name\"] == name:\n                if result.statistics.total.critical.failed != 0:\n                    msg[\"To\"] = p[\"fail_list\"]\n                else:\n                    msg[\"To\"] = p[\"success_list\"]\n                break\n\n        # conf_path = app.config[\"AUTO_HOME\"] + \"/auto.json\"\n        conf_path = auto_home + \"/auto.json\"\n        config = json.load(codecs.open(conf_path, 'r', 'utf-8'))\n        msg[\"From\"] = config[\"smtp\"][\"username\"]\n        if config[\"smtp\"][\"ssl\"]:\n            smtp = smtplib.SMTP_SSL()\n        else:\n            smtp = smtp.SMTP()\n\n        # 连接至服务器\n        smtp.connect(config[\"smtp\"][\"server\"], int(config[\"smtp\"][\"port\"]))\n        # 登录\n        smtp.login(config[\"smtp\"][\"username\"], config[\"smtp\"][\"password\"])\n        # 发送邮件\n        smtp.sendmail(msg[\"From\"], msg[\"To\"].split(\",\"), msg.as_string().encode(\"utf8\"))\n        # 断开连接\n        smtp.quit()\n    except Exception as e:\n        print(\"邮件发送错误: %s\" % e)\n\n\nclass RobotRun(threading.Thread):\n    def __init__(self, name, output, lock, executor=\"auto\"):\n        threading.Thread.__init__(self)\n        self.lock = lock\n        self.project = name\n        self.output = output\n        self.executor = executor\n        self.suite = None\n        self.result = None\n\n    def run(self):\n        #lock = threading.Lock()\n\n        # self.lock.acquire()\n        if not exists_path(self.output):\n            mk_dirs(self.output)\n\n        self.suite = TestSuiteBuilder().build(self.project)\n\n        (output, index) = self.reset_next_build_numb()\n\n        self.setName(output)\n\n        self.result = self.suite.run(output_directory=output,\n                                     output=output + \"/output.xml\",\n                                     debugfile=output + \"/debug.txt\",\n                                     loglevel=\"TRACE\")\n\n        # self.reset_last_status(index)\n\n        # Report and xUnit files can be generated based on the result object.\n        # ResultWriter(self.result).write_results(report=output + '/report.html', log=output + '/log.html')\n\n        # self.lock.release()\n\n        # Generating log files requires processing the earlier generated output XML.\n        # ResultWriter(self.output + '/output.xml').write_results()\n\n        self.result = ExecutionResult(out + \"/output.xml\")\n\n        self.reset_last_status(self.result, output, index)\n\n        # Report and xUnit files can be generated based on the result object.\n        ResultWriter(self.result).write_results(report=out + '/report.html', log=out + '/log.html')\n\n    def reset_next_build_numb(self):\n\n        next_build_number = self.output + \"/nextBuildNumber\"\n        index = 1\n        data = \"%d\" % (index + 1)\n        if not exists_path(next_build_number):\n            make_nod(next_build_number)\n        else:\n            index = int(read_file(next_build_number)[\"data\"])\n            data = \"%d\" % (index + 1)\n        write_file(next_build_number, data)\n\n        output = self.output + \"/%d\" % index\n        if not exists_path(output):\n            mk_dirs(output)\n\n        return (output, index)\n\n    def reset_last_status(self, index):\n        stats = self.result.statistics\n        fail = stats.total.critical.failed\n\n        lock = threading.Lock()\n\n        lock.acquire()\n        last_fail = self.output + \"/lastFail\"\n        last_passed = self.output + \"/lastPassed\"\n        data = \"%d\" % index\n\n        if fail != 0:\n            if not exists_path(last_fail):\n                make_nod(last_fail)\n\n            write_file(last_fail, data)\n        else:\n            if not exists_path(last_passed):\n                make_nod(last_passed)\n            write_file(last_passed, data)\n\n        lock.release()"
  },
  {
    "path": "version.txt",
    "content": "1.0.12"
  }
]