├── .circleci └── config.yml ├── .gitignore ├── .travis.yml ├── README.rst ├── USAGE.rst ├── appveyor.yml ├── logs └── .gitkeep ├── pytest.ini ├── rest-api ├── common │ └── pest │ │ ├── .gitignore │ │ ├── Pest.php │ │ ├── PestJSON.php │ │ ├── PestXML.php │ │ ├── README.markdown │ │ ├── composer.json │ │ └── examples │ │ ├── intouch_example.php │ │ ├── open_street_map_example.php │ │ └── rollcall_example.php ├── v1 │ ├── curl │ │ ├── execution │ │ │ └── createTestCaseExecution.sh │ │ ├── generic │ │ │ └── createTestCaseX.sh │ │ ├── json │ │ │ ├── README │ │ │ ├── createTestCase.json │ │ │ ├── createTestCaseB.json │ │ │ ├── createTestCaseExecution.json │ │ │ ├── createTestCaseWithSteps.json │ │ │ ├── createTestCaseXL5.json │ │ │ ├── createTestCaseXL5B.json │ │ │ ├── createTestCaseXL5C.json │ │ │ ├── createTestCaseXL5D.json │ │ │ ├── createTestPlan.json │ │ │ ├── createTestProject.json │ │ │ ├── createTestSuite.json │ │ │ ├── setTestPlanActive.json │ │ │ ├── setTestPlanInactive.json │ │ │ ├── setTestPlanNotes.json │ │ │ ├── setTestPlanPrivate.json │ │ │ ├── testCaseComplex.json │ │ │ └── testCaseComplex2.json │ │ ├── testplan │ │ │ ├── createTestPlan.sh │ │ │ ├── setTestPlanActive.sh │ │ │ ├── setTestPlanInactive.sh │ │ │ ├── setTestPlanNotes.sh │ │ │ ├── setTestPlanPrivate.sh │ │ │ └── updateTestPlan.sh │ │ └── testproject │ │ │ ├── createTestCase.sh │ │ │ ├── createTestCaseWithSteps.sh │ │ │ ├── createTestProject.sh │ │ │ ├── createTestSuite.sh │ │ │ ├── getAllTestProjects.sh │ │ │ ├── getOneTestProjectByID.sh │ │ │ ├── getOneTestProjectByName.sh │ │ │ ├── getTestCasesInTestProject.sh │ │ │ ├── getTestCasesInTestProjectByTestProjectName.sh │ │ │ ├── getTestPlansInTestProject.sh │ │ │ └── getTestPlansInTestProjectByTestProjectName.sh │ └── php │ │ ├── execution │ │ └── createTestCaseExecution.php │ │ └── testproject │ │ ├── createTestProject.php │ │ ├── getProjects.php │ │ ├── getTestCasesInTestProject.php │ │ └── getTestPlansInTestProject.php └── v2 │ ├── README │ ├── curl │ ├── builds │ │ ├── createBuild.sh │ │ └── createKeyword.sh │ ├── callisthenics │ │ ├── README │ │ └── who.sh │ └── keywords │ │ └── createKeyword.sh │ └── json │ ├── README │ ├── createBuild.json │ ├── createKeyword.json │ ├── createTestCaseUsingAttrID.json │ └── createTestCaseUsingAttrVerboseKey.json ├── sample-data ├── README.md ├── RSPEC-01-req-spec.barebones.xml ├── TS-100.barebones.xml ├── images │ ├── RSPEC-01-req-spec.barebones.png │ └── TS-100.barebones.png └── sample-keywords01.xml ├── sample-db ├── mysql │ ├── README.md │ └── testlink_30k_testcases.sql.gz └── postgres │ ├── README.md │ └── testlinkP2.tar ├── setup.cfg ├── setup.py ├── testlinktests ├── __init__.py ├── configs │ ├── __init__.py │ └── settings.json └── core │ ├── __init__.py │ ├── pages │ ├── __init__.py │ ├── page_index.py │ └── page_login.py │ ├── specs │ ├── __init__.py │ └── req_information.py │ ├── test_info.py │ └── utils.py ├── tests ├── api │ ├── __init__.py │ ├── suite_checkdevkey.py │ ├── suite_tplan_by_name.py │ ├── suite_tproject.py │ ├── suite_tproject_tplans.py │ └── suite_tprojects.py └── web │ ├── __init__.py │ ├── suite_index.py │ └── suite_login.py └── tox.ini /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Python CircleCI 2.0 configuration file 2 | version: 2 3 | jobs: 4 | build: 5 | environment: 6 | CC_TEST_REPORTER_ID: 181a274c2c68483ff80af63a8a87e1f5fab71f37744eab702f25695501b5ca85 7 | docker: 8 | - image: circleci/python:3.6.1 9 | 10 | working_directory: ~/repo 11 | 12 | steps: 13 | - checkout 14 | - run: 15 | name: Install dependencies 16 | command: | 17 | # CI, CircleCI dependencies 18 | python3 -m venv venv 19 | . venv/bin/activate 20 | # CI, codeclimate dependencies 21 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 22 | chmod +x ./cc-test-reporter 23 | pip install codeclimate-test-reporter 24 | # project dependencies 25 | pip install coverage==4.2 26 | - run: 27 | name: Install project 28 | command: | 29 | . venv/bin/activate 30 | pip install qacode==0.6.2 31 | python setup.py install 32 | - run: 33 | name: Before Tests 34 | command: | 35 | ./cc-test-reporter before-build 36 | - run: 37 | name: Run Tests 38 | command: | 39 | . venv/bin/activate 40 | tox -e flake8,coverage 41 | - run: 42 | name: After Tests 43 | command: | 44 | . venv/bin/activate 45 | ls -ltra tests/reports/ 46 | codeclimate-test-reporter --token $CC_TEST_REPORTER_ID --file .coverage 47 | - store_artifacts: 48 | path: dist 49 | destination: dist -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *.cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # Jupyter Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # SageMath parsed files 79 | *.sage.py 80 | 81 | # Environments 82 | .env 83 | .venv 84 | env/ 85 | venv/ 86 | ENV/ 87 | 88 | # Spyder project settings 89 | .spyderproject 90 | .spyproject 91 | 92 | # Rope project settings 93 | .ropeproject 94 | 95 | # mkdocs documentation 96 | /site 97 | 98 | # mypy 99 | .mypy_cache/ 100 | 101 | # Sonar 102 | .sonar/ 103 | 104 | # TESTLINKTESTS ignores 105 | tests/reports/* 106 | tests/reports/coverage/* 107 | tests/reports/benchmarks/benchmark.*.svg 108 | # Visual Studio 109 | .vs/ 110 | # Visual code 111 | .vscode/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | global: 3 | # GITHUB_TOKEN 4 | - secure: LYqe4kN1i8BqVEAO8fdO+CYrx9z8h1ab+NxCgYubLcP70DZoKNRj1/egULOd3uvDHFigFVeaACRR6vUQnHbHsffSkMr4nqwOeJMbxOdspHPuA4oMUy5RZKRC7P+v6Dlwpg4Psg/VsEnP6lHWb/RbosBE8tU8ukewD14EU3o29Ql+lBfvvOW/81UTGokMQa1zrW9o70oEaTYqePT9YzM9UcKgukshYtcxKE3oiwLm3fB8zBAUJwzkEvBGNpXjqBEDCUjrS80CqgY3BRsPfnPk90LgjeORaZKoP1f/bZE4UtgeESK0xpWsKEcDCRkSfGxYw8rD7nwc6H5LiCeoS87oQpUCMsoijmGL1yLrBBMIRAWoMC/o+h/QI5bb+IyLba6KLihY4LpExnWj2V8Mh6qs6AeV8v/Dl1WdyIi72WIkMBrmVrOhtoJJ45QaZJbrwSMeM4/ShS5NdiLFnGKeYOjMJ+DQZa+//SIwnvsOegm918TmOZqCr2YabI6WYixOzYjQPvI2KliZGekaX9uSNxVwoWZDE8jvdhvBWtHsL6hMqAv5ehEJw1aseLwjEGHQjIY0YBqJI38QmFr0vro6/9U2EehXHeW3ycCrPguQJ50nIK/DwiGMadUhrxU0xfZ6+TfFpGY0RivEPLD+Cup9C5oVFAppPJncdBdXUqouR90/8NI= 5 | addons: 6 | sonarqube: 7 | branches: 8 | - master 9 | language: python 10 | matrix: 11 | include: 12 | - os: linux 13 | sudo;: required 14 | python: 2.7 15 | env: TOXENV=py27 16 | - os: linux 17 | sudo;: required 18 | python: 3.4 19 | env: TOXENV=py34 20 | - os: linux 21 | sudo;: required 22 | python: 3.5 23 | env: TOXENV=py35 24 | - os: linux 25 | sudo;: required 26 | python: 3.6 27 | env: TOXENV=py36 28 | 29 | before_install: 30 | - pip freeze 31 | install: 32 | - pip install tox 33 | script: 34 | - tox -e "flake8,{$TOXENV}" 35 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Testlink-tests 2 | ============== 3 | 4 | 5 | .. image:: https://img.shields.io/github/issues/TestLinkOpenSourceTRMS/testlink-tests.svg 6 | :alt: Issues on Github 7 | :target: https://github.com/TestLinkOpenSourceTRMS/testlink-tests/issues 8 | 9 | .. image:: https://img.shields.io/github/issues-pr/TestLinkOpenSourceTRMS/testlink-tests.svg 10 | :alt: Pull Request opened on Github 11 | :target: https://github.com/TestLinkOpenSourceTRMS/testlink-tests/issues 12 | 13 | .. image:: https://img.shields.io/github/release/TestLinkOpenSourceTRMS/testlink-tests.svg 14 | :alt: Release version on Github 15 | :target: https://github.com/TestLinkOpenSourceTRMS/testlink-tests/releases/latest 16 | 17 | .. image:: https://img.shields.io/github/release-date/TestLinkOpenSourceTRMS/testlink-tests.svg 18 | :alt: Release date on Github 19 | :target: https://github.com/TestLinkOpenSourceTRMS/testlink-tests/releases/latest 20 | 21 | 22 | +-----------------------+-----------------------+--------------------------+--------------------------+------------------------------+ 23 | | Branch | TravisCI | Appveyor | CircleCi - Docker | CodeClimate | 24 | +=======================+=======================+==========================+==========================+==============================+ 25 | | master | |master_ci_travis| | |master_ci_appveyor| | |master_ci_circleci| | |master_ci_codeclimate| | 26 | +-----------------------+-----------------------+--------------------------+--------------------------+------------------------------+ 27 | 28 | 29 | Python tested versions 30 | ---------------------- 31 | 32 | +-------------------+-------------------+-------------------+-------------------+-------------------+-------------------+ 33 | | **3.6** | **3.5** | **3.4** | **3.3** | **3.2** | **2.7** | 34 | +===================+===================+===================+===================+===================+===================+ 35 | | *Supported* | *Supported* | *Supported* | *Not Supported* | *Not Supported* | *Supported* | 36 | +-------------------+-------------------+-------------------+-------------------+-------------------+-------------------+ 37 | 38 | 39 | Prerequisites 40 | ------------- 41 | 42 | + Need Testlink WebApp **Up and Running** 43 | 44 | 45 | How to install ? 46 | ---------------- 47 | 48 | + Install from PIP : ``pip install testlinktests`` 49 | 50 | + Install from setup.py file : ``python setup.py install`` 51 | 52 | 53 | 54 | How to exec tests ? 55 | ------------------- 56 | 57 | + Tests from setup.py file : ``python setup.py test`` 58 | 59 | + Install from PIP file : ``pip install tox`` 60 | + Tests from tox : ``tox -l && tox -e TOX_ENV_NAME`` ( *see tox.ini file to get environment names* ) 61 | 62 | 63 | +---------------------+--------------------------------+ 64 | | TOX Env name | Env description | 65 | +=====================+================================+ 66 | | py27,py34,py35,py36 | Python supported versions | 67 | +---------------------+--------------------------------+ 68 | | flake8 | Exec linter in qalab/ tests/ | 69 | +---------------------+--------------------------------+ 70 | | coverage | Generate XML and HTML reports | 71 | +---------------------+--------------------------------+ 72 | 73 | Configuration File 74 | ~~~~~~~~~~~~~~~~~~ 75 | 76 | 77 | :: 78 | 79 | { 80 | "connection":{ 81 | "is_https": false, 82 | "host": "qalab.tk", 83 | "port": 86 84 | }, 85 | "dev_key": "1bfd2ef4ceda22b482b12f2b25457495", 86 | "log_level":"DEBUG", 87 | } 88 | 89 | Getting Started 90 | ~~~~~~~~~~~~~~~ 91 | 92 | *Just starting example of usage before read* `Usage Guide`_. 93 | 94 | 95 | .. _Usage Guide: USAGE.rst 96 | .. |master_ci_travis| image:: https://travis-ci.com/TestLinkOpenSourceTRMS/testlink-tests.svg?branch=master 97 | :target: https://travis-ci.com/TestLinkOpenSourceTRMS/testlink-tests 98 | .. |master_ci_appveyor| image:: https://ci.appveyor.com/api/projects/status/5rcuh1wj3rissqu64wmb/branch/master?svg=true 99 | :target: https://ci.appveyor.com/project/netzulo/testlink-tests 100 | .. |master_ci_circleci| image:: https://circleci.com/gh/TestLinkOpenSourceTRMS/testlink-tests.svg?style=svg 101 | :target: https://circleci.com/gh/netzulo/testlink-tests) 102 | .. |master_ci_codeclimate| image:: https://api.codeclimate.com/v1/badges/-/maintainability 103 | :target: https://codeclimate.com/github/TestLinkOpenSourceTRMS/testlink-tests/maintainability 104 | -------------------------------------------------------------------------------- /USAGE.rst: -------------------------------------------------------------------------------- 1 | Usage Guide 2 | =========== 3 | 4 | 1. Usage ( *WebAPP* ) 5 | ********************* 6 | 7 | + 1. Create JSON configuration ( runtime or read from file, *read config section* ) 8 | + 2. Instance **Bot** object 9 | 10 | .. code:: python 11 | 12 | 13 | from qacode.core.bot import BotBase 14 | from qautils.files import settings 15 | 16 | cfg = settings(file_path="./testlink-tests/configs/") 17 | bot = BotBase(**cfg) 18 | 19 | 20 | + 3. Instance **Testlink Page** object 21 | 22 | .. code:: python 23 | 24 | 25 | from testlinktests.core.pages import (PageLogin, PageIndex) 26 | 27 | login = PageLogin(bot, **{"url": "http://netzulo.tk:88/login.php"}) 28 | index = PageIndex(bot, **{"url": "http://netzulo.tk:88/index.php"}) 29 | 30 | is_logged = login.login("admin", "admin") 31 | if not is_logged: 32 | raise Exception("Login not working") 33 | index.menu_top() 34 | index.menu_right() 35 | index.menu_left() 36 | 37 | 38 | 39 | 2. Usage ( *XMLRPC* ) 40 | ********************* 41 | 42 | + 1. Create JSON configuration ( runtime or read from file, *read config section* ) 43 | + 2. Instance **testlink_manager** object ``testlink_manager = TLManager(settings=my_json_config)`` 44 | + 3. Use some *method name with prefix* '**api_**' 45 | 46 | **api_login** 47 | +++++++++++++ 48 | 49 | * **XMLRPC**: *call to method named* '*tl.checkDevKey*' 50 | * **Description** : check if dev_key it's valid 51 | 52 | **api_tprojects** 53 | +++++++++++++++++ 54 | 55 | * **XMLRPC**: *call to method named* '*tl.getProjects*' 56 | * **Description** : get all test projects 57 | 58 | 59 | **api_tproject** 60 | ++++++++++++++++ 61 | 62 | * **XMLRPC**: *call to method named* '*tl.getTestProjectByName*' 63 | * **Description** : get one test project filtered by name 64 | 65 | **api_tproject_tplans** 66 | +++++++++++++++++++++++ 67 | 68 | * **XMLRPC**: *call to method named* '*tl.getProjectTestPlans*' 69 | * **Description** : get all test plans for one test project 70 | 71 | **api_tproject_tsuites_first_level** 72 | ++++++++++++++++++++++++++++++++++++ 73 | 74 | * **XMLRPC**: *call to method named* '*tl.getFirstLevelTestSuitesForTestProject*' 75 | * **Description** : get all test suites on first level for one test project 76 | 77 | **api_tplan** 78 | +++++++++++++ 79 | 80 | * **XMLRPC**: *call to method named* '*tl.getTestPlanByName*' 81 | * **Description** : get one test plan filtered by project and plan names 82 | 83 | **api_tplan_platforms** 84 | +++++++++++++++++++++++ 85 | 86 | * **XMLRPC**: *call to method named* '*tl.getTestPlanPlatforms*' 87 | * **Description** : get one test plan filtered by project and plan names 88 | 89 | **api_tplan_builds** 90 | ++++++++++++++++++++ 91 | 92 | * **XMLRPC**: *call to method named* '*tl.getBuildsForTestPlan*' 93 | * **Description** : get all builds for test project filtered by id 94 | 95 | **api_tplan_tsuites** 96 | +++++++++++++++++++++ 97 | 98 | * **XMLRPC**: *call to method named* '*tl.getTestSuitesForTestPlan*' 99 | * **Description** : get all test suites assigned to test plan filtered by id 100 | 101 | **api_tplan_tcases** 102 | ++++++++++++++++++++ 103 | 104 | * **XMLRPC**: *call to method named* '*tl.getTestCasesForTestPlan*' 105 | * **Description** : get all test cases assigned to test plan filtered by id 106 | 107 | **api_tplan_build_latest** 108 | ++++++++++++++++++++++++++ 109 | 110 | * **XMLRPC**: *call to method named* '*tl.getLatestBuildForTestPlan*' 111 | * **Description** : get latest build by choosing the maximum build id for a specific test plan id 112 | 113 | **api_tplan_totals** 114 | ++++++++++++++++++++ 115 | 116 | * **XMLRPC**: *call to method named* '*tl.getTotalsForTestPlan*' 117 | * **Description** : get totals for testplan filtered by id 118 | 119 | **api_tsuite** 120 | ++++++++++++++ 121 | 122 | * **XMLRPC**: *call to method named* '*tl.getTestSuiteByID*' 123 | * **Description** : get test suite filtered by id 124 | 125 | **api_tsuite_tsuites** 126 | ++++++++++++++++++++++ 127 | 128 | * **XMLRPC**: *call to method named* '*tl.getTestSuitesForTestSuite*' 129 | * **Description** : get test suites down of tree for one test suite filtered by id 130 | 131 | **api_tcase** 132 | +++++++++++++ 133 | 134 | * **XMLRPC**: *call to method named* '*tl.getTestCase*' 135 | * **Description** : get test case filtered by id or external id 136 | 137 | **api_tcase_by_name** 138 | +++++++++++++++++++++ 139 | 140 | * **XMLRPC**: *call to method named* '*tl.getTestCaseIDByName*' 141 | * **Description** : get test case filtered by name 142 | 143 | **api_tcase_report** 144 | ++++++++++++++++++++ 145 | 146 | * **XMLRPC**: *call to method named* '*tl.reportTCResult*' 147 | * **Description** : reports a result for a single test case 148 | 149 | **api_user_exist** 150 | ++++++++++++++++++ 151 | 152 | * **XMLRPC**: *call to method named* '*tl.doesUserExist*' 153 | * **Description** : check if user name it's valid 154 | 155 | **api_about** 156 | +++++++++++++ 157 | 158 | * **XMLRPC**: *call to method named* '*tl.about*' 159 | * **Description** : get default message with author and testlink version 160 | 161 | **api_say_hello** 162 | +++++++++++++++++ 163 | 164 | * **XMLRPC**: *call to method named* '*tl.sayHello*' 165 | * **Description** : get **'Hello!'** message 166 | 167 | **api_ping** 168 | ++++++++++++ 169 | 170 | * **XMLRPC**: *call to method named* '*tl.ping*' 171 | * **Description** : get **'Hello!'** message 172 | 173 | **api_ping** 174 | ++++++++++++ 175 | 176 | * **XMLRPC**: *call to method named* '*tl.repeat*' 177 | * **Description** : get **You said: 'your message here'** as message 178 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | build: off 2 | branches: 3 | only: 4 | - master 5 | environment: 6 | global: 7 | # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the 8 | # /E:ON and /V:ON options are not enabled in the batch script intepreter 9 | # See: http://stackoverflow.com/a/13751649/163740 10 | CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd" 11 | CLCACHE_NODIRECT: "true" 12 | 13 | matrix: 14 | - JOB: "2.7 32-bit" 15 | TOXENV: "py27" 16 | PYTHON: "C:\\Python27.10" 17 | PYTHON_VERSION: "2.7.10" 18 | PYTHON_ARCH: "32" 19 | - JOB: "2.7 64-bit" 20 | TOXENV: "py27" 21 | PYTHON: "C:\\Python27.10-x64" 22 | PYTHON_VERSION: "2.7.10" 23 | PYTHON_ARCH: "64" 24 | 25 | - JOB: "3.4 64-bit" 26 | TOXENV: "py34" 27 | PYTHON: "C:\\Python34-x64" 28 | PYTHON_VERSION: "3.4" 29 | PYTHON_ARCH: "64" 30 | 31 | - JOB: "3.5 64-bit" 32 | TOXENV: "py35" 33 | PYTHON: "C:\\Python35-x64" 34 | PYTHON_VERSION: "3.5.0" 35 | PYTHON_ARCH: "64" 36 | 37 | - JOB: "3.6 64-bit" 38 | TOXENV: "py36" 39 | PYTHON: "C:\\Python36" 40 | PYTHON_VERSION: "3.6.3" 41 | PYTHON_ARCH: "64" 42 | 43 | install: 44 | # Prepend newly installed Python to the PATH of this build (this cannot be 45 | # done from inside the powershell script as it would require to restart 46 | # the parent CMD process). 47 | - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" 48 | # Check that we have the expected version and architecture for Python 49 | - "python --version" 50 | - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" 51 | # Upgrade to the latest version of pip to avoid it displaying warnings 52 | # about it being out of date. 53 | - "python -m pip install wheel" 54 | - "python -m pip install --pre -U tox" 55 | 56 | test_script: 57 | - "tox -e flake8,%TOXENV%" 58 | 59 | after_test: 60 | - "python setup.py sdist" 61 | 62 | artifacts: 63 | - path: "dist\\*" 64 | -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TestLinkOpenSourceTRMS/testlink-tests/af30ba3635ad9027ab8ea6de861f05f5cecbf335/logs/.gitkeep -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts = --verbose 3 | testpaths = tests 4 | console_output_style = progress 5 | python_files = suite_*.py 6 | python_classes = Test* 7 | python_functions = test_* 8 | filterwarnings = 9 | error 10 | ignore::UserWarning -------------------------------------------------------------------------------- /rest-api/common/pest/.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | -------------------------------------------------------------------------------- /rest-api/common/pest/Pest.php: -------------------------------------------------------------------------------- 1 | true, // return result instead of echoing 14 | CURLOPT_SSL_VERIFYPEER => false, // stop cURL from verifying the peer's certificate 15 | CURLOPT_FOLLOWLOCATION => false, // follow redirects, Location: headers 16 | CURLOPT_MAXREDIRS => 10, // but dont redirect more than 10 times 17 | CURLOPT_HTTPHEADER => array() 18 | ); 19 | 20 | public $base_url; 21 | 22 | public $last_response; 23 | public $last_request; 24 | public $last_headers; 25 | 26 | public $throw_exceptions = true; 27 | 28 | public function __construct($base_url) { 29 | if (!function_exists('curl_init')) { 30 | throw new Exception('CURL module not available! Pest requires CURL. See http://php.net/manual/en/book.curl.php'); 31 | } 32 | 33 | // only enable CURLOPT_FOLLOWLOCATION if safe_mode and open_base_dir are not in use 34 | if(ini_get('open_basedir') == '' && strtolower(ini_get('safe_mode')) == 'off') { 35 | $this->curl_opts['CURLOPT_FOLLOWLOCATION'] = true; 36 | } 37 | 38 | $this->base_url = $base_url; 39 | 40 | // The callback to handle return headers 41 | // Using PHP 5.2, it cannot be initialised in the static context 42 | $this->curl_opts[CURLOPT_HEADERFUNCTION] = array($this, 'handle_header'); 43 | } 44 | 45 | // $auth can be 'basic' or 'digest' 46 | public function setupAuth($user, $pass, $auth = 'basic') { 47 | $this->curl_opts[CURLOPT_HTTPAUTH] = constant('CURLAUTH_'.strtoupper($auth)); 48 | $this->curl_opts[CURLOPT_USERPWD] = $user . ":" . $pass; 49 | } 50 | 51 | // Enable a proxy 52 | public function setupProxy($host, $port, $user = NULL, $pass = NULL) { 53 | $this->curl_opts[CURLOPT_PROXYTYPE] = 'HTTP'; 54 | $this->curl_opts[CURLOPT_PROXY] = $host; 55 | $this->curl_opts[CURLOPT_PROXYPORT] = $port; 56 | if ($user && $pass) { 57 | $this->curl_opts[CURLOPT_PROXYUSERPWD] = $user . ":" . $pass; 58 | } 59 | } 60 | 61 | public function get($url, $data=array()) { 62 | if (!empty($data)) { 63 | $pos = strpos($url, '?'); 64 | if ($pos !== false) { 65 | $url = substr($url, 0, $pos); 66 | } 67 | $url .= '?' . http_build_query($data); 68 | } 69 | 70 | $curl = $this->prepRequest($this->curl_opts, $url); 71 | $body = $this->doRequest($curl); 72 | 73 | $body = $this->processBody($body); 74 | 75 | return $body; 76 | } 77 | 78 | public function head($url) { 79 | $curl_opts = $this->curl_opts; 80 | $curl_opts[CURLOPT_NOBODY] = true; 81 | 82 | $curl = $this->prepRequest($this->curl_opts, $url); 83 | $body = $this->doRequest($curl); 84 | 85 | $body = $this->processBody($body); 86 | 87 | return $body; 88 | } 89 | 90 | public function prepData($data) { 91 | if (is_array($data)) { 92 | $multipart = false; 93 | 94 | foreach ($data as $item) { 95 | if (is_string($item) && strncmp($item, "@", 1) == 0 && is_file(substr($item, 1))) { 96 | $multipart = true; 97 | break; 98 | } 99 | } 100 | 101 | return ($multipart) ? $data : http_build_query($data); 102 | } else { 103 | return $data; 104 | } 105 | } 106 | 107 | public function post($url, $data, $headers=array()) { 108 | $data = $this->prepData($data); 109 | 110 | $curl_opts = $this->curl_opts; 111 | $curl_opts[CURLOPT_CUSTOMREQUEST] = 'POST'; 112 | if (!is_array($data)) $headers[] = 'Content-Length: '.strlen($data); 113 | $curl_opts[CURLOPT_HTTPHEADER] = array_merge($headers,$curl_opts[CURLOPT_HTTPHEADER]); 114 | $curl_opts[CURLOPT_POSTFIELDS] = $data; 115 | 116 | $curl = $this->prepRequest($curl_opts, $url); 117 | $body = $this->doRequest($curl); 118 | 119 | $body = $this->processBody($body); 120 | 121 | return $body; 122 | } 123 | 124 | public function put($url, $data, $headers=array()) { 125 | $data = $this->prepData($data); 126 | 127 | $curl_opts = $this->curl_opts; 128 | $curl_opts[CURLOPT_CUSTOMREQUEST] = 'PUT'; 129 | if (!is_array($data)) $headers[] = 'Content-Length: '.strlen($data); 130 | $curl_opts[CURLOPT_HTTPHEADER] = $headers; 131 | $curl_opts[CURLOPT_POSTFIELDS] = $data; 132 | 133 | $curl = $this->prepRequest($curl_opts, $url); 134 | $body = $this->doRequest($curl); 135 | 136 | $body = $this->processBody($body); 137 | 138 | return $body; 139 | } 140 | 141 | public function patch($url, $data, $headers=array()) { 142 | $data = (is_array($data)) ? http_build_query($data) : $data; 143 | 144 | $curl_opts = $this->curl_opts; 145 | $curl_opts[CURLOPT_CUSTOMREQUEST] = 'PATCH'; 146 | $headers[] = 'Content-Length: '.strlen($data); 147 | $curl_opts[CURLOPT_HTTPHEADER] = $headers; 148 | $curl_opts[CURLOPT_POSTFIELDS] = $data; 149 | 150 | $curl = $this->prepRequest($curl_opts, $url); 151 | $body = $this->doRequest($curl); 152 | 153 | $body = $this->processBody($body); 154 | 155 | return $body; 156 | } 157 | 158 | public function delete($url) { 159 | $curl_opts = $this->curl_opts; 160 | $curl_opts[CURLOPT_CUSTOMREQUEST] = 'DELETE'; 161 | 162 | $curl = $this->prepRequest($curl_opts, $url); 163 | $body = $this->doRequest($curl); 164 | 165 | $body = $this->processBody($body); 166 | 167 | return $body; 168 | } 169 | 170 | public function lastBody() { 171 | return $this->last_response['body']; 172 | } 173 | 174 | public function lastStatus() { 175 | return $this->last_response['meta']['http_code']; 176 | } 177 | 178 | /** 179 | * Return the last response header (case insensitive) or NULL if not present. 180 | * HTTP allows empty headers (e.g. RFC 2616, Section 14.23), thus is_null() 181 | * and not negation or empty() should be used. 182 | */ 183 | public function lastHeader($header) { 184 | if (empty($this->last_headers[strtolower($header)])) { 185 | return NULL; 186 | } 187 | return $this->last_headers[strtolower($header)]; 188 | } 189 | 190 | protected function processBody($body) { 191 | // Override this in classes that extend Pest. 192 | // The body of every GET/POST/PUT/DELETE response goes through 193 | // here prior to being returned. 194 | return $body; 195 | } 196 | 197 | protected function processError($body) { 198 | // Override this in classes that extend Pest. 199 | // The body of every erroneous (non-2xx/3xx) GET/POST/PUT/DELETE 200 | // response goes through here prior to being used as the 'message' 201 | // of the resulting Pest_Exception 202 | return $body; 203 | } 204 | 205 | 206 | protected function prepRequest($opts, $url) { 207 | if (strncmp($url, $this->base_url, strlen($this->base_url)) != 0) { 208 | $url = rtrim($this->base_url, '/') . '/' . ltrim($url, '/'); 209 | } 210 | $curl = curl_init($url); 211 | 212 | foreach ($opts as $opt => $val) 213 | curl_setopt($curl, $opt, $val); 214 | 215 | $this->last_request = array( 216 | 'url' => $url 217 | ); 218 | 219 | if (isset($opts[CURLOPT_CUSTOMREQUEST])) 220 | $this->last_request['method'] = $opts[CURLOPT_CUSTOMREQUEST]; 221 | else 222 | $this->last_request['method'] = 'GET'; 223 | 224 | if (isset($opts[CURLOPT_POSTFIELDS])) 225 | $this->last_request['data'] = $opts[CURLOPT_POSTFIELDS]; 226 | 227 | return $curl; 228 | } 229 | 230 | private function handle_header($ch, $str) { 231 | if (preg_match('/([^:]+):\s(.+)/m', $str, $match) ) { 232 | $this->last_headers[strtolower($match[1])] = trim($match[2]); 233 | } 234 | return strlen($str); 235 | } 236 | 237 | private function doRequest($curl) { 238 | $this->last_headers = array(); 239 | 240 | $body = curl_exec($curl); 241 | $meta = curl_getinfo($curl); 242 | 243 | $this->last_response = array( 244 | 'body' => $body, 245 | 'meta' => $meta 246 | ); 247 | 248 | curl_close($curl); 249 | 250 | $this->checkLastResponseForError(); 251 | 252 | return $body; 253 | } 254 | 255 | protected function checkLastResponseForError() { 256 | if ( !$this->throw_exceptions) 257 | return; 258 | 259 | $meta = $this->last_response['meta']; 260 | $body = $this->last_response['body']; 261 | 262 | if (!$meta) 263 | return; 264 | 265 | $err = null; 266 | switch ($meta['http_code']) { 267 | case 400: 268 | throw new Pest_BadRequest($this->processError($body)); 269 | break; 270 | case 401: 271 | throw new Pest_Unauthorized($this->processError($body)); 272 | break; 273 | case 403: 274 | throw new Pest_Forbidden($this->processError($body)); 275 | break; 276 | case 404: 277 | throw new Pest_NotFound($this->processError($body)); 278 | break; 279 | case 405: 280 | throw new Pest_MethodNotAllowed($this->processError($body)); 281 | break; 282 | case 409: 283 | throw new Pest_Conflict($this->processError($body)); 284 | break; 285 | case 410: 286 | throw new Pest_Gone($this->processError($body)); 287 | break; 288 | case 422: 289 | // Unprocessable Entity -- see http://www.iana.org/assignments/http-status-codes 290 | // This is now commonly used (in Rails, at least) to indicate 291 | // a response to a request that is syntactically correct, 292 | // but semantically invalid (for example, when trying to 293 | // create a resource with some required fields missing) 294 | throw new Pest_InvalidRecord($this->processError($body)); 295 | break; 296 | default: 297 | if ($meta['http_code'] >= 400 && $meta['http_code'] <= 499) 298 | throw new Pest_ClientError($this->processError($body)); 299 | elseif ($meta['http_code'] >= 500 && $meta['http_code'] <= 599) 300 | throw new Pest_ServerError($this->processError($body)); 301 | elseif (!$meta['http_code'] || $meta['http_code'] >= 600) { 302 | throw new Pest_UnknownResponse($this->processError($body)); 303 | } 304 | } 305 | } 306 | } 307 | 308 | 309 | class Pest_Exception extends Exception { } 310 | class Pest_UnknownResponse extends Pest_Exception { } 311 | 312 | /* 401-499 */ class Pest_ClientError extends Pest_Exception {} 313 | /* 400 */ class Pest_BadRequest extends Pest_ClientError {} 314 | /* 401 */ class Pest_Unauthorized extends Pest_ClientError {} 315 | /* 403 */ class Pest_Forbidden extends Pest_ClientError {} 316 | /* 404 */ class Pest_NotFound extends Pest_ClientError {} 317 | /* 405 */ class Pest_MethodNotAllowed extends Pest_ClientError {} 318 | /* 409 */ class Pest_Conflict extends Pest_ClientError {} 319 | /* 410 */ class Pest_Gone extends Pest_ClientError {} 320 | /* 422 */ class Pest_InvalidRecord extends Pest_ClientError {} 321 | 322 | /* 500-599 */ class Pest_ServerError extends Pest_Exception {} 323 | 324 | ?> 325 | -------------------------------------------------------------------------------- /rest-api/common/pest/PestJSON.php: -------------------------------------------------------------------------------- 1 | = 400 status codes, an exception is thrown with $e->getMessage() 16 | * containing the error message that the server produced. User code will have to 17 | * json_decode() that manually, if applicable, because the PHP Exception base 18 | * class does not accept arrays for the exception message and some JSON/REST servers 19 | * do not produce nice JSON 20 | * 21 | * See http://github.com/educoder/pest for details. 22 | * 23 | * This code is licensed for use, modification, and distribution 24 | * under the terms of the MIT License (see http://en.wikipedia.org/wiki/MIT_License) 25 | */ 26 | class PestJSON extends Pest 27 | { 28 | public function post($url, $data, $headers=array()) { 29 | return parent::post($url, json_encode($data), $headers); 30 | } 31 | 32 | public function put($url, $data, $headers=array()) { 33 | return parent::put($url, json_encode($data), $headers); 34 | } 35 | 36 | protected function prepRequest($opts, $url) { 37 | $opts[CURLOPT_HTTPHEADER][] = 'Accept: application/json'; 38 | $opts[CURLOPT_HTTPHEADER][] = 'Content-Type: application/json'; 39 | return parent::prepRequest($opts, $url); 40 | } 41 | 42 | public function processBody($body) { 43 | return json_decode($body, true); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /rest-api/common/pest/PestXML.php: -------------------------------------------------------------------------------- 1 | tag in the response is assumed to be the error mssage) 16 | * 17 | * See http://github.com/educoder/pest for details. 18 | * 19 | * This code is licensed for use, modification, and distribution 20 | * under the terms of the MIT License (see http://en.wikipedia.org/wiki/MIT_License) 21 | */ 22 | class PestXML extends Pest { 23 | public function processBody($body) { 24 | libxml_use_internal_errors(true); 25 | if (empty($body) || preg_match('/^\s+$/', $body)) 26 | return null; 27 | 28 | $xml = simplexml_load_string($body); 29 | 30 | if (!$xml) { 31 | $err = "Couldn't parse XML response because:\n"; 32 | $xml_errors = libxml_get_errors(); 33 | libxml_clear_errors(); 34 | if(!empty($xml_errors)) 35 | { 36 | foreach($xml_errors as $xml_err) 37 | $err .= "\n - " . $xml_err->message; 38 | $err .= "\nThe response was:\n"; 39 | $err .= $body; 40 | throw new PestXML_Exception($err); 41 | } 42 | } 43 | 44 | return $xml; 45 | } 46 | 47 | public function processError($body) { 48 | try { 49 | $xml = $this->processBody($body); 50 | if (!$xml) 51 | return $body; 52 | 53 | $error = $xml->xpath('//error'); 54 | 55 | if ($error && $error[0]) 56 | return strval($error[0]); 57 | else 58 | return $body; 59 | } catch (PestXML_Exception $e) { 60 | return $body; 61 | } 62 | } 63 | } 64 | 65 | class PestXML_Exception extends Pest_Exception { } 66 | 67 | ?> -------------------------------------------------------------------------------- /rest-api/common/pest/README.markdown: -------------------------------------------------------------------------------- 1 | Pest 2 | ==== 3 | 4 | **Pest** is a PHP client library for [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) 5 | web services. 6 | 7 | Unlike [Zend_Rest_Client](http://framework.zend.com/manual/en/zend.rest.client.html), which is not 8 | really a "REST" client at all (more like RPC-over-HTTP), Pest supports the four REST verbs 9 | (GET/POST/PUT/DELETE) and pays attention to HTTP response status codes. 10 | 11 | 12 | Basic Example 13 | ------------- 14 | 15 | Pest's get/post/put/delete() return the raw response body as a string. 16 | See the info on PestXML (below) if you're working with XML-based REST services and 17 | PestJSON if you're working with JSON. 18 | 19 | get('/things'); 25 | 26 | $thing = $pest->post('/things', 27 | array( 28 | 'name' => "Foo", 29 | 'colour' => "Red" 30 | ) 31 | ); 32 | 33 | $thing = $pest->put('/things/15', 34 | array( 35 | 'colour' => "Blue" 36 | ) 37 | ); 38 | 39 | $pest->delete('/things/15'); 40 | 41 | ?> 42 | 43 | Responses with error status codes (4xx and 5xx) raise exceptions. 44 | 45 | get('/things/18'); 49 | } catch (Pest_NotFound $e) { 50 | // 404 51 | echo "Thing with ID 18 doesn't exist!"; 52 | } 53 | 54 | try { 55 | $thing = $pest->post('/things', array('colour' => "Red")); 56 | } catch (Pest_InvalidRecord $e) { 57 | // 422 58 | echo "Data for Thing is invalid because: ".$e->getMessage(); 59 | } 60 | 61 | ?> 62 | 63 | PestXML 64 | ------- 65 | 66 | **PestXML** is an XML-centric version of Pest, specifically targeted at REST services that 67 | return XML data. Rather than returning the raw response body as a string, PestXML will 68 | try to parse the service's response into a [SimpleXML](http://php.net/manual/en/book.simplexml.php) object. 69 | 70 | get('/things.xml'); 76 | 77 | $colours = $things->xpath('//colour'); 78 | foreach($colours as $colour) { 79 | echo $colour."\n"; 80 | } 81 | 82 | ?> 83 | 84 | Similarly, **PestJSON** is a JSON-centric version of Pest. 85 | 86 | Much more detailed examples are available in the `examples` directory: 87 | 88 | * [Rollcall Example](http://github.com/educoder/pest/blob/master/examples/rollcall_example.php) 89 | * [OpenStreetMap Example](http://github.com/educoder/pest/blob/master/examples/open_street_map_example.php) 90 | 91 | 92 | TODO 93 | ---- 94 | 95 | * Authentication 96 | * Follow Redirects 97 | 98 | 99 | License 100 | ------- 101 | 102 | Copyright (C) 2011 by University of Toronto 103 | 104 | Permission is hereby granted, free of charge, to any person obtaining a copy 105 | of this software and associated documentation files (the "Software"), to deal 106 | in the Software without restriction, including without limitation the rights 107 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 108 | copies of the Software, and to permit persons to whom the Software is 109 | furnished to do so, subject to the following conditions: 110 | 111 | The above copyright notice and this permission notice shall be included in 112 | all copies or substantial portions of the Software. 113 | 114 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 115 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 116 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 117 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 118 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 119 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 120 | THE SOFTWARE. -------------------------------------------------------------------------------- /rest-api/common/pest/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "educoder/pest", 3 | "description": "A proper REST client for PHP.", 4 | "require": { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /rest-api/common/pest/examples/intouch_example.php: -------------------------------------------------------------------------------- 1 | setupAuth($user ."@". $logindomain, $password); 25 | $pest->curl_opts[CURLOPT_FOLLOWLOCATION] = false; // Not supported on hosts running safe_mode! 26 | 27 | 28 | echo "

1. Creating a contact in the address book

"; 29 | try 30 | { 31 | $contactdata = array( 32 | 'Firstname' => $firstname, 33 | 'Lastname' => $lastname, 34 | 'PhoneNumber' => $phonenumber 35 | ); 36 | 37 | $contactjson = $pest->post('/contacts', $contactdata); 38 | $contactarray = json_decode($contactjson,true); 39 | $contactId = $contactarray[ContactId]; 40 | echo "
Created contact with id: " . $contactId; 41 | } 42 | catch (Exception $e) 43 | { 44 | echo "
Caught exception when creating contact : " . $e->getMessage() . "
"; 45 | } 46 | 47 | 48 | echo "

2. Getting the contact by its phonenumber and updating it.

"; 49 | try 50 | { 51 | $contactjson = $pest->get('/contacts/phonenumber/' . $phonenumber); 52 | $contactarray = json_decode($contactjson,true); 53 | echo "
Retrieved contact with id: " . $contactarray[ContactId] . ", should be identical with " . $contactId . " from part 1.
"; 54 | 55 | $contactarray[Description] = $description; 56 | $contactjson = json_encode($contactarray); 57 | 58 | $pest->put('/contacts/' . $contactarray[ContactId], $contactjson); 59 | echo "
Updated contact
"; 60 | } 61 | catch (Exception $e) 62 | { 63 | echo "
Caught exception when retrieving or updating contact : " . $e->getMessage() . "
"; 64 | } 65 | 66 | 67 | echo "

3. Sending a message to the contact

"; 68 | try 69 | { 70 | $messagedata = array( 71 | 'Receivers' => "/contacts/" . $contactId, 72 | 'SenderNumber' => $sendernumber, 73 | 'Text' => $messagetext 74 | ); 75 | 76 | $messagejson = $pest->post('/messages', $messagedata); 77 | $messagearray = json_decode($messagejson,true); 78 | echo "
Message was sent! MessageId: " . $messagearray[MessageId]; 79 | } 80 | catch (Exception $e) 81 | { 82 | echo "
Caught exception when sending message : " . $e->getMessage() . "
"; 83 | } 84 | 85 | 86 | echo "

4. Deleting contact.

"; 87 | try 88 | { 89 | $pest->delete('/contacts/' . $contactId); 90 | echo "
Deleted contact.
"; 91 | } 92 | catch (Exception $e) 93 | { 94 | echo "
Caught exception when deleting contact : " . $e->getMessage() . "
"; 95 | } 96 | ?> -------------------------------------------------------------------------------- /rest-api/common/pest/examples/open_street_map_example.php: -------------------------------------------------------------------------------- 1 | get('/map?bbox=-79.39997,43.65827,-79.39344,43.66903'); 14 | 15 | // Print all of the street names in the map 16 | $streets = $map->xpath('//way/tag[@k="name"]'); 17 | foreach ($streets as $s) { 18 | echo $s['v'] . "\n"; 19 | } 20 | 21 | ?> -------------------------------------------------------------------------------- /rest-api/common/pest/examples/rollcall_example.php: -------------------------------------------------------------------------------- 1 | get('/users.xml'); 14 | 15 | foreach($users->user as $user) { 16 | echo $user->{'display-name'}." (".$user->username.")\n"; 17 | } 18 | echo "\n"; 19 | 20 | // Create a new User 21 | $data = array( 22 | 'user' => array( 23 | 'username' => "jcricket", 24 | 'password' => "pinocchio", 25 | 'display_name' => "Jiminy Cricket", 26 | 'kind' => "Student" 27 | ) 28 | ); 29 | 30 | $user = $pest->post('/users.xml', $data); 31 | 32 | echo "New User's ID: ".$user->id."\n"; 33 | echo "\n"; 34 | 35 | 36 | // Update the newly created User's attributes 37 | $data = array( 38 | 'user' => array( 39 | 'kind' => "Instructor", 40 | 'metadata' => array( 41 | 'gender' => 'male', 42 | 'age' => 30 43 | ) 44 | ) 45 | ); 46 | 47 | $pest->put('/users/'.$user->id.'.xml', $data); 48 | 49 | 50 | // Retrieve the User 51 | $user = $pest->get('/users/'.$user->id.'.xml'); 52 | echo "User XML: \n"; 53 | echo $user->asXML(); 54 | echo "\n"; 55 | echo "Name: ".$user->{'display-name'}."\n"; 56 | echo "Kind: ".$user->kind."\n"; 57 | echo "Age: ".$user->metadata->age."\n"; 58 | echo "\n"; 59 | 60 | // Delete the User 61 | $user = $pest->delete('/users/'.$user->id.'.xml'); 62 | 63 | 64 | // Try to create a User with invalid data (missing username) 65 | $data = array( 66 | 'user' => array( 67 | 'password' => "pinocchio", 68 | 'display_name' => "Jiminy Cricket", 69 | 'kind' => "Student" 70 | ) 71 | ); 72 | 73 | try { 74 | $user = $pest->post('/users.xml', $data); 75 | } catch (Pest_InvalidRecord $e) { 76 | echo $e->getMessage(); 77 | echo "\n"; 78 | } 79 | 80 | ?> -------------------------------------------------------------------------------- /rest-api/v1/curl/execution/createTestCaseExecution.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - PUT /executions' 13 | curl -i -H "Content-Type: application/json" -X POST --data "@../json/createTestCaseExecution.json" \ 14 | -u dev02:thisisnoytmypassword http://localhost/development/tlrepo/lib/api/rest/v1/executions -------------------------------------------------------------------------------- /rest-api/v1/curl/generic/createTestCaseX.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | clear 13 | echo 'Testing TestLink REST API - POST /testcases' 14 | echo 15 | if [ -z "$1" ]; then 16 | echo usage: $0 .JSONFILE \(searched in ../json/\) 17 | exit 18 | fi 19 | 20 | curl -i -H "Content-Type: application/json" -X POST --data "@../json/$1" \ 21 | -u restadminapikey:followthewitherabbitneo \ 22 | http://localhost/development/tlrepo/lib/api/rest/v1/testcases -------------------------------------------------------------------------------- /rest-api/v1/curl/json/README: -------------------------------------------------------------------------------- 1 | Files present on this directory can be used to test TestLink REST API using curl 2 | -------------------------------------------------------------------------------- /rest-api/v1/curl/json/createTestCase.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test Case ALFA", 3 | "testSuiteID":360, 4 | "testProjectID":358, 5 | "summary":"This is a summary for Test Case", 6 | "preconditions":"No preconditions", 7 | "order":100, 8 | "authorID":1, 9 | "importance":2, 10 | "executionType":1 11 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/json/createTestCaseB.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test Case ALFA", 3 | "testSuiteID":360, 4 | "testProjectID":358, 5 | "summary":"This is a summary for Test Case", 6 | "preconditions":"No preconditions", 7 | "order":100, 8 | "authorID":1, 9 | "importance":2, 10 | "executionType":1 11 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/json/createTestCaseExecution.json: -------------------------------------------------------------------------------- 1 | { 2 | "testPlanID":1195, 3 | "buildID":22, 4 | "platformID":28, 5 | "testCaseExternalID":"PON-3", 6 | "notes":"This is an execution created via REST API", 7 | "statusCode":"b", 8 | "executionType":"1", 9 | "executionTimeStampISO":"2013-04-27 12:09:00" 10 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/json/createTestCaseWithSteps.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test Case BETA has STEPS", 3 | "testSuiteID":378, 4 | "testProject":{"id":358}, 5 | "summary":"This is a summary for Test Case", 6 | "preconditions":"No preconditions", 7 | "order":100, 8 | "authorID":1, 9 | "importance":2, 10 | "executionType":1, 11 | "steps": [ 12 | { "step_number":1, 13 | "actions": "red", 14 | "expected_results": "#f00", 15 | "execution_type":1 16 | }, 17 | { "step_number":12, 18 | "actions": "red12", 19 | "expected_results": "#f00", 20 | "execution_type":2 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/json/createTestCaseXL5.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test Case XL5-A", 3 | "testSuiteID":378, 4 | "testProjectID": 358, 5 | "summary": "This is a summary for Test Case second line line #3", 6 | "preconditions":"No preconditions", 7 | "order":100, 8 | "authorID":1, 9 | "importance":2, 10 | "executionType":1 11 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/json/createTestCaseXL5B.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test Case XL5-B", 3 | "testSuiteID":378,"testProjectID":358, 4 | "summary": 5 | "This is a summary for Test Case 6 | second line 7 | line #3", 8 | "preconditions":"No preconditions", 9 | "order":100,"authorID":1,"importance":2,"executionType":1 10 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/json/createTestCaseXL5C.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test Case XL5-C", 3 | "testSuiteID":378,"testProjectID":358, 4 | "summary": 5 | "This is a summary for Test Case 6 | second line 7 | line #3", 8 | "preconditions":"No preconditions", 9 | "order":100,"authorID":1,"importance":2,"executionType":1 10 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/json/createTestCaseXL5D.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test Case XL5-D", 3 | "testSuiteID":378,"testProjectID":358, 4 | "summary": 5 | "This is a summary for Test Case
6 | second line
7 | line #3", 8 | "preconditions":"No preconditions for this testcase. 9 |
If you use an array [] can be empty 10 |
If you need to use a object {}", 11 | "order":100,"authorID":1,"importance":2,"executionType":1 12 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/json/createTestPlan.json: -------------------------------------------------------------------------------- 1 | {"name":"TestPlan Frenchitas", 2 | "testProjectID":358, 3 | "notes":"This is a FrenchToJD", 4 | "active":1,"is_public":1 5 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/json/createTestProject.json: -------------------------------------------------------------------------------- 1 | {"name":"FrenchToJD(month, day, year)", 2 | "notes":"This is a FrenchToJD", 3 | "color":"", 4 | "prefix":"FTJD", 5 | "active":1, 6 | "is_public":1, 7 | "options":{"requirementsEnabled":0,"testPriorityEnabled":1, 8 | "automationEnabled":1,"inventoryEnabled":0} 9 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/json/createTestSuite.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"TestSuite TS100-B", 3 | "testProjectID":358, 4 | "parentID":358, 5 | "notes":"This is my first TS via REST", 6 | "order":100 7 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/json/setTestPlanActive.json: -------------------------------------------------------------------------------- 1 | {"active":1} -------------------------------------------------------------------------------- /rest-api/v1/curl/json/setTestPlanInactive.json: -------------------------------------------------------------------------------- 1 | {"active":0} -------------------------------------------------------------------------------- /rest-api/v1/curl/json/setTestPlanNotes.json: -------------------------------------------------------------------------------- 1 | {"notes":"What's up doc"} -------------------------------------------------------------------------------- /rest-api/v1/curl/json/setTestPlanPrivate.json: -------------------------------------------------------------------------------- 1 | {"is_public":0} -------------------------------------------------------------------------------- /rest-api/v1/curl/json/testCaseComplex.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test Scenarios for Filter Criteria", 3 | "testSuiteID":378,"testProjectID":358, 4 | "preconditions":"No preconditions for this testcase. 5 |
If you use an array [] can be empty 6 |
If you need to use a object {}", 7 | "order":100,"authorID":1,"importance":2,"executionType":1, 8 | "summary": 9 | "
1. User should be able to filter results using all parameters on the page 10 |
2. Refine search functionality should load search page with all user selected search parameters 11 |
3. When there is at least one filter criteria is required to perform search operation, 12 |
make sure proper error message is displayed when user submits the page without selecting any filter criteria. 13 |
4. When at least one filter criteria selection is not compulsory user 14 |
should be able to submit page and default search criteria should get used to query results 15 |
5. Proper validation messages should be displayed for invalid values for filter criteria" 16 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/json/testCaseComplex2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test Scenarios for a Window", 3 | "testSuiteID":378,"testProjectID":358, 4 | "preconditions":"No preconditions for this testcase. 5 |
If you use an array [] can be empty 6 |
If you need to use a object {}", 7 | "order":100,"authorID":1,"importance":2,"executionType":1, 8 | "summary": 9 | "
1. Check if default window size is correct 10 |
2. Check if child window size is correct 11 |
3. Check if there is any field on page with default focus (in general, the focus should be set on first input field of the screen) 12 |
4. Check if child windows are getting closed on closing parent/opener window 13 |
5. If child window is opened, user should not be able to use or update any field on background or parent window 14 |
6. Check window minimize, maximize and close functionality 15 |
7. Check if window is re-sizable 16 |
8. Check scroll bar functionality for parent and child windows 17 |
9. Check cancel button functionality for child window" 18 | } -------------------------------------------------------------------------------- /rest-api/v1/curl/testplan/createTestPlan.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - POST /testplans' 13 | curl -i -H "Content-Type: application/json" -X POST --data "@../json/createTestPlan.json" \ 14 | -u restadminapikey:thebrownfox http://localhost/development/tlrepo/lib/api/rest/v1/testplans -------------------------------------------------------------------------------- /rest-api/v1/curl/testplan/setTestPlanActive.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - POST /testplans' 13 | curl -i -H "Content-Type: application/json" -X POST --data "@../json/setTestPlanActive.json" \ 14 | -u restadminapikey:thebrownfox http://localhost/development/tlrepo/lib/api/rest/v1/testplans/359 -------------------------------------------------------------------------------- /rest-api/v1/curl/testplan/setTestPlanInactive.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - POST /testplans' 13 | curl -i -H "Content-Type: application/json" -X POST --data "@../json/setTestPlanInactive.json" \ 14 | -u restadminapikey:thebrownfox http://localhost/development/tlrepo/lib/api/rest/v1/testplans/359 -------------------------------------------------------------------------------- /rest-api/v1/curl/testplan/setTestPlanNotes.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - POST /testplans' 13 | curl -i -H "Content-Type: application/json" -X POST --data "@../json/setTestPlanNotes.json" \ 14 | -u restadminapikey:thebrownfox http://localhost/development/tlrepo/lib/api/rest/v1/testplans/359 -------------------------------------------------------------------------------- /rest-api/v1/curl/testplan/setTestPlanPrivate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - POST /testplans' 13 | curl -i -H "Content-Type: application/json" -X POST --data "@../json/setTestPlanPrivate.json" \ 14 | -u restadminapikey:thebrownfox http://localhost/development/tlrepo/lib/api/rest/v1/testplans/359 -------------------------------------------------------------------------------- /rest-api/v1/curl/testplan/updateTestPlan.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - POST /testplans' 13 | curl -i -H "Content-Type: application/json" -X POST --data "@../json/setTestPlanInactive.json" \ 14 | -u restadminapikey:thebrownfox http://localhost/development/tlrepo/lib/api/rest/v1/testplans/359 -------------------------------------------------------------------------------- /rest-api/v1/curl/testproject/createTestCase.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - POST /testcases' 13 | curl -i -H "Content-Type: application/json" -X POST --data "@../json/createTestCase.json" \ 14 | -u restadminapikey:followthewitherabbitneo \ 15 | http://localhost/development/tlrepo/lib/api/rest/v1/testcases -------------------------------------------------------------------------------- /rest-api/v1/curl/testproject/createTestCaseWithSteps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - POST /testcases' 13 | curl -i -H "Content-Type: application/json" -X POST --data "@../json/createTestCaseWithSteps.json" \ 14 | -u restadminapikey:followthewitherabbitneo \ 15 | http://localhost/development/tlrepo/lib/api/rest/v1/testcases -------------------------------------------------------------------------------- /rest-api/v1/curl/testproject/createTestProject.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - POST /testprojects' 13 | TURL="http://localhost/development/github/testlink-code"; 14 | TURL="$TURL/lib/api/rest/v2/testprojects" 15 | # AUTH="restapi-leader:dev01" 16 | AUTH="restapi-admin:fruta" 17 | 18 | curl -i -H "Content-Type: application/json" -X POST --data "@../json/createTestProject.json" -u $AUTH $TURL -------------------------------------------------------------------------------- /rest-api/v1/curl/testproject/createTestSuite.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - POST /testsuites' 13 | curl -i -H "Content-Type: application/json" -X POST --data "@../json/createTestSuite.json" \ 14 | -u restadminapikey:followthewitherabbitneo \ 15 | http://localhost/development/tlrepo/lib/api/rest/v1/testsuites -------------------------------------------------------------------------------- /rest-api/v1/curl/testproject/getAllTestProjects.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - GET /testprojects' 13 | curl -i -X GET -u dev01xxx:dev01 http://localhost/development/test_tl/testlink/lib/api/rest/v1/testprojects -------------------------------------------------------------------------------- /rest-api/v1/curl/testproject/getOneTestProjectByID.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - GET /testprojects/374' 13 | curl -i -X GET -u 524993143f974f97903f5565a783594b:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects/374 -------------------------------------------------------------------------------- /rest-api/v1/curl/testproject/getOneTestProjectByName.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | clear 13 | echo '*****************************************************************' 14 | echo 'Testing TestLink REST API - GET /testprojects/Slim+REST+Framework' 15 | echo '' 16 | echo 'We have added + to escape BLANK IN Test Project Name' 17 | echo '*****************************************************************' 18 | echo '' 19 | echo '' 20 | curl -i -X GET -u 524993143f974f97903f5565a783594b:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects/Slim+REST+Framework -------------------------------------------------------------------------------- /rest-api/v1/curl/testproject/getTestCasesInTestProject.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - GET /testprojects/94/testcases' 13 | curl -i -X GET -u admin:noway http://localhost/development/test_tl/testlink/lib/api/rest/v1/testprojects/94/testcases -------------------------------------------------------------------------------- /rest-api/v1/curl/testproject/getTestCasesInTestProjectByTestProjectName.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - GET /testprojects/94/testcases' 13 | curl -i -X GET -u admin:noway http://localhost/development/test_tl/testlink/lib/api/rest/v1/testprojects/FLOWER/testcases -------------------------------------------------------------------------------- /rest-api/v1/curl/testproject/getTestPlansInTestProject.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - GET /testprojects/94/testplans' 13 | curl -i -X GET -u admin:noway http://localhost/development/test_tl/testlink/lib/api/rest/v1/testprojects/94/testplans -------------------------------------------------------------------------------- /rest-api/v1/curl/testproject/getTestPlansInTestProjectByTestProjectName.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | echo 'Testing TestLink REST API - GET /testprojects/94/testplans' 13 | curl -i -X GET -u admin:noway http://localhost/development/test_tl/testlink/lib/api/rest/v1/testprojects/FLOWER/testplans -------------------------------------------------------------------------------- /rest-api/v1/php/execution/createTestCaseExecution.php: -------------------------------------------------------------------------------- 1 | getBody() 11 | * I've got always null. 12 | * Seems that POST BODY is not created. 13 | * 14 | * Changing to PestJSON.php, did the trick 15 | * 16 | */ 17 | 18 | require '../../../common/pest/PestJSON.php'; 19 | 20 | if(!isset($_REQUEST['env'])) 21 | { 22 | die('env not provided: valid options: bit,gito - call URL?env='); 23 | } 24 | 25 | if($_REQUEST['env'] == 'bit') 26 | { 27 | $pest = new PestJSON('http://localhost/development/bitbucket/testlink/lib/api/rest/v1/'); 28 | } 29 | 30 | if($_REQUEST['env'] == 'gito') 31 | { 32 | $pest = new PestJSON('http://localhost/development/tlrepo/lib/api/rest/v1/'); 33 | } 34 | 35 | $password = $user = 'dev01'; 36 | $pest->setupAuth($user, $password); 37 | 38 | $item = array(); 39 | $item['testPlanID'] = 1185; 40 | $item['buildID'] = 1; 41 | $item['platformID'] = 0; 42 | $item['testCaseExternalID'] = 'TX::1'; 43 | $item['notes'] = 'This is an execution created via REST API'; 44 | $item['statusCode'] = 'p'; 45 | $item['executionType'] = 1; 46 | $item['executionTimeStamp'] = '2012-03-27 06:09:00'; 47 | 48 | 49 | 50 | 51 | // How to test using cUrl 52 | // curl -i -X POST --data "jsonFile"-u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 53 | // -i include headers 54 | // -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 55 | 56 | echo '
IN CLIENT
'; 57 | $xx = json_encode($item); 58 | var_dump($xx); 59 | 60 | echo '

THING GOT FROM SERVER
'; 61 | $thing = $pest->post('/executions',$item); 62 | var_dump($thing); 63 | -------------------------------------------------------------------------------- /rest-api/v1/php/testproject/createTestProject.php: -------------------------------------------------------------------------------- 1 | getBody() 11 | * I've got always null. 12 | * Seems that POST BODY is not created. 13 | * 14 | * Changing to PestJSON.php, did the trick 15 | * 16 | */ 17 | 18 | require '../../../common/pest/PestJSON.php'; 19 | 20 | if(!isset($_REQUEST['env'])) 21 | { 22 | die('env not provided: valid options: bit,gito - call URL?env='); 23 | } 24 | 25 | if($_REQUEST['env'] == 'bit') 26 | { 27 | $pest = new PestJSON('http://localhost/development/bitbucket/testlink/lib/api/rest/v1/'); 28 | } 29 | 30 | if($_REQUEST['env'] == 'gito') 31 | { 32 | $pest = new PestJSON('http://localhost/development/tlrepo/lib/api/rest/v1/'); 33 | } 34 | 35 | $password = $user = 'dev01'; 36 | $pest->setupAuth($user, $password); 37 | 38 | $item = array(); 39 | $item['name'] = 'FrenchToJD(month, day, year)'; 40 | $item['notes'] = 'This is a FrenchToJD'; 41 | $item['color'] = ''; 42 | $item['prefix'] = 'FTJD'; 43 | $item['active'] = 1; 44 | $item['is_public'] = 1; 45 | $item['options'] = array(); 46 | $item['options']['requirementsEnabled']=0; 47 | $item['options']['testPriorityEnabled'] = 1; 48 | $item['options']['automationEnabled'] = 1; 49 | $item['options']['inventoryEnabled'] = 0; 50 | 51 | 52 | // How to test using cUrl 53 | // curl -i -X POST --data "jsonFile"-u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 54 | // -i include headers 55 | // -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 56 | 57 | 58 | echo '
IN CLIENT
'; 59 | $xx = json_encode($item); 60 | var_dump($xx); 61 | 62 | echo '

THING GOT FROM SERVER
'; 63 | $thing = $pest->post('/testprojects',$item); 64 | var_dump($thing); 65 | -------------------------------------------------------------------------------- /rest-api/v1/php/testproject/getProjects.php: -------------------------------------------------------------------------------- 1 | setupAuth($user, $password); 32 | 33 | echo 'Access ALL TESTPROJECTS
'; 34 | $thing = json_decode($pest->get('/testprojects')); 35 | echo '
';var_dump($thing);echo '

'; 36 | 37 | 38 | echo 'Access Only ONE TESTPROJECT
'; 39 | $ox = (object) array('id' => 1208); 40 | echo 'Route:' . '/testprojects/' . $ox->id; 41 | 42 | $thing = json_decode($pest->get('/testprojects/' . $ox->id)); 43 | echo '
';var_dump($thing);echo '

'; 44 | 45 | 46 | 47 | echo 'Access Only ONE TESTPROJECT
'; 48 | $ox = (object) array('id' => 'Slim REST Framework'); 49 | echo 'Route:' . '/testprojects/' . $ox->id; 50 | 51 | $thing = json_decode($pest->get('/testprojects/' . urlencode($ox->id))); 52 | echo '
';var_dump($thing);echo '

'; 53 | -------------------------------------------------------------------------------- /rest-api/v1/php/testproject/getTestCasesInTestProject.php: -------------------------------------------------------------------------------- 1 | setupAuth($user, $password); 32 | 33 | echo 'Access ALL TEST CASES present in a test project
'; 34 | $thing = json_decode($pest->get('/testprojects/94/testcases')); 35 | echo '
';var_dump($thing);echo '

'; 36 | -------------------------------------------------------------------------------- /rest-api/v1/php/testproject/getTestPlansInTestProject.php: -------------------------------------------------------------------------------- 1 | setupAuth($user, $password); 32 | 33 | echo 'Access ALL TEST PLANS present in a test project (Accessible to user ??)
'; 34 | $thing = json_decode($pest->get('/testprojects/94/testplans')); 35 | echo '
';var_dump($thing);echo '

'; 36 | -------------------------------------------------------------------------------- /rest-api/v2/README: -------------------------------------------------------------------------------- 1 | Directory content 2 | 3 | json 4 | files that can be used as API documentation and also to test TestLink REST API using curl 5 | 6 | curl 7 | bash scripts to test TestLink REST API using curl 8 | 9 | php 10 | php examples of TestLink REST API usage 11 | -------------------------------------------------------------------------------- /rest-api/v2/curl/builds/createBuild.sh: -------------------------------------------------------------------------------- 1 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 2 | # -i include headers 3 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 4 | # -u user:password 5 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 6 | # or - if you want curl to read the data from stdin. 7 | # The contents of the file must already be URL-encoded. 8 | # Multiple files can also be specified. 9 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 10 | # 11 | echo 'Testing TestLink REST API - POST /builds' 12 | curl -i -H "Content-Type: application/json" \ 13 | -X POST --data "@../../json/createBuild.json" \ 14 | -u b8359492ca233fd1ce55d6363cd026cf:b8359492ca233fd1ce55d6363cd026cf \ 15 | http://localhost/development/github/testlink-code/lib/api/rest/v2/builds -------------------------------------------------------------------------------- /rest-api/v2/curl/builds/createKeyword.sh: -------------------------------------------------------------------------------- 1 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 2 | # -i include headers 3 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 4 | # -u user:password 5 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 6 | # or - if you want curl to read the data from stdin. 7 | # The contents of the file must already be URL-encoded. 8 | # Multiple files can also be specified. 9 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 10 | # 11 | echo 'Testing TestLink REST API - POST /keywords' 12 | curl -i \ 13 | -H "Content-Type: application/json" \ 14 | -H @{'custom_header'='custom_header_value'} \ 15 | -X POST --data "@../../json/createKeyword.json" \ 16 | -u b8359492ca233fd1ce55d6363cd026cf:b8359492ca233fd1ce55d6363cd026cf \ 17 | http://localhost/development/github/testlink-code/lib/api/rest/v2/keywords -------------------------------------------------------------------------------- /rest-api/v2/curl/callisthenics/README: -------------------------------------------------------------------------------- 1 | Test simple methods to understand if REST API is up & running -------------------------------------------------------------------------------- /rest-api/v2/curl/callisthenics/who.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 3 | # -i include headers 4 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 5 | # -u user:password 6 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 7 | # or - if you want curl to read the data from stdin. 8 | # The contents of the file must already be URL-encoded. 9 | # Multiple files can also be specified. 10 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 11 | # 12 | clear 13 | echo 'Testing TestLink REST API - POST /who' 14 | 15 | curl -i -H "Content-Type: application/json" \ 16 | -u restadminapikey:followthewitherabbitneo \ 17 | http://localhost/development/github/testlink-code/lib/api/rest/v2/who -------------------------------------------------------------------------------- /rest-api/v2/curl/keywords/createKeyword.sh: -------------------------------------------------------------------------------- 1 | # curl -i -X POST -u dev01:dev01 http://localhost/development/tlrepo/lib/api/rest/v1/testprojects 2 | # -i include headers 3 | # -X (HTTP) Specifies a custom request method to use when communicating with the HTTP server 4 | # -u user:password 5 | # --data If you start the data with the letter @, the rest should be a file name to read the data from, 6 | # or - if you want curl to read the data from stdin. 7 | # The contents of the file must already be URL-encoded. 8 | # Multiple files can also be specified. 9 | # Posting data from a file named 'foobar' would thus be done with --data @foobar. 10 | # 11 | echo 'Testing TestLink REST API - POST /keywords' 12 | curl -i \ 13 | -H "Content-Type: application/json" \ 14 | -H "APIKEY: b8359492ca233fd1ce55d6363cd026cf" \ 15 | -X POST --data "@../../json/createKeyword.json" \ 16 | -u developer:followthewitherabbitneo \ 17 | http://localhost/development/github/testlink-code/lib/api/rest/v2/keywords -------------------------------------------------------------------------------- /rest-api/v2/json/README: -------------------------------------------------------------------------------- 1 | Files present on this directory can be used to test TestLink REST API using curl 2 | -------------------------------------------------------------------------------- /rest-api/v2/json/createBuild.json: -------------------------------------------------------------------------------- 1 | { 2 | "operation": "create build", 3 | "testplan": "134112", 4 | "name": "6.1@34455" 5 | } -------------------------------------------------------------------------------- /rest-api/v2/json/createKeyword.json: -------------------------------------------------------------------------------- 1 | { 2 | "keyword": "ALFA K-134112", 3 | "notes": "This is a Keyword", 4 | "testProject": {"prefix": "JUN"} 5 | } -------------------------------------------------------------------------------- /rest-api/v2/json/createTestCaseUsingAttrID.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test Case ALFA", 3 | "summary":"This is a summary for Test Case", 4 | "preconditions":"No preconditions", 5 | "order":100, 6 | 7 | 8 | "testProject":{"id":358}, 9 | "testSuite":{"id":360}, 10 | 11 | "author":{"id":1}, 12 | "importance":{"id":2}, 13 | "executionType":{"id":1}, 14 | "status":{"id":1}, 15 | "estimatedExecutionTime":0 16 | } -------------------------------------------------------------------------------- /rest-api/v2/json/createTestCaseUsingAttrVerboseKey.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test Case ALFA", 3 | "summary":"This is a summary for Test Case", 4 | "preconditions":"No preconditions", 5 | "order":100, 6 | 7 | "testProject":{"prefix":"APR"}, 8 | "testSuite":{"id":360}, 9 | 10 | "author":{"login":"Mr Spock"}, 11 | "importance":{"name":"high"}, 12 | "executionType":{"name":"manual"}, 13 | "status":{"name":"draft"}, 14 | "estimatedExecutionTime":0 15 | } -------------------------------------------------------------------------------- /sample-data/README.md: -------------------------------------------------------------------------------- 1 | The idea is provide some simple and silly data that can be used 2 | to discuss issues with the users. 3 | 4 | TS-100.barebones.xml 5 | Contains: 6 | 1 Test Suite TS-100 7 | 5 Test Cases (TC-1A,...,TC-1F) with 2 steps on each one. 8 | 3 Test Cases (TC-BX,TC-CX,TC-DX) without steps. 9 | NO Keywords 10 | NO Custom Fields 11 | NO Attachments 12 | NO Requirements 13 | 14 | ![](./images/TS-100.barebones.png) 15 | 16 | RSPEC-01-req-spec.barebones.xml 17 | Contains: 18 | 1 Req Specification 19 | 6 Requirements 20 | 21 | ![](./images/RSPEC-01-req-spec.barebones.png) -------------------------------------------------------------------------------- /sample-data/RSPEC-01-req-spec.barebones.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | <![CDATA[REQ-01 TITLE]]> 12 | 1 13 | 1 14 | 1 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | <![CDATA[REQ-02 TITLE]]> 24 | 1 25 | 1 26 | 2 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | <![CDATA[REQ-03 TITLE]]> 36 | 1 37 | 1 38 | 3 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | <![CDATA[REQ-04 TITLE]]> 48 | 1 49 | 1 50 | 4 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | <![CDATA[REQ-05 TITLE]]> 60 | 1 61 | 1 62 | 5 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | <![CDATA[REQ-06 TITLE]]> 72 | 1 73 | 1 74 | 6 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /sample-data/TS-100.barebones.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 1 17 | 1 18 | 1 19 | 20 | 21 | 22 | STEP-1-1A

23 | ]]>
24 | ER-1-1A

25 | ]]>
26 | 27 |
28 | 29 | 30 | 31 | STEP-2-TC-1A

32 | ]]>
33 | ER-2-TC-1A

34 | ]]>
35 | 36 |
37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 1 51 | 1 52 | 1 53 | 54 | 55 | 56 | STEP-1-1A

57 | ]]>
58 | ER-1-1A

59 | ]]>
60 | 61 |
62 | 63 | 64 | 65 | STEP-2-TC-1A

66 | ]]>
67 | ER-2-TC-1A

68 | ]]>
69 | 70 |
71 |
72 |
73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 1 85 | 1 86 | 1 87 | 88 | 89 | 90 | STEP-1-1A

91 | ]]>
92 | ER-1-1A

93 | ]]>
94 | 95 |
96 | 97 | 98 | 99 | STEP-2-TC-1A

100 | ]]>
101 | ER-2-TC-1A

102 | ]]>
103 | 104 |
105 |
106 |
107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 1 119 | 1 120 | 1 121 | 122 | 123 | 124 | STEP-1-1A

125 | ]]>
126 | ER-1-1A

127 | ]]>
128 | 129 |
130 | 131 | 132 | 133 | STEP-2-TC-1A

134 | ]]>
135 | ER-2-TC-1A

136 | ]]>
137 | 138 |
139 |
140 |
141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 1 153 | 1 154 | 1 155 | 156 | 157 | 158 | STEP-1-1A

159 | ]]>
160 | ER-1-1A

161 | ]]>
162 | 163 |
164 | 165 | 166 | 167 | STEP-2-TC-1A

168 | ]]>
169 | ER-2-TC-1A

170 | ]]>
171 | 172 |
173 |
174 |
175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 1 187 | 1 188 | 1 189 | 190 | 191 | 192 | STEP-1-1A

193 | ]]>
194 | ER-1-1A

195 | ]]>
196 | 197 |
198 | 199 | 200 | 201 | STEP-2-TC-1A

202 | ]]>
203 | ER-2-TC-1A

204 | ]]>
205 | 206 |
207 |
208 |
209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 1 221 | 1 222 | 1 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 1 237 | 1 238 | 1 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 1 253 | 1 254 | 1 255 | 256 | 257 |
-------------------------------------------------------------------------------- /sample-data/images/RSPEC-01-req-spec.barebones.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TestLinkOpenSourceTRMS/testlink-tests/af30ba3635ad9027ab8ea6de861f05f5cecbf335/sample-data/images/RSPEC-01-req-spec.barebones.png -------------------------------------------------------------------------------- /sample-data/images/TS-100.barebones.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TestLinkOpenSourceTRMS/testlink-tests/af30ba3635ad9027ab8ea6de861f05f5cecbf335/sample-data/images/TS-100.barebones.png -------------------------------------------------------------------------------- /sample-data/sample-keywords01.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 12 | 15 | 18 | -------------------------------------------------------------------------------- /sample-db/mysql/README.md: -------------------------------------------------------------------------------- 1 | db with more than 30000 test cases 2 | -------------------------------------------------------------------------------- /sample-db/mysql/testlink_30k_testcases.sql.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TestLinkOpenSourceTRMS/testlink-tests/af30ba3635ad9027ab8ea6de861f05f5cecbf335/sample-db/mysql/testlink_30k_testcases.sql.gz -------------------------------------------------------------------------------- /sample-db/postgres/README.md: -------------------------------------------------------------------------------- 1 | db with 28000 test cases 2 | -------------------------------------------------------------------------------- /sample-db/postgres/testlinkP2.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TestLinkOpenSourceTRMS/testlink-tests/af30ba3635ad9027ab8ea6de861f05f5cecbf335/sample-db/postgres/testlinkP2.tar -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.rst 3 | 4 | [aliases] 5 | test=pytest 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # pylint: disable=broad-except 3 | """qatestlink module can be installed and configured from here""" 4 | 5 | from os import path 6 | from setuptools import setup, find_packages 7 | from testlinktests.core.utils import read_file 8 | from testlinktests.core.utils import path_format 9 | 10 | 11 | VERSION = '0.0.2' 12 | CURR_PATH = "{}{}".format(path.abspath(path.dirname(__file__)), '/') 13 | 14 | 15 | def read(file_name=None, is_encoding=True, ignore_raises=False): 16 | """Read file""" 17 | if file_name is None: 18 | raise Exception("File name not provided") 19 | if ignore_raises: 20 | try: 21 | return read_file(is_encoding=is_encoding, 22 | file_path=path_format( 23 | file_path=CURR_PATH, 24 | file_name=file_name, 25 | ignore_raises=ignore_raises)) 26 | except Exception: 27 | # TODO: not silence like this, 28 | # must be on setup.cfg, README path 29 | return 'NOTFOUND' 30 | return read_file(is_encoding=is_encoding, 31 | file_path=path_format( 32 | file_path=CURR_PATH, 33 | file_name=file_name, 34 | ignore_raises=ignore_raises)) 35 | 36 | 37 | setup( 38 | name='testlinktests', 39 | version=VERSION, 40 | license=read("LICENSE", is_encoding=False, ignore_raises=True), 41 | packages=find_packages(exclude=['tests']), 42 | description='Main automation lib to ensure behaviour of Testlink WebApp', 43 | long_description=read("README.rst"), 44 | author='TestLinkOpenSourceTRMS', 45 | author_email='netzuleando@gmail.com', # Can be replaced 46 | url='https://github.com/TestLinkOpenSourceTRMS/testlink-tests', 47 | download_url=("https://github.com/TestLinkOpenSourceTRMS" 48 | "/testlink-tests/tarball/v{}").format(VERSION), 49 | keywords=[ 50 | 'testing', 51 | 'logging', 52 | 'functional', 53 | 'http', 54 | 'test', 55 | 'testlink', 56 | 'XMLRPC', 57 | 'requests', 58 | 'webapp', 59 | 'selenium', 60 | 'functional' 61 | ], 62 | install_requires=[ 63 | 'qacode==0.6.2', 64 | ], 65 | setup_requires=[ 66 | 'tox', 67 | 'pytest-runner', 68 | ], 69 | tests_require=[ 70 | 'pytest-raises', 71 | 'pytest-html', 72 | 'pytest-dependency', 73 | 'pytest-benchmark', 74 | 'pytest-benchmark[histogram]', 75 | 'coverage==4.3.4', 76 | 'pytest-cov==2.5.0', 77 | 'flake8' 78 | ], 79 | classifiers=[ 80 | 'Development Status :: 4 - Beta', 81 | 'Intended Audience :: Developers', 82 | 'Topic :: Software Development :: Build Tools', 83 | 'Programming Language :: Python :: 2.7', 84 | 'Programming Language :: Python :: 3.4', 85 | 'Programming Language :: Python :: 3.5', 86 | 'Programming Language :: Python :: 3.6', 87 | 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 88 | ], 89 | ) 90 | -------------------------------------------------------------------------------- /testlinktests/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | """package testlinktests 4 | Created on 2018-07-03 5 | 6 | @author: netzulo 7 | """ 8 | 9 | 10 | from testlinktests import configs 11 | from testlinktests import core 12 | 13 | __all__ = ['configs', 'core'] 14 | -------------------------------------------------------------------------------- /testlinktests/configs/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | """package testlinktests.configs 4 | Created on 2018-07-03 5 | 6 | @author: netzulo 7 | """ 8 | 9 | 10 | __all__ = [] 11 | -------------------------------------------------------------------------------- /testlinktests/configs/settings.json: -------------------------------------------------------------------------------- 1 | { "bot": { 2 | "log_output_file": "./logs/", 3 | "log_name": "testlinktests", 4 | "log_level": "INFO", 5 | "mode": "remote", 6 | "browser": "chrome", 7 | "options": { "headless": false }, 8 | "url_hub": "http://netzulo.tk:11000/wd/hub", 9 | "drivers_path": "../qadrivers", 10 | "drivers_names": [] 11 | }, 12 | "testlink": { 13 | "connection":{ 14 | "is_https": false, 15 | "host": "netzulo.tk", 16 | "port": 86 17 | }, 18 | "dev_key": "1bfd2ef4ceda22b482b12f2b25457495", 19 | "log_level":"INFO" 20 | }, 21 | "tests": { 22 | "skip":{ 23 | "api": true, 24 | "login": false, 25 | "index": false 26 | }, 27 | "apps": [ 28 | { "name": "testlink", 29 | "data": { "usr": "admin", "pwd": "admin"}, 30 | "pages": [ 31 | { "name": "login", "url": "http://netzulo.tk:88/login.php"}, 32 | { "name": "index", "url": "http://netzulo.tk:88/index.php"} 33 | ] 34 | } 35 | ] 36 | } 37 | } -------------------------------------------------------------------------------- /testlinktests/core/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | """package testlinktests.core 4 | Created on 2018-07-03 5 | 6 | @author: netzulo 7 | """ 8 | 9 | 10 | from testlinktests.core import specs 11 | from testlinktests.core import utils 12 | 13 | 14 | __all__ = ['specs', 'utils'] 15 | -------------------------------------------------------------------------------- /testlinktests/core/pages/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """package testlinktests.core.pages 3 | Created on 2019-10-27 4 | 5 | @author: netzulo 6 | """ 7 | 8 | 9 | from testlinktests.core.pages import page_index 10 | from testlinktests.core.pages import page_login 11 | 12 | 13 | __all__ = ['page_index', 'page_login'] 14 | -------------------------------------------------------------------------------- /testlinktests/core/pages/page_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """package testlinktests.core.pages.page_login 3 | Created on 2019-10-27 4 | 5 | @author: netzulo 6 | """ 7 | 8 | 9 | from qacode.core.webs.pages.page_base import PageBase 10 | 11 | 12 | class PageIndex(PageBase): 13 | """TODO:doc class""" 14 | 15 | def __init__(self, bot, **kwargs): 16 | """ 17 | USAGE: 18 | on a browser javascript console 19 | 20 | var iframe = document.querySelectorAll("[name='mainframe']")[0] 21 | var doc = iframe.contentDocument 22 | var menuRight = doc.querySelectorAll(".vertical_menu:nth-child(1)") 23 | var menuLeft = doc.querySelectorAll(".vertical_menu:nth-child(3)") 24 | """ 25 | kwargs.update({"locator": "css selector"}) 26 | kwargs.update({"go_url": False}) 27 | kwargs.update({"wait_url": 0}) 28 | kwargs.update({"maximize": False}) 29 | controls = [ 30 | {"name": "iframe_title", "selector": "[name='titlebar']"}, 31 | {"name": "logo", "selector": "[title='logo']"}, 32 | {"name": "menu_title", "selector": ""}, 33 | {"name": "menu_bar", "selector": ""}, 34 | {"name": "iframe_content", "selector": "[name='mainframe']"}, 35 | {"name": "menu_right", "selector": ".vertical_menu:nth-child(1)"}, 36 | {"name": "menu_left", "selector": ".vertical_menu:nth-child(3)"}, 37 | ] 38 | kwargs.update({"controls": controls}) 39 | super(PageIndex, self).__init__(bot, **kwargs) 40 | 41 | def menu_right(self): 42 | return self.iframe_content.find_child(self.menu_right) 43 | 44 | def menu_left(self): 45 | return self.iframe_content.find_child(self.menu_left) 46 | 47 | def is_iframe_content(self): 48 | self.menu_right() 49 | self.menu_left() 50 | return True 51 | 52 | def logo(self): 53 | return self.iframe_title.find_child(self.logo) 54 | 55 | def menu_title(self): 56 | return self.iframe_title.find_child(self.menu_title) 57 | 58 | def menu_bar(self): 59 | return self.iframe_title.find_child(self.menu_bar) 60 | 61 | def is_iframe_title(self): 62 | self.logo() 63 | self.menu_title() 64 | self.menu_bar() 65 | -------------------------------------------------------------------------------- /testlinktests/core/pages/page_login.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """package testlinktests.core.pages.page_login 3 | Created on 2019-10-27 4 | 5 | @author: netzulo 6 | """ 7 | 8 | 9 | from qacode.core.webs.pages.page_base import PageBase 10 | 11 | 12 | class PageLogin(PageBase): 13 | """TODO:doc class""" 14 | 15 | def __init__(self, bot, **kwargs): 16 | """TODO: doc method""" 17 | kwargs.update({"locator": "css selector"}) 18 | kwargs.update({"go_url": False}) 19 | kwargs.update({"wait_url": 0}) 20 | kwargs.update({"maximize": False}) 21 | controls = [ 22 | {"name": "str_version", "selector": "div span"}, 23 | {"name": "txt_username", "selector": "#tl_login"}, 24 | {"name": "txt_password", "selector": "#tl_password"}, 25 | {"name": "btn_submit", "selector": "#tl_login_button"}, 26 | {"name": "lnk_signup", "selector": "#tl_sign_up"}, 27 | {"name": "lnk_lost_password", "selector": "#tl_lost_password"} 28 | ] 29 | kwargs.update({"controls": controls}) 30 | super(PageLogin, self).__init__(bot, **kwargs) 31 | 32 | def version(self): 33 | """GET testlink version showing at top of login form""" 34 | text = self.str_version.get_text() 35 | # texts = text.split(" ") 36 | # version = texts[0] 37 | # mode = texts[1] 38 | # release_name = texts[2] 39 | return text 40 | 41 | def username(self, name, clear=True): 42 | """Fill up field for username""" 43 | self.txt_username.type_text(name, clear=clear) 44 | 45 | def password(self, name, clear=True): 46 | """Fill up field for password""" 47 | self.txt_password.type_text(name, clear=clear) 48 | 49 | def submit(self): 50 | """Click on submit button for login form""" 51 | self.btn_submit.click() 52 | 53 | def is_logged(self): 54 | url = self.bot.curr_driver.current_url 55 | is_login_url = "login.php" in url 56 | is_index_url = "index.php" in url 57 | if not is_login_url and is_index_url: 58 | return True 59 | return False 60 | 61 | def login(self, username, password): 62 | """Fill up login form and do login""" 63 | self.username(username) 64 | self.password(password) 65 | self.submit() 66 | return self.is_logged() 67 | -------------------------------------------------------------------------------- /testlinktests/core/specs/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | """package testlinktests.core 4 | Created on 2018-07-03 5 | 6 | @author: netzulo 7 | """ 8 | 9 | 10 | from testlinktests.core.specs import req_information 11 | 12 | 13 | __all__ = ['req_information'] 14 | -------------------------------------------------------------------------------- /testlinktests/core/specs/req_information.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | """package testlinktests.core 4 | Created on 2018-07-03 5 | 6 | @author: netzulo 7 | """ 8 | 9 | 10 | from qacode.core.testing.test_info import TestInfoBase 11 | from testlinktests.core.pages.page_index import PageIndex 12 | from testlinktests.core.pages.page_login import PageLogin 13 | 14 | 15 | def url(name, page, go_url=True): 16 | """Information requirement about URLs 17 | 18 | Arguments: 19 | name {str} -- Name of a page instance 20 | page {PageBase} -- Instance of a qacode Page or inherit object 21 | 22 | Keyword Arguments: 23 | go_url {True} -- Force BOT to navigate at requirement url (default: {True}) 24 | """ 25 | pageType = { 26 | "login": PageLogin, 27 | "index": PageIndex, 28 | }[name] 29 | TestInfoBase().assert_is_instance(page, pageType) 30 | if not page.is_url() and go_url: 31 | page.go_url() 32 | -------------------------------------------------------------------------------- /testlinktests/core/test_info.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Base module for inherit new Test Suites""" 3 | 4 | 5 | import os 6 | import re 7 | import time 8 | import pytest 9 | from qatestlink.core.testlink_manager import TLManager 10 | 11 | 12 | ASSERT_MSG_DEFAULT = "Fails at '{}': actual={}, expected={}" 13 | ASSERT_REGEX_URL = r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+" # noqa: E501 14 | 15 | 16 | class TestInfoBase(object): 17 | """Base class for inherit new Test classes""" 18 | 19 | tlm = None 20 | log = None 21 | 22 | @classmethod 23 | def setup_class(cls, **kwargs): 24 | """Configure 'cls.attribute'. If name start with 'test_' and have 25 | decorator skipIf with value True, then not open bot 26 | """ 27 | tests_methods = [] 28 | skip_methods = [] 29 | skip_force = kwargs.get('skip_force') 30 | for method_name in dir(cls): 31 | if method_name.startswith("test_"): 32 | method = getattr(cls, method_name) 33 | tests_methods.append(method) 34 | if 'skipIf' in dir(method) and method.skipIf.args[0]: 35 | skip_methods.append(method) 36 | if tests_methods == skip_methods or skip_force: 37 | pytest.skip("Testsuite skipped") 38 | 39 | def setup_method(self, test_method, **kwargs): 40 | """Configure self.attribute. 41 | If skipIf mark applied and True as first param for args tuple 42 | then not open bot 43 | """ 44 | if 'skipIf' in dir(test_method) and test_method.skipIf.args[0]: 45 | pytest.skip(test_method.skipIf.args[1]) 46 | return 47 | settings = kwargs.get('settings') 48 | tlm = kwargs.get('tlm') 49 | if settings is None and tlm is None: 50 | raise Exception( 51 | ("Not settings or TLManager " 52 | "instance provided, read README first")) 53 | if tlm is None: 54 | tlm = TLManager(settings=settings) 55 | self.tlm = tlm 56 | self.log = tlm.log 57 | self.log.info("Started testcase named='{}'".format( 58 | test_method.__name__)) 59 | 60 | def teardown_method(self, test_method): 61 | """Unload self.attribute""" 62 | self.log.info("Finished testcase named='{}'".format( 63 | test_method.__name__)) 64 | 65 | @classmethod 66 | def add_property(cls, name, value=None): 67 | """Add property to test instance using param 'name', will setup 68 | None if any value it's passed by param 69 | """ 70 | setattr(cls, name, value) 71 | 72 | def timer(self, wait=5, print_each=5): 73 | """Timer to sleep browser on testcases 74 | 75 | Keyword Arguments: 76 | wait {int} -- seconds to wait (default: {5}) 77 | print_each {int} -- print message each seconds, must be divisible 78 | by 5, negatives are accepted (default: {5}) 79 | 80 | Raises: 81 | Exception -- [description] 82 | """ 83 | msg_err = "Timer can't works if print_each param isn't divisible by 1" 84 | if (print_each % 1) != 0: 85 | raise Exception(msg_err) 86 | while wait > 0: 87 | self.sleep(print_each) 88 | wait -= print_each 89 | 90 | def sleep(self, wait=0): 91 | """Just call to native python time.sleep method 92 | 93 | Keyword Arguments: 94 | wait {int} -- Wait time on Runtime execution before execute 95 | next lane of code (default: {0}) 96 | """ 97 | if wait > 0: 98 | time.sleep(wait) 99 | 100 | def assert_equals(self, actual, expected, msg=None): 101 | """Allow to compare 2 values and check if 1st it's equals to 102 | 2nd value 103 | """ 104 | if not msg: 105 | msg = ASSERT_MSG_DEFAULT.format( 106 | "assert_equals", actual, expected) 107 | if actual != expected: 108 | raise AssertionError(actual, expected, msg) 109 | return True 110 | 111 | def assert_not_equals(self, actual, expected, msg=None): 112 | """Allow to compare 2 value to check if 1st isn't equals to 113 | 2nd value 114 | """ 115 | if not msg: 116 | msg = ASSERT_MSG_DEFAULT.format( 117 | "assert_not_equals", actual, expected) 118 | if actual == expected: 119 | raise AssertionError(actual, expected, msg) 120 | return True 121 | 122 | def assert_equals_url(self, actual, expected, msg=None, wait=0): 123 | """Allow to compare 2 urls and check if 1st it's equals to 2nd url 124 | 125 | Arguments: 126 | actual {type} -- actual value 127 | expected {type} -- expected value 128 | 129 | Keyword Arguments: 130 | wait {int} -- Wait time on Runtime execution before execute 131 | next lane of code (default: {0}) 132 | 133 | Raises: 134 | AssertionError -- [description] 135 | """ 136 | if not msg: 137 | msg = ASSERT_MSG_DEFAULT.format( 138 | "assert_equals_url", actual, expected) 139 | self.sleep(wait) 140 | if actual != expected: 141 | raise AssertionError(actual, expected, msg) 142 | return True 143 | 144 | def assert_not_equals_url(self, actual, expected, msg=None, wait=0): 145 | """Allow to compare 2 urls to check if 1st isn't equals to 2nd url""" 146 | if not msg: 147 | msg = ASSERT_MSG_DEFAULT.format( 148 | "assert_not_equals_url", actual, expected) 149 | self.sleep(wait) 150 | if actual == expected: 151 | raise AssertionError(actual, expected, msg) 152 | return True 153 | 154 | def assert_contains_url(self, actual, contains, msg=None, wait=0): 155 | """Allow to compare 2 urls and check if 1st contains 2nd url""" 156 | if not msg: 157 | msg = ASSERT_MSG_DEFAULT.format( 158 | "assert_contains_url", actual, contains) 159 | self.sleep(wait) 160 | if actual not in contains: 161 | raise AssertionError(actual, contains, msg) 162 | return True 163 | 164 | def assert_not_contains_url(self, actual, contains, msg=None, wait=0): 165 | """Allow to compare 2 urls and check if 1st not contains 2nd url""" 166 | if not msg: 167 | msg = ASSERT_MSG_DEFAULT.format( 168 | "assert_not_contains_url", actual, contains) 169 | self.sleep(wait) 170 | if actual in contains: 171 | raise AssertionError(actual, contains, msg) 172 | return True 173 | 174 | def assert_is_instance(self, instance, class_type, msg=None): 175 | """Allow to encapsulate method assertIsInstance(obj, cls, msg='')""" 176 | if not msg: 177 | msg = ASSERT_MSG_DEFAULT.format( 178 | "assert_is_instance", instance, class_type) 179 | if not isinstance(class_type, type): 180 | class_type = type(class_type) 181 | if not isinstance(instance, class_type): 182 | raise AssertionError(instance, class_type, msg) 183 | return True 184 | 185 | def assert_raises(self, expected_exception, function, *args, **kwargs): 186 | """Allow to encapsulate pytest.raises method( 187 | *args=( 188 | expected_exception, 189 | function, 190 | ), 191 | **kwargs={ 192 | msg: ASSERT_MSG_DEFAULT 193 | } 194 | ) 195 | """ 196 | msg = kwargs.get('msg') 197 | if not msg: 198 | msg = ASSERT_MSG_DEFAULT.format( 199 | "assert_raises", 200 | "TODO:not implemented value", 201 | expected_exception) 202 | return pytest.raises(expected_exception, function, *args, **kwargs) 203 | 204 | def assert_greater(self, actual, greater, msg=None): 205 | """Allow to encapsulate method assertGreater(a, b, msg=msg)""" 206 | if not msg: 207 | msg = ASSERT_MSG_DEFAULT.format( 208 | "assert_greater", actual, greater) 209 | if actual < greater: 210 | raise AssertionError(actual, greater, msg) 211 | return True 212 | 213 | def assert_lower(self, actual, lower, msg=None): 214 | """Allow to encapsulate method assertLower(a, b, msg=msg)""" 215 | if not msg: 216 | msg = ASSERT_MSG_DEFAULT.format( 217 | "assert_greater", actual, lower) 218 | if actual > lower: 219 | raise AssertionError(actual, lower, msg) 220 | return True 221 | 222 | def assert_in(self, actual, valid_values, msg=None): 223 | """Allow to compare if value it's in to 2nd list of values""" 224 | if not msg: 225 | msg = ASSERT_MSG_DEFAULT.format( 226 | "assert_in", actual, valid_values) 227 | if actual not in valid_values: 228 | raise AssertionError(actual, valid_values, msg) 229 | return True 230 | 231 | def assert_not_in(self, actual, invalid_values, msg=None): 232 | """Allow to compare if value it's not in to 2nd list of values""" 233 | if not msg: 234 | msg = ASSERT_MSG_DEFAULT.format( 235 | "assert_in", actual, invalid_values) 236 | if actual in invalid_values: 237 | raise AssertionError(actual, invalid_values, msg) 238 | return True 239 | 240 | def assert_regex(self, actual, pattern, msg=None): 241 | """Allow to compare if value match pattern""" 242 | if not msg: 243 | msg = ASSERT_MSG_DEFAULT.format( 244 | "assert_regex", actual, pattern) 245 | is_match = re.match(pattern, actual) 246 | if not is_match: 247 | raise AssertionError(actual, pattern, msg) 248 | return True 249 | 250 | def assert_not_regex(self, actual, pattern, msg=None): 251 | """Allow to compare if value not match pattern""" 252 | if not msg: 253 | msg = ASSERT_MSG_DEFAULT.format( 254 | "assert_not_regex", actual, pattern) 255 | is_match = re.match(pattern, actual) 256 | if is_match: 257 | raise AssertionError(actual, pattern, msg) 258 | return True 259 | 260 | def assert_regex_url(self, actual, pattern=None, msg=None): 261 | """Allow to compare if value match url pattern, can use 262 | custom pattern 263 | """ 264 | if not msg: 265 | msg = ASSERT_MSG_DEFAULT.format( 266 | "assert_regex_url", actual, pattern) 267 | if not pattern: 268 | pattern = ASSERT_REGEX_URL 269 | return self.assert_regex(actual, pattern, msg=msg) 270 | 271 | def assert_path_exist(self, actual, is_dir=True, msg=None): 272 | """Allow to check if path exist, can check if is_dir also""" 273 | if not msg: 274 | msg = ASSERT_MSG_DEFAULT.format( 275 | "assert_path_exist", 276 | actual, 277 | "is_dir={}".format(is_dir)) 278 | if not os.path.exists(actual): 279 | raise AssertionError(actual, "NEED_PATH_FOUND", msg) 280 | _is_dir = os.path.isdir(actual) 281 | if is_dir: 282 | if not _is_dir: 283 | raise AssertionError(actual, "NEED_PATH_IS_DIR", msg) 284 | else: 285 | if _is_dir: 286 | raise AssertionError(actual, "NEED_PATH_NOT_DIR", msg) 287 | return True 288 | 289 | def assert_path_not_exist(self, actual, msg=None): 290 | """Allow to check if path not exist, can check if is_dir also""" 291 | if not msg: 292 | msg = ASSERT_MSG_DEFAULT.format( 293 | "assert_path_not_exist", actual, "") 294 | if os.path.exists(actual): 295 | raise AssertionError(actual, "NEED_PATH_NOT_FOUND", msg) 296 | return True 297 | 298 | def assert_true(self, actual, msg=None): 299 | """Allow to compare and check if value it's equals to 'True'""" 300 | if not msg: 301 | msg = ASSERT_MSG_DEFAULT.format( 302 | "assert_true", actual, "") 303 | self.assert_is_instance(actual, bool) 304 | self.assert_equals(actual, True, msg=msg) 305 | return True 306 | 307 | def assert_false(self, actual, msg=None): 308 | """Allow to compare and check if value it's equals to 'False'""" 309 | if not msg: 310 | msg = ASSERT_MSG_DEFAULT.format( 311 | "assert_false", actual, "") 312 | self.assert_is_instance(actual, bool) 313 | self.assert_equals(actual, False, msg=msg) 314 | return True 315 | 316 | def assert_none(self, actual, msg=None): 317 | """Allow to compare and check if value it's equals to 'None'""" 318 | if not msg: 319 | msg = ASSERT_MSG_DEFAULT.format( 320 | "assert_false", actual, "") 321 | return self.assert_equals(actual, None, msg=msg) 322 | 323 | def assert_not_none(self, actual, msg=None): 324 | """Allow to compare and check if value it's not equals to 'None'""" 325 | if not msg: 326 | msg = ASSERT_MSG_DEFAULT.format( 327 | "assert_false", actual, "") 328 | return self.assert_not_equals(actual, None, msg=msg) 329 | -------------------------------------------------------------------------------- /testlinktests/core/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Utils tasks for files operations and settings operations""" 3 | 4 | 5 | import json 6 | from os import path 7 | from sys import version_info 8 | 9 | 10 | def path_format(file_path=None, file_name=None, is_abspath=False, 11 | ignore_raises=False): 12 | """ 13 | Get path joined checking before if path and filepath exist, 14 | if not, raise an Exception 15 | if ignore_raise it's enabled, then file_path must include '/' at end lane 16 | """ 17 | path_formatted = "{}{}".format(file_path, file_name) 18 | if ignore_raises: 19 | return path_formatted 20 | if file_path is None or not path.exists(file_path): 21 | raise IOError("Path '{}' doesn't exists".format(file_path)) 22 | if file_name is None or not path.exists(path_formatted): 23 | raise IOError( 24 | "File '{}{}' doesn't exists".format(file_path, file_name)) 25 | if is_abspath: 26 | return path.abspath(path.join(file_path, file_name)) 27 | else: 28 | return path.join(file_path, file_name) 29 | 30 | 31 | def read_file(is_json=False, file_path=None, encoding='utf-8', 32 | is_encoding=True): 33 | """Returns file object from file_path, 34 | compatible with all py versiones 35 | optionals: 36 | can be use to return dict from json path 37 | can modify encoding used to obtain file 38 | """ 39 | text = None 40 | if file_path is None: 41 | raise Exception("File path received it's None") 42 | if version_info.major >= 3: 43 | if not is_encoding: 44 | encoding = None 45 | with open(file_path, encoding=encoding) as buff: 46 | text = buff.read() 47 | if version_info.major <= 2: 48 | with open(file_path) as buff: 49 | if is_encoding: 50 | text = buff.read().decode(encoding) 51 | else: 52 | text = buff.read() 53 | if is_json: 54 | return json.loads(text) 55 | return text 56 | 57 | 58 | def settings(file_path='./', file_name='settings.json', 59 | is_abspath=True): 60 | """Returns file settings as a dict to be use on qacode lib""" 61 | return read_file(is_json=True, 62 | file_path=path_format(file_path=file_path, 63 | file_name=file_name, 64 | is_abspath=is_abspath)) -------------------------------------------------------------------------------- /tests/api/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | # -*- coding: utf-8 -*- 3 | """package tests.api 4 | Created on 2018-07-06 5 | 6 | @author: netzulo 7 | """ 8 | 9 | 10 | __all__ = [] 11 | -------------------------------------------------------------------------------- /tests/api/suite_checkdevkey.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """TODO: doc module""" 3 | 4 | 5 | import pytest 6 | from qatestlink.core.exceptions.response_exception import ResponseException 7 | from qatestlink.core.testlink_manager import TLManager 8 | from testlinktests.core.test_info import TestInfoBase 9 | from testlinktests.core.utils import settings 10 | 11 | 12 | SETTINGS = settings(file_path="testlinktests/configs/") 13 | SKIP = SETTINGS['tests']['skip']['api'] 14 | SKIP_MSG = 'DISABLED by config file' 15 | 16 | 17 | class TestCheckDevKey(TestInfoBase): 18 | """TODO: doc class""" 19 | 20 | def setup_method(self, test_method, **kwargs): 21 | """TODO: doc method""" 22 | super(TestCheckDevKey, self).setup_method( 23 | test_method, **{"tlm": TLManager(config=SETTINGS)}) 24 | 25 | @pytest.mark.skipIf(SKIP, SKIP_MSG) 26 | def test_checkdevkey(self): 27 | """TestCase: test_checkdevkey 28 | Login success with valid config 29 | """ 30 | is_logged = self.tlm.api_login() 31 | self.assert_true( 32 | is_logged, msg="API_KEY it's invalid when must be valid") 33 | 34 | @pytest.mark.skipIf(SKIP, SKIP_MSG) 35 | @pytest.mark.raises(exception=ResponseException) 36 | def test_raises_checkdevkey(self): 37 | """TestCase: test_raises_checkdevkey""" 38 | with pytest.raises(ResponseException): 39 | self.tlm.api_login(dev_key='willfail') 40 | -------------------------------------------------------------------------------- /tests/api/suite_tplan_by_name.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """TODO: doc module""" 3 | 4 | 5 | import pytest 6 | from qatestlink.core.exceptions.response_exception import ResponseException 7 | from qatestlink.core.models.tl_models import TPlan 8 | from qatestlink.core.testlink_manager import TLManager 9 | from testlinktests.core.test_info import TestInfoBase 10 | from testlinktests.core.utils import settings 11 | 12 | 13 | SETTINGS = settings(file_path="testlinktests/configs/") 14 | SKIP = SETTINGS['tests']['skip']['api'] 15 | SKIP_MSG = 'DISABLED by config file' 16 | 17 | 18 | class TestTProjects(TestInfoBase): 19 | """TODO: doc class""" 20 | 21 | tproject_name = None 22 | tplan_name = None 23 | 24 | def setup_method(self, test_method, **kwargs): 25 | """TODO: doc method""" 26 | super(TestTProjects, self).setup_method( 27 | test_method, **{"tlm": TLManager(config=SETTINGS)}) 28 | # Tplan must be assigned to testproject to get tests working 29 | self.tproject_name = 'testlink-tests' 30 | self.tplan_name = 'xmlrpc' 31 | 32 | @pytest.mark.skipIf(SKIP, SKIP_MSG) 33 | def test_get_tplan_by_name(self): 34 | """TestCase: test_get_tplan_by_name 35 | At least must exist one TestPlan 36 | and assigned to TestProject 37 | 38 | Issue opened at main lib because not 39 | raise nothing if tplanname it's invalid 40 | https://github.com/netzulo/qatestlink/issues/53 41 | """ 42 | msg_error = ("If empty testlink install, create at" 43 | " least one and assign to testprojectname") 44 | tplan = self.tlm.api_tplan(self.tproject_name, self.tplan_name) 45 | self.log.debug(repr(tplan)) 46 | self.assert_is_instance(tplan, TPlan, msg=msg_error) 47 | self.assert_is_instance(tplan.id, int) 48 | self.assert_equals(tplan.name, self.tplan_name) 49 | 50 | @pytest.mark.skipIf(True, "Issue opened at qatestlink library, #52") 51 | @pytest.mark.raises(exception=ResponseException) 52 | def test_raises_tplan_by_name_baddevkey(self): 53 | """TestCase: test_raises_tplan_by_name_baddevkey""" 54 | msg_error = "With invalid key, must return ResponseException" 55 | self.tlm.api_tplan( 56 | self.tproject_name, 57 | self.tplan_name, 58 | dev_key='willfail') 59 | raise AssertionError(msg_error) 60 | -------------------------------------------------------------------------------- /tests/api/suite_tproject.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """TODO: doc module""" 3 | 4 | 5 | import pytest 6 | from qatestlink.core.models.tl_models import TProject 7 | from qatestlink.core.testlink_manager import TLManager 8 | from testlinktests.core.test_info import TestInfoBase 9 | from testlinktests.core.utils import settings 10 | 11 | 12 | SETTINGS = settings(file_path="testlinktests/configs/") 13 | SKIP = SETTINGS['tests']['skip']['api'] 14 | SKIP_MSG = 'DISABLED by config file' 15 | 16 | 17 | class TestTProject(TestInfoBase): 18 | """TODO: doc class""" 19 | 20 | def setup_method(self, test_method, **kwargs): 21 | """TODO: doc method""" 22 | super(TestTProject, self).setup_method( 23 | test_method, **{"tlm": TLManager(config=SETTINGS)}) 24 | 25 | @pytest.mark.skipIf(SKIP, SKIP_MSG) 26 | def test_get_tproject_by_name(self): 27 | """TestCase: test_get_tproject 28 | At least must exist this TestProject NAME 29 | """ 30 | msg_error = ("If empty testlink install, At least" 31 | " must exist this TestProject NAME.") 32 | tproject_name = "testlink-tests" 33 | tproject = self.tlm.api_tproject(tproject_name) 34 | self.assert_is_instance(tproject, TProject, msg=msg_error) 35 | self.assert_equals(tproject.name, tproject_name, msg=msg_error) 36 | -------------------------------------------------------------------------------- /tests/api/suite_tproject_tplans.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """TODO: doc module""" 3 | 4 | 5 | import pytest 6 | from qatestlink.core.models.tl_models import TPlan 7 | from qatestlink.core.testlink_manager import TLManager 8 | from testlinktests.core.test_info import TestInfoBase 9 | from testlinktests.core.utils import settings 10 | 11 | 12 | SETTINGS = settings(file_path="testlinktests/configs/") 13 | SKIP = SETTINGS['tests']['skip']['api'] 14 | SKIP_MSG = 'DISABLED by config file' 15 | 16 | 17 | class TestTProjectTPlans(TestInfoBase): 18 | """TODO: doc class""" 19 | 20 | def setup_method(self, test_method, **kwargs): 21 | """TODO: doc method""" 22 | super(TestTProjectTPlans, self).setup_method( 23 | test_method, **{"tlm": TLManager(config=SETTINGS)}) 24 | 25 | @pytest.mark.skipIf(SKIP, SKIP_MSG) 26 | def test_get_tproject_tplans(self): 27 | """TestCase: test_get_tprojects_minimal_one 28 | At least must exist one TPlan for this TProject 29 | """ 30 | tproject_id = 1 31 | tplans = self.tlm.api_tproject_tplans(tproject_id) 32 | self.assert_is_instance(tplans, list) 33 | self.assert_greater(len(tplans), 1) 34 | for tplan in tplans: 35 | self.log.debug(repr(tplan)) 36 | self.assert_is_instance(tplan, TPlan) 37 | -------------------------------------------------------------------------------- /tests/api/suite_tprojects.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """TODO: doc module""" 3 | 4 | 5 | import pytest 6 | from qatestlink.core.exceptions.response_exception import ResponseException 7 | from qatestlink.core.models.tl_models import TProject 8 | from qatestlink.core.testlink_manager import TLManager 9 | from testlinktests.core.test_info import TestInfoBase 10 | from testlinktests.core.utils import settings 11 | 12 | 13 | SETTINGS = settings(file_path="testlinktests/configs/") 14 | SKIP = SETTINGS['tests']['skip']['api'] 15 | SKIP_MSG = 'DISABLED by config file' 16 | 17 | 18 | class TestTProjects(TestInfoBase): 19 | """TODO: doc class""" 20 | 21 | def setup_method(self, test_method, **kwargs): 22 | """TODO: doc method""" 23 | super(TestTProjects, self).setup_method( 24 | test_method, **{"tlm": TLManager(config=SETTINGS)}) 25 | 26 | @pytest.mark.skipIf(SKIP, SKIP_MSG) 27 | def test_get_tprojects(self): 28 | """TestCase: test_get_tprojects 29 | At least must exist one TestProject 30 | """ 31 | msg_error = "If empty testlink install, create at least one." 32 | tprojects = self.tlm.api_tprojects() 33 | self.assert_greater(len(tprojects), 1, msg=msg_error) 34 | for tproject in tprojects: 35 | self.log.debug(repr(tproject)) 36 | self.assert_is_instance(tproject, TProject) 37 | 38 | @pytest.mark.skipIf(True, "Issue oneped at qatestlink library, #52") 39 | @pytest.mark.raises(exception=ResponseException) 40 | def test_raises_tprojects_baddevkey(self): 41 | """TestCase: test_raises_tprojects_baddevkey""" 42 | msg_error = "With invalid key, must return ResponseException" 43 | self.tlm.api_tprojects(dev_key='willfail') 44 | raise AssertionError(msg_error) 45 | -------------------------------------------------------------------------------- /tests/web/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """package tests.web 3 | Created on 2019-10-27 4 | 5 | @author: netzulo 6 | """ 7 | 8 | 9 | __all__ = [] 10 | -------------------------------------------------------------------------------- /tests/web/suite_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """package tests.web.suite_login 3 | Created on 2019-10-27 4 | 5 | @author: netzulo 6 | """ 7 | 8 | 9 | import pytest 10 | from qacode.core.testing.test_info import TestInfoBotUnique 11 | from qautils.files import settings 12 | from testlinktests.core.pages.page_index import PageIndex 13 | from testlinktests.core.pages.page_login import PageLogin 14 | from testlinktests.core.specs import req_information 15 | 16 | 17 | SETTINGS = settings(file_path="testlinktests/configs/") 18 | SKIP = SETTINGS['tests']['skip']['index'] 19 | SKIP_MSG = 'DISABLED by config file' 20 | 21 | 22 | class TestPageIndex(TestInfoBotUnique): 23 | """TODO: doc class""" 24 | 25 | @classmethod 26 | def setup_class(cls, **kwargs): 27 | """Setup class (suite) to be executed""" 28 | super(TestPageIndex, cls).setup_class( 29 | config=SETTINGS, skip_force=SKIP) 30 | cls.add_property('app', cls.cfg_app('testlink')) 31 | cls.add_property('cfg_login', cls.cfg_page('login')) 32 | cls.add_property('cfg', cls.cfg_page('index')) 33 | # suite preconditions 34 | try: 35 | _page = PageLogin(cls.bot, **cls.cfg_login.copy()) 36 | req_information.url("login", _page) 37 | creeds = cls.app["data"] 38 | _page.login(creeds["usr"], creeds["pwd"]) 39 | except Exception as err: 40 | cls.log.error("Bot Fails at precondition: %s", err.message) 41 | 42 | def setup_method(self, test_method): 43 | """Configure self.attribute""" 44 | super(TestPageIndex, self).setup_method( 45 | test_method, config=SETTINGS) 46 | # testcase preconditions 47 | try: 48 | self.page = PageIndex(self.bot, **self.cfg.copy()) 49 | except Exception as err: 50 | self.log.error("Bot Fails at precondition: %s", err.message) 51 | 52 | @pytest.mark.skipIf(SKIP, SKIP_MSG) 53 | @pytest.mark.dependency(name="loads") 54 | def test_index_loads(self): 55 | """Testcase: test_index_loads""" 56 | try: 57 | req_information.url("index", self.page) 58 | self.assert_true(self.page.is_url()) 59 | except AssertionError as err: 60 | self.log.error("Bot Fails at assert %s", err.message) 61 | -------------------------------------------------------------------------------- /tests/web/suite_login.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """package tests.web.suite_login 3 | Created on 2019-10-27 4 | 5 | @author: netzulo 6 | """ 7 | 8 | 9 | import pytest 10 | from qacode.core.testing.test_info import TestInfoBotUnique 11 | from qautils.files import settings 12 | from testlinktests.core.pages.page_login import PageLogin 13 | from testlinktests.core.specs import req_information 14 | 15 | 16 | SETTINGS = settings(file_path="testlinktests/configs/") 17 | SKIP = SETTINGS['tests']['skip']['login'] 18 | SKIP_MSG = 'DISABLED by config file' 19 | 20 | 21 | class TestPageLogin(TestInfoBotUnique): 22 | """TODO: doc class""" 23 | 24 | @classmethod 25 | def setup_class(cls, **kwargs): 26 | """Setup class (suite) to be executed""" 27 | super(TestPageLogin, cls).setup_class( 28 | config=SETTINGS, skip_force=SKIP) 29 | cls.add_property('app', cls.cfg_app('testlink')) 30 | cls.add_property('cfg', cls.cfg_page('login')) 31 | 32 | def setup_method(self, test_method): 33 | """Configure self.attribute""" 34 | super(TestPageLogin, self).setup_method( 35 | test_method, config=SETTINGS) 36 | # testcase preconditions 37 | try: 38 | self.page = PageLogin(self.bot, **self.cfg.copy()) 39 | except Exception as err: 40 | self.log.error("Bot Fails at precondition: %s", err.message) 41 | 42 | @pytest.mark.skipIf(SKIP, SKIP_MSG) 43 | @pytest.mark.dependency(name="loads") 44 | def test_login_loads(self): 45 | """Testcase: test_login_loads""" 46 | try: 47 | req_information.url("login", self.page) 48 | self.assert_true(self.page.is_url()) 49 | except AssertionError as err: 50 | self.log.error("Bot Fails at assert %s", err.message) 51 | 52 | @pytest.mark.skipIf(SKIP, SKIP_MSG) 53 | @pytest.mark.dependency(depends=["loads"]) 54 | @pytest.mark.parametrize('username,password', [ 55 | ('admin', 'admin'), 56 | ]) 57 | def test_login_ok(self, username, password): 58 | """Testcase: test_login_ok""" 59 | try: 60 | req_information.url("login", self.page) 61 | is_logged = self.page.login(username, password) 62 | self.assert_true(is_logged) 63 | except AssertionError as err: 64 | self.log.error("Bot Fails at assert %s", err.message) 65 | 66 | @pytest.mark.skipIf(SKIP, SKIP_MSG) 67 | @pytest.mark.dependency(depends=["loads"]) 68 | @pytest.mark.parametrize('username,password', [ 69 | ('notexist', 'notexist'), 70 | ('admin', ''), 71 | ('', 'admin'), 72 | ('|@#~€¬[]{}.-´ç`+', '|@#~€¬[]{}.-´ç`+'), 73 | ]) 74 | def test_login_ko(self, username, password): 75 | """Testcase: test_login_ko""" 76 | try: 77 | req_information.url("login", self.page) 78 | is_logged = self.page.login(username, password) 79 | self.assert_false(is_logged) 80 | except AssertionError as err: 81 | self.log.error("Bot Fails at assert %s", err.message) 82 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27,py34,py35,py36,flake8,docs 3 | deps = 4 | qacode==0.6.2 5 | [testenv:py27] 6 | commands = python setup.py test 7 | [testenv:py34] 8 | commands = python setup.py test 9 | [testenv:py35] 10 | commands = python setup.py test 11 | [testenv:py36] 12 | commands = python setup.py test 13 | [docs] 14 | skip_install = true 15 | commands = cd docs && make clean && make html 16 | deps = 17 | Sphinx 18 | 19 | [testenv:flake8] 20 | skip_install = true 21 | max-complexity = 10 22 | commands = flake8 --ignore=D400,D205,I201,N812,D401,D413,D208 testlink-tests/ tests/ 23 | deps = 24 | flake8 25 | flake8-docstrings>=0.2.7 26 | flake8-import-order>=0.9 27 | pep8-naming 28 | flake8-colors 29 | exclude = 30 | .git, 31 | __pycache__, 32 | docs/source/conf.py, 33 | old, 34 | build, 35 | dist, 36 | logs, 37 | .vscode, 38 | .eggs, 39 | .cache 40 | 41 | [testenv:coverage] 42 | description = Environment to generate coverage reports 43 | commands = 44 | py.test --cov=testlink-tests tests/ --cov-report html:tests/reports/coverage/ --cov-report xml:tests/reports/coverage.xml --cov-report term 45 | deps = 46 | pytest 47 | pytest-html 48 | pytest-dependency 49 | coverage==4.3.4 50 | pytest-cov==2.5.0 51 | pytest-benchmark 52 | pytest-benchmark[histogram] 53 | 54 | exclude = 55 | .git, 56 | __pycache__, 57 | docs/source/conf.py, 58 | old, 59 | build, 60 | dist, 61 | logs, 62 | .vscode, 63 | .eggs, 64 | .cache 65 | --------------------------------------------------------------------------------