diff --git a/.env b/.env
new file mode 100644
index 0000000..f465cdd
--- /dev/null
+++ b/.env
@@ -0,0 +1,3 @@
+PYTHONPATH="./src"
+TESTLINK_API_PYTHON_SERVER_URL="http://localhost:8085/lib/api/xmlrpc/v1/xmlrpc.php"
+TESTLINK_API_PYTHON_DEVKEY="48072c25257af9f477a22c97a3858337"
diff --git a/.gitignore b/.gitignore
index 1a76690..e0ff092 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ build/
*.pyc
dist/
MANIFEST
+.pytest_cache/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 35e7301..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-sudo: false
-language: python
-python:
- - "2.6"
- - "2.7"
- - "3.3"
- - "3.4"
-
- # command to install dependencies
-install:
- - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi
- - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install argparse; fi
- - pip install .
-
-# command to run tests
-# online tests uses TL connection, defined in
-# TESTLINK_API_PYTHON_DEVKEY and TESTLINK_API_PYTHON_SERVER_URL
-# see https://travis-ci.org/USER/TestLink-API-Python-client/settings/env_vars
-# suggestion: use TL demo project with user pyTLapi, see tox.ini
-script:
- - py.test test/utest-offline
- - if [[ $TESTLINK_API_PYTHON_SERVER_URL ]]; then py.test test/utest-online; fi
-# see known problem: countTestCasesTS should handle the sufficient right errors #62
-# - if [[ $TESTLINK_API_PYTHON_SERVER_URL && $TRAVIS_PYTHON_VERSION == '3.4' ]]; then python example/TestLinkExample.py; fi
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..0bac8a5
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,11 @@
+{
+ "python.autoComplete.extraPaths": [
+ "${workspaceRoot}/src"
+ ],
+ "python.testing.pytestArgs": [
+ "test"
+ ],
+ "python.testing.unittestEnabled": false,
+ "python.testing.pytestEnabled": true
+
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..0fafc44
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,41 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "echo",
+ "type": "shell",
+ "command": "echo Hello"
+ },
+ {
+ "label": "test Testlink Api sample",
+ "type": "process",
+ "detail": "Run sample Testlink Api Python Client communication",
+ "command": "${config:python.defaultInterpreterPath}",
+ "args": [ "${workspaceFolder}/example/${input:apiSample}.py" ],
+ "group": "test",
+ "presentation": {
+ "clear": true,
+ "panel": "dedicated"
+ },
+ "problemMatcher": ["$python"],
+ "options": {
+ "cwd": "${workspaceFolder}",
+ "env": { "PYTHONPATH" : "./src",
+ "TESTLINK_API_PYTHON_SERVER_URL" : "http://localhost:8085/lib/api/xmlrpc/v1/xmlrpc.php",
+ "TESTLINK_API_PYTHON_DEVKEY" : "48072c25257af9f477a22c97a3858337"
+ }
+ }
+ }
+ ],
+ "inputs": [
+ {
+ "type": "pickString",
+ "id": "apiSample",
+ "description": "Which TL API sample to run ?",
+ "options": ["TestLinkExample", "TestLinkExampleGenericApi","TestLinkExample_CF_KW","TestLinkExampleGenericApi_Req"],
+ "default": "TestLinkExample"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/CHANGES.rst b/CHANGES.rst
index ec05c8e..c6453ef 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,15 +1,243 @@
Changes in TestLink-API-Python-client Source Distribution
=========================================================
-TestLink-API-Python-client v0.6.2 release notes v0.6.2 (Oct. 2015)
--------------------------------------------------------------------
+TestLink-API-Python-client v0.8.2 (under develop)
+------------------------------------------------
+support for TL 1.9.20_fixed changes and py39
+
+main topic is to support TL 1.9.20_fixed api changes
+
+implement 1.9.20_fixed new api interfaces - #141
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+new TestlinkAPIGeneric and TestlinkAPIClient api methods
+
+- createUser(, , , , [password=])
+- setUserRoleOnProject(, , )
+
+new TestlinkAPIClient service methods
+
+- ensureUserExist(, [firstname=], [lastname=],
+ [email=, [password=])
+- ensureUserExistWithProjectRole(, , ,
+ [firstname=], [lastname=], [email=, [password=])
+
+implement 1.9.20_fixed changed api interfaces - #139
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+changed TestlinkAPIGeneric and TestlinkAPIClient api methods
+
+- createPlatform() is adapted to support new optional boolean arguments
+ and
+ - When they are not set to , assignTestCaseExecutionTask() might fail
+ with an error like
+ - TLResponseError: 3041: Test plan (name:TestPlan_API A) has no platforms linked
+
+TestLink-API-Python-client v0.8.1-fix131 (Mar. 2020)
+------------------------------------------------------------
+
+fix missing supported API 1.9.17 interfaces
+- Pull request #131 by heuy - add closeBuild api function
+
+TestLink-API-Python-client v0.8.1 (Aug. 2019)
+------------------------------------------------------------
+support for TL 1.9.20 (dev) release and py27, py36, py37
+
+main topic is to support TL 1.9.19 and TL 1.9.20 (dev) api changes related to
+test case attachments, which are stored since TL 1.9.19 with a reference to
+the test case version instead the test case id .
+
+Parameter is now mandatory for _uploadTestCaseAttachment_ and optional
+for _getTestCaseAttachments_.
+
+implement other 1.9.20 (dev) changed api interfaces - #122
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+changed TestlinkAPIGeneric and TestlinkAPIClient api methods
+
+- getTestCasesForTestPlan() is adapted to support the new optional argument .
+ Sample see ``_
+
+known TL 1.9.19 issue:
+~~~~~~~~~~~~~~~~~~~~~~~
+API-XMLRPC - getTestCaseAttachments returns no attachment, uploaded with uploadTestCaseAttachment
+
+- see TL Mantis Ticket 8658 `_
+- recommended to use the TL 1.9.20 development state with github commit
+ 6a4984164 or later (even for the TL upgrade or installation).
+
+known TL 1.9.20 development state issue (github commit a1c7aca97):
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Calling getTestCasesForTestPlan() with the new optional argument returns only internal db values.
+As alternative, loop over all returned test cases and call getTestCaseCustomFieldDesignValue().
+Sample see ``_
+
+TestLink-API-Python-client v0.8.0 (May. 2018)
+---------------------------------------------
+support for TL 1.9.17 release and py27, py36
+
+
+implement 1.9.17 new api interfaces - #76, #81, #82, #83, #101
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- getExecutionSet(, [testcaseid=], [testcaseexternalid=],
+ [buildid=], [buildname=], [platformid=],
+ [platformname=], [options=], [devKey=])
+- getRequirements(, [testplanid=], [platformid=], [devKey=])
+- getReqCoverage(, , [devKey=])
+- setTestCaseTestSuite(, , [devKey=])
+- getTestSuiteAttachments(, [devKey=])
+- getAllExecutionsResults(, [testcaseid=],
+ [testcaseexternalid=], [platformid=],
+ [buildid=], [options=])
+
+TestReporter and other improvements pull request #94
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+pull request by Brian-Williams:
+
+- TestReporter's subclasses can be used to idempotently generate required
+ components of testlink before sending the report to a specified testcase.
+- TestGenReporter is a default combination of all the TestReporter's subclasses
+ and will try to generate everything it's subclasses is capable of.
+- Added TestLinkHelper._setParams to simplify libraries that need to overwrite
+ how a helper aquires it's parameters
+- Added TestlinkAPIClient.getTestCaseByVersion to expose the common need of
+ gettting the latest testcase by default. It was already used, but not a
+ function in this class and has uses elsewhere.
+
+``_ includes sample, how to use
+TestGenReporter and TestlinkAPIClient.getTestCaseByVersion
+
+Self signed / Let's Encrypt SSL certificate support #90
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+TestlinkAPIGeneric and TestlinkAPIClient accepts know the additional
+xmlrpclib.ServerProxy() arguments *use_datetime* and *context* (new with Py2.7.9)
+- https://docs.python.org/2/library/xmlrpclib.html
+
+*context* allows to define a SSL context, which can holds various data
+longer-lived than single SSL connections, such as SSL configuration options,
+certificate(s) and private key(s). (new with Py2.7.9)
+- https://docs.python.org/2/library/ssl.html#ssl-contexts
+
+TestLinkHelper will set the *unverified_context()* if the server url starts with
+*https* and no *context* is defined, calling
+- TestLinkHelper().connect(TestlinkAPIClient)
+
+
+known TL 1.9.17 issues:
+~~~~~~~~~~~~~~~~~~~~~~~
+API-XMLRPC - new 1.9.17 xmlrpc.class function getAllExecutionsResults not callable via XMLRPC api
+
+- see `TL Mantis Ticket 8259 `_
+
+fixed TL 1.9.17-DEV issues:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+API-XMLRPC - getRequirements raise PHP Fatal error (github commit 0c8feb6)
+
+- see `TL Mantis Ticket 7902 `_
+
+API-XMLRPC - getExecutionSet raise database error (github commit 1ee5f78)
+
+- see `TL Mantis Ticket 7900 `_
+
+TestLink-API-Python-client release notes v0.6.4 (Mar. 2017)
+-----------------------------------------------------------
+support for TL 1.9.16 release and py27, py34, py35 and py36
+
+implement 1.9.16 new api interfaces - #80
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+new TestlinkAPIGeneric and TestlinkAPIClient api method
+
+- updateBuildCustomFieldsValues(, , ,
+ , [devKey=])
+
+example ``_ shows, how to set and get
+customer field values
+
+TestLink-API-Python-client release notes v0.6.3 (Nov. 2016)
+-----------------------------------------------------------
+support for TL 1.9.15 release and py26, py27, py33, py34 and py35
+
+- further releases will be developed only against py27, py34 and py35
+- If there is a need to support other py versions, please give feedback
+
+implement 1.9.15 new api interfaces - #54 #67 #69
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+new TestlinkAPIGeneric and TestlinkAPIClient api methods
+
+- updateTestSuite(, [testprojectid=],
+ [prefix=], [parentid=], [testsuitename=],
+ [details=], [order=], [devKey=])
+- getTestSuite(, , [devKey=])
+- getIssueTrackerSystem(, [devKey=])
+
+implement 1.9.15 changed api interfaces - #68 #70 #72 #71 #69
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+changed TestlinkAPIGeneric and TestlinkAPIClient api methods
+
+- reportTCResult() is adapted to support the new optional argument
+ for setting test step results
+- createBuild() is adapted to support new optional arguments
+
+ - : 1 (default) = activ 0 = inactiv
+ - : 1 (default) = open 1 = closed
+ - : YYYY-MM-DD
+ - : valid buildid tester assignments will be copied.
+
+- addTestCaseToTestPlan() is adapted to to support the new optional argument
+ to update linked Test Case Versions
+- createTestCase() is adapted to to support the new optional arguments
+ and
+- createTestProject() is adapted to to support the new optional arguments
+ and to link a project with an ITS
+
+examples:
+
+ >>> tls = testlink.TestLinkHelper().connect(testlink.TestlinkAPIClient)
+ >>> tls.reportTCResult(None, 'aTPlanID', 'aBuildName', 'f', 'result note',
+ >>> testcaseexternalid='aTCaseFullExID', overwrite=True,
+ >>> platformname='Small Birds', execduration=4.1,
+ >>> timestamp='2015-09-19 14:33:02',
+ >>> steps=[{'step_number' : 3, 'result' : 'p', 'notes' : 'a exec note3'},
+ >>> {'step_number' : 4, 'result' : 'f', 'notes' : 'a exec note4'}])
+ >>> tls.createBuild(aTPlanID, 'newBuildName', 'a build note',
+ >>> active=1, open=1, releasedate='2016-11-30'
+ >>> copytestersfrombuild=existingBuildID)
+ >>> tls.addTestCaseToTestPlan(aTProjectID, aTPlanID, 'aTCaseFullExID',
+ >>> aTCVersion, overwrite=1)
+
+
+known TL 1.9.15 issues:
+~~~~~~~~~~~~~~~~~~~~~~~
+
+changing test suite order with updateTestSuite raise internal server error
+
+- same reason as `TL Mantis Ticket 7696 `_
+- solution: change *testlink-1.9.15/lib/functions/testsuite.class.php - update* as
+ descriped in `TL GitHub Commit 1fa41e7 `_
+
+TestLink web presents no login page (internal server error)
+
+- see `TL Mantis Ticket 7708 `_
+- solution: change *testlink-1.9.15/lib/functions/common.php* as described in `TL GitHub Commit db74644 `_
+
+Test projects with execution step results can not be deleted
+- details and solution see `TL Mantis Ticket 7765 `_
+
+TestLink-API-Python-client v0.6.2 release notes v0.6.2 (Oct. 2015)
+------------------------------------------------------------------
support for TL 1.9.14 release and py26, py27, py33 and py34
- further releases will be developed only against py27 and py34.
- If there is a need to support other py versions, please give feedback
implement 1.9.14 new api interfaces - #53 #61
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
new TestlinkAPIGeneric and TestlinkAPIClient api methods
@@ -24,7 +252,7 @@ example ``_ shows, how to set and get
customer field values
implement 1.9.14 changed api interfaces - #48 #49 #54 #59
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
changed TestlinkAPIGeneric and TestlinkAPIClient api methods
@@ -61,7 +289,7 @@ a different error code
- 1.9.13 error code *5000* - 1.9.14 error code *5040*
Bugfixes TestLink-API-Python-client v0.6.1 - #51 #55 #56 #45
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
whatArgs reports incorrect arg name for createTestCase
@@ -87,11 +315,11 @@ Known TL 1.9.14 limitations:
- 7282 updateTestSuiteCustomFieldDesignValue() does change customer field values
-TestLink-API-Python-client release notes v0.6.1 (Mar. 2015)
-------------------------------------------------------------
+TestLink-API-Python-client release notes v0.6.1 (Mar. 2015)
+-----------------------------------------------------------
support for TL 1.9.13 release
-Proxy configuration support in TestLinkHelper - pull request #36
+Proxy configuration support in TestLinkHelper - pull request #36
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Integrates `Maberi `_
@@ -102,7 +330,7 @@ pull request `#36 `
- Recognizes "http_proxy" environment variable.
implement 1.9.13 new api methods #32 #41 #42 #44 #47 #46
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
new TestlinkAPIGeneric and TestlinkAPIClient api methods
@@ -130,7 +358,7 @@ new TestlinkAPIGeneric and TestlinkAPIClient api methods
examples see ``_ and ``_
implement 1.9.13 api change - getTestCasesForTestPlan #41
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TestlinkAPIGeneric and TestlinkAPIClient api method getTestCasesForTestPlan()
accepts now the additional optional argument platformid=
@@ -144,13 +372,13 @@ example:
Also the optional argument buildid= could now be used
-TestLink-API-Python-client release notes v0.6.0 (Dec. 2014)
-------------------------------------------------------------
+TestLink-API-Python-client release notes v0.6.0 (Dec. 2014)
+-----------------------------------------------------------
support for TestLink release 1.9.12 and py26, py27, py33 and py34
python 3 support - pull requests #33 #37
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Integrates `manojklm `_
pull requests
@@ -209,7 +437,7 @@ new TestlinkAPIGeneric and TestlinkAPIClient api method
examples see ``_
-TestLink-API-Python-client release notes v0.5.1 (Aug. 2014)
+TestLink-API-Python-client release notes v0.5.1 (Aug. 2014)
-----------------------------------------------------------
support for TestLink release 1.9.11
@@ -239,7 +467,7 @@ new TestlinkAPIGeneric and TestlinkAPIClient api method
examples see ``_
-TestLink-API-Python-client release notes v0.5.0 (Jul. 2014)
+TestLink-API-Python-client release notes v0.5.0 (Jul. 2014)
-----------------------------------------------------------
support for TestLink release 1.9.10
@@ -439,7 +667,7 @@ Other API methods can be used with the new method
- callServerWithPosArgs(apiMethodame, [apiArgName=apiArgValue])
-generic api class TestlinkAPIGeneric #7
+generic api class TestlinkAPIGeneric #7
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
new class TestlinkAPIGeneric implements the Testlink API methods as generic PY methods
@@ -470,20 +698,20 @@ helper method .whatArgs(apiMethodName) #8
The Teslink API Client can now be asked, what arguments a API method expects::
- import testlink
- tlh = testlink.TestLinkHelper()
- tls = tlh.connect(testlink.TestlinkAPIClient)
- print tls.whatArgs('createTestPlan')
- createTestPlan(, , [note=], [active=], [public=], [devKey=])
- create a test plan
+ import testlink
+ tlh = testlink.TestLinkHelper()
+ tls = tlh.connect(testlink.TestlinkAPIClient)
+ print tls.whatArgs('createTestPlan')
+ createTestPlan(, , [note=], [active=], [public=], [devKey=])
+ create a test plan
or for a description of all implemented api method ::
- import testlink
- tlh = testlink.TestLinkHelper()
- tls = tlh.connect(testlink.TestlinkAPIClient)
- for m in testlink.testlinkargs._apiMethodsArgs.keys():
- print tls.whatArgs(m), '\n'
+ import testlink
+ tlh = testlink.TestLinkHelper()
+ tls = tlh.connect(testlink.TestlinkAPIClient)
+ for m in testlink.testlinkargs._apiMethodsArgs.keys():
+ print tls.whatArgs(m), '\n'
other changes
~~~~~~~~~~~~~
diff --git a/README.rst b/README.rst
index 0c6a215..d8f5856 100644
--- a/README.rst
+++ b/README.rst
@@ -1,17 +1,11 @@
TestLink API Python Client
==========================
-Copyright 2011-2015
+Copyright 2011-2022
James Stock, Olivier Renault, Luiko Czub, TestLink-API-Python-client developers
License `Apache License 2.0`_
-.. image:: https://travis-ci.org/lczub/TestLink-API-Python-client.svg?branch=master
- :target: https://travis-ci.org/lczub/TestLink-API-Python-client
-
-.. contents::
- :local:
-
Introduction
------------
@@ -66,7 +60,7 @@ src/
Source for TestLink API Python Client
doc/
- `Installation`_ and `Usage`_ documentation
+ `Installation`_ and `Usage (EN)`_ / `Usage (FR)`_ documentation
examples/
Examples, how to use `TestlinkAPIGeneric`_ and `TestlinkAPIClient`_.
@@ -93,7 +87,8 @@ TestLink-API-Python-client developers
-------------------------------------
* `James Stock`_, `Olivier Renault`_, `lczub`_, `manojklm`_ (PY3)
* `g4l4drim`_, `pade`_, `anton-matosov`_, `citizen-stig`_, `charz`_, `Maberi`_
-* anyone forgotten?
+* `Brian-Williams`_, `alexei-drozdov`_, `janLo`_, `heuj`_, `elapfra`_
+* `Mikycid`_, anyone forgotten?
.. _Apache License 2.0: http://www.apache.org/licenses/LICENSE-2.0
.. _TestLink: http://testlink.org
@@ -101,7 +96,8 @@ TestLink-API-Python-client developers
.. _Robot Framework: http://code.google.com/p/robotframework
.. _Jenkins: http://jenkins-ci.org
.. _Installation: doc/install.rst
-.. _Usage: doc/usage.rst
+.. _Usage (EN): doc/usage.rst
+.. _Usage (FR): doc/fr_usage.rst
.. _TestlinkAPIGeneric: example/TestLinkExampleGenericApi.py
.. _TestlinkAPIClient: example/TestLinkExample.py
.. _tox.ini: tox.ini
@@ -116,4 +112,10 @@ TestLink-API-Python-client developers
.. _citizen-stig: https://github.com/citizen-stig/TestLink-API-Python-client
.. _charz: https://github.com/charz/TestLink-API-Python-client.git
.. _manojklm: https://github.com/manojklm/TestLink-API-Python-client
-.. _Maberi: https://github.com/Maberi/TestLink-API-Python-client.git
+.. _Maberi: https://github.com/Maberi/TestLink-API-Python-client
+.. _Brian-Williams: https://github.com/Brian-Williams/TestLink-API-Python-client
+.. _alexei-drozdov: https://github.com/alexei-drozdov/TestLink-API-Python-client
+.. _janLo: https://github.com/janLo/TestLink-API-Python-client
+.. _heuj: https://github.com/heuj/TestLink-API-Python-client
+.. _elapfra: https://github.com/elapfra/TestLink-API-Python-client
+.. _Mikycid: https://github.com/Mikycid/TestLink-API-Python-client
\ No newline at end of file
diff --git a/doc/fr_usage.rst b/doc/fr_usage.rst
new file mode 100644
index 0000000..366e94a
--- /dev/null
+++ b/doc/fr_usage.rst
@@ -0,0 +1,229 @@
+TestLink-API-Python-client Usage
+================================
+
+.. contents::
+ :local:
+
+Comment communiquer avec testlink dans une interface système python
+-------------------------------------------
+
+Se connecter à TestLink, compter les projets existants et récupérer les données d'un cas de test: ::
+
+ [PYENV]\testlink\Scripts\activate
+ set TESTLINK_API_PYTHON_SERVER_URL=http://[YOURSERVER]/testlink/lib/api/xmlrpc/v1/xmlrpc.php
+ set TESTLINK_API_PYTHON_DEVKEY=[Users devKey generated by TestLink]
+ python
+ >>> import testlink
+ >>> tls = testlink.TestLinkHelper().connect(testlink.TestlinkAPIClient)
+ >>> tls.countProjects()
+ 3
+ >>> tls.getTestCase(None, testcaseexternalid='NPROAPI3-1')
+ [{'full_tc_external_id': 'NPROAPI3-1', 'node_order': '0', 'is_open': '1', 'id': '2757', ...}]
+
+Demander au TestLink API Client quels arguments attend une des méthodes de l'API: ::
+
+ import testlink
+ tlh = testlink.TestLinkHelper()
+ tls = tlh.connect(testlink.TestlinkAPIClient)
+ print tls.whatArgs('createTestPlan')
+ > createTestPlan(, , [note=], [active=],
+ [public=], [devKey=])
+ > create a test plan
+
+Générer une description de toutes les méthodes implémentées par l'API: ::
+
+ import testlink
+ tlh = testlink.TestLinkHelper()
+ tls = tlh.connect(testlink.TestlinkAPIClient)
+ for m in testlink.testlinkargs._apiMethodsArgs.keys():
+ print(tls.whatArgs(m), '\n')
+
+Copier les cas de test
+---------------
+
+Copier un cas de test dans une autre suite en changeant son nom::
+
+ >>> import testlink
+ >>> tls = testlink.TestLinkHelper().connect(testlink.TestlinkAPIClient)
+ >>> tc_info = tls.getTestCase(None, testcaseexternalid='NPROAPI-3')
+ [{'full_tc_external_id': 'NPROAPI-3', ..., 'id': '5440', 'version': '2',
+ 'testsuite_id': '5415', 'tc_external_id': '3','testcase_id': '5425', ...}]
+ >>> tls.copyTCnewTestCase(tc_info[0]['testcase_id'], testsuiteid=newSuiteID,
+ testcasename='a new test case name')
+
+Créer une nouvelle version d'un cas de test en changeant sa description et son importance::
+
+ >>> import testlink
+ >>> tls = testlink.TestLinkHelper().connect(testlink.TestlinkAPIClient)
+ >>> tc_info = tls.getTestCase(None, testcaseexternalid='NPROAPI-3')
+ [{'full_tc_external_id': 'NPROAPI-3', ..., 'id': '5440', 'version': '2',
+ 'testsuite_id': '5415', 'tc_external_id': '3','testcase_id': '5425', ...}]
+ >>> tls.copyTCnewVersion(tc_info[0]['testcase_id'], summary='new summary',
+ importance='1')
+
+
+Par défaut, la dernière version d'un cas de test sera utilisée pour la copie.
+Si une autre version doit être copiée, il est possible de spécifier la version
+attendue en tant que deuxième argument. Example::
+
+ >>> tls.copyTCnewTestCase(tc_info[0]['testcase_id'], 1, testsuiteid=newSuiteID,
+ testcasename='a new test case name')
+ >>> tls.copyTCnewVersion(tc_info[0]['testcase_id'], 1, summary='new summary',
+ importance='1')
+
+Rapporter les résultats du test
+-------------------
+
+En utilisant la classe TestlinkAPIClient - exemple d'un cas de test échoué
+sans auteur (l'argument 'user' n'est utilisable qu'à partir d'une version
+de TestLink de 1.9.10 ou supérieure):
+
+ >>> import testlink
+ >>> tls = testlink.TestLinkHelper().connect(testlink.TestlinkAPIClient)
+ >>> tls.reportTCResult(a_TestCaseID, a_TestPlanID, 'a build name', 'f',
+ 'some notes',
+ user='a user login name', platformid=a_platformID)
+
+
+En utilisant la classe TestlinkAPIGeneric - exemple d'un cas de test passé
+en utilisant un auteur (argument 'user'):
+
+ >>> import testlink
+ >>> tls = testlink.TestLinkHelper().connect(testlink.TestlinkAPIGeneric)
+ >>> tls.reportTCResult(a_TestPlanID, 'p', testcaseid=a_TestCaseID,
+ buildname='a build name', notes='some notes',
+ user='a login name', platformid=a_platformID)
+
+
+En utilisant la classe TestlinkAPIGeneric - exemple d'un cas de test bloqué
+sans auteur
+
+ >>> import testlink
+ >>> tls = testlink.TestLinkHelper().connect(testlink.TestlinkAPIGeneric)
+ >>> exTCID = tls.getTestCase(testcaseid=a_TestCaseID)[0]['full_tc_external_id']
+ >>> tls.reportTCResult(a_TestPlanID, 'b', testcaseexternalid=exTCID,
+ buildid='a build name', platformname='a platform name')
+
+Rapport de résultats de tests avec horodatage et résultat des étapes
+--------------------------------------------------
+
+Ce résultat de test utilise son id externe (testcaseexternalid), et non l'id interne (testcaseid)
+
+- Les arguments 'execduration' et 'timestamp' requièrent une version de
+ TestLink de 1.9.14 ou supérieure
+- L'argument 'steps' requiert une version de TestLink de 1.9.15 ou supérieure
+
+ >>> import testlink
+ >>> tls = testlink.TestLinkHelper().connect(testlink.TestlinkAPIClient)
+ >>> tls.reportTCResult(None, newTestPlanID_A, None, 'f', '', guess=True,
+ testcaseexternalid=tc_aa_full_ext_id, platformname=NEWPLATFORM_A,
+ execduration=3.9, timestamp='2015-09-18 14:33',
+ steps=[{'step_number' : 6, 'result' : 'p', 'notes' : 'result note for passed step 6'},
+ {'step_number' : 7, 'result' : 'f', 'notes' : 'result note for failed step 7'}] )
+
+Envoyer des pièces jointes
+------------------
+
+Télécharger des pièces jointes peut être fait de deux différentes manières:
+
+Avec un descripteur de fichier :
+
+ a_file_obj=open(CHEMIN_VALIDE_VERS_LE_FICHIER)
+ newAttachment = myTestLink.uploadExecutionAttachment(a_file_obj, A_Result_ID,
+ 'Attachment Title', 'Attachment Description')
+
+
+Ou avec un chemin de fichier :
+
+ a_file_path=A_VALID_FILE_PATH
+ newAttachment = myTestLink.uploadExecutionAttachment(CHEMIN_VALIDE_VERS_LE_FICHIER, A_Result_ID,
+ 'Attachment Title', 'Attachment Description')
+
+Lister les mots-clés
+-------------
+
+En utilisant une méthode de l'API (classe TestlinkAPIGeneric) -
+Lister les mots-clés de tous les cas de test d'une suite:
+
+ >>> import testlink
+ >>> tls = testlink.TestLinkHelper().connect(testlink.TestlinkAPIClient)
+ >>> ts_kw = tls.getTestCasesForTestSuite(SuiteID, False, 'full', getkeywords=True)
+
+
+En utilisant une méthode de l'API (classe TestlinkAPIGeneric) -
+Lister tous les mots clés d'une suite de test et ses sous-suites
+
+ >>> ts_kw = tls.getTestCasesForTestSuite(SuiteID, True, 'full', getkeywords=True)
+
+En utilisant une méthode du service (classe TestlinkAPIClient) -
+Lister tous les mots clés sans ses détails pour un cas de test
+
+ >>> tc_kw = tls.listKeywordsForTC(5440)
+ >>> tc_kw = tls.listKeywordsForTC('NPROAPI-3')
+
+En utilisant une méthode du service (classe TestlinkAPIClient) -
+Lister tous les mots clés sans ses détails pour tous les cas de test d'une suite
+
+ >>> ts_kw = tls.listKeywordsForTS('5415')
+
+
+Lancement d'un exemple
+------------
+
+Pour lancer l'exemple "comment utiliser la classe TestlinkAPIClient", en
+spécifiant les paramètres de connexion en tant qu'arguments de ligne de commande [1]_: ::
+
+ [PYENV]\testlink\Scripts\activate
+ python example\TestLinkExample.py
+ --server_url http://[YOURSERVER]/testlink/lib/api/xmlrpc.php
+ --devKey [Users devKey generated by TestLink]
+
+Pour lancer l'exemple "comment utiliser la classe TestlinkAPIGeneric", en
+spécifiant les paramètres de connexion en tant que variable d'environment [2]_: ::
+
+ [PYENV]\testlink\Scripts\activate
+ set TESTLINK_API_PYTHON_SERVER_URL=http://[YOURSERVER]/testlink/lib/api/xmlrpc/v1/xmlrpc.php
+ set TESTLINK_API_PYTHON_DEVKEY=[Users devKey generated by TestLink]
+ python example\TestLinkExampleGenericApi.py
+
+.. [1] TestLinkExample.py creates a new test project NEW_PROJECT_API-[CountProjects+1].
+.. [2] TestLinkExampleGenericApi.py creates a new test project PROJECT_API_GENERIC-[CountProjects+1].
+
+Lancer des tests unitaires
+-------------
+
+Lancer des tests unitaires avec interaction du serveur de TestLink: ::
+
+ [PYENV]\testlink\Scripts\activate
+ set TESTLINK_API_PYTHON_SERVER_URL=http://[YOURSERVER]/testlink/lib/api/xmlrpc.php
+ set TESTLINK_API_PYTHON_DEVKEY=[Users devKey generated by TestLink]
+ cd test\utest
+ python -m unittest discover -s test\utest-online
+
+Lancer des tests unitaires sans interaction du serveur de TestLink: ::
+
+ [PYENV]\testlink\Scripts\activate
+ cd test\utest
+ python -m unittest discover -s test\utest-offline
+
+En deca de Py26, unittest2_ doit être utilisé.
+
+.. _unittest2: https://pypi.python.org/pypi/unittest2
+
+
+Comment accéder aux données originelles d'échange de XML
+------------------------------------------
+
+Si pour des raisons de débogage les versions originelles d'échange de XML sont requises,
+il est possible d'initialiser l'API client avec le paramètre optionnel *verbose* mis à *True*: ::
+
+ >>> tlh = testlink.TestLinkHelper()
+ >>> tls = testlink.TestlinkAPIClient(tlh._server_url, tl._devkey, verbose=True)
+ send: b"POST /testlink/lib/api/xmlrpc/v1/xmlrpc.php HTTP/1.1\r\nHost: ...
+ \n\ntl.getUserByLogin\n...\n\n"
+ reply: 'HTTP/1.1 200 OK\r\n'
+ header: Date header: Server header: ... body: b'\n\n ...'
+ body: b'1\n\n \n ...'
+ body: b'... \n\n'
+
+
diff --git a/doc/install.rst b/doc/install.rst
index a55cb3f..92a50c6 100644
--- a/doc/install.rst
+++ b/doc/install.rst
@@ -7,14 +7,8 @@ TestLink-API-Python-client Installation
Preconditions
-------------
-Currently the combinations Python 2.6.9/2.7.9/3.3.5/3.4.2 and TestLink 1.9.14 are tested.
-
-- Other combination might work - feedback is welcome :-)
-
-To use TestLink-API-Python-client under Py26, the module **argparse** must be
-installed additionally (Py27, Py33 and Py34 already includes this)::
-
- pip install argparse
+Currently the combinations Python 2.7.16 / 3.6.8 / 3.7.4 are tested with
+TestLink 1.9.20 (development state, github a1c7aca97). Other combination might work - feedback is welcome :-)
TestLink configuration
----------------------
@@ -48,7 +42,7 @@ The source code can be retrieved as source distribution either
Install the archives using pip by running::
- pip install TestLink-API-Python-client-0.4.7.zip
+ pip install TestLink-API-Python-client-0.8.1.zip
Installing from source
----------------------
@@ -110,12 +104,6 @@ with connection parameter as environment variables
- TESTLINK_API_PYTHON_SERVER_URL and TESTLINK_API_PYTHON_DEVKEY
-Known Installation Problems
-----------------------------
-
-If the installation for the combination **linux + py26** fails, maybe the workaround from `issue 50`_ helps.
-
-
.. _PyPI: https://pypi.python.org/pypi
.. _pip: http://www.pip-installer.org
diff --git a/doc/usage.rst b/doc/usage.rst
index b577604..9468106 100644
--- a/doc/usage.rst
+++ b/doc/usage.rst
@@ -104,6 +104,7 @@ Report test results with timestamp and step result
--------------------------------------------------
This test result uses the external test case id and not the internal.
+
- argument 'execduration' and 'timestamp' usable with TL >= 1.9.14:
- argument 'steps' usable with TL >= 1.9.15:
@@ -112,8 +113,8 @@ This test result uses the external test case id and not the internal.
>>> tls.reportTCResult(None, newTestPlanID_A, None, 'f', '', guess=True,
testcaseexternalid=tc_aa_full_ext_id, platformname=NEWPLATFORM_A,
execduration=3.9, timestamp='2015-09-18 14:33',
- steps=[{'step_number' : 6, 'result' : 'p', 'notes" : 'result note for passed step 6'},
- {'step_number' : 7, 'result' : 'f', 'notes" : 'result note for failed step 7'}] )
+ steps=[{'step_number' : 6, 'result' : 'p', 'notes' : 'result note for passed step 6'},
+ {'step_number' : 7, 'result' : 'f', 'notes' : 'result note for failed step 7'}] )
Upload attachments
------------------
diff --git a/example/Last7DaysTestCases.py b/example/Last7DaysTestCases.py
index 362c88d..6607469 100644
--- a/example/Last7DaysTestCases.py
+++ b/example/Last7DaysTestCases.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2011-2012 Olivier Renault, James Stock, TestLink-API-Python-client developers
+# Copyright 2011-2019 Olivier Renault, James Stock, TestLink-API-Python-client developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ def iterTCasesfromTProject(api, TProjName, date1, date2):
""" returns as iterator all test cases of project TPROJTNAME, which are
created between DATE1 and DATE2
DATE1 and DATE2 must be of type time.struct_time """
- TProjId = api.getTestProjectByName(TProjName)[0]['id']
+ TProjId = api.getTestProjectByName(TProjName)['id']
for TSinfo in api.getFirstLevelTestSuitesForTestProject(TProjId):
TSuiteId = TSinfo['id']
for TCid in api.getTestCasesForTestSuite(TSuiteId, deep=1,details='only_id'):
diff --git a/example/TestLinkExample.py b/example/TestLinkExample.py
index f2c0a89..ed10ef9 100644
--- a/example/TestLinkExample.py
+++ b/example/TestLinkExample.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2011-2015 Olivier Renault, Luiko Czub, TestLink-API-Python-client developers
+# Copyright 2011-2021 Olivier Renault, Luiko Czub, TestLink-API-Python-client developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -56,7 +56,7 @@
"""
from __future__ import print_function
-from testlink import TestlinkAPIClient, TestLinkHelper
+from testlink import TestlinkAPIClient, TestLinkHelper, TestGenReporter
from testlink.testlinkerrors import TLResponseError
import sys, os.path
from platform import python_version
@@ -88,9 +88,11 @@
NEWTESTPLAN_A="TestPlan_API A"
NEWTESTPLAN_B="TestPlan_API B"
NEWTESTPLAN_C="TestPlan_API C - DeleteTest"
+NEWTESTPLAN_G="TestPlan_API G - generated"
NEWPLATFORM_A='Big Birds %s' % myPyVersionShort
NEWPLATFORM_B='Small Birds'
NEWPLATFORM_C='Ugly Birds'
+NEWPLATFORM_G='generated Birds'
NEWTESTSUITE_A="A - First Level"
NEWTESTSUITE_B="B - First Level"
NEWTESTSUITE_AA="AA - Second Level"
@@ -100,6 +102,8 @@
NEWBUILD_A='%s' % myApiVersion
NEWBUILD_B='%s' % myApiVersion
NEWBUILD_C='%s - DeleteTest' % myApiVersion
+NEWBUILD_D='%s - copyTestersTest' % myApiVersion
+NEWBUILD_G='%s - generated' % myApiVersion
this_file_dirname=os.path.dirname(__file__)
NEWATTACHMENT_PY= os.path.join(this_file_dirname, 'TestLinkExample.py')
@@ -111,14 +115,22 @@
NEWPROJECT="NEW_PROJECT_API-%s" % myPyVersionShort
NEWPREFIX="NPROAPI%s" % myPyVersionShort
+ITSNAME="myITS"
# used connection settings
print(myTestLink.connectionInfo())
print("")
-# CHANGE this name into a valid account, known in your TL application
-myTestUserName="pyTLapi"
-myTestUserName2="admin"
+# ensure tester and expert users exists
+myTestUserName="myTester"
+myTestUser1_ID=myTestLink.ensureUserExist(myTestUserName,
+ firstname="myFirstName", lastname="myLastName", mail="myTester@example.com")
+print("ensureUserExist", myTestUserName, myTestUser1_ID)
+
+myTestUserName2="myExpert"
+myTestUser2_ID=myTestLink.ensureUserExist(myTestUserName2)
+print("checkUser", myTestUserName2, myTestUser2_ID)
+
# get user information
response = myTestLink.getUserByLogin(myTestUserName)
print("getUserByLogin", response)
@@ -155,18 +167,33 @@
print("deleteTestProject", response)
except TLResponseError:
print("No project with prefix %s exists" % NEWPREFIX)
+
+# # get IssueTrackerSystem
+# aITS=myTestLink.getIssueTrackerSystem(aITSNAME)
+# print("getIssueTrackerSystem", aITS)
# Creates the project
projInfo = 'Example created with Python %s API class %s in TL %s' % \
( python_version(), myApiVersion, myTLVersion )
newProject = myTestLink.createTestProject(NEWPROJECT, NEWPREFIX,
notes=projInfo, active=1, public=1,
+# itsname=ITSNAME, itsenabled=1,
options={'requirementsEnabled' : 0, 'testPriorityEnabled' : 1,
'automationEnabled' : 1, 'inventoryEnabled' : 0})
print("createTestProject", newProject)
newProjectID = newProject[0]['id']
print("New Project '%s' - id: %s" % (NEWPROJECT,newProjectID))
+# assign project roles to user 1 and get user information
+response = myTestLink.ensureUserExistWithProjectRole(myTestUserName, "tester", NEWPROJECT)
+print("ensureUserExistWithProjectRole user1 role tester", response)
+
+# assign project roles to user 2 and get user information
+response = myTestLink.ensureUserExistWithProjectRole(myTestUserName2, "senior tester", NEWPROJECT)
+print("ensureUserExistWithProjectRole user2 role senior tester", response)
+response = myTestLink.ensureUserExistWithProjectRole(myTestUserName2, "test designer", NEWPROJECT)
+print("ensureUserExistWithProjectRole user2 role test designer", response)
+
# Creates the test plan
newTestPlan = myTestLink.createTestPlan(NEWTESTPLAN_A, testprojectname=NEWPROJECT,
notes='New TestPlan created with the API',active=1, public=1)
@@ -184,7 +211,8 @@
# Create platform 'Big Birds x'
newPlatForm = myTestLink.createPlatform(NEWPROJECT, NEWPLATFORM_A,
- notes='Platform for Big Birds, unique name, only used in this project')
+ notes='Platform for Big Birds, unique name, only used in this project',
+ platformondesign=True, platformonexecution=True)
print("createPlatform", newPlatForm)
newPlatFormID_A = newPlatForm['id']
# Add Platform 'Big Bird x' to platform
@@ -193,7 +221,8 @@
# Create platform 'Small Birds'
newPlatForm = myTestLink.createPlatform(NEWPROJECT, NEWPLATFORM_B,
- notes='Platform for Small Birds, name used in all example projects')
+ notes='Platform for Small Birds, name used in all example projects',
+ platformondesign=True, platformonexecution=True)
print("createPlatform", newPlatForm)
newPlatFormID_B = newPlatForm['id']
# Add Platform 'Small Bird' to platform
@@ -202,7 +231,8 @@
# Create platform 'Ugly Birds'
newPlatForm = myTestLink.createPlatform(NEWPROJECT, NEWPLATFORM_C,
- notes='Platform for Ugly Birds, will be removed from test plan')
+ notes='Platform for Ugly Birds, will be removed from test plan',
+ platformondesign=True, platformonexecution=True)
print("createPlatform", newPlatForm)
newPlatFormID_C = newPlatForm['id']
# Add Platform 'Ugly Bird' to platform
@@ -234,8 +264,13 @@
MANUAL = 1
AUTOMATED = 2
+READFORREVIEW=2
+REWORK=4
+HIGH=3
+MEDIUM=2
+LOW=1
-#Creates the test case TC_AA
+#Creates the test case TC_AA with state ready for review
myTestLink.initStep("Step action 1", "Step result 1", MANUAL)
myTestLink.appendStep("Step action 2", "Step result 2", MANUAL)
myTestLink.appendStep("Step action 3", "Step result 3", MANUAL)
@@ -246,24 +281,31 @@
newTestCase = myTestLink.createTestCase(NEWTESTCASE_AA, newTestSuiteID_AA,
newProjectID, myTestUserName, "This is the summary of the Test Case AA",
- preconditions='these are the preconditions')
+ preconditions='these are the preconditions',
+ importance=LOW, state=READFORREVIEW, estimatedexecduration=10.1)
print("createTestCase", newTestCase)
newTestCaseID_AA = newTestCase[0]['id']
print("New Test Case '%s' - id: %s" % (NEWTESTCASE_AA, newTestCaseID_AA))
-#Creates the test case TC_B
+#Creates the test case TC_B with state rework - in wrong test suite A
myTestLink.initStep("Step action 1", "Step result 1", AUTOMATED)
myTestLink.appendStep("Step action 2", "Step result 2", AUTOMATED)
myTestLink.appendStep("Step action 3", "Step result 3", AUTOMATED)
myTestLink.appendStep("Step action 4", "Step result 4", AUTOMATED)
myTestLink.appendStep("Step action 5", "Step result 5", AUTOMATED)
-newTestCase = myTestLink.createTestCase(NEWTESTCASE_B, newTestSuiteID_B,
+newTestCase = myTestLink.createTestCase(NEWTESTCASE_B, newTestSuiteID_A,
newProjectID, myTestUserName, "This is the summary of the Test Case B",
- preconditions='these are the preconditions', executiontype=AUTOMATED)
-print("createTestCase", newTestCase)
+ preconditions='these are the preconditions', executiontype=AUTOMATED,
+ status=REWORK, estimatedexecduration=0.5)
+print("createTestCase TC-B in TS-A", newTestCase)
newTestCaseID_B = newTestCase[0]['id']
print("New Test Case '%s' - id: %s" % (NEWTESTCASE_B, newTestCaseID_B))
+
+# Move test case TC_B to correct test suite B
+tc_b_full_ext_id = myTestLink.getTestCase(newTestCaseID_B)[0]['full_tc_external_id']
+response = myTestLink.setTestCaseTestSuite(tc_b_full_ext_id, newTestSuiteID_B)
+print("setTestCaseTestSuite TC-B to TS-B" , response)
# Add test cases to test plan - we need the full external id !
# for every test case version 1 is used
@@ -298,7 +340,7 @@
{'step_number' : 6, 'actions' : "Step action 6 -b added by updateTestCase" ,
'expected_results' : "Step result 6 - b added", 'execution_type' : AUTOMATED})
response = myTestLink.updateTestCase(tc_b_full_ext_id, version=tc_version,
- steps=steps_tc_b_v1u, importance='high', estimatedexecduration=3)
+ steps=steps_tc_b_v1u, importance=MEDIUM, estimatedexecduration=3)
print("updateTestCase", response)
# create additional steps via createTestCaseSteps - action create
@@ -335,7 +377,8 @@
print("removePlatformFromTestPlan", response)
# -- Create Build for TestPlan A (uses platforms)
-newBuild = myTestLink.createBuild(newTestPlanID_A, NEWBUILD_A, 'Notes for the Build')
+newBuild = myTestLink.createBuild(newTestPlanID_A, NEWBUILD_A,
+ 'Notes for the Build', releasedate="2016-12-31")
print("createBuild", newBuild)
newBuildID_A = newBuild[0]['id']
print("New Build '%s' - id: %s" % (NEWBUILD_A, newBuildID_A))
@@ -373,12 +416,14 @@
testcaseexternalid=tc_aa_full_ext_id)
print("getTestCaseBugs TC_AA in TP_A (TC is not executed)", response)
-# report Test Case Results for platform 'Big Bird'
+# report Test Case Results for platform 'Big Bird' with step results
# TC_AA failed, build should be guessed, TC identified with external id
newResult = myTestLink.reportTCResult(None, newTestPlanID_A, None, 'f', '', guess=True,
testcaseexternalid=tc_aa_full_ext_id,
platformname=NEWPLATFORM_A,
- execduration=3.9, timestamp='2015-09-18 14:33')
+ execduration=3.9, timestamp='2015-09-18 14:33',
+ steps=[{'step_number' : 3, 'result' : 'p', 'notes' : 'result note for passed step 3'},
+ {'step_number' : 4, 'result' : 'f', 'notes' : 'result note for failed step 4'}] )
print("reportTCResult", newResult)
newResultID_AA = newResult[0]['id']
@@ -401,24 +446,30 @@
print("reportTCResult", newResult)
newResultID_B = newResult[0]['id']
-# add this (text) file as Attachemnt to last execution of TC_B with
-# different filename 'MyPyExampleApiClient.py'
-a_file=open(NEWATTACHMENT_PY)
-newAttachment = myTestLink.uploadExecutionAttachment(a_file, newResultID_B,
- 'Textfile Example', 'Text Attachment Example for a TestCase Execution',
- filename='MyPyExampleApiClient.py')
-print("uploadExecutionAttachment", newAttachment)
-# add png file as Attachemnt to last execution of TC_AA
-# !Attention - on WINDOWS use binary mode for none text file
-# see http://docs.python.org/2/tutorial/inputoutput.html#reading-and-writing-files
-a_file=open(NEWATTACHMENT_PNG, mode='rb')
-newAttachment = myTestLink.uploadExecutionAttachment(a_file, newResultID_AA,
- 'PNG Example', 'PNG Attachment Example for a TestCase Execution')
-print("uploadExecutionAttachment", newAttachment)
+#FIXME: know 1.9.20_fixed issue
+# testlink.testlinkerrors.TLResponseError: 6002: (uploadExecutionAttachment) - Error inserting attachment on DB
+#
+# E_WARNING base64_decode() expects parameter 1 to be string, array given
+# - in /var/www/html/lib/api/xmlrpc/v1/xmlrpc.class.php - Line 5834
+#
+# # add this (text) file as Attachemnt to last execution of TC_B with
+# # different filename 'MyPyExampleApiClient.py'
+# a_file=open(NEWATTACHMENT_PY)
+# newAttachment = myTestLink.uploadExecutionAttachment(a_file, newResultID_B,
+# 'Textfile Example', 'Text Attachment Example for a TestCase Execution',
+# filename='MyPyExampleApiClient.py')
+# print("uploadExecutionAttachment", newAttachment)
+# # add png file as Attachemnt to last execution of TC_AA
+# # !Attention - on WINDOWS use binary mode for none text file
+# # see http://docs.python.org/2/tutorial/inputoutput.html#reading-and-writing-files
+# a_file=open(NEWATTACHMENT_PNG, mode='rb')
+# newAttachment = myTestLink.uploadExecutionAttachment(a_file, newResultID_AA,
+# 'PNG Example', 'PNG Attachment Example for a TestCase Execution')
+# print("uploadExecutionAttachment", newAttachment)
# -- Create Build for TestPlan B (uses no platforms)
newBuild = myTestLink.createBuild(newTestPlanID_B, NEWBUILD_B,
- 'Build for TestPlan without platforms')
+ 'Build for TestPlan without platforms', releasedate='2016-11-30')
print("createBuild", newBuild)
newBuildID_B = newBuild[0]['id']
print("New Build '%s' - id: %s" % (NEWBUILD_B, newBuildID_B))
@@ -531,10 +582,17 @@
platformname=NEWPLATFORM_C)
print("reportTCResult", newResult)
newResultID_B = newResult[0]['id']
-newAttachment = myTestLink.uploadExecutionAttachment(NEWATTACHMENT_PY, newResultID_B,
- 'Textfile Example', 'Attachment Example for a TC Execution and TP delete test',
- filename='MyPyTPDeleteTest.py')
-print("uploadExecutionAttachment", newAttachment)
+
+#FIXME: know 1.9.20_fixed issue
+# testlink.testlinkerrors.TLResponseError: 6002: (uploadExecutionAttachment) - Error inserting attachment on DB
+#
+# E_WARNING base64_decode() expects parameter 1 to be string, array given
+# - in /var/www/html/lib/api/xmlrpc/v1/xmlrpc.class.php - Line 5834
+#
+# newAttachment = myTestLink.uploadExecutionAttachment(NEWATTACHMENT_PY, newResultID_B,
+# 'Textfile Example', 'Attachment Example for a TC Execution and TP delete test',
+# filename='MyPyTPDeleteTest.py')
+# print("uploadExecutionAttachment", newAttachment)
response = myTestLink.getTotalsForTestPlan(newTestPlanID_C)
print("getTotalsForTestPlan before delete", response)
response = myTestLink.deleteTestPlan(newTestPlanID_C)
@@ -545,6 +603,18 @@
except TLResponseError as tl_err:
print(tl_err.message)
+# -- Create Build D and copy Testers from Build A
+newBuild = myTestLink.createBuild(newTestPlanID_A, NEWBUILD_D,
+ 'Build with copied testers from Build ' + NEWBUILD_A,
+ active=1, open=1, copytestersfrombuild=newBuildID_A)
+print("createBuild", newBuild)
+newBuildID_D = newBuild[0]['id']
+print("New Build '%s' - id: %s" % (NEWBUILD_D, newBuildID_D))
+
+# close build A - buildid must be converted to an integer
+response = myTestLink.closeBuild( int(newBuildID_A) )
+print("closeBuild", response)
+
# get information - TestProject
response = myTestLink.getTestProjectByName(NEWPROJECT)
print("getTestProjectByName", response)
@@ -590,27 +660,59 @@
response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_B, False, 'only_id')
print("getTestCasesForTestSuite B", response)
+# Update test suite B details - Using Project ID
+updatedTestSuite = myTestLink.updateTestSuite(newTestSuiteID_B,
+ testprojectid=newProjectID,
+ details="updated Details of the Test Suite B")
+print("updateTestSuite", updatedTestSuite)
+
+# Update test suite A name and order details - Using Project Name
+# with TL 1.9.15 this step fails - solution see TL Mantis Ticket 7696
+#
+changedNEWTESTSUITE_A = NEWTESTSUITE_A + ' - Changed'
+updatedTestSuite = myTestLink.updateTestSuite(newTestSuiteID_A, prefix=NEWPREFIX,
+ testsuitename = changedNEWTESTSUITE_A, order=1)
+print("updateTestSuite", updatedTestSuite)
+
+# get all test suites, using the same name - test Suite B
+response = myTestLink.getTestSuite(NEWTESTSUITE_B, NEWPREFIX)
+print("getTestSuite", response)
+
# get informationen - TestCase
# -- Start CHANGE v0.4.5 --
#response = myTestLink.getTestCaseIDByName(NEWTESTCASE_B, None, NEWPROJECT)
response = myTestLink.getTestCaseIDByName(NEWTESTCASE_B, testprojectname=NEWPROJECT)
# -- END CHANGE v0.4.5 --
print("getTestCaseIDByName", response)
-tcpathname = '::'.join([NEWPROJECT, NEWTESTSUITE_A, NEWTESTSUITE_AA, NEWTESTCASE_AA])
+tcpathname = '::'.join([NEWPROJECT, changedNEWTESTSUITE_A, NEWTESTSUITE_AA, NEWTESTCASE_AA])
response = myTestLink.getTestCaseIDByName('unknown', testcasepathname=tcpathname)
print("getTestCaseIDByName", response)
# get execution result
response = myTestLink.getLastExecutionResult(newTestPlanID_A, None,
testcaseexternalid=tc_aa_full_ext_id)
print("getLastExecutionResult", response)
+response = myTestLink.getExecutionSet(newTestPlanID_A,
+ testcaseexternalid=tc_aa_full_ext_id)
+print("getExecutionSet", response)
response = myTestLink.getLastExecutionResult(newTestPlanID_A, newTestCaseID_B)
print("getLastExecutionResult", response)
-if not myTLVersion == '<= 1.9.8':
- # new optional arguments platformid , buildid with TL 1.9.9
- response = myTestLink.getLastExecutionResult(
- newTestPlanID_A, newTestCaseID_AA,
- platformid=newPlatFormID_A)
- print("getLastExecutionResult", response)
+response = myTestLink.getExecutionSet(newTestPlanID_A,
+ testcaseid=newTestCaseID_B)
+print("getExecutionSet", response)
+response = myTestLink.getLastExecutionResult(newTestPlanID_A, newTestCaseID_AA,
+ platformid=newPlatFormID_A)
+print("getLastExecutionResult", response)
+response = myTestLink.getExecutionSet(newTestPlanID_A,
+ testcaseid=newTestCaseID_AA, platformid=newPlatFormID_A)
+print("getExecutionSet", response)
+
+response = myTestLink.getAllExecutionsResults(newTestPlanID_A,
+ testcaseid=newTestCaseID_AA, options={'getBugs' : 1})
+print("getAllExecutionsResults", response)
+response = myTestLink.getAllExecutionsResults(newTestPlanID_B,
+ testcaseid=newTestCaseID_B, options={'getBugs' : 1})
+print("getAllExecutionsResults", response)
+
response = myTestLink.getExecCountersByBuild(newTestPlanID_A)
print("getExecCountersByBuild", response)
@@ -635,22 +737,56 @@
newAttachment = myTestLink.uploadTestSuiteAttachment(NEWATTACHMENT_PNG, newTestSuiteID_A,
title='PNG Example', description='PNG Attachment Example for a TestSuite')
print("uploadTestSuiteAttachment", newAttachment)
-# add png file as Attachment to test case B
-a_file=open(NEWATTACHMENT_PNG, mode='rb')
-newAttachment = myTestLink.uploadTestCaseAttachment(a_file, newTestCaseID_B,
- title='PNG Example', description='PNG Attachment Example for a TestCase')
-print("uploadTestCaseAttachment", newAttachment)
-# get Attachment of test case B
-# response = myTestLink.getTestCaseAttachments(testcaseexternalid=tc_aa_full_ext_id)
-# print "getTestCaseAttachments", response
-response = myTestLink.getTestCaseAttachments(testcaseid=newTestCaseID_B)
-print("getTestCaseAttachments", response)
-
-# copy test cases
+# get Attachment of test suite A
+response = myTestLink.getTestSuiteAttachments(newTestSuiteID_A)
+print("getTestSuiteAttachments", response)
+
+# copy test case - as a new TC version
print("create new version of TC B")
response = myTestLink.copyTCnewVersion(newTestCaseID_B,
summary='new version of TC B', importance='1')
print('copyTCnewVersion', response)
+
+# get the different TC versions
+response = myTestLink.getTestCaseByVersion(newTestCaseID_B, 1)
+print('getTestCaseByVersion v1', response)
+response = myTestLink.getTestCaseByVersion(newTestCaseID_B, 2)
+print('getTestCaseByVersion v2', response)
+response = myTestLink.getTestCaseByVersion(newTestCaseID_B)
+print('getTestCaseByVersion vNone', response)
+
+#FIXME: know 1.9.19 issue
+# E_WARNING base64_decode() expects parameter 1 to be string, array given
+# - in /var/www/html/lib/api/xmlrpc/v1/xmlrpc.class.php - Line 5103
+#
+# # add png file as Attachment to test case B version 1
+# a_file=open(NEWATTACHMENT_PNG, mode='rb')
+# newAttachment = myTestLink.uploadTestCaseAttachment(a_file, newTestCaseID_B, 1,
+# title='PNG Example v1', description='PNG Attachment Example for a TestCase v1')
+# print("uploadTestCaseAttachment v1", newAttachment)
+# # add py file as Attachment to test case B version 2
+# a_file=open(NEWATTACHMENT_PY, mode='rb')
+# newAttachment = myTestLink.uploadTestCaseAttachment(a_file, newTestCaseID_B, 2,
+# title='Textfile Example v2', description='Text Attachment Example for a TestCase v2')
+# print("uploadTestCaseAttachment v2", newAttachment)
+#
+# # get Attachment of test case B v1
+# # response = myTestLink.getTestCaseAttachments(testcaseexternalid=tc_aa_full_ext_id)
+# # print "getTestCaseAttachments", response
+# response = myTestLink.getTestCaseAttachments(testcaseid=newTestCaseID_B, version=1)
+# print("getTestCaseAttachments v1", response)
+#
+# # get Attachment of test case B - last version
+# # response = myTestLink.getTestCaseAttachments(testcaseexternalid=tc_aa_full_ext_id)
+# # print "getTestCaseAttachments", response
+# response = myTestLink.getTestCaseAttachments(testcaseid=newTestCaseID_B, version=2)
+# attachment_id = list(response)[0]
+# print("getTestCaseAttachments v2", response[attachment_id]['title'])
+# response = myTestLink.getTestCaseAttachments(testcaseid=newTestCaseID_B)
+# print("getTestCaseAttachments vNone", response[attachment_id]['name'])
+
+
+# copy test case - as new TC in a different TestSuite
print("copy TC B as TC BA into Test suite A")
response = myTestLink.copyTCnewTestCase(newTestCaseID_B,
testsuiteid=newTestSuiteID_A, testcasename='%sA' % NEWTESTCASE_B)
@@ -660,23 +796,26 @@
response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_A, True, 'simple')
print('getTestCasesForTestSuite A', response)
-# no test data
-# response = myTestLink.getTestCaseCustomFieldDesignValue(
-# tc_aa_full_ext_id, 1, newProjectID, 'cfieldname', 'simple')
-# print "getTestCaseCustomFieldDesignValue", response
-print("getTestCaseCustomFieldDesignValue", "Sorry currently no testdata")
-# add png file as Attachemnt to a requirement specification.
-print("uploadRequirementSpecificationAttachment", "Sorry currently no testdata")
-# add png file as Attachemnt to a requirement.
-print("uploadRequirementAttachment", "Sorry currently no testdata")
-# add requirements to testcase AA
-# response = myTestLink.assignRequirements(tc_aa_full_ext_id, newProjectID,
-# [{'req_spec' : 6729, 'requirements' : [6731]},
-# {'req_spec' : 6733, 'requirements' : [6735, 6737]}])
-print("assignRequirements", "Sorry currently no testdata")
+# sample, how the test plan can be updated to use the new tc version
+# site effect of this step, assigned testers and existing execution results are
+# not accessible anymore via the TL Web Gui.
+# That is the reason, why we have uncomment it for the normal sample execution
+# response = myTestLink.addTestCaseToTestPlan(newProjectID, newTestPlanID_B,
+# tc_b_full_ext_id, tc_version+1,
+# overwrite=1)
+# print("addTestCaseToTestPlan overwrite", response)
+
+# sample, how to use TestGenReporter for adding test case result for test plans,
+# test builds, which are not yet defined
+tgr = TestGenReporter(myTestLink, [tc_aa_full_ext_id, tc_b_full_ext_id],
+ testprojectname=NEWPROJECT,
+ testplanname=NEWTESTPLAN_G, platformname=NEWPLATFORM_G,
+ buildname=NEWBUILD_G, status='p')
+tgr.report()
+print("TestPlan, Build, Platform generated with reporting TC results", tgr )
print("")
diff --git a/example/TestLinkExampleGenericApi.py b/example/TestLinkExampleGenericApi.py
index f7a4c53..1618ff2 100644
--- a/example/TestLinkExampleGenericApi.py
+++ b/example/TestLinkExampleGenericApi.py
@@ -1,683 +1,808 @@
-#! /usr/bin/python
-# -*- coding: UTF-8 -*-
-
-# Copyright 2013-2015 Luiko Czub, TestLink-API-Python-client developers
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# ------------------------------------------------------------------------
-
-
-"""
-
-Shows how to use the TestLinkAPIGeneric.
-- does equal things as Example TestLinkAPI in TestLinkExample.py
- - exception - this test project uses platforms
-
-=> Counts and lists the Projects
-=> Create a new Project with the following structure:
-
-
-NewProject
- |
- ----NewTestPlan
- |
- ------ Test Suite A
- | |
- | ------- Test Suite AA
- | |
- | --------- Test Case AA
- | |
- ------ Test Suite B --- 5 manual test steps
- |
- --------- Test Case B
- |
- --- 5 automated test steps
-"""
-from __future__ import print_function
-from testlink import TestlinkAPIGeneric, TestLinkHelper
-from testlink.testlinkerrors import TLResponseError
-import sys, os.path
-from platform import python_version
-
-# precondition a)
-# SERVER_URL and KEY are defined in environment
-# TESTLINK_API_PYTHON_SERVER_URL=http://YOURSERVER/testlink/lib/api/xmlrpc.php
-# TESTLINK_API_PYTHON_DEVKEY=7ec252ab966ce88fd92c25d08635672b
-#
-# alternative precondition b)
-# SERVEUR_URL and KEY are defined as command line arguments
-# python TestLinkExample.py --server_url http://YOURSERVER/testlink/lib/api/xmlrpc.php
-# --devKey 7ec252ab966ce88fd92c25d08635672b
-#
-# ATTENTION: With TestLink 1.9.7, cause of the new REST API, the SERVER_URL
-# has changed from
-# (old) http://YOURSERVER/testlink/lib/api/xmlrpc.php
-# to
-# (new) http://YOURSERVER/testlink/lib/api/xmlrpc/v1/xmlrpc.php
-tl_helper = TestLinkHelper()
-tl_helper.setParamsFromArgs('''Shows how to use the TestLinkAPI.
-=> Counts and lists the Projects
-=> Create a new Project with the following structure:''')
-myTestLink = tl_helper.connect(TestlinkAPIGeneric)
-
-myPyVersion = python_version()
-myPyVersionShort = myPyVersion.replace('.', '')[:2]
-
-NEWTESTPLAN_A="TestPlan_API_GENERIC A"
-NEWTESTPLAN_B="TestPlan_API_GENERIC B"
-NEWTESTPLAN_C="TestPlan_API_GENERIC C - DeleteTest"
-NEWPLATFORM_A='Big Bird %s' % myPyVersionShort
-NEWPLATFORM_B='Small Bird'
-NEWPLATFORM_C='Ugly Bird'
-NEWTESTSUITE_A="A - First Level"
-NEWTESTSUITE_B="B - First Level"
-NEWTESTSUITE_AA="AA - Second Level"
-NEWTESTCASE_AA="TESTCASE_AA"
-NEWTESTCASE_B="TESTCASE_B"
-myApiVersion='%s v%s' % (myTestLink.__class__.__name__ , myTestLink.__version__)
-NEWBUILD_A='%s' % myApiVersion
-NEWBUILD_B='%s' % myApiVersion
-NEWBUILD_C='%s - DeleteTest' % myApiVersion
-
-this_file_dirname=os.path.dirname(__file__)
-NEWATTACHMENT_PY= os.path.join(this_file_dirname, 'TestLinkExampleGenericApi.py')
-NEWATTACHMENT_PNG=os.path.join(this_file_dirname, 'PyGreat.png')
-
-# Servers TestLink Version
-myTLVersion = myTestLink.testLinkVersion()
-myTLVersionShort = myTLVersion.replace('.', '')
-
-NEWPROJECT="PROJECT_API_GENERIC-%s" % myPyVersionShort
-NEWPREFIX="GPROAPI%s" % myPyVersionShort
-
-# used connection settings
-print(myTestLink.connectionInfo())
-print("")
-
-# CHANGE this name into a valid account, known in your TL application
-myTestUserName="pyTLapi"
-myTestUserName2="admin"
-# get user information
-response = myTestLink.getUserByLogin(myTestUserName)
-print("getUserByLogin", response)
-myTestUserID=response[0]['dbID']
-response = myTestLink.getUserByID(myTestUserID)
-print("getUserByID ", response)
-
-# example asking the api client about methods arguments
-print(myTestLink.whatArgs('createTestCase'))
-
-
-# example handling Response Error Codes
-# first check an invalid devKey and than the own one
-try:
- myTestLink.checkDevKey(devKey='007')
-except TLResponseError as tl_err:
- if tl_err.code == 2000:
- # expected invalid devKey Error
- # now check the own one - just call with default settings
- myTestLink.checkDevKey()
- else:
- # seems to be another response failure - we forward it
- raise
-
-print("Number of Projects in TestLink: %i " % len(myTestLink.getProjects()))
-print("")
-for project in myTestLink.getProjects():
- print("Name: %(name)s ID: %(id)s " % project)
-print("")
-
-# Delete the project, if it already exists
-try:
- response = myTestLink.deleteTestProject(NEWPREFIX)
- print("deleteTestProject", response)
-except TLResponseError:
- print("No project with prefix %s exists" % NEWPREFIX)
-
-# Creates the project
-projInfo = 'Example created with Python %s API class %s in TL %s' % \
- ( myPyVersion, myApiVersion, myTLVersion )
-newProject = myTestLink.createTestProject(NEWPROJECT, NEWPREFIX,
- notes=projInfo, active=1, public=1,
- options={'requirementsEnabled' : 1, 'testPriorityEnabled' : 1,
- 'automationEnabled' : 1, 'inventoryEnabled' : 1})
-print("createTestProject", newProject)
-newProjectID = newProject[0]['id']
-print("New Project '%s' - id: %s" % (NEWPROJECT,newProjectID))
-
-# Create test plan A - uses platforms
-newTestPlan = myTestLink.createTestPlan(NEWTESTPLAN_A, testprojectname=NEWPROJECT,
- notes='New TestPlan created with the Generic API - uses platforms.',
- active=1, public=1)
-print("createTestPlan", newTestPlan)
-newTestPlanID_A = newTestPlan[0]['id']
-print("New Test Plan '%s' - id: %s" % (NEWTESTPLAN_A,newTestPlanID_A))
-
-# Create test plan B - uses no platforms
-newTestPlan = myTestLink.createTestPlan(NEWTESTPLAN_B, prefix=NEWPREFIX,
- notes='New TestPlan created with the Generic API - uses no platforms.',
- active=1, public=1)
-print("createTestPlan", newTestPlan)
-newTestPlanID_B = newTestPlan[0]['id']
-print("New Test Plan '%s' - id: %s" % (NEWTESTPLAN_B,newTestPlanID_B))
-
-# Create platform 'Big Bird x'
-newPlatForm = myTestLink.createPlatform(NEWPROJECT, NEWPLATFORM_A,
- notes='Platform for Big Birds, unique name, only used in this project')
-print("createPlatform", newPlatForm)
-newPlatFormID_A = newPlatForm['id']
-# Add Platform 'Big Bird x' to platform
-response = myTestLink.addPlatformToTestPlan(newTestPlanID_A, NEWPLATFORM_A)
-print("addPlatformToTestPlan", response)
-
-# Create platform 'Small Bird'
-newPlatForm = myTestLink.createPlatform(NEWPROJECT, NEWPLATFORM_B,
- notes='Platform for Small Birds, name used in all example projects')
-print("createPlatform", newPlatForm)
-newPlatFormID_B = newPlatForm['id']
-# Add Platform 'Small Bird' to platform
-response = myTestLink.addPlatformToTestPlan(newTestPlanID_A, NEWPLATFORM_B)
-print("addPlatformToTestPlan", response)
-
-# Create platform 'Ugly Bird'
-newPlatForm = myTestLink.createPlatform(NEWPROJECT, NEWPLATFORM_C,
- notes='Platform for Ugly Birds, will be removed from test plan')
-print("createPlatform", newPlatForm)
-newPlatFormID_C = newPlatForm['id']
-# Add Platform 'Ugly Bird' to platform
-response = myTestLink.addPlatformToTestPlan(newTestPlanID_A, NEWPLATFORM_C)
-print("addPlatformToTestPlan", response)
-
-#Creates the test Suite A
-newTestSuite = myTestLink.createTestSuite(newProjectID, NEWTESTSUITE_A,
- "Details of the Test Suite A")
-print("createTestSuite", newTestSuite)
-newTestSuiteID_A = newTestSuite[0]['id']
-print("New Test Suite '%s' - id: %s" % (NEWTESTSUITE_A, newTestSuiteID_A))
-
-FirstLevelID = newTestSuiteID_A
-
-#Creates the test Suite B
-newTestSuite = myTestLink.createTestSuite(newProjectID, NEWTESTSUITE_B,
- "Details of the Test Suite B")
-print("createTestSuite", newTestSuite)
-newTestSuiteID_B = newTestSuite[0]['id']
-print("New Test Suite '%s' - id: %s" % (NEWTESTSUITE_B, newTestSuiteID_B))
-
-#Creates the test Suite AA
-newTestSuite = myTestLink.createTestSuite(newProjectID, NEWTESTSUITE_AA,
- "Details of the Test Suite AA",parentid=FirstLevelID)
-print("createTestSuite", newTestSuite)
-newTestSuiteID_AA = newTestSuite[0]['id']
-print("New Test Suite '%s' - id: %s" % (NEWTESTSUITE_AA, newTestSuiteID_AA))
-
-MANUAL = 1
-AUTOMATED = 2
-#
-# #Creates the test case TC_AA
-steps_tc_aa = [
- {'step_number' : 1, 'actions' : "Step action 1 - aa" ,
- 'expected_results' : "Step result 1 - aa", 'execution_type' : MANUAL},
- {'step_number' : 2, 'actions' : "Step action 2 - aa" ,
- 'expected_results' : "Step result 2 - aa", 'execution_type' : MANUAL},
- {'step_number' : 3, 'actions' : "Step action 3 - aa" ,
- 'expected_results' : "Step result 3 - aa", 'execution_type' : MANUAL},
- {'step_number' : 4, 'actions' : "Step action 4 - aa" ,
- 'expected_results' : "Step result 4 - aa", 'execution_type' : MANUAL},
- {'step_number' : 5, 'actions' : "Step action 5 - aa" ,
- 'expected_results' : "Step result 5 - aa", 'execution_type' : MANUAL},
- {'step_number' : 8, 'actions' : "Dummy step for delete tests" ,
- 'expected_results' : "should be delete with deleteTestCaseSteps",
- 'execution_type' : MANUAL}
- ]
-newTestCase = myTestLink.createTestCase(NEWTESTCASE_AA, newTestSuiteID_AA,
- newProjectID, myTestUserName, "This is the summary of the Test Case AA",
- steps_tc_aa, preconditions='these are the preconditions')
-print("createTestCase", newTestCase)
-newTestCaseID_AA = newTestCase[0]['id']
-print("New Test Case '%s' - id: %s" % (NEWTESTCASE_AA, newTestCaseID_AA))
-
-#Creates the test case TC_B
-steps_tc_b = [
- {'step_number' : 1, 'actions' : "Step action 1 -b " ,
- 'expected_results' : "Step result 1 - b", 'execution_type' : AUTOMATED},
- {'step_number' : 2, 'actions' : "Step action 2 -b " ,
- 'expected_results' : "Step result 2 - b", 'execution_type' : AUTOMATED},
- {'step_number' : 3, 'actions' : "Step action 3 -b " ,
- 'expected_results' : "Step result 3 - b", 'execution_type' : AUTOMATED},
- {'step_number' : 4, 'actions' : "Step action 4 -b " ,
- 'expected_results' : "Step result 4 - b", 'execution_type' : AUTOMATED},
- {'step_number' : 5, 'actions' : "Step action 5 -b " ,
- 'expected_results' : "Step result 5 - b", 'execution_type' : AUTOMATED}]
-
-newTestCase = myTestLink.createTestCase(NEWTESTCASE_B, newTestSuiteID_B,
- newProjectID, myTestUserName, "This is the summary of the Test Case B",
- steps_tc_b, preconditions='these are the preconditions',
- executiontype=AUTOMATED)
-print("createTestCase", newTestCase)
-newTestCaseID_B = newTestCase[0]['id']
-print("New Test Case '%s' - id: %s" % (NEWTESTCASE_B, newTestCaseID_B))
-
-# Add test cases to test plan - we need the full external id !
-# for every test case version 1 is used
-tc_version=1
-# TC AA should be tested with platforms 'Big Bird'+'Small Bird'
-response = myTestLink.getTestCase(testcaseid=newTestCaseID_AA)
-print("getTestCase", response)
-tc_aa_full_ext_id = response[0]['full_tc_external_id']
-response = myTestLink.addTestCaseToTestPlan(newProjectID, newTestPlanID_A,
- tc_aa_full_ext_id, tc_version, platformid=newPlatFormID_A)
-print("addTestCaseToTestPlan", response)
-tc_aa_full_ext_id = myTestLink.getTestCase(testcaseid=newTestCaseID_AA)[0]['full_tc_external_id']
-response = myTestLink.addTestCaseToTestPlan(newProjectID, newTestPlanID_A,
- tc_aa_full_ext_id, tc_version, platformid=newPlatFormID_B)
-print("addTestCaseToTestPlan", response)
-# change test case TC_AA - delete step 8 (step 7 does not exist)
-response = myTestLink.deleteTestCaseSteps(tc_aa_full_ext_id, [7,8],
- version=tc_version)
-print("deleteTestCaseSteps", response)
-
-# TC B should be tested with platform 'Small Bird'
-tc_b_full_ext_id = myTestLink.getTestCase(testcaseid=newTestCaseID_B)[0]['full_tc_external_id']
-response = myTestLink.addTestCaseToTestPlan(newProjectID, newTestPlanID_A,
- tc_b_full_ext_id, tc_version, platformid=newPlatFormID_B)
-print("addTestCaseToTestPlan", response)
-
-#Update test case TC_B -> high, change step 5, new step 6
-steps_tc_b_v1u = steps_tc_b[:4]
-steps_tc_b_v1u.append(
- {'step_number' : 5, 'actions' : "Step action 5 -b changed by updateTestCase" ,
- 'expected_results' : "Step result 5 - b changed", 'execution_type' : AUTOMATED})
-steps_tc_b_v1u.append(
- {'step_number' : 6, 'actions' : "Step action 6 -b added by updateTestCase" ,
- 'expected_results' : "Step result 6 - b added", 'execution_type' : AUTOMATED})
-response = myTestLink.updateTestCase(tc_b_full_ext_id, version=tc_version,
- steps=steps_tc_b_v1u, importance='high', estimatedexecduration=3)
-print("updateTestCase", response)
-
-# create additional steps via createTestCaseSteps - action create
-steps_tc_b_c67 = [
- {'step_number' : 6, 'actions' : "action 6 createTestCaseSteps.create" ,
- 'expected_results' : "skip - cause step 6 already exist", 'execution_type' : AUTOMATED},
- {'step_number' : 7, 'actions' : "action 7 createTestCaseSteps.create" ,
- 'expected_results' : "create - cause step 7 not yet exist", 'execution_type' : AUTOMATED}]
-response = myTestLink.createTestCaseSteps('create', steps_tc_b_c67,
- testcaseexternalid=tc_b_full_ext_id, version=tc_version)
-print("createTestCaseSteps.create", response)
-# create additional steps via createTestCaseSteps - action update
-steps_tc_b_c38 = [
- {'step_number' : 3, 'actions' : "action 3 createTestCaseSteps.update" ,
- 'expected_results' : "update - cause step 3 already exist", 'execution_type' : AUTOMATED},
- {'step_number' : 8, 'actions' : "action 8 createTestCaseSteps.update" ,
- 'expected_results' : "create - cause step 8 not yet exist", 'execution_type' : AUTOMATED}]
-response = myTestLink.createTestCaseSteps('update', steps_tc_b_c38,
- testcaseid=newTestCaseID_B, version=tc_version)
-print("createTestCaseSteps.update", response)
-
-
-# In test plan B TC B should be tested without platform
-response = myTestLink.addTestCaseToTestPlan(newProjectID, newTestPlanID_B,
- tc_b_full_ext_id, tc_version)
-print("addTestCaseToTestPlan", response)
-
-# # Try to Remove Platform 'Big Bird' from platform
-# response = myTestLink.removePlatformFromTestPlan(newTestPlanID_A, NEWPLATFORM_C)
-# print "removePlatformFromTestPlan", response
-
-# Remove Platform 'Ugly Bird' from platform
-response = myTestLink.removePlatformFromTestPlan(newTestPlanID_A, NEWPLATFORM_C)
-print("removePlatformFromTestPlan", response)
-
-
-# -- Create Build for TestPlan A (uses platforms)
-newBuild = myTestLink.createBuild(newTestPlanID_A, NEWBUILD_A,
- buildnotes='Build for TestPlan with platforms')
-print("createBuild", newBuild)
-newBuildID_A = newBuild[0]['id']
-print("New Build '%s' - id: %s" % (NEWBUILD_A, newBuildID_A))
-
-# assign user to test case execution tasks - test plan with platforms
-response = myTestLink.assignTestCaseExecutionTask( myTestUserName,
- newTestPlanID_A, tc_aa_full_ext_id,
- buildid=newBuildID_A, platformname=NEWPLATFORM_A)
-print("assignTestCaseExecutionTask", response)
-response = myTestLink.assignTestCaseExecutionTask( myTestUserName2,
- newTestPlanID_A, tc_aa_full_ext_id,
- buildname=NEWBUILD_A, platformid=newPlatFormID_B)
-print("assignTestCaseExecutionTask", response)
-response = myTestLink.assignTestCaseExecutionTask( myTestUserName,
- newTestPlanID_A, tc_b_full_ext_id,
- buildname=NEWBUILD_A, platformname=NEWPLATFORM_B)
-print("assignTestCaseExecutionTask", response)
-
-# get test case assigned tester
-response = myTestLink.getTestCaseAssignedTester(
- newTestPlanID_A, tc_aa_full_ext_id,
- buildid=newBuildID_A, platformname=NEWPLATFORM_A)
-print("getTestCaseAssignedTester TC_AA TP_A Platform A", response)
-response = myTestLink.getTestCaseAssignedTester(
- newTestPlanID_A, tc_aa_full_ext_id,
- buildname=NEWBUILD_A, platformid=newPlatFormID_B)
-print("getTestCaseAssignedTester TC_AA TP_A Platform B", response)
-response = myTestLink.getTestCaseAssignedTester(
- newTestPlanID_A, tc_b_full_ext_id,
- buildname=NEWBUILD_A, platformname=NEWPLATFORM_B)
-print("getTestCaseAssignedTester TC_B TP_A Platform B", response)
-
-# get bugs for test case TC_AA in test plan A - state TC not executed
-response = myTestLink.getTestCaseBugs(newTestPlanID_A,
- testcaseexternalid=tc_aa_full_ext_id)
-print("getTestCaseBugs TC_AA in TP_A (TC is not executed)", response)
-
-# report Test Case Results for platform 'Big Bird'
-# TC_AA failed, build should be guessed, TC identified with external id
-newResult = myTestLink.reportTCResult(newTestPlanID_A, 'f', guess=True,
- testcaseexternalid=tc_aa_full_ext_id,
- platformname=NEWPLATFORM_A,
- execduration=2.9, timestamp='2014-09-18 14:33')
-print("reportTCResult", newResult)
-newResultID_AA = newResult[0]['id']
-
-# get bugs for test case TC_AA in test plan A - state TC is executed
-response = myTestLink.getTestCaseBugs(newTestPlanID_A,
- testcaseexternalid=tc_aa_full_ext_id)
-print("getTestCaseBugs TC_AA in TP_A (TC is executed, no bug)", response)
-
-# report Test Case Results for platform 'Small Bird'
-# TC_AA passed, build should be guessed, TC identified with external id
-newResult = myTestLink.reportTCResult(newTestPlanID_A, 'p', guess=True,
- testcaseexternalid=tc_aa_full_ext_id,
- platformid=newPlatFormID_B,
- execduration='3.2', timestamp='2014-09-19 14:33:02')
-print("reportTCResult", newResult)
-newResultID_AA_p = newResult[0]['id']
-# TC_B passed, explicit build and some notes , TC identified with internal id
-newResult = myTestLink.reportTCResult(newTestPlanID_A, 'p',
- buildid=newBuildID_A, testcaseid=newTestCaseID_B,
- platformname=NEWPLATFORM_B, notes="first try")
-print("reportTCResult", newResult)
-newResultID_B = newResult[0]['id']
-
-# add this python file as Attachemnt to last execution of TC_B with
-# different filename 'MyPyExampleApiGeneric.py'
-a_file=open(NEWATTACHMENT_PY)
-newAttachment = myTestLink.uploadExecutionAttachment(a_file, newResultID_B,
- title='Textfile Example', description='Text Attachment Example for a TestCase Execution',
- filename='MyPyExampleApiGeneric.py')
-print("uploadExecutionAttachment", newAttachment)
-# add png file as Attachemnt to last execution of TC_AA
-# !Attention - on WINDOWS use binary mode for none text file
-# see http://docs.python.org/2/tutorial/inputoutput.html#reading-and-writing-files
-a_file=open(NEWATTACHMENT_PNG, mode='rb')
-newAttachment = myTestLink.uploadExecutionAttachment(a_file, newResultID_AA,
- title='PNG Example', description='PNG Attachment Example for a TestCase Execution')
-print("uploadExecutionAttachment", newAttachment)
-
-# -- Create Build for TestPlan B (uses no platforms)
-newBuild = myTestLink.createBuild(newTestPlanID_B, NEWBUILD_B,
- buildnotes='Build for TestPlan without platforms')
-print("createBuild", newBuild)
-newBuildID_B = newBuild[0]['id']
-print("New Build '%s' - id: %s" % (NEWBUILD_B, newBuildID_B))
-
-# assign user to test case execution tasks - test plans without platforms
-response = myTestLink.assignTestCaseExecutionTask( myTestUserName,
- newTestPlanID_B, tc_b_full_ext_id, buildname=NEWBUILD_B)
-print("assignTestCaseExecutionTask", response)
-
-# get test case assigned tester
-response = myTestLink.getTestCaseAssignedTester(
- newTestPlanID_B, tc_b_full_ext_id, buildname=NEWBUILD_B)
-print("getTestCaseAssignedTester TC_B TP_B no Platform", response)
-
-# try to remove not assigned tester
-response = myTestLink.unassignTestCaseExecutionTask(
- newTestPlanID_B, tc_b_full_ext_id, buildname=NEWBUILD_B,
- user=myTestUserName2)
-print("unassignTestCaseExecutionTask not assigned user", response)
-response = myTestLink.getTestCaseAssignedTester(
- newTestPlanID_B, tc_b_full_ext_id, buildname=NEWBUILD_B)
-print("getTestCaseAssignedTester TC_B TP_B no Platform", response)
-
-# try to remove all assigned tester
-response = myTestLink.unassignTestCaseExecutionTask(
- newTestPlanID_B, tc_b_full_ext_id, buildid=newBuildID_B,
- action='unassignAll')
-print("unassignTestCaseExecutionTask unassignAll", response)
-response = myTestLink.getTestCaseAssignedTester(
- newTestPlanID_B, tc_b_full_ext_id, buildname=NEWBUILD_B)
-print("getTestCaseAssignedTester TC_B TP_B no Platform", response)
-
-# reassign user to test case execution tasks - test plans without platforms
-response = myTestLink.assignTestCaseExecutionTask( myTestUserName,
- newTestPlanID_B, tc_b_full_ext_id, buildid=newBuildID_B)
-print("assignTestCaseExecutionTask", response)
-response = myTestLink.getTestCaseAssignedTester(
- newTestPlanID_B, tc_b_full_ext_id, buildname=NEWBUILD_B)
-print("getTestCaseAssignedTester TC_B TP_B no Platform", response)
-
-# TC_B in test plan b (without platform)
-# first try failed (with bug), second blocked - all by user myTestUserName
-newResult = myTestLink.reportTCResult(newTestPlanID_B, 'f',
- buildid=newBuildID_B, testcaseid=newTestCaseID_B, bugid='007',
- notes="no birds are singing", user=myTestUserName)
-print("reportTCResult", newResult)
-newResultID_B_f = newResult[0]['id']
-newResult = myTestLink.reportTCResult(newTestPlanID_B, 'b',
- buildid=newBuildID_B, testcaseid=newTestCaseID_B, bugid='008',
- notes="hungry birds blocks the execution", user=myTestUserName)
-print("reportTCResult", newResult)
-newResultID_B_b = newResult[0]['id']
-# get bugs for test case TC_B in test plan B - state TC is executed with bug
-response = myTestLink.getTestCaseBugs(newTestPlanID_B,
- testcaseid=newTestCaseID_B)
-print("getTestCaseBugs TC_B in TP_B (TC is executed with 2 bugs)", response)
-
-
-# now we make a mistake and commit the same result a second time
-# and try to delete this mistake
-newResult = myTestLink.reportTCResult(newTestPlanID_B, 'b',
- buildid=newBuildID_B, testcaseid=newTestCaseID_B,
- notes="mistake, commit same result a second time")
-print("reportTCResult", newResult)
-newResultID_B_b2 = int(newResult[0]['id'])
-try:
- # if TL configuration allows deletion of executions, no error will occur
- response = myTestLink.deleteExecution(newResultID_B_b2)
- print("deleteExecution", response)
-except TLResponseError as tl_err:
- if tl_err.code == 232:
- # TL configuration does not allow deletion of executions
- pass
- else:
- # sh..: another problem occurs
- raise
-
-# now we try to change the execution types of the test cases
-# - AA from manual -> auto and B from auto -> manual
-newResult = myTestLink.setTestCaseExecutionType(tc_aa_full_ext_id, tc_version,
- newProjectID, AUTOMATED)
-print("setTestCaseExecutionType", response)
-newResult = myTestLink.setTestCaseExecutionType(tc_b_full_ext_id, tc_version,
- newProjectID, MANUAL)
-print("setTestCaseExecutionType", response)
-
-# create TestPlan C with Platform, Build , TestCase, assigned TestCase
-# and delete it
-newTestPlan = myTestLink.createTestPlan(NEWTESTPLAN_C, NEWPROJECT,
- notes='TestPlan for delete test.',
- active=1, public=1)
-print("createTestPlan for DeleteTest", newTestPlan)
-newTestPlanID_C = newTestPlan[0]['id']
-print("Test Plan '%s' - id: %s" % (NEWTESTPLAN_C,newTestPlanID_C))
-newBuild = myTestLink.createBuild(newTestPlanID_C, NEWBUILD_C,
- buildnotes='Build for TestPlan delete test')
-print("createBuild for DeleteTest", newBuild)
-newBuildID_C = newBuild[0]['id']
-print("Build '%s' - id: %s" % (NEWBUILD_C, newBuildID_C))
-response = myTestLink.addPlatformToTestPlan(newTestPlanID_C, NEWPLATFORM_C)
-print("addPlatformToTestPlan", response)
-response = myTestLink.addTestCaseToTestPlan(newProjectID, newTestPlanID_C,
- tc_aa_full_ext_id, tc_version, platformid=newPlatFormID_C)
-print("addTestCaseToTestPlan", response)
-response = myTestLink.assignTestCaseExecutionTask( myTestUserName,
- newTestPlanID_C, tc_aa_full_ext_id, buildid=newBuildID_C,
- platformid=newPlatFormID_C)
-print("assignTestCaseExecutionTask", response)
-newResult = myTestLink.reportTCResult(newTestPlanID_C, 'p',
- buildid=newBuildID_C, testcaseid=newTestCaseID_AA,
- platformname=NEWPLATFORM_C, notes="TP delete test")
-print("reportTCResult", newResult)
-newResultID_B = newResult[0]['id']
-newAttachment = myTestLink.uploadExecutionAttachment(NEWATTACHMENT_PY, newResultID_B,
- title='Textfile Example', filename='MyPyTPDeleteTest.py',
- description='Attachment Example for a TC Execution and TP delete test')
-print("uploadExecutionAttachment", newAttachment)
-response = myTestLink.getTotalsForTestPlan(newTestPlanID_C)
-print("getTotalsForTestPlan before delete", response)
-response = myTestLink.deleteTestPlan(newTestPlanID_C)
-print("deleteTestPlan", response)
-try:
- response = myTestLink.getTotalsForTestPlan(newTestPlanID_C)
- print("getTotalsForTestPlan after delete", response)
-except TLResponseError as tl_err:
- print(tl_err.message)
-
-# get information - TestProject
-response = myTestLink.getTestProjectByName(NEWPROJECT)
-print("getTestProjectByName", response)
-response = myTestLink.getProjectTestPlans(newProjectID)
-print("getProjectTestPlans", response)
-response = myTestLink.getFirstLevelTestSuitesForTestProject(newProjectID)
-print("getFirstLevelTestSuitesForTestProject", response)
-response = myTestLink.getProjectPlatforms(newProjectID)
-print("getProjectPlatforms", response)
-response = myTestLink.getProjectKeywords(newProjectID)
-print("getProjectKeywords", response)
-
-# get information - TestPlan
-response = myTestLink.getTestPlanByName(NEWPROJECT, NEWTESTPLAN_A)
-print("getTestPlanByName", response)
-response = myTestLink.getTotalsForTestPlan(newTestPlanID_A)
-print("getTotalsForTestPlan", response)
-response = myTestLink.getBuildsForTestPlan(newTestPlanID_A)
-print("getBuildsForTestPlan", response)
-response = myTestLink.getLatestBuildForTestPlan(newTestPlanID_A)
-print("getLatestBuildForTestPlan", response)
-response = myTestLink.getTestPlanPlatforms(newTestPlanID_A)
-print("getTestPlanPlatforms", response)
-response = myTestLink.getTestSuitesForTestPlan(newTestPlanID_A)
-print("getTestSuitesForTestPlan", response)
-# get failed Testcases
-response = myTestLink.getTestCasesForTestPlan(newTestPlanID_A, executestatus='f')
-print("getTestCasesForTestPlan A failed ", response)
-# get Testcases for Plattform SmallBird
-response = myTestLink.getTestCasesForTestPlan(newTestPlanID_A, platformid=newPlatFormID_B)
-print("getTestCasesForTestPlan A SmallBirds", response)
-
-# get information - TestSuite
-response = myTestLink.getTestSuiteByID(newTestSuiteID_B)
-print("getTestSuiteByID", response)
-response = myTestLink.getTestSuitesForTestSuite(newTestSuiteID_A)
-print("getTestSuitesForTestSuite", response)
-response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_A,
- deep=True, details='full')
-print("getTestCasesForTestSuite", response)
-response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_B,
- deep=False, details='only_id')
-print("getTestCasesForTestSuite", response)
-
-# get informationen - TestCase_B
-response = myTestLink.getTestCaseIDByName(NEWTESTCASE_B, testprojectname=NEWPROJECT)
-print("getTestCaseIDByName", response)
-# get informationen - TestCase_AA via Pathname
-tcpathname = '::'.join([NEWPROJECT, NEWTESTSUITE_A, NEWTESTSUITE_AA, NEWTESTCASE_AA])
-response = myTestLink.getTestCaseIDByName('unknown', testcasepathname=tcpathname)
-print("getTestCaseIDByName", response)
-# get execution result
-response = myTestLink.getLastExecutionResult(newTestPlanID_A,
- testcaseexternalid=tc_aa_full_ext_id)
-print("getLastExecutionResult", response)
-response = myTestLink.getLastExecutionResult(newTestPlanID_A,
- testcaseid=newTestCaseID_B)
-print("getLastExecutionResult", response)
-if not myTLVersion == '<= 1.9.8':
- # new optional arguments platformid , buildid with TL 1.9.9
- response = myTestLink.getLastExecutionResult(
- newTestPlanID_A, testcaseid=newTestCaseID_AA,
- platformid=newPlatFormID_A)
- print("getLastExecutionResult", response)
-
-response = myTestLink.getExecCountersByBuild(newTestPlanID_A)
-print("getExecCountersByBuild", response)
-response = myTestLink.getExecCountersByBuild(newTestPlanID_B)
-print("getExecCountersByBuild", response)
-response = myTestLink.getTestCaseKeywords(testcaseexternalid=tc_b_full_ext_id)
-print("getTestCaseKeywords noKeyWords", response)
-
-# get information - general
-response = myTestLink.getFullPath(int(newTestSuiteID_AA))
-print("getFullPath", response)
-response = myTestLink.getFullPath([int(newTestCaseID_AA), int(newTestCaseID_B)])
-print("getFullPath", response)
-
-# attachments
-# add png file as Attachment to test project
-a_file=open(NEWATTACHMENT_PNG, mode='rb')
-newAttachment = myTestLink.uploadTestProjectAttachment(a_file, newProjectID,
- title='PNG Example', description='PNG Attachment Example for a TestProject')
-print("uploadTestProjectAttachment", newAttachment)
-# add png file as Attachnent to test suite A - uploadXyzAttachmemt also file path
-newAttachment = myTestLink.uploadTestSuiteAttachment(NEWATTACHMENT_PNG, newTestSuiteID_A,
- title='PNG Example', description='PNG Attachment Example for a TestSuite')
-print("uploadTestSuiteAttachment", newAttachment)
-# add png file as Attachment to test case B
-a_file=open(NEWATTACHMENT_PNG, mode='rb')
-newAttachment = myTestLink.uploadTestCaseAttachment(a_file, newTestCaseID_B,
- title='PNG Example', description='PNG Attachment Example for a TestCase')
-print("uploadTestCaseAttachment", newAttachment)
-# get Attachment of test case B
-# response = myTestLink.getTestCaseAttachments(testcaseexternalid=tc_aa_full_ext_id)
-# print "getTestCaseAttachments", response
-response = myTestLink.getTestCaseAttachments(testcaseid=newTestCaseID_B)
-print("getTestCaseAttachments", response)
-
-# no test data
-# response = myTestLink.getTestCaseCustomFieldDesignValue(
-# tc_aa_full_ext_id, 1, newProjectID, 'cfieldname', details='simple')
-# print "getTestCaseCustomFieldDesignValue", response
-print("getTestCaseCustomFieldDesignValue", "Sorry currently no testdata")
-
-# add png file as Attachemnt to a requirement specification.
-print("uploadRequirementSpecificationAttachment", "Sorry currently no testdata")
-# add png file as Attachemnt to a requirement.
-print("uploadRequirementAttachment", "Sorry currently no testdata")
-
-# add requirements to testcase AA
-# response = myTestLink.assignRequirements(tc_aa_full_ext_id, newProjectID,
-# [{'req_spec' : 6729, 'requirements' : [6731]},
-# {'req_spec' : 6733, 'requirements' : [6735, 6737]}])
-print("assignRequirements", "Sorry currently no testdata")
-
-print("")
-print("Number of Projects in TestLink: %i " % len(myTestLink.getProjects()))
-print("")
-for project in myTestLink.getProjects():
- print("Name: %(name)s ID: %(id)s " % project)
-print("")
-
-
-#
-#
-#
+#! /usr/bin/python
+# -*- coding: UTF-8 -*-
+
+# Copyright 2013-2021 Luiko Czub, TestLink-API-Python-client developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# ------------------------------------------------------------------------
+
+
+"""
+
+Shows how to use the TestLinkAPIGeneric.
+- does equal things as Example TestLinkAPI in TestLinkExample.py
+ - exception - this test project uses platforms
+
+=> Counts and lists the Projects
+=> Create a new Project with the following structure:
+
+
+NewProject
+ |
+ ----NewTestPlan
+ |
+ ------ Test Suite A
+ | |
+ | ------- Test Suite AA
+ | |
+ | --------- Test Case AA
+ | |
+ ------ Test Suite B --- 5 manual test steps
+ |
+ --------- Test Case B
+ |
+ --- 5 automated test steps
+"""
+from __future__ import print_function
+from testlink import TestlinkAPIGeneric, TestLinkHelper
+from testlink.testlinkerrors import TLResponseError
+import sys, os.path
+from platform import python_version
+
+# precondition a)
+# SERVER_URL and KEY are defined in environment
+# TESTLINK_API_PYTHON_SERVER_URL=http://YOURSERVER/testlink/lib/api/xmlrpc.php
+# TESTLINK_API_PYTHON_DEVKEY=7ec252ab966ce88fd92c25d08635672b
+#
+# alternative precondition b)
+# SERVEUR_URL and KEY are defined as command line arguments
+# python TestLinkExample.py --server_url http://YOURSERVER/testlink/lib/api/xmlrpc.php
+# --devKey 7ec252ab966ce88fd92c25d08635672b
+#
+# ATTENTION: With TestLink 1.9.7, cause of the new REST API, the SERVER_URL
+# has changed from
+# (old) http://YOURSERVER/testlink/lib/api/xmlrpc.php
+# to
+# (new) http://YOURSERVER/testlink/lib/api/xmlrpc/v1/xmlrpc.php
+tl_helper = TestLinkHelper()
+tl_helper.setParamsFromArgs('''Shows how to use the TestLinkAPI.
+=> Counts and lists the Projects
+=> Create a new Project with the following structure:''')
+myTestLink = tl_helper.connect(TestlinkAPIGeneric)
+
+myPyVersion = python_version()
+myPyVersionShort = myPyVersion.replace('.', '')[:2]
+
+NEWTESTPLAN_A="TestPlan_API_GENERIC A"
+NEWTESTPLAN_B="TestPlan_API_GENERIC B"
+NEWTESTPLAN_C="TestPlan_API_GENERIC C - DeleteTest"
+NEWPLATFORM_A='Big Bird %s' % myPyVersionShort
+NEWPLATFORM_B='Small Bird'
+NEWPLATFORM_C='Ugly Bird'
+NEWTESTSUITE_A="A - First Level"
+NEWTESTSUITE_B="B - First Level"
+NEWTESTSUITE_AA="AA - Second Level"
+NEWTESTCASE_AA="TESTCASE_AA"
+NEWTESTCASE_B="TESTCASE_B"
+myApiVersion='%s v%s' % (myTestLink.__class__.__name__ , myTestLink.__version__)
+NEWBUILD_A='%s' % myApiVersion
+NEWBUILD_B='%s' % myApiVersion
+NEWBUILD_C='%s - DeleteTest' % myApiVersion
+NEWBUILD_D='%s - copyTestersTest' % myApiVersion
+
+this_file_dirname=os.path.dirname(__file__)
+NEWATTACHMENT_PY= os.path.join(this_file_dirname, 'TestLinkExampleGenericApi.py')
+NEWATTACHMENT_PNG=os.path.join(this_file_dirname, 'PyGreat.png')
+
+# Servers TestLink Version
+myTLVersion = myTestLink.testLinkVersion()
+myTLVersionShort = myTLVersion.replace('.', '')
+
+NEWPROJECT="PROJECT_API_GENERIC-%s" % myPyVersionShort
+NEWPREFIX="GPROAPI%s" % myPyVersionShort
+ITSNAME="myITS"
+
+# used connection settings
+print(myTestLink.connectionInfo())
+print("")
+
+def checkUser(name1, name2):
+ """ checks if user NAME1_NAME2 exists
+ when not , user will be created
+ returns username + userid
+ """
+
+ login = "{}_{}".format(name1, name2)
+ mail = "{}.{}@example.com".format(name1, name2)
+ try:
+ response = myTestLink.getUserByLogin(login)
+ userID = response[0]['dbID']
+ except TLResponseError as tl_err:
+ if tl_err.code == 10000:
+ # Cannot Find User Login - create new user
+ userID = myTestLink.createUser(login, name1, name2, mail)
+ else:
+ # seems to be another response failure - we forward it
+ raise
+
+ return login, userID
+
+# ensure tester and expert users exists
+myTestUserName, myTestUser1_ID=checkUser("myTester", "pyTLapi")
+print("checkUser", myTestUserName, myTestUser1_ID)
+myTestUserName2, myTestUser2_ID=checkUser("myExpert", "pyTLapi")
+print("checkUser", myTestUserName2, myTestUser2_ID)
+
+# get user information
+response = myTestLink.getUserByLogin(myTestUserName)
+print("getUserByLogin", response)
+myTestUserID=response[0]['dbID']
+response = myTestLink.getUserByID(myTestUserID)
+print("getUserByID ", response)
+
+# example asking the api client about methods arguments
+print(myTestLink.whatArgs('createTestCase'))
+
+# example handling Response Error Codes
+# first check an invalid devKey and than the own one
+try:
+ myTestLink.checkDevKey(devKey='007')
+except TLResponseError as tl_err:
+ if tl_err.code == 2000:
+ # expected invalid devKey Error
+ # now check the own one - just call with default settings
+ myTestLink.checkDevKey()
+ else:
+ # seems to be another response failure - we forward it
+ raise
+
+print("Number of Projects in TestLink: %i " % len(myTestLink.getProjects()))
+print("")
+for project in myTestLink.getProjects():
+ print("Name: %(name)s ID: %(id)s " % project)
+print("")
+
+# Delete the project, if it already exists
+try:
+ response = myTestLink.deleteTestProject(NEWPREFIX)
+ print("deleteTestProject", response)
+except TLResponseError:
+ print("No project with prefix %s exists" % NEWPREFIX)
+
+# # get IssueTrackerSystem
+# aITS=myTestLink.getIssueTrackerSystem(aITSNAME)
+# print("getIssueTrackerSystem", aITS)
+
+# Creates the project
+projInfo = 'Example created with Python %s API class %s in TL %s' % \
+ ( myPyVersion, myApiVersion, myTLVersion )
+newProject = myTestLink.createTestProject(NEWPROJECT, NEWPREFIX,
+ notes=projInfo, active=1, public=1,
+# itsname=ITSNAME, itsenabled=1,
+ options={'requirementsEnabled' : 1, 'testPriorityEnabled' : 1,
+ 'automationEnabled' : 1, 'inventoryEnabled' : 1})
+print("createTestProject", newProject)
+newProjectID = newProject[0]['id']
+print("New Project '%s' - id: %s" % (NEWPROJECT,newProjectID))
+
+# assign project roles to user 1 and get user information
+response = myTestLink.setUserRoleOnProject(myTestUser1_ID, "tester", newProjectID)
+print("setUserRoleOnProject user1 role tester", response)
+response = myTestLink.getUserByID(myTestUser1_ID)
+print("getUserByID user1", response)
+
+# assign project roles to user 2 and get user information
+response = myTestLink.setUserRoleOnProject(myTestUser2_ID, "senior tester", newProjectID)
+print("setUserRoleOnProject user2 role senior tester", response)
+response = myTestLink.getUserByID(myTestUser2_ID)
+print("getUserByID user2", response)
+response = myTestLink.setUserRoleOnProject(myTestUser2_ID, "test designer", newProjectID)
+print("setUserRoleOnProject user2 role test designer", response)
+response = myTestLink.getUserByID(myTestUser2_ID)
+print("getUserByID user2", response)
+
+
+# Create test plan A - uses platforms
+newTestPlan = myTestLink.createTestPlan(NEWTESTPLAN_A, testprojectname=NEWPROJECT,
+ notes='New TestPlan created with the Generic API - uses platforms.',
+ active=1, public=1)
+print("createTestPlan", newTestPlan)
+newTestPlanID_A = newTestPlan[0]['id']
+print("New Test Plan '%s' - id: %s" % (NEWTESTPLAN_A,newTestPlanID_A))
+
+# Create test plan B - uses no platforms
+newTestPlan = myTestLink.createTestPlan(NEWTESTPLAN_B, prefix=NEWPREFIX,
+ notes='New TestPlan created with the Generic API - uses no platforms.',
+ active=1, public=1)
+print("createTestPlan", newTestPlan)
+newTestPlanID_B = newTestPlan[0]['id']
+print("New Test Plan '%s' - id: %s" % (NEWTESTPLAN_B,newTestPlanID_B))
+
+# Create platform 'Big Bird x'
+newPlatForm = myTestLink.createPlatform(NEWPROJECT, NEWPLATFORM_A,
+ notes='Platform for Big Birds, unique name, only used in this project',
+ platformondesign=True, platformonexecution=True)
+print("createPlatform", newPlatForm)
+newPlatFormID_A = newPlatForm['id']
+# Add Platform 'Big Bird x' to platform
+response = myTestLink.addPlatformToTestPlan(newTestPlanID_A, NEWPLATFORM_A)
+print("addPlatformToTestPlan", response)
+
+# Create platform 'Small Bird'
+newPlatForm = myTestLink.createPlatform(NEWPROJECT, NEWPLATFORM_B,
+ notes='Platform for Small Birds, name used in all example projects',
+ platformondesign=True, platformonexecution=True)
+print("createPlatform", newPlatForm)
+newPlatFormID_B = newPlatForm['id']
+# Add Platform 'Small Bird' to platform
+response = myTestLink.addPlatformToTestPlan(newTestPlanID_A, NEWPLATFORM_B)
+print("addPlatformToTestPlan", response)
+
+# Create platform 'Ugly Bird'
+newPlatForm = myTestLink.createPlatform(NEWPROJECT, NEWPLATFORM_C,
+ notes='Platform for Ugly Birds, will be removed from test plan',
+ platformondesign=True, platformonexecution=True)
+print("createPlatform", newPlatForm)
+newPlatFormID_C = newPlatForm['id']
+# Add Platform 'Ugly Bird' to platform
+response = myTestLink.addPlatformToTestPlan(newTestPlanID_A, NEWPLATFORM_C)
+print("addPlatformToTestPlan", response)
+
+#Creates the test Suite A
+newTestSuite = myTestLink.createTestSuite(newProjectID, NEWTESTSUITE_A,
+ "Details of the Test Suite A")
+print("createTestSuite", newTestSuite)
+newTestSuiteID_A = newTestSuite[0]['id']
+print("New Test Suite '%s' - id: %s" % (NEWTESTSUITE_A, newTestSuiteID_A))
+
+FirstLevelID = newTestSuiteID_A
+
+#Creates the test Suite B
+newTestSuite = myTestLink.createTestSuite(newProjectID, NEWTESTSUITE_B,
+ "Details of the Test Suite B")
+print("createTestSuite", newTestSuite)
+newTestSuiteID_B = newTestSuite[0]['id']
+print("New Test Suite '%s' - id: %s" % (NEWTESTSUITE_B, newTestSuiteID_B))
+
+#Creates the test Suite AA
+newTestSuite = myTestLink.createTestSuite(newProjectID, NEWTESTSUITE_AA,
+ "Details of the Test Suite AA",parentid=FirstLevelID)
+print("createTestSuite", newTestSuite)
+newTestSuiteID_AA = newTestSuite[0]['id']
+print("New Test Suite '%s' - id: %s" % (NEWTESTSUITE_AA, newTestSuiteID_AA))
+
+MANUAL = 1
+AUTOMATED = 2
+READFORREVIEW=2
+REWORK=4
+HIGH=3
+MEDIUM=2
+LOW=1
+#
+# #Creates the test case TC_AA with state ready for review
+steps_tc_aa = [
+ {'step_number' : 1, 'actions' : "Step action 1 - aa" ,
+ 'expected_results' : "Step result 1 - aa", 'execution_type' : MANUAL},
+ {'step_number' : 2, 'actions' : "Step action 2 - aa" ,
+ 'expected_results' : "Step result 2 - aa", 'execution_type' : MANUAL},
+ {'step_number' : 3, 'actions' : "Step action 3 - aa" ,
+ 'expected_results' : "Step result 3 - aa", 'execution_type' : MANUAL},
+ {'step_number' : 4, 'actions' : "Step action 4 - aa" ,
+ 'expected_results' : "Step result 4 - aa", 'execution_type' : MANUAL},
+ {'step_number' : 5, 'actions' : "Step action 5 - aa" ,
+ 'expected_results' : "Step result 5 - aa", 'execution_type' : MANUAL},
+ {'step_number' : 8, 'actions' : "Dummy step for delete tests" ,
+ 'expected_results' : "should be delete with deleteTestCaseSteps",
+ 'execution_type' : MANUAL}
+ ]
+newTestCase = myTestLink.createTestCase(NEWTESTCASE_AA, newTestSuiteID_AA,
+ newProjectID, myTestUserName, "This is the summary of the Test Case AA",
+ steps_tc_aa, preconditions='these are the preconditions',
+ importance=LOW, state=READFORREVIEW, estimatedexecduration=10.1)
+print("createTestCase", newTestCase)
+newTestCaseID_AA = newTestCase[0]['id']
+print("New Test Case '%s' - id: %s" % (NEWTESTCASE_AA, newTestCaseID_AA))
+
+# Creates the test case TC_B with state rework - in wrong test suite A
+steps_tc_b = [
+ {'step_number' : 1, 'actions' : "Step action 1 -b " ,
+ 'expected_results' : "Step result 1 - b", 'execution_type' : AUTOMATED},
+ {'step_number' : 2, 'actions' : "Step action 2 -b " ,
+ 'expected_results' : "Step result 2 - b", 'execution_type' : AUTOMATED},
+ {'step_number' : 3, 'actions' : "Step action 3 -b " ,
+ 'expected_results' : "Step result 3 - b", 'execution_type' : AUTOMATED},
+ {'step_number' : 4, 'actions' : "Step action 4 -b " ,
+ 'expected_results' : "Step result 4 - b", 'execution_type' : AUTOMATED},
+ {'step_number' : 5, 'actions' : "Step action 5 -b " ,
+ 'expected_results' : "Step result 5 - b", 'execution_type' : AUTOMATED}]
+
+newTestCase = myTestLink.createTestCase(NEWTESTCASE_B, newTestSuiteID_A,
+ newProjectID, myTestUserName, "This is the summary of the Test Case B",
+ steps_tc_b, preconditions='these are the preconditions',
+ executiontype=AUTOMATED, status=REWORK, estimatedexecduration=0.5)
+print("createTestCase TC-B in TS-A", newTestCase)
+newTestCaseID_B = newTestCase[0]['id']
+print("New Test Case '%s' - id: %s" % (NEWTESTCASE_B, newTestCaseID_B))
+
+# Move test case TC_B to correct test suite B
+tc_b_full_ext_id = myTestLink.getTestCase(testcaseid=newTestCaseID_B)[0]['full_tc_external_id']
+response = myTestLink.setTestCaseTestSuite(tc_b_full_ext_id, newTestSuiteID_B)
+print("setTestCaseTestSuite TC-B to TS-B" , response)
+
+# Add test cases to test plan - we need the full external id !
+# for every test case version 1 is used
+tc_version=1
+# TC AA should be tested with platforms 'Big Bird'+'Small Bird'
+response = myTestLink.getTestCase(testcaseid=newTestCaseID_AA)
+print("getTestCase", response)
+tc_aa_full_ext_id = response[0]['full_tc_external_id']
+response = myTestLink.addTestCaseToTestPlan(newProjectID, newTestPlanID_A,
+ tc_aa_full_ext_id, tc_version, platformid=newPlatFormID_A)
+print("addTestCaseToTestPlan", response)
+tc_aa_full_ext_id = myTestLink.getTestCase(testcaseid=newTestCaseID_AA)[0]['full_tc_external_id']
+response = myTestLink.addTestCaseToTestPlan(newProjectID, newTestPlanID_A,
+ tc_aa_full_ext_id, tc_version, platformid=newPlatFormID_B)
+print("addTestCaseToTestPlan", response)
+# change test case TC_AA - delete step 8 (step 7 does not exist)
+response = myTestLink.deleteTestCaseSteps(tc_aa_full_ext_id, [7,8],
+ version=tc_version)
+print("deleteTestCaseSteps", response)
+
+# TC B should be tested with platform 'Small Bird'
+tc_b_full_ext_id = myTestLink.getTestCase(testcaseid=newTestCaseID_B)[0]['full_tc_external_id']
+response = myTestLink.addTestCaseToTestPlan(newProjectID, newTestPlanID_A,
+ tc_b_full_ext_id, tc_version, platformid=newPlatFormID_B)
+print("addTestCaseToTestPlan", response)
+
+#Update test case TC_B -> high, change step 5, new step 6
+steps_tc_b_v1u = steps_tc_b[:4]
+steps_tc_b_v1u.append(
+ {'step_number' : 5, 'actions' : "Step action 5 -b changed by updateTestCase" ,
+ 'expected_results' : "Step result 5 - b changed", 'execution_type' : AUTOMATED})
+steps_tc_b_v1u.append(
+ {'step_number' : 6, 'actions' : "Step action 6 -b added by updateTestCase" ,
+ 'expected_results' : "Step result 6 - b added", 'execution_type' : AUTOMATED})
+response = myTestLink.updateTestCase(tc_b_full_ext_id, version=tc_version,
+ steps=steps_tc_b_v1u, importance=MEDIUM, estimatedexecduration=3)
+print("updateTestCase", response)
+
+# create additional steps via createTestCaseSteps - action create
+steps_tc_b_c67 = [
+ {'step_number' : 6, 'actions' : "action 6 createTestCaseSteps.create" ,
+ 'expected_results' : "skip - cause step 6 already exist", 'execution_type' : AUTOMATED},
+ {'step_number' : 7, 'actions' : "action 7 createTestCaseSteps.create" ,
+ 'expected_results' : "create - cause step 7 not yet exist", 'execution_type' : AUTOMATED}]
+response = myTestLink.createTestCaseSteps('create', steps_tc_b_c67,
+ testcaseexternalid=tc_b_full_ext_id, version=tc_version)
+print("createTestCaseSteps.create", response)
+# create additional steps via createTestCaseSteps - action update
+steps_tc_b_c38 = [
+ {'step_number' : 3, 'actions' : "action 3 createTestCaseSteps.update" ,
+ 'expected_results' : "update - cause step 3 already exist", 'execution_type' : AUTOMATED},
+ {'step_number' : 8, 'actions' : "action 8 createTestCaseSteps.update" ,
+ 'expected_results' : "create - cause step 8 not yet exist", 'execution_type' : AUTOMATED}]
+response = myTestLink.createTestCaseSteps('update', steps_tc_b_c38,
+ testcaseid=newTestCaseID_B, version=tc_version)
+print("createTestCaseSteps.update", response)
+
+
+# In test plan B TC B should be tested without platform
+response = myTestLink.addTestCaseToTestPlan(newProjectID, newTestPlanID_B,
+ tc_b_full_ext_id, tc_version)
+print("addTestCaseToTestPlan", response)
+
+# # Try to Remove Platform 'Big Bird' from platform
+# response = myTestLink.removePlatformFromTestPlan(newTestPlanID_A, NEWPLATFORM_C)
+# print "removePlatformFromTestPlan", response
+
+# Remove Platform 'Ugly Bird' from platform
+response = myTestLink.removePlatformFromTestPlan(newTestPlanID_A, NEWPLATFORM_C)
+print("removePlatformFromTestPlan", response)
+
+
+# -- Create Build for TestPlan A (uses platforms)
+newBuild = myTestLink.createBuild(newTestPlanID_A, NEWBUILD_A,
+ buildnotes='Build for TestPlan with platforms')
+print("createBuild", newBuild)
+newBuildID_A = newBuild[0]['id']
+print("New Build '%s' - id: %s" % (NEWBUILD_A, newBuildID_A))
+
+# assign user to test case execution tasks - test plan with platforms
+response = myTestLink.assignTestCaseExecutionTask( myTestUserName,
+ newTestPlanID_A, tc_aa_full_ext_id,
+ buildid=newBuildID_A, platformname=NEWPLATFORM_A)
+print("assignTestCaseExecutionTask", response)
+response = myTestLink.assignTestCaseExecutionTask( myTestUserName2,
+ newTestPlanID_A, tc_aa_full_ext_id,
+ buildname=NEWBUILD_A, platformid=newPlatFormID_B)
+print("assignTestCaseExecutionTask", response)
+response = myTestLink.assignTestCaseExecutionTask( myTestUserName,
+ newTestPlanID_A, tc_b_full_ext_id,
+ buildname=NEWBUILD_A, platformname=NEWPLATFORM_B)
+print("assignTestCaseExecutionTask", response)
+
+# get test case assigned tester
+response = myTestLink.getTestCaseAssignedTester(
+ newTestPlanID_A, tc_aa_full_ext_id,
+ buildid=newBuildID_A, platformname=NEWPLATFORM_A)
+print("getTestCaseAssignedTester TC_AA TP_A Platform A", response)
+response = myTestLink.getTestCaseAssignedTester(
+ newTestPlanID_A, tc_aa_full_ext_id,
+ buildname=NEWBUILD_A, platformid=newPlatFormID_B)
+print("getTestCaseAssignedTester TC_AA TP_A Platform B", response)
+response = myTestLink.getTestCaseAssignedTester(
+ newTestPlanID_A, tc_b_full_ext_id,
+ buildname=NEWBUILD_A, platformname=NEWPLATFORM_B)
+print("getTestCaseAssignedTester TC_B TP_A Platform B", response)
+
+# get bugs for test case TC_AA in test plan A - state TC not executed
+response = myTestLink.getTestCaseBugs(newTestPlanID_A,
+ testcaseexternalid=tc_aa_full_ext_id)
+print("getTestCaseBugs TC_AA in TP_A (TC is not executed)", response)
+
+# report Test Case Results for platform 'Big Bird' with step results
+# TC_AA failed, build should be guessed, TC identified with external id
+newResult = myTestLink.reportTCResult(newTestPlanID_A, 'f', guess=True,
+ testcaseexternalid=tc_aa_full_ext_id,
+ platformname=NEWPLATFORM_A,
+ execduration=2.9, timestamp='2014-09-18 14:33',
+ steps=[{'step_number' : 3, 'result' : 'p', 'notes' : 'result note for passed step 3'},
+ {'step_number' : 4, 'result' : 'f', 'notes' : 'result note for failed step 4'}] )
+print("reportTCResult", newResult)
+newResultID_AA = newResult[0]['id']
+
+# get bugs for test case TC_AA in test plan A - state TC is executed
+response = myTestLink.getTestCaseBugs(newTestPlanID_A,
+ testcaseexternalid=tc_aa_full_ext_id)
+print("getTestCaseBugs TC_AA in TP_A (TC is executed, no bug)", response)
+
+# report Test Case Results for platform 'Small Bird'
+# TC_AA passed, build should be guessed, TC identified with external id
+newResult = myTestLink.reportTCResult(newTestPlanID_A, 'p', guess=True,
+ testcaseexternalid=tc_aa_full_ext_id,
+ platformid=newPlatFormID_B,
+ execduration='3.2', timestamp='2014-09-19 14:33:02')
+print("reportTCResult", newResult)
+newResultID_AA_p = newResult[0]['id']
+# TC_B passed, explicit build and some notes , TC identified with internal id
+newResult = myTestLink.reportTCResult(newTestPlanID_A, 'p',
+ buildid=newBuildID_A, testcaseid=newTestCaseID_B,
+ platformname=NEWPLATFORM_B, notes="first try")
+print("reportTCResult", newResult)
+newResultID_B = newResult[0]['id']
+
+#FIXME: know 1.9.20_fixed issue
+# testlink.testlinkerrors.TLResponseError: 6002: (uploadExecutionAttachment) - Error inserting attachment on DB
+#
+# E_WARNING base64_decode() expects parameter 1 to be string, array given
+# - in /var/www/html/lib/api/xmlrpc/v1/xmlrpc.class.php - Line 5834
+#
+# # add this python file as Attachemnt to last execution of TC_B with
+# # different filename 'MyPyExampleApiGeneric.py'
+# a_file=open(NEWATTACHMENT_PY)
+# newAttachment = myTestLink.uploadExecutionAttachment(a_file, newResultID_B,
+# title='Textfile Example', description='Text Attachment Example for a TestCase Execution',
+# filename='MyPyExampleApiGeneric.py')
+# print("uploadExecutionAttachment", newAttachment)
+# # add png file as Attachemnt to last execution of TC_AA
+# # !Attention - on WINDOWS use binary mode for none text file
+# # see http://docs.python.org/2/tutorial/inputoutput.html#reading-and-writing-files
+# a_file=open(NEWATTACHMENT_PNG, mode='rb')
+# newAttachment = myTestLink.uploadExecutionAttachment(a_file, newResultID_AA,
+# title='PNG Example', description='PNG Attachment Example for a TestCase Execution')
+# print("uploadExecutionAttachment", newAttachment)
+
+# -- Create Build for TestPlan B (uses no platforms)
+newBuild = myTestLink.createBuild(newTestPlanID_B, NEWBUILD_B,
+ buildnotes='Build for TestPlan without platforms',
+ releasedate='2016-11-30')
+print("createBuild", newBuild)
+newBuildID_B = newBuild[0]['id']
+print("New Build '%s' - id: %s" % (NEWBUILD_B, newBuildID_B))
+
+# assign user to test case execution tasks - test plans without platforms
+response = myTestLink.assignTestCaseExecutionTask( myTestUserName,
+ newTestPlanID_B, tc_b_full_ext_id, buildname=NEWBUILD_B)
+print("assignTestCaseExecutionTask", response)
+
+# get test case assigned tester
+response = myTestLink.getTestCaseAssignedTester(
+ newTestPlanID_B, tc_b_full_ext_id, buildname=NEWBUILD_B)
+print("getTestCaseAssignedTester TC_B TP_B no Platform", response)
+
+# try to remove not assigned tester
+response = myTestLink.unassignTestCaseExecutionTask(
+ newTestPlanID_B, tc_b_full_ext_id, buildname=NEWBUILD_B,
+ user=myTestUserName2)
+print("unassignTestCaseExecutionTask not assigned user", response)
+response = myTestLink.getTestCaseAssignedTester(
+ newTestPlanID_B, tc_b_full_ext_id, buildname=NEWBUILD_B)
+print("getTestCaseAssignedTester TC_B TP_B no Platform", response)
+
+# try to remove all assigned tester
+response = myTestLink.unassignTestCaseExecutionTask(
+ newTestPlanID_B, tc_b_full_ext_id, buildid=newBuildID_B,
+ action='unassignAll')
+print("unassignTestCaseExecutionTask unassignAll", response)
+response = myTestLink.getTestCaseAssignedTester(
+ newTestPlanID_B, tc_b_full_ext_id, buildname=NEWBUILD_B)
+print("getTestCaseAssignedTester TC_B TP_B no Platform", response)
+
+# reassign user to test case execution tasks - test plans without platforms
+response = myTestLink.assignTestCaseExecutionTask( myTestUserName,
+ newTestPlanID_B, tc_b_full_ext_id, buildid=newBuildID_B)
+print("assignTestCaseExecutionTask", response)
+response = myTestLink.getTestCaseAssignedTester(
+ newTestPlanID_B, tc_b_full_ext_id, buildname=NEWBUILD_B)
+print("getTestCaseAssignedTester TC_B TP_B no Platform", response)
+
+# TC_B in test plan b (without platform)
+# first try failed (with bug), second blocked - all by user myTestUserName
+newResult = myTestLink.reportTCResult(newTestPlanID_B, 'f',
+ buildid=newBuildID_B, testcaseid=newTestCaseID_B, bugid='007',
+ notes="no birds are singing", user=myTestUserName)
+print("reportTCResult", newResult)
+newResultID_B_f = newResult[0]['id']
+newResult = myTestLink.reportTCResult(newTestPlanID_B, 'b',
+ buildid=newBuildID_B, testcaseid=newTestCaseID_B, bugid='008',
+ notes="hungry birds blocks the execution", user=myTestUserName)
+print("reportTCResult", newResult)
+newResultID_B_b = newResult[0]['id']
+# get bugs for test case TC_B in test plan B - state TC is executed with bug
+response = myTestLink.getTestCaseBugs(newTestPlanID_B,
+ testcaseid=newTestCaseID_B)
+print("getTestCaseBugs TC_B in TP_B (TC is executed with 2 bugs)", response)
+
+
+# now we make a mistake and commit the same result a second time
+# and try to delete this mistake
+newResult = myTestLink.reportTCResult(newTestPlanID_B, 'b',
+ buildid=newBuildID_B, testcaseid=newTestCaseID_B,
+ notes="mistake, commit same result a second time")
+print("reportTCResult", newResult)
+newResultID_B_b2 = int(newResult[0]['id'])
+try:
+ # if TL configuration allows deletion of executions, no error will occur
+ response = myTestLink.deleteExecution(newResultID_B_b2)
+ print("deleteExecution", response)
+except TLResponseError as tl_err:
+ if tl_err.code == 232:
+ # TL configuration does not allow deletion of executions
+ pass
+ else:
+ # sh..: another problem occurs
+ raise
+
+# now we try to change the execution types of the test cases
+# - AA from manual -> auto and B from auto -> manual
+newResult = myTestLink.setTestCaseExecutionType(tc_aa_full_ext_id, tc_version,
+ newProjectID, AUTOMATED)
+print("setTestCaseExecutionType", response)
+newResult = myTestLink.setTestCaseExecutionType(tc_b_full_ext_id, tc_version,
+ newProjectID, MANUAL)
+print("setTestCaseExecutionType", response)
+
+# create TestPlan C with Platform, Build , TestCase, assigned TestCase
+# and delete it
+newTestPlan = myTestLink.createTestPlan(NEWTESTPLAN_C, NEWPROJECT,
+ notes='TestPlan for delete test.',
+ active=1, public=1)
+print("createTestPlan for DeleteTest", newTestPlan)
+newTestPlanID_C = newTestPlan[0]['id']
+print("Test Plan '%s' - id: %s" % (NEWTESTPLAN_C,newTestPlanID_C))
+newBuild = myTestLink.createBuild(newTestPlanID_C, NEWBUILD_C,
+ buildnotes='Build for TestPlan delete test')
+print("createBuild for DeleteTest", newBuild)
+newBuildID_C = newBuild[0]['id']
+print("Build '%s' - id: %s" % (NEWBUILD_C, newBuildID_C))
+response = myTestLink.addPlatformToTestPlan(newTestPlanID_C, NEWPLATFORM_C)
+print("addPlatformToTestPlan", response)
+response = myTestLink.addTestCaseToTestPlan(newProjectID, newTestPlanID_C,
+ tc_aa_full_ext_id, tc_version, platformid=newPlatFormID_C)
+print("addTestCaseToTestPlan", response)
+response = myTestLink.assignTestCaseExecutionTask( myTestUserName,
+ newTestPlanID_C, tc_aa_full_ext_id, buildid=newBuildID_C,
+ platformid=newPlatFormID_C)
+print("assignTestCaseExecutionTask", response)
+newResult = myTestLink.reportTCResult(newTestPlanID_C, 'p',
+ buildid=newBuildID_C, testcaseid=newTestCaseID_AA,
+ platformname=NEWPLATFORM_C, notes="TP delete test")
+print("reportTCResult", newResult)
+newResultID_B = newResult[0]['id']
+
+#FIXME: know 1.9.20_fixed issue
+# testlink.testlinkerrors.TLResponseError: 6002: (uploadExecutionAttachment) - Error inserting attachment on DB
+#
+# E_WARNING base64_decode() expects parameter 1 to be string, array given
+# - in /var/www/html/lib/api/xmlrpc/v1/xmlrpc.class.php - Line 5834
+#
+# newAttachment = myTestLink.uploadExecutionAttachment(NEWATTACHMENT_PY, newResultID_B,
+# title='Textfile Example', filename='MyPyTPDeleteTest.py',
+# description='Attachment Example for a TC Execution and TP delete test')
+# print("uploadExecutionAttachment", newAttachment)
+response = myTestLink.getTotalsForTestPlan(newTestPlanID_C)
+print("getTotalsForTestPlan before delete", response)
+response = myTestLink.deleteTestPlan(newTestPlanID_C)
+print("deleteTestPlan", response)
+try:
+ response = myTestLink.getTotalsForTestPlan(newTestPlanID_C)
+ print("getTotalsForTestPlan after delete", response)
+except TLResponseError as tl_err:
+ print(tl_err.message)
+
+# -- Create Build D and copy Testers from Build A
+newBuild = myTestLink.createBuild(newTestPlanID_A, NEWBUILD_D,
+ buildnotes='Build with copied testers from Build ' + NEWBUILD_A,
+ active=1, open=1, copytestersfrombuild=newBuildID_A)
+print("createBuild", newBuild)
+newBuildID_D = newBuild[0]['id']
+print("New Build '%s' - id: %s" % (NEWBUILD_D, newBuildID_D))
+
+# close build A - buildid must be converted to an integer
+response = myTestLink.closeBuild( int(newBuildID_A) )
+print("closeBuild", response)
+
+# get information - TestProject
+response = myTestLink.getTestProjectByName(NEWPROJECT)
+print("getTestProjectByName", response)
+response = myTestLink.getProjectTestPlans(newProjectID)
+print("getProjectTestPlans", response)
+response = myTestLink.getFirstLevelTestSuitesForTestProject(newProjectID)
+print("getFirstLevelTestSuitesForTestProject", response)
+response = myTestLink.getProjectPlatforms(newProjectID)
+print("getProjectPlatforms", response)
+response = myTestLink.getProjectKeywords(newProjectID)
+print("getProjectKeywords", response)
+
+# get information - TestPlan
+response = myTestLink.getTestPlanByName(NEWPROJECT, NEWTESTPLAN_A)
+print("getTestPlanByName", response)
+response = myTestLink.getTotalsForTestPlan(newTestPlanID_A)
+print("getTotalsForTestPlan", response)
+response = myTestLink.getBuildsForTestPlan(newTestPlanID_A)
+print("getBuildsForTestPlan", response)
+response = myTestLink.getLatestBuildForTestPlan(newTestPlanID_A)
+print("getLatestBuildForTestPlan", response)
+response = myTestLink.getTestPlanPlatforms(newTestPlanID_A)
+print("getTestPlanPlatforms", response)
+response = myTestLink.getTestSuitesForTestPlan(newTestPlanID_A)
+print("getTestSuitesForTestPlan", response)
+# get failed Testcases
+response = myTestLink.getTestCasesForTestPlan(newTestPlanID_A, executestatus='f')
+print("getTestCasesForTestPlan A failed ", response)
+# get Testcases for Plattform SmallBird
+response = myTestLink.getTestCasesForTestPlan(newTestPlanID_A, platformid=newPlatFormID_B)
+print("getTestCasesForTestPlan A SmallBirds", response)
+
+# get information - TestSuite
+response = myTestLink.getTestSuiteByID(newTestSuiteID_B)
+print("getTestSuiteByID", response)
+response = myTestLink.getTestSuitesForTestSuite(newTestSuiteID_A)
+print("getTestSuitesForTestSuite", response)
+response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_A,
+ deep=True, details='full')
+print("getTestCasesForTestSuite", response)
+response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_B,
+ deep=False, details='only_id')
+print("getTestCasesForTestSuite", response)
+
+# Update test suite B details - Using Project ID
+updatedTestSuite = myTestLink.updateTestSuite(newTestSuiteID_B,
+ testprojectid=newProjectID,
+ details="updated Details of the Test Suite B")
+print("updateTestSuite", updatedTestSuite)
+
+# Update test suite A name and order details - Using Project Name
+# with TL 1.9.15 this step fails - solution see TL Mantis Ticket 7696
+#
+changedNEWTESTSUITE_A = NEWTESTSUITE_A + ' - Changed'
+updatedTestSuite = myTestLink.updateTestSuite(newTestSuiteID_A, prefix=NEWPREFIX,
+ testsuitename = changedNEWTESTSUITE_A, order=1)
+print("updateTestSuite", updatedTestSuite)
+
+# get all test suites, using the same name - test Suite B
+response = myTestLink.getTestSuite(NEWTESTSUITE_B, NEWPREFIX)
+print("getTestSuite", response)
+
+# get informationen - TestCase_B
+response = myTestLink.getTestCaseIDByName(NEWTESTCASE_B, testprojectname=NEWPROJECT)
+print("getTestCaseIDByName", response)
+# get informationen - TestCase_AA via Pathname
+tcpathname = '::'.join([NEWPROJECT, changedNEWTESTSUITE_A, NEWTESTSUITE_AA, NEWTESTCASE_AA])
+response = myTestLink.getTestCaseIDByName('unknown', testcasepathname=tcpathname)
+print("getTestCaseIDByName", response)
+# get execution result
+response = myTestLink.getLastExecutionResult(newTestPlanID_A,
+ testcaseexternalid=tc_aa_full_ext_id)
+print("getLastExecutionResult", response)
+response = myTestLink.getExecutionSet(newTestPlanID_A,
+ testcaseexternalid=tc_aa_full_ext_id)
+print("getExecutionSet", response)
+response = myTestLink.getLastExecutionResult(newTestPlanID_A,
+ testcaseid=newTestCaseID_B)
+print("getLastExecutionResult", response)
+response = myTestLink.getExecutionSet(newTestPlanID_A,
+ testcaseid=newTestCaseID_B)
+print("getExecutionSet", response)
+
+response = myTestLink.getLastExecutionResult(newTestPlanID_A,
+ testcaseid=newTestCaseID_AA, platformid=newPlatFormID_A)
+print("getLastExecutionResult", response)
+response = myTestLink.getExecutionSet(newTestPlanID_A,
+ testcaseid=newTestCaseID_AA, platformid=newPlatFormID_A)
+print("getExecutionSet", response)
+
+response = myTestLink.getAllExecutionsResults(newTestPlanID_A,
+ testcaseid=newTestCaseID_AA, options={'getBugs' : 1})
+print("getAllExecutionsResults", response)
+response = myTestLink.getAllExecutionsResults(newTestPlanID_B,
+ testcaseid=newTestCaseID_B, options={'getBugs' : 1})
+print("getAllExecutionsResults", response)
+
+response = myTestLink.getExecCountersByBuild(newTestPlanID_A)
+print("getExecCountersByBuild", response)
+response = myTestLink.getExecCountersByBuild(newTestPlanID_B)
+print("getExecCountersByBuild", response)
+response = myTestLink.getTestCaseKeywords(testcaseexternalid=tc_b_full_ext_id)
+print("getTestCaseKeywords noKeyWords", response)
+
+# get information - general
+response = myTestLink.getFullPath(int(newTestSuiteID_AA))
+print("getFullPath", response)
+response = myTestLink.getFullPath([int(newTestCaseID_AA), int(newTestCaseID_B)])
+print("getFullPath", response)
+
+# attachments
+# add png file as Attachment to test project
+a_file=open(NEWATTACHMENT_PNG, mode='rb')
+newAttachment = myTestLink.uploadTestProjectAttachment(a_file, newProjectID,
+ title='PNG Example', description='PNG Attachment Example for a TestProject')
+print("uploadTestProjectAttachment", newAttachment)
+# add png file as Attachnent to test suite A - uploadXyzAttachmemt also file path
+newAttachment = myTestLink.uploadTestSuiteAttachment(NEWATTACHMENT_PNG, newTestSuiteID_A,
+ title='PNG Example', description='PNG Attachment Example for a TestSuite')
+print("uploadTestSuiteAttachment", newAttachment)
+# get Attachment of test suite A
+response = myTestLink.getTestSuiteAttachments(newTestSuiteID_A)
+print("getTestSuiteAttachments", response)
+
+# FIXME: know 1.9.19 issue
+# E_WARNING base64_decode() expects parameter 1 to be string, array given
+# - in /var/www/html/lib/api/xmlrpc/v1/xmlrpc.class.php - Line 5103
+#
+# # add png file as Attachment to test case B
+# a_file=open(NEWATTACHMENT_PNG, mode='rb')
+# newAttachment = myTestLink.uploadTestCaseAttachment(a_file, newTestCaseID_B, 1,
+# title='PNG Example', description='PNG Attachment Example for a TestCase')
+# print("uploadTestCaseAttachment", newAttachment)
+# # get Attachment of test case B
+# # response = myTestLink.getTestCaseAttachments(testcaseexternalid=tc_aa_full_ext_id)
+# # print "getTestCaseAttachments", response
+# response = myTestLink.getTestCaseAttachments(testcaseid=newTestCaseID_B, version=1)
+# print("getTestCaseAttachments v1", response)
+# response = myTestLink.getTestCaseAttachments(testcaseid=newTestCaseID_B)
+# print("getTestCaseAttachments vNone", response)
+
+# get requirements for the test project - empty result
+response = myTestLink.getRequirements(newProjectID)
+print("getRequirements test project", "Sorry currently no requirments", response)
+
+# get requirements for the test plan - empty result
+response = myTestLink.getRequirements(newProjectID,
+ testplanid = newTestPlanID_A,
+ platformid = newPlatFormID_B)
+print("getRequirements test plan", "Sorry currently no requirments", response)
+
+
+print("")
+print("Number of Projects in TestLink: %i " % len(myTestLink.getProjects()))
+print("")
+for project in myTestLink.getProjects():
+ print("Name: %(name)s ID: %(id)s " % project)
+print("")
+
+
+#
+#
+#
diff --git a/example/TestLinkExampleGenericApi_Req.py b/example/TestLinkExampleGenericApi_Req.py
new file mode 100644
index 0000000..10991b7
--- /dev/null
+++ b/example/TestLinkExampleGenericApi_Req.py
@@ -0,0 +1,348 @@
+#! /usr/bin/python
+# -*- coding: UTF-8 -*-
+
+# Copyright 2017-2019 Luiko Czub, TestLink-API-Python-client developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# ------------------------------------------------------------------------
+
+
+"""
+
+Shows how to use the TestLinkAPIGeneric for requirements
+This example requires a special existing project with special custom fields
+assigned and a set of requirements
+
+a) run example TestLinkExampleGenericApi.py
+ - this creates a project like PROJECT_API_GENERIC-36
+b) load custom field definitions customFields_ExampleDefs.xml
+ TL - Desktop - System - Define Custom Fields - Import
+c) assign custom fields to project PROJECT_API_GENERIC-36
+ TL - Desktop - Test Project - Assign Custom Fields
+d) load requirement definitions all-req.xml to project PROJECT_API_GENERIC-36
+ TL - Desktop - Requirements - Requirement Specification
+
+Script works with:
+
+TestProject PROJECT_API_GENERIC-36
+- TestSuite B - First Level
+ - TestCase TESTCASE_B
+- TestPlan TestPlan_API_GENERIC A (Platform Small Bird)
+ - Build TestlinkAPIGeneric v0.x.y
+
+Script creates custom values for TestCase TESTCASE_B
+- scope test specification and test execution
+
+Script returns custom field values from TestPlan and TestSuite, if the user has
+added manually some values.
+
+Cause of missing knowledge, how ids of kind
+- requirement and requirement specifications
+- testplan - testcase link
+could be requested via api, these example does not work currently.
+
+Script adds keywords KeyWord01 KeyWord02 KeyWord03 to test case TESTCASE_B,
+removes keyword KeyWord02 again.
+
+Script adds keywords KeyWord01 KeyWord02 to test case TESTCASE_AA,
+removes keyword KeyWord01 again.
+
+"""
+from testlink import TestlinkAPIGeneric, TestLinkHelper
+from testlink.testlinkerrors import TLResponseError
+import sys, os.path
+from platform import python_version
+
+# precondition a)
+# SERVER_URL and KEY are defined in environment
+# TESTLINK_API_PYTHON_SERVER_URL=http://YOURSERVER/testlink/lib/api/xmlrpc.php
+# TESTLINK_API_PYTHON_DEVKEY=7ec252ab966ce88fd92c25d08635672b
+#
+# alternative precondition b)
+# SERVEUR_URL and KEY are defined as command line arguments
+# python TestLinkExample.py --server_url http://YOURSERVER/testlink/lib/api/xmlrpc.php
+# --devKey 7ec252ab966ce88fd92c25d08635672b
+#
+# ATTENTION: With TestLink 1.9.7, cause of the new REST API, the SERVER_URL
+# has changed from
+# (old) http://YOURSERVER/testlink/lib/api/xmlrpc.php
+# to
+# (new) http://YOURSERVER/testlink/lib/api/xmlrpc/v1/xmlrpc.php
+tl_helper = TestLinkHelper()
+tl_helper.setParamsFromArgs('''Shows how to use the TestLinkAPI for CustomFields.
+=> requires an existing project PROJECT_API_GENERIC-*''')
+myTestLink = tl_helper.connect(TestlinkAPIGeneric)
+
+myPyVersion = python_version()
+myPyVersionShort = myPyVersion.replace('.', '')[:2]
+
+NEWTESTPLAN_A="TestPlan_API_GENERIC A"
+# NEWTESTPLAN_B="TestPlan_API_GENERIC B"
+# NEWTESTPLAN_C="TestPlan_API_GENERIC C - DeleteTest"
+# NEWPLATFORM_A='Big Bird %s' % myPyVersionShort
+NEWPLATFORM_B='Small Bird'
+# NEWPLATFORM_C='Ugly Bird'
+NEWTESTSUITE_A="A - First Level"
+NEWTESTSUITE_B="B - First Level"
+NEWTESTSUITE_AA="AA - Second Level"
+NEWTESTCASE_AA="TESTCASE_AA"
+NEWTESTCASE_B="TESTCASE_B"
+# myApiVersion='%s v%s' % (myTestLink.__class__.__name__ , myTestLink.__version__)
+# NEWBUILD_A='%s' % myApiVersion
+# NEWBUILD_B='%s' % myApiVersion
+# NEWBUILD_C='%s - DeleteTest' % myApiVersion
+# NEWBUILD_D='%s - copyTestersTest' % myApiVersion
+
+this_file_dirname=os.path.dirname(__file__)
+NEWATTACHMENT_PY= os.path.join(this_file_dirname, 'TestLinkExampleGenericApi.py')
+NEWATTACHMENT_PNG=os.path.join(this_file_dirname, 'PyGreat.png')
+
+# Servers TestLink Version
+myTLVersion = myTestLink.testLinkVersion()
+myTLVersionShort = myTLVersion.replace('.', '')
+
+NEWPROJECT="PROJECT_API_GENERIC-%s" % myPyVersionShort
+NEWPREFIX="GPROAPI%s" % myPyVersionShort
+ITSNAME="myITS"
+
+# used connection settings
+print( myTestLink.connectionInfo() )
+print( "" )
+
+# get information - TestProject
+newProject = myTestLink.getTestProjectByName(NEWPROJECT)
+print( "getTestProjectByName", newProject )
+newProjectID = newProject['id']
+print( "Project '%s' - id: %s" % (NEWPROJECT,newProjectID) )
+response = myTestLink.getProjectKeywords(newProjectID)
+print("getProjectKeywords", response)
+
+# get information - TestPlan
+newTestPlan = myTestLink.getTestPlanByName(NEWPROJECT, NEWTESTPLAN_A)
+print( "getTestPlanByName", newTestPlan )
+newTestPlanID_A = newTestPlan[0]['id']
+print( "Test Plan '%s' - id: %s" % (NEWTESTPLAN_A,newTestPlanID_A) )
+response = myTestLink.getTotalsForTestPlan(newTestPlanID_A)
+print( "getTotalsForTestPlan", response )
+response = myTestLink.getBuildsForTestPlan(newTestPlanID_A)
+print( "getBuildsForTestPlan", response )
+newBuildID_A = response[0]['id']
+newBuildName_A = response[0]['name']
+# get information - TestSuite
+response = myTestLink.getTestSuitesForTestPlan(newTestPlanID_A)
+print( "getTestSuitesForTestPlan", response )
+newTestSuiteID_A=response[0]['id']
+newTestSuiteID_AA=response[1]['id']
+newTestSuiteID_B=response[2]['id']
+newTestSuite = myTestLink.getTestSuiteByID(newTestSuiteID_B)
+print( "getTestSuiteByID", newTestSuite )
+# get informationen - TestCase_B
+response = myTestLink.getTestCaseIDByName(NEWTESTCASE_B, testprojectname=NEWPROJECT)
+print( "getTestCaseIDByName", response )
+newTestCaseID_B = response[0]['id']
+tc_b_full_ext_id = myTestLink.getTestCase(testcaseid=newTestCaseID_B)[0]['full_tc_external_id']
+print( "Test Case '%s' - id: %s - ext-id %s" % (NEWTESTCASE_B, newTestCaseID_B, tc_b_full_ext_id) )
+# get informationen - TestCase_AA
+response = myTestLink.getTestCaseIDByName(NEWTESTCASE_AA, testprojectname=NEWPROJECT)
+print( "getTestCaseIDByName", response )
+newTestCaseID_AA = response[0]['id']
+tc_aa_full_ext_id = myTestLink.getTestCase(testcaseid=newTestCaseID_AA)[0]['full_tc_external_id']
+print( "Test Case '%s' - id: %s - ext-id %s" % (NEWTESTCASE_AA, newTestCaseID_AA, tc_aa_full_ext_id) )
+
+
+# add keywords to TestCase B and TestCase AA
+response = myTestLink.addTestCaseKeywords(
+ {tc_b_full_ext_id : ['KeyWord01', 'KeyWord03', 'KeyWord02'],
+ tc_aa_full_ext_id : ['KeyWord01', 'KeyWord02', 'KeyWord03']})
+print( "addTestCaseKeywords", response )
+# remove keywords from TestCase B and TestCase AA
+response = myTestLink.removeTestCaseKeywords(
+ {tc_b_full_ext_id : ['KeyWord02'],
+ tc_aa_full_ext_id : ['KeyWord01', 'KeyWord03']})
+print( "removeTestCaseKeywords", response )
+
+
+# list test cases with assigned keywords B
+response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_B, deep=True,
+ details='full', getkeywords=True)
+print( "getTestCasesForTestSuite B (deep=True)", response )
+response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_B, deep=False,
+ details='full', getkeywords=True)
+print( "getTestCasesForTestSuite B (deep=False)", response )
+
+# get informationen - TestCase_B again
+newTestCase_B = myTestLink.getTestCase(testcaseid=newTestCaseID_B)[0]
+print( "getTestCase B", newTestCase_B )
+
+# # return keyword list for TestCase_B - service method TestLinkAPIClient
+# response = myTestLink.listKeywordsForTC(newTestCaseID_B)
+# print( "listKeywordsForTC B", response )
+# # return keyword lists for all test cases of test newTestSuite_B - service method TestLinkAPIClient
+# response = myTestLink.listKeywordsForTS(newTestSuiteID_B)
+# print( "listKeywordsForTS B", response )
+
+# list test cases with assigned keywords AA
+response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_A, deep=True,
+ details='full', getkeywords=True)
+print( "getTestCasesForTestSuite A (deep=True)", response )
+response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_A, deep=False,
+ details='full', getkeywords=True)
+print( "getTestCasesForTestSuite A (deep=False)", response )
+
+# get informationen - TestCase_AA again
+newTestCase_AA = myTestLink.getTestCase(testcaseid=newTestCaseID_AA)[0]
+print( "getTestCase AA", newTestCase_AA )
+
+# # return keyword list for TestCase_AA - service method TestLinkAPIClient
+# response = myTestLink.listKeywordsForTC(newTestCaseID_AA)
+# print( "listKeywordsForTC AA", response ) - service method TestLinkAPIClient
+# # return keyword lists for all test cases of test newTestSuite_A
+# response = myTestLink.listKeywordsForTS(newTestSuiteID_AA)
+# print( "listKeywordsForTS AA", response )
+
+
+response = myTestLink.getTestCaseKeywords(testcaseid=newTestCaseID_B)
+print("getTestCaseKeywords B", response)
+response = myTestLink.getTestCaseKeywords(testcaseid=newTestCaseID_AA)
+print("getTestCaseKeywords AA", response)
+
+# new execution result with custom field data
+# TC_B passed, explicit build and some notes , TC identified with internal id
+newResult = myTestLink.reportTCResult(newTestPlanID_A, 'p',
+ buildname=newBuildName_A, testcaseid=newTestCaseID_B,
+ platformname=NEWPLATFORM_B, notes="bugid 4711 is assigned",
+ bugid='4711',
+ customfields={'cf_tc_ex_string' : 'a custom exec value set via api',
+ 'cf_tc_sd_listen' : 'ernie'})
+print( "reportTCResult", newResult )
+
+# get execution results
+lastResult = myTestLink.getLastExecutionResult(newTestPlanID_A,
+ testcaseid=newTestCaseID_B,
+ options={'getBugs' : True})[0]
+print( "getLastExecutionResult", lastResult )
+
+# get all requirement for the testprojekt
+req_list = myTestLink.getRequirements(newProjectID)
+print ( "getRequirements all", req_list )
+reqA = req_list[0]
+reqB = req_list[1]
+
+# add requirement reqA to testcase B
+# response = myTestLink.assignRequirements(newTestCase_B['full_tc_external_id'], newProjectID,
+# [{'req_spec' : reqA['srs_id'], 'requirements' : [ reqA['id'] ]} ]
+# )
+print("assignRequirements reqA to TC-B",
+ "sorry not possible - required srs_id -rec_spec(id) not available")
+
+# get coverage for requirements reqA
+response = myTestLink.getReqCoverage(newProjectID, reqA['req_doc_id'])
+print("getReqCoverage reqA", response)
+
+# add png file as Attachemnt to a requirement specification.
+print("uploadRequirementSpecificationAttachment",
+ "sorry not possible - required srs_id -rec_spec(id) not available")
+# add png file as Attachemnt to a requirement.
+#a_file=open(NEWATTACHMENT_PNG, mode='rb')
+newAttachment = myTestLink.uploadRequirementAttachment(NEWATTACHMENT_PNG, reqA['id'],
+ title='PNG Example', description='PNG Attachment Example for a requirement')
+print("uploadRequirementAttachment", newAttachment)
+
+
+# map of used ids
+args = {'devKey' : myTestLink.devKey,
+ 'testprojectid' : newProjectID,
+ 'testcaseexternalid' : newTestCase_B['full_tc_external_id'],
+ 'version' : int(newTestCase_B['version']),
+ 'tcversion_number' : lastResult['tcversion_number'],
+ 'executionid' : lastResult['id'],
+ 'linkid' : 779,
+ 'testsuiteid': newTestSuiteID_B,
+ 'testplanid': lastResult['testplan_id'],
+ 'reqspecid': 7789,
+# 'reqspecid': reqA['srs_id'],
+ 'requirementid': reqA['id'],
+ 'buildid':newBuildID_A}
+
+# get CustomField Value - TestCase Execution
+response = myTestLink.getTestCaseCustomFieldExecutionValue(
+ 'cf_tc_ex_string', args['testprojectid'], args['tcversion_number'],
+ args['executionid'] , args['testplanid'] )
+print( "getTestCaseCustomFieldExecutionValue", response )
+
+# update CustomField Value - TestCase SpecDesign
+response = myTestLink.updateTestCaseCustomFieldDesignValue(
+ args['testcaseexternalid'], args['version'],
+ args['testprojectid'],
+ {'cf_tc_sd_string' : 'A custom SpecDesign value set via api',
+ 'cf_tc_sd_list' : 'bibo'})
+print( "updateTestCaseCustomFieldDesignValue", response )
+
+# get CustomField Value - TestCase SpecDesign
+#response = myTestLink._callServer('getTestCaseCustomFieldDesignValue', args)
+response = myTestLink.getTestCaseCustomFieldDesignValue(
+ args['testcaseexternalid'], args['version'],
+ args['testprojectid'], 'cf_tc_sd_string', details='full')
+print( "getTestCaseCustomFieldDesignValue full", response )
+
+response = myTestLink.getTestCaseCustomFieldDesignValue(
+ args['testcaseexternalid'], args['version'],
+ args['testprojectid'], 'cf_tc_sd_string', details='value')
+print( "getTestCaseCustomFieldDesignValue value", response )
+
+response = myTestLink.getTestCaseCustomFieldDesignValue(
+ args['testcaseexternalid'], args['version'],
+ args['testprojectid'], 'cf_tc_sd_list', details='simple')
+print( "getTestCaseCustomFieldDesignValue simple", response )
+
+# get CustomField Value - TestCase Testplan Design
+response = myTestLink.getTestCaseCustomFieldTestPlanDesignValue(
+ 'cf_tc_pd_string', args['testprojectid'], args['tcversion_number'],
+ args['testplanid'], args['linkid'])
+print( "getTestCaseCustomFieldTestPlanDesignValue", response )
+
+# update CustomField Value - TestSuite SpecDesign
+response = myTestLink.updateTestSuiteCustomFieldDesignValue(
+ args['testprojectid'], args['testsuiteid'],
+ {'cf_ts_string' : 'A custom TestSuite value set via api'})
+print( "updateTestSuiteCustomFieldDesignValue", response )
+
+# get CustomField Value - TestSuite
+response = myTestLink.getTestSuiteCustomFieldDesignValue(
+ 'cf_ts_string', args['testprojectid'], args['testsuiteid'])
+print( "getTestSuiteCustomFieldDesignValue", response )
+
+# get CustomField Value - TestPlan
+response = myTestLink.getTestPlanCustomFieldDesignValue(
+ 'cf_tp_string', args['testprojectid'], args['testplanid'])
+print( "getTestPlanCustomFieldDesignValue", response )
+
+# get CustomField Value - Requirement Specification
+response = myTestLink.getReqSpecCustomFieldDesignValue(
+ 'cf_req_sd_string', args['testprojectid'], args['reqspecid'])
+print( "getReqSpecCustomFieldDesignValue", response )
+
+
+# get CustomField Value - Requirement Specification
+response = myTestLink.getRequirementCustomFieldDesignValue(
+ 'cf_req_string',args['testprojectid'], args['requirementid'])
+print( "getRequirementCustomFieldDesignValue", response )
+
+# update CustomField Value - Build
+response = myTestLink.updateBuildCustomFieldsValues(
+ args['testprojectid'], args['testplanid'], args['buildid'],
+ {'cf_b_string' : 'A custom Build value set via api'})
+print( "updateBuildCustomFieldsValues", response )
+
+
diff --git a/example/TestLinkExample_CF_KW.py b/example/TestLinkExample_CF_KW.py
index e276942..b7e19aa 100644
--- a/example/TestLinkExample_CF_KW.py
+++ b/example/TestLinkExample_CF_KW.py
@@ -1,305 +1,326 @@
-#! /usr/bin/python
-# -*- coding: UTF-8 -*-
-
-# Copyright 2013-2015 Luiko Czub, TestLink-API-Python-client developers
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# ------------------------------------------------------------------------
-
-
-"""
-
-Shows how to use the TestLinkAPI for custom fields
-This example requires a special existing project with special custom fields
-assigned
-
-a) run example TestLinkExample.py
- - this creates a project like NEW_PROJECT_API-34
- if some additional project are created since running that example, adapt
- variable projNr in this script your are reading currently
-b) load custom field definitions customFields_ExampleDefs.xml
- TL - Desktop - System - Define Custom Fields - Import
-c) assign custom fields to project NEW_PROJECT_API-34
- TL - Desktop - Test Project - Assign Custom Fields
-d) load keyword definitions keywords_ExampleDefs.xml
- TL - Desktop - Test Project - Keyword Management
-
-Script works with:
-
-TestProject NEW_PROJECT_API-34
-- TestSuite B - First Level
- - TestCase TESTCASE_B
-- TestPlan TestPlan_API_GENERIC A (Platform Small Bird)
- - Build TestlinkAPIGeneric v0.x.y
-
-Script creates custom values for TestCase TESTCASE_B
-- scope test specification and test execution
-
-Script returns custom field values from TestPlan and TestSuite, if the user has
-added manually some values.
-
-Cause of missing knowledge, how ids of kind
-- requirement and requirement specifications
-- testplan - testcase link
-could be requested via api, these example does not work currently.
-
-Script adds keywords KeyWord01 KeyWord02 KeyWord03 to test case TESTCASE_B,
-removes keyword KeyWord02 again.
-
-Script adds keywords KeyWord01 KeyWord02 to test case TESTCASE_AA,
-removes keyword KeyWord01 again.
-
-"""
-from testlink import TestlinkAPIClient, TestLinkHelper
-from testlink.testlinkerrors import TLResponseError
-import sys, os.path
-from platform import python_version
-
-# precondition a)
-# SERVER_URL and KEY are defined in environment
-# TESTLINK_API_PYTHON_SERVER_URL=http://YOURSERVER/testlink/lib/api/xmlrpc.php
-# TESTLINK_API_PYTHON_DEVKEY=7ec252ab966ce88fd92c25d08635672b
-#
-# alternative precondition b)
-# SERVEUR_URL and KEY are defined as command line arguments
-# python TestLinkExample.py --server_url http://YOURSERVER/testlink/lib/api/xmlrpc.php
-# --devKey 7ec252ab966ce88fd92c25d08635672b
-#
-# ATTENTION: With TestLink 1.9.7, cause of the new REST API, the SERVER_URL
-# has changed from
-# (old) http://YOURSERVER/testlink/lib/api/xmlrpc.php
-# to
-# (new) http://YOURSERVER/testlink/lib/api/xmlrpc/v1/xmlrpc.php
-tl_helper = TestLinkHelper()
-tl_helper.setParamsFromArgs('''Shows how to use the TestLinkAPI for CustomFields.
-=> requires an existing project NEW_PROJECT_API-*''')
-myTestLink = tl_helper.connect(TestlinkAPIClient)
-
-myPyVersion = python_version()
-myPyVersionShort = myPyVersion.replace('.', '')[:2]
-
-NEWPROJECT="NEW_PROJECT_API-%s" % myPyVersionShort
-NEWPREFIX="NPROAPI%s" % myPyVersionShort
-NEWTESTPLAN_A="TestPlan_API A"
-# NEWTESTPLAN_B="TestPlan_API B"
-# NEWPLATFORM_A='Big Bird %i' % projNr
-NEWPLATFORM_B='Small Birds'
-# NEWPLATFORM_C='Ugly Bird'
-NEWTESTSUITE_A="A - First Level"
-NEWTESTSUITE_B="B - First Level"
-NEWTESTSUITE_AA="AA - Second Level"
-NEWTESTCASE_AA="TESTCASE_AA"
-NEWTESTCASE_B="TESTCASE_B"
-# myApiVersion='%s v%s' % (myTestLink.__class__.__name__ , myTestLink.__version__)
-# NEWBUILD_A='%s' % myApiVersion
-# NEWBUILD_B='%s' % myApiVersion
-
-NEWATTACHMENT_PY= os.path.realpath(__file__)
-this_file_dirname=os.path.dirname(NEWATTACHMENT_PY)
-NEWATTACHMENT_PNG=os.path.join(this_file_dirname, 'PyGreat.png')
-
-# Servers TestLink Version
-myTLVersion = myTestLink.testLinkVersion()
-
-# used connection settings
-print( myTestLink.connectionInfo() )
-print( "" )
-
-# get information - TestProject
-newProject = myTestLink.getTestProjectByName(NEWPROJECT)
-print( "getTestProjectByName", newProject )
-newProjectID = newProject['id']
-print( "Project '%s' - id: %s" % (NEWPROJECT,newProjectID) )
-response = myTestLink.getProjectKeywords(newProjectID)
-print("getProjectKeywords", response)
-
-# get information - TestPlan
-newTestPlan = myTestLink.getTestPlanByName(NEWPROJECT, NEWTESTPLAN_A)
-print( "getTestPlanByName", newTestPlan )
-newTestPlanID_A = newTestPlan[0]['id']
-print( "Test Plan '%s' - id: %s" % (NEWTESTPLAN_A,newTestPlanID_A) )
-response = myTestLink.getTotalsForTestPlan(newTestPlanID_A)
-print( "getTotalsForTestPlan", response )
-response = myTestLink.getBuildsForTestPlan(newTestPlanID_A)
-print( "getBuildsForTestPlan", response )
-newBuildID_A = response[0]['id']
-newBuildName_A = response[0]['name']
-# get information - TestSuite
-response = myTestLink.getTestSuitesForTestPlan(newTestPlanID_A)
-print( "getTestSuitesForTestPlan", response )
-newTestSuiteID_A=response[0]['id']
-newTestSuiteID_AA=response[1]['id']
-newTestSuiteID_B=response[2]['id']
-newTestSuite = myTestLink.getTestSuiteByID(newTestSuiteID_B)
-print( "getTestSuiteByID", newTestSuite )
-# get informationen - TestCase_B
-response = myTestLink.getTestCaseIDByName(NEWTESTCASE_B, testprojectname=NEWPROJECT)
-print( "getTestCaseIDByName", response )
-newTestCaseID_B = response[0]['id']
-tc_b_full_ext_id = myTestLink.getTestCase(newTestCaseID_B)[0]['full_tc_external_id']
-print( "Test Case '%s' - id: %s - ext-id %s" % (NEWTESTCASE_B, newTestCaseID_B, tc_b_full_ext_id) )
-# get informationen - TestCase_AA
-response = myTestLink.getTestCaseIDByName(NEWTESTCASE_AA, testprojectname=NEWPROJECT)
-print( "getTestCaseIDByName", response )
-newTestCaseID_AA = response[0]['id']
-tc_aa_full_ext_id = myTestLink.getTestCase(newTestCaseID_AA)[0]['full_tc_external_id']
-print( "Test Case '%s' - id: %s - ext-id %s" % (NEWTESTCASE_AA, newTestCaseID_AA, tc_aa_full_ext_id) )
-
-
-# add keywords to TestCase B and TestCase AA
-response = myTestLink.addTestCaseKeywords(
- {tc_b_full_ext_id : ['KeyWord01', 'KeyWord03', 'KeyWord02'],
- tc_aa_full_ext_id : ['KeyWord01', 'KeyWord02', 'KeyWord03']})
-print( "addTestCaseKeywords", response )
-# remove keywords from TestCase B and TestCase AA
-response = myTestLink.removeTestCaseKeywords(
- {tc_b_full_ext_id : ['KeyWord02'],
- tc_aa_full_ext_id : ['KeyWord01', 'KeyWord03']})
-print( "removeTestCaseKeywords", response )
-
-
-# list test cases with assigned keywords B
-response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_B, True,
- 'full', getkeywords=True)
-print( "getTestCasesForTestSuite B (deep=True)", response )
-response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_B, False,
- 'full', getkeywords=True)
-print( "getTestCasesForTestSuite B (deep=False)", response )
-
-# get informationen - TestCase_B again
-newTestCase_B = myTestLink.getTestCase(testcaseid=newTestCaseID_B)[0]
-print( "getTestCase B", newTestCase_B )
-
-# return keyword list for TestCase_B
-response = myTestLink.listKeywordsForTC(newTestCaseID_B)
-print( "listKeywordsForTC B", response )
-# return keyword lists for all test cases of test newTestSuite_B
-response = myTestLink.listKeywordsForTS(newTestSuiteID_B)
-print( "listKeywordsForTS B", response )
-
-# list test cases with assigned keywords AA
-response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_A, True,
- 'full', getkeywords=True)
-print( "getTestCasesForTestSuite A (deep=True)", response )
-response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_A, False,
- 'full', getkeywords=True)
-print( "getTestCasesForTestSuite A (deep=False)", response )
-
-# get informationen - TestCase_AA again
-newTestCase_AA = myTestLink.getTestCase(testcaseid=newTestCaseID_AA)[0]
-print( "getTestCase AA", newTestCase_AA )
-
-# return keyword list for TestCase_AA
-response = myTestLink.listKeywordsForTC(newTestCaseID_AA)
-print( "listKeywordsForTC AA", response )
-# return keyword lists for all test cases of test newTestSuite_A
-response = myTestLink.listKeywordsForTS(newTestSuiteID_AA)
-print( "listKeywordsForTS AA", response )
-
-
-response = myTestLink.getTestCaseKeywords(testcaseid=newTestCaseID_B)
-print("getTestCaseKeywords B", response)
-response = myTestLink.getTestCaseKeywords(testcaseid=newTestCaseID_AA)
-print("getTestCaseKeywords AA", response)
-
-# new execution result with custom field data
-# TC_B passed, explicit build and some notes , TC identified with internal id
-newResult = myTestLink.reportTCResult(newTestCaseID_B, newTestPlanID_A,
- newBuildName_A, 'p', "bugid 4711 is assigned",
- platformname=NEWPLATFORM_B, bugid='4711',
- customfields={'cf_tc_ex_string' : 'a custom exec value set via api',
- 'cf_tc_sd_listen' : 'ernie'})
-print( "reportTCResult", newResult )
-
-# get execution results
-lastResult = myTestLink.getLastExecutionResult(newTestPlanID_A, newTestCaseID_B,
- options={'getBugs' : True})[0]
-print( "getLastExecutionResult", lastResult )
-
-# map of used ids
-args = {'devKey' : myTestLink.devKey,
- 'testprojectid' : newProjectID,
- 'testcaseexternalid' : newTestCase_B['full_tc_external_id'],
- 'version' : int(newTestCase_B['version']),
- 'tcversion_number' : lastResult['tcversion_number'],
- 'executionid' : lastResult['id'],
- 'linkid' : 779,
- 'testsuiteid': newTestSuiteID_B,
- 'testplanid': lastResult['testplan_id'],
- 'reqspecid': 7789,
- 'requirementid': 7791}
-
-# get CustomField Value - TestCase Execution
-response = myTestLink.getTestCaseCustomFieldExecutionValue(
- 'cf_tc_ex_string', args['testprojectid'], args['tcversion_number'],
- args['executionid'] , args['testplanid'] )
-print( "getTestCaseCustomFieldExecutionValue", response )
-
-# update CustomField Value - TestCase SpecDesign
-response = myTestLink.updateTestCaseCustomFieldDesignValue(
- args['testcaseexternalid'], args['version'],
- args['testprojectid'],
- {'cf_tc_sd_string' : 'A custom SpecDesign value set via api',
- 'cf_tc_sd_list' : 'bibo'})
-print( "updateTestCaseCustomFieldDesignValue", response )
-
-# get CustomField Value - TestCase SpecDesign
-#response = myTestLink._callServer('getTestCaseCustomFieldDesignValue', args)
-response = myTestLink.getTestCaseCustomFieldDesignValue(
- args['testcaseexternalid'], args['version'],
- args['testprojectid'], 'cf_tc_sd_string', 'full')
-print( "getTestCaseCustomFieldDesignValue full", response )
-
-response = myTestLink.getTestCaseCustomFieldDesignValue(
- args['testcaseexternalid'], args['version'],
- args['testprojectid'], 'cf_tc_sd_string', 'value')
-print( "getTestCaseCustomFieldDesignValue value", response )
-
-response = myTestLink.getTestCaseCustomFieldDesignValue(
- args['testcaseexternalid'], args['version'],
- args['testprojectid'], 'cf_tc_sd_list', 'simple')
-print( "getTestCaseCustomFieldDesignValue simple", response )
-
-# get CustomField Value - TestCase Testplan Design
-response = myTestLink.getTestCaseCustomFieldTestPlanDesignValue(
- 'cf_tc_pd_string', args['testprojectid'], args['tcversion_number'],
- args['testplanid'], args['linkid'])
-print( "getTestCaseCustomFieldTestPlanDesignValue", response )
-
-# update CustomField Value - TestSuite SpecDesign
-response = myTestLink.updateTestSuiteCustomFieldDesignValue(
- args['testprojectid'], args['testsuiteid'],
- {'cf_ts_string' : 'A custom TestSuite value set via api'})
-print( "updateTestSuiteCustomFieldDesignValue", response )
-
-# get CustomField Value - TestSuite
-response = myTestLink.getTestSuiteCustomFieldDesignValue(
- 'cf_ts_string', args['testprojectid'], args['testsuiteid'])
-print( "getTestSuiteCustomFieldDesignValue", response )
-
-# get CustomField Value - TestPlan
-response = myTestLink.getTestPlanCustomFieldDesignValue(
- 'cf_tp_string', args['testprojectid'], args['testplanid'])
-print( "getTestPlanCustomFieldDesignValue", response )
-
-# get CustomField Value - Requirement Specification
-response = myTestLink.getReqSpecCustomFieldDesignValue(
- 'cf_req_sd_string', args['testprojectid'], args['reqspecid'])
-print( "getReqSpecCustomFieldDesignValue", response )
-
-
-# get CustomField Value - Requirement Specification
-response = myTestLink.getRequirementCustomFieldDesignValue(
- 'cf_req_string',args['testprojectid'], args['requirementid'])
-print( "getRequirementCustomFieldDesignValue", response )
+#! /usr/bin/python
+# -*- coding: UTF-8 -*-
+
+# Copyright 2013-2019 Luiko Czub, TestLink-API-Python-client developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# ------------------------------------------------------------------------
+
+
+"""
+
+Shows how to use the TestLinkAPI for custom fields
+This example requires a special existing project with special custom fields
+assigned
+
+a) run example TestLinkExample.py
+ - this creates a project like NEW_PROJECT_API-37
+b) load custom field definitions customFields_ExampleDefs.xml
+ TL - Desktop - System - Define Custom Fields - Import
+c) assign custom fields to project NEW_PROJECT_API-37
+ TL - Desktop - Test Project - Assign Custom Fields
+d) load keyword definitions keywords_ExampleDefs.xml
+ TL - Desktop - Test Project - Keyword Management
+
+Script works with:
+
+TestProject NEW_PROJECT_API-37
+- TestSuite B - First Level
+ - TestCase TESTCASE_B
+- TestPlan TestPlan_API A (Platform Small Bird)
+ - Build TestlinkAPIClient v0.x.y
+
+Script creates custom values for TestCase TESTCASE_B
+- scope test specification and test execution
+
+Script returns custom field values from TestPlan and TestSuite, if the user has
+added manually some values.
+
+Cause of missing knowledge, how ids of kind
+- requirement and requirement specifications
+- testplan - testcase link
+could be requested via api, these example does not work currently.
+
+Script adds keywords KeyWord01 KeyWord02 KeyWord03 to test case TESTCASE_B,
+removes keyword KeyWord02 again.
+
+Script adds keywords KeyWord01 KeyWord02 to test case TESTCASE_AA,
+removes keyword KeyWord01 again.
+
+"""
+from testlink import TestlinkAPIClient, TestLinkHelper
+from testlink.testlinkerrors import TLResponseError
+import sys, os.path
+from platform import python_version
+
+# precondition a)
+# SERVER_URL and KEY are defined in environment
+# TESTLINK_API_PYTHON_SERVER_URL=http://YOURSERVER/testlink/lib/api/xmlrpc.php
+# TESTLINK_API_PYTHON_DEVKEY=7ec252ab966ce88fd92c25d08635672b
+#
+# alternative precondition b)
+# SERVEUR_URL and KEY are defined as command line arguments
+# python TestLinkExample.py --server_url http://YOURSERVER/testlink/lib/api/xmlrpc.php
+# --devKey 7ec252ab966ce88fd92c25d08635672b
+#
+# ATTENTION: With TestLink 1.9.7, cause of the new REST API, the SERVER_URL
+# has changed from
+# (old) http://YOURSERVER/testlink/lib/api/xmlrpc.php
+# to
+# (new) http://YOURSERVER/testlink/lib/api/xmlrpc/v1/xmlrpc.php
+tl_helper = TestLinkHelper()
+tl_helper.setParamsFromArgs('''Shows how to use the TestLinkAPI for CustomFields.
+=> requires an existing project NEW_PROJECT_API-*''')
+myTestLink = tl_helper.connect(TestlinkAPIClient)
+
+myPyVersion = python_version()
+myPyVersionShort = myPyVersion.replace('.', '')[:2]
+
+NEWTESTPLAN_A="TestPlan_API A"
+# NEWTESTPLAN_B="TestPlan_API B"
+# NEWTESTPLAN_C="TestPlan_API C - DeleteTest"
+# NEWPLATFORM_A='Big Birds %s' % myPyVersionShort
+NEWPLATFORM_B='Small Birds'
+# NEWPLATFORM_C='Ugly Birds'
+NEWTESTSUITE_A="A - First Level"
+NEWTESTSUITE_B="B - First Level"
+NEWTESTSUITE_AA="AA - Second Level"
+NEWTESTCASE_AA="TESTCASE_AA"
+NEWTESTCASE_B="TESTCASE_B"
+# myApiVersion='%s v%s' % (myTestLink.__class__.__name__ , myTestLink.__version__)
+# NEWBUILD_A='%s' % myApiVersion
+# NEWBUILD_B='%s' % myApiVersion
+# NEWBUILD_C='%s - DeleteTest' % myApiVersion
+# NEWBUILD_D='%s - copyTestersTest' % myApiVersion
+
+this_file_dirname=os.path.dirname(__file__)
+NEWATTACHMENT_PY= os.path.join(this_file_dirname, 'TestLinkExample.py')
+NEWATTACHMENT_PNG=os.path.join(this_file_dirname, 'PyGreat.png')
+
+# Servers TestLink Version
+myTLVersion = myTestLink.testLinkVersion()
+myTLVersionShort = myTLVersion.replace('.', '')
+
+NEWPROJECT="NEW_PROJECT_API-%s" % myPyVersionShort
+NEWPREFIX="NPROAPI%s" % myPyVersionShort
+ITSNAME="myITS"
+
+# used connection settings
+print( myTestLink.connectionInfo() )
+print( "" )
+
+# get information - TestProject
+newProject = myTestLink.getTestProjectByName(NEWPROJECT)
+print( "getTestProjectByName", newProject )
+newProjectID = newProject['id']
+print( "Project '%s' - id: %s" % (NEWPROJECT,newProjectID) )
+response = myTestLink.getProjectKeywords(newProjectID)
+print("getProjectKeywords", response)
+
+# get information - TestPlan
+newTestPlan = myTestLink.getTestPlanByName(NEWPROJECT, NEWTESTPLAN_A)
+print( "getTestPlanByName", newTestPlan )
+newTestPlanID_A = newTestPlan[0]['id']
+print( "Test Plan '%s' - id: %s" % (NEWTESTPLAN_A,newTestPlanID_A) )
+response = myTestLink.getTotalsForTestPlan(newTestPlanID_A)
+print( "getTotalsForTestPlan", response )
+response = myTestLink.getBuildsForTestPlan(newTestPlanID_A)
+print( "getBuildsForTestPlan", response )
+newBuildID_A = response[0]['id']
+newBuildName_A = response[0]['name']
+# get information - TestSuite
+response = myTestLink.getTestSuitesForTestPlan(newTestPlanID_A)
+print( "getTestSuitesForTestPlan", response )
+newTestSuiteID_A=response[0]['id']
+newTestSuiteID_AA=response[1]['id']
+newTestSuiteID_B=response[2]['id']
+newTestSuite = myTestLink.getTestSuiteByID(newTestSuiteID_B)
+print( "getTestSuiteByID", newTestSuite )
+# get informationen - TestCase_B
+response = myTestLink.getTestCaseIDByName(NEWTESTCASE_B, testprojectname=NEWPROJECT)
+print( "getTestCaseIDByName", response )
+newTestCaseID_B = response[0]['id']
+tc_b_full_ext_id = myTestLink.getTestCase(newTestCaseID_B)[0]['full_tc_external_id']
+print( "Test Case '%s' - id: %s - ext-id %s" % (NEWTESTCASE_B, newTestCaseID_B, tc_b_full_ext_id) )
+# get informationen - TestCase_AA
+response = myTestLink.getTestCaseIDByName(NEWTESTCASE_AA, testprojectname=NEWPROJECT)
+print( "getTestCaseIDByName", response )
+newTestCaseID_AA = response[0]['id']
+tc_aa_full_ext_id = myTestLink.getTestCase(newTestCaseID_AA)[0]['full_tc_external_id']
+print( "Test Case '%s' - id: %s - ext-id %s" % (NEWTESTCASE_AA, newTestCaseID_AA, tc_aa_full_ext_id) )
+
+
+# add keywords to TestCase B and TestCase AA
+response = myTestLink.addTestCaseKeywords(
+ {tc_b_full_ext_id : ['KeyWord01', 'KeyWord03', 'KeyWord02'],
+ tc_aa_full_ext_id : ['KeyWord01', 'KeyWord02', 'KeyWord03']})
+print( "addTestCaseKeywords", response )
+# remove keywords from TestCase B and TestCase AA
+response = myTestLink.removeTestCaseKeywords(
+ {tc_b_full_ext_id : ['KeyWord02'],
+ tc_aa_full_ext_id : ['KeyWord01', 'KeyWord03']})
+print( "removeTestCaseKeywords", response )
+
+
+# list test cases with assigned keywords B
+response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_B, True,
+ 'full', getkeywords=True)
+print( "getTestCasesForTestSuite B (deep=True)", response )
+response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_B, False,
+ 'full', getkeywords=True)
+print( "getTestCasesForTestSuite B (deep=False)", response )
+
+# get informationen - TestCase_B again
+newTestCase_B = myTestLink.getTestCase(testcaseid=newTestCaseID_B)[0]
+print( "getTestCase B", newTestCase_B )
+
+# return keyword list for TestCase_B
+response = myTestLink.listKeywordsForTC(newTestCaseID_B)
+print( "listKeywordsForTC B", response )
+# return keyword lists for all test cases of test newTestSuite_B
+response = myTestLink.listKeywordsForTS(newTestSuiteID_B)
+print( "listKeywordsForTS B", response )
+
+# list test cases with assigned keywords AA
+response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_A, True,
+ 'full', getkeywords=True)
+print( "getTestCasesForTestSuite A (deep=True)", response )
+response = myTestLink.getTestCasesForTestSuite(newTestSuiteID_A, False,
+ 'full', getkeywords=True)
+print( "getTestCasesForTestSuite A (deep=False)", response )
+
+# get informationen - TestCase_AA again
+newTestCase_AA = myTestLink.getTestCase(testcaseid=newTestCaseID_AA)[0]
+print( "getTestCase AA", newTestCase_AA )
+
+# return keyword list for TestCase_AA
+response = myTestLink.listKeywordsForTC(newTestCaseID_AA)
+print( "listKeywordsForTC AA", response )
+# return keyword lists for all test cases of test newTestSuite_A
+response = myTestLink.listKeywordsForTS(newTestSuiteID_AA)
+print( "listKeywordsForTS AA", response )
+
+
+response = myTestLink.getTestCaseKeywords(testcaseid=newTestCaseID_B)
+print("getTestCaseKeywords B", response)
+response = myTestLink.getTestCaseKeywords(testcaseid=newTestCaseID_AA)
+print("getTestCaseKeywords AA", response)
+
+# new execution result with custom field data
+# TC_B passed, explicit build and some notes , TC identified with internal id
+newResult = myTestLink.reportTCResult(newTestCaseID_B, newTestPlanID_A,
+ newBuildName_A, 'p', "bugid 4711 is assigned",
+ platformname=NEWPLATFORM_B, bugid='4711',
+ customfields={'cf_tc_ex_string' : 'a custom exec value set via api',
+ 'cf_tc_sd_listen' : 'ernie'})
+print( "reportTCResult", newResult )
+
+# get execution results
+lastResult = myTestLink.getLastExecutionResult(newTestPlanID_A, newTestCaseID_B,
+ options={'getBugs' : True})[0]
+print( "getLastExecutionResult", lastResult )
+
+# map of used ids
+args = {'devKey' : myTestLink.devKey,
+ 'testprojectid' : newProjectID,
+ 'testcaseexternalid' : newTestCase_B['full_tc_external_id'],
+ 'version' : int(newTestCase_B['version']),
+ 'tcversion_number' : lastResult['tcversion_number'],
+ 'executionid' : lastResult['id'],
+ 'linkid' : 779,
+ 'testsuiteid': newTestSuiteID_B,
+ 'testplanid': lastResult['testplan_id'],
+ 'reqspecid': 7789,
+ 'requirementid': 7791,
+ 'buildid':newBuildID_A}
+
+# get CustomField Value - TestCase Execution
+response = myTestLink.getTestCaseCustomFieldExecutionValue(
+ 'cf_tc_ex_string', args['testprojectid'], args['tcversion_number'],
+ args['executionid'] , args['testplanid'] )
+print( "getTestCaseCustomFieldExecutionValue", response )
+
+# update CustomField Value - TestCase SpecDesign
+response = myTestLink.updateTestCaseCustomFieldDesignValue(
+ args['testcaseexternalid'], args['version'],
+ args['testprojectid'],
+ {'cf_tc_sd_string' : 'A custom SpecDesign value set via api',
+ 'cf_tc_sd_list' : 'bibo'})
+print( "updateTestCaseCustomFieldDesignValue", response )
+
+# get CustomField Value - TestCase SpecDesign
+#response = myTestLink._callServer('getTestCaseCustomFieldDesignValue', args)
+response = myTestLink.getTestCaseCustomFieldDesignValue(
+ args['testcaseexternalid'], args['version'],
+ args['testprojectid'], 'cf_tc_sd_string', 'full')
+print( "getTestCaseCustomFieldDesignValue full", response )
+
+response = myTestLink.getTestCaseCustomFieldDesignValue(
+ args['testcaseexternalid'], args['version'],
+ args['testprojectid'], 'cf_tc_sd_string', 'value')
+print( "getTestCaseCustomFieldDesignValue value", response )
+
+response = myTestLink.getTestCaseCustomFieldDesignValue(
+ args['testcaseexternalid'], args['version'],
+ args['testprojectid'], 'cf_tc_sd_list', 'simple')
+print( "getTestCaseCustomFieldDesignValue simple", response )
+
+# get CustomField Value - TestCase Testplan Design
+response = myTestLink.getTestCaseCustomFieldTestPlanDesignValue(
+ 'cf_tc_pd_string', args['testprojectid'], args['tcversion_number'],
+ args['testplanid'], args['linkid'])
+print( "getTestCaseCustomFieldTestPlanDesignValue", response )
+
+# get CustomFields Values from all test cases of a TestPlan
+response = myTestLink.getTestCasesForTestPlan(args['testplanid'],
+ customfields=True)
+print("getTestCasesForTestPlan with all customfields", response)
+response = myTestLink.getTestCasesForTestPlan(args['testplanid'],
+ customfields=['cf_tc_sd_string'])
+print("getTestCasesForTestPlan with specific customfields", response)
+
+# update CustomField Value - TestSuite SpecDesign
+response = myTestLink.updateTestSuiteCustomFieldDesignValue(
+ args['testprojectid'], args['testsuiteid'],
+ {'cf_ts_string' : 'A custom TestSuite value set via api'})
+print( "updateTestSuiteCustomFieldDesignValue", response )
+
+# get CustomField Value - TestSuite
+response = myTestLink.getTestSuiteCustomFieldDesignValue(
+ 'cf_ts_string', args['testprojectid'], args['testsuiteid'])
+print( "getTestSuiteCustomFieldDesignValue", response )
+
+# get CustomField Value - TestPlan
+response = myTestLink.getTestPlanCustomFieldDesignValue(
+ 'cf_tp_string', args['testprojectid'], args['testplanid'])
+print( "getTestPlanCustomFieldDesignValue", response )
+
+# get CustomField Value - Requirement Specification
+response = myTestLink.getReqSpecCustomFieldDesignValue(
+ 'cf_req_sd_string', args['testprojectid'], args['reqspecid'])
+print( "getReqSpecCustomFieldDesignValue", response )
+
+
+# get CustomField Value - Requirement Specification
+response = myTestLink.getRequirementCustomFieldDesignValue(
+ 'cf_req_string',args['testprojectid'], args['requirementid'])
+print( "getRequirementCustomFieldDesignValue", response )
+
+# update CustomField Value - Build
+response = myTestLink.updateBuildCustomFieldsValues(
+ args['testprojectid'], args['testplanid'], args['buildid'],
+ {'cf_b_string' : 'A custom Build value set via api'})
+print( "updateBuildCustomFieldsValues", response )
+
+
diff --git a/example/all-req.xml b/example/all-req.xml
new file mode 100644
index 0000000..1a1b4f8
--- /dev/null
+++ b/example/all-req.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+a section
]]>
+
+
+
+
+ 1
+ 1
+ 1
+ a use case]]>
+
+
+
+
+
+
+
+
+ 1
+ 1
+ 2
+ a none functional requirement]]>
+
+
+
+
+
+
+ none-function-01
+ use-case-01
+ 2
+
+
+
+
+
+
+
+
+
+
+a user requirement]]>
+
+
+
+
+ 1
+ 1
+ 1
+ a restriction]]>
+
+
+
+
+
+
+
+
+ 1
+ 1
+ 2
+ a user gui]]>
+
+
+
+
+
+
+ system-func-01
+ user-gui-01
+ 2
+
+
+
+
+
+
+
+
+
+
+a system requirement]]>
+
+
+
+
+ 1
+ 1
+ 2
+ a system function]]>
+
+
+
+
+
+
+
+
+ 1
+ 1
+ 3
+ a feature]]>
+
+
+
+
+
+
+ system-func-01
+ user-gui-01
+ 2
+
+
diff --git a/example/customFields_ExampleDefs.xml b/example/customFields_ExampleDefs.xml
index 54a5635..02ca574 100644
--- a/example/customFields_ExampleDefs.xml
+++ b/example/customFields_ExampleDefs.xml
@@ -136,4 +136,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/robot/TestlinkAPILibrary.py b/robot/TestlinkAPILibrary.py
index 5074dd1..41f0f99 100644
--- a/robot/TestlinkAPILibrary.py
+++ b/robot/TestlinkAPILibrary.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2014 Luiko Czub
+# Copyright 2014-2019 Luiko Czub
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/robot/TestlinkSeLibExtension.py b/robot/TestlinkSeLibExtension.py
index e0b8cbb..0f291f2 100644
--- a/robot/TestlinkSeLibExtension.py
+++ b/robot/TestlinkSeLibExtension.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2014 Luiko Czub
+# Copyright 2014-2019 Luiko Czub
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/robot/tlapi.robot b/robot/tlapi.robot
index 94df447..069c8c1 100644
--- a/robot/tlapi.robot
+++ b/robot/tlapi.robot
@@ -1,4 +1,4 @@
-# Copyright 2014 Luiko Czub
+# Copyright 2014-2019 Luiko Czub
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/robot/tlweb.robot b/robot/tlweb.robot
index 784fa54..f7d4900 100644
--- a/robot/tlweb.robot
+++ b/robot/tlweb.robot
@@ -1,4 +1,4 @@
-# Copyright 2014 Luiko Czub
+# Copyright 2014-2019 Luiko Czub
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/robot/tlweb_resource.robot b/robot/tlweb_resource.robot
index 94bcda4..bdc7a8c 100644
--- a/robot/tlweb_resource.robot
+++ b/robot/tlweb_resource.robot
@@ -1,4 +1,4 @@
-# Copyright 2014 Luiko Czub
+# Copyright 2014-2019 Luiko Czub
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/setup.py b/setup.py
index 660d584..86ced60 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2012-2015 Luiko Czub, TestLink-API-Python-client developers
+# Copyright 2012-2019 Luiko Czub, TestLink-API-Python-client developers
#
# Licensed under Apache 2.0
#
@@ -17,10 +17,9 @@
'Development Status :: 5 - Production/Stable',
'License :: OSI Approved :: Apache Software License',
'Operating System :: OS Independent',
- 'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3.3',
- 'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
'Topic :: Software Development :: Testing',
'Topic :: Software Development :: Libraries :: Python Modules'
]
@@ -87,4 +86,3 @@
keywords = ['testing', 'testlink', 'xml-rpc', 'testautomation']
)
-
diff --git a/src/testlink/__init__.py b/src/testlink/__init__.py
index b62ffef..7b5de11 100644
--- a/src/testlink/__init__.py
+++ b/src/testlink/__init__.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2012-2015 TestLink-API-Python-client developers
+# Copyright 2012-2019 TestLink-API-Python-client developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -21,4 +21,6 @@
from .testlinkerrors import TestLinkError
from .testlinkapigeneric import TestlinkAPIGeneric
from .testlinkapi import TestlinkAPIClient
-from .testlinkhelper import TestLinkHelper
\ No newline at end of file
+from .testlinkhelper import TestLinkHelper
+from .testreporter import TestReporter
+from .testgenreporter import TestGenReporter
diff --git a/src/testlink/proxiedtransport.py b/src/testlink/proxiedtransport.py
deleted file mode 100644
index 0b907ab..0000000
--- a/src/testlink/proxiedtransport.py
+++ /dev/null
@@ -1,106 +0,0 @@
-#! /usr/bin/python
-# -*- coding: UTF-8 -*-
-
-# Copyright 2014-2015 Mario Benito, TestLink-API-Python-client developers
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# ------------------------------------------------------------------------
-
-import sys
-IS_PY3 = sys.version_info[0] > 2
-if IS_PY3:
- from xmlrpc.client import Transport
- from http.client import HTTPConnection
-else:
- from xmlrpclib import Transport
- from httplib import HTTPConnection
-
-try:
- import gzip
-except ImportError:
- gzip = None #python can be built without zlib/gzip support
-
-
-class ProxiedTransport(Transport):
- def __init__(self):
- if IS_PY3:
- super(ProxiedTransport, self).__init__()
- else:
- Transport.__init__(self)
- self.realhost = None
- self.proxy = None
-
- def set_proxy(self, proxy):
- """Define HTTP proxy (with optional basic auth)
-
- :param str proxy: Proxy string
- """
- cproxy, auth, x509 = self.get_host_info(proxy)
- self.proxy = cproxy
- if auth:
- auth = [ ('Proxy-Authorization', auth[0][1]) ]
- if self._extra_headers:
- self._extra_headers.extend(auth)
- else:
- self._extra_headers = auth
-
- def make_connection(self, host):
- """return an existing connection if possible. This allows HTTP/1.1 keep-alive.
-
- :param str|(str, {}) host: Host descriptor (URL or (URL, x509 info) tuple)
- :return httplib.HTTPConnection:
- """
- if self._connection and host == self._connection[0]:
- return self._connection[1]
-
- # create a HTTP connection object from a host descriptor
- chost, auth, x509 = self.get_host_info(host)
- if auth:
- if self._extra_headers:
- self._extra_headers.extend(auth)
- else:
- self._extra_headers = auth
- self.realhost = host
- self._connection = host, HTTPConnection(self.proxy)
- return self._connection[1]
-
- def send_request(self, connection, handler, request_body):
- """Send request header
-
- :param httplib.HTTPConnection connection: Connection handle
- :param str handler: Target RPC handler
- :param str request_body:XML-RPC body
- """
- if self.accept_gzip_encoding and gzip:
- connection.putrequest("POST", 'http://%s%s' % (self.realhost, handler), skip_accept_encoding=True)
- connection.putheader("Accept-Encoding", "gzip")
- else:
- connection.putrequest("POST", 'http://%s%s' % (self.realhost, handler))
-
- def send_host(self, connection, host):
- """Send host name
-
- Note: This function doesn't actually add the "Host"
- header anymore, it is done as part of the connection.putrequest() in
- send_request() above.
-
- :param httplib.HTTPConnection connection: Connection handle
- :param str host: Host name
- """
- extra_headers = self._extra_headers
- if extra_headers:
- if isinstance(extra_headers, dict()):
- extra_headers = extra_headers.items()
- for key, value in extra_headers:
- connection.putheader(key, value)
diff --git a/src/testlink/proxiedtransport2.py b/src/testlink/proxiedtransport2.py
new file mode 100644
index 0000000..dd7bf2c
--- /dev/null
+++ b/src/testlink/proxiedtransport2.py
@@ -0,0 +1,43 @@
+#! /usr/bin/python
+# -*- coding: UTF-8 -*-
+
+# Copyright 2014-2020 Mario Benito, Luiko Czub, TestLink-API-Python-client developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# ------------------------------------------------------------------------
+# XMLRPC ProxiedTransport for Py27 as described in
+# https://docs.python.org/2.7/library/xmlrpclib.html#example-of-client-usage
+# ------------------------------------------------------------------------
+
+import xmlrpclib, httplib
+
+class ProxiedTransport(xmlrpclib.Transport):
+ ''' XMLRPC ProxiedTransport for Py27 as described in
+ https://docs.python.org/2.7/library/xmlrpclib.html#example-of-client-usage
+ '''
+
+ def set_proxy(self, proxy):
+ self.proxy = proxy
+
+ def make_connection(self, host):
+ self.realhost = host
+ h = httplib.HTTPConnection(self.proxy)
+ return h
+
+ def send_request(self, connection, handler, request_body):
+ connection.putrequest("POST", 'http://%s%s' % (self.realhost, handler))
+
+ def send_host(self, connection, host):
+ connection.putheader('Host', self.realhost)
+
diff --git a/src/testlink/proxiedtransport3.py b/src/testlink/proxiedtransport3.py
new file mode 100644
index 0000000..e12ab77
--- /dev/null
+++ b/src/testlink/proxiedtransport3.py
@@ -0,0 +1,46 @@
+#! /usr/bin/python
+# -*- coding: UTF-8 -*-
+
+# Copyright 2020 Luiko Czub, TestLink-API-Python-client developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# ------------------------------------------------------------------------
+
+from http.client import HTTPConnection
+from xmlrpc.client import Transport
+from urllib.parse import urlparse, urlunparse
+
+class ProxiedTransport(Transport):
+ ''' XMLRPC ProxiedTransport for Py37+ as described in
+ https://docs.python.org/3.8/library/xmlrpc.client.html#example-of-client-usage
+ '''
+
+ def set_proxy(self, host, port=None, headers=None):
+ ''' if host includes a port definition (e.g. http://myHost:1111)
+ this will be used instead the optional PORT arg
+ '''
+
+ u1 = urlparse(host)
+ uport = u1.port
+ u2 = u1._replace(netloc=u1.hostname)
+ uhost = urlunparse(u2)
+
+ self.proxy = uhost, uport or port
+ self.proxy_headers = headers
+
+ def make_connection(self, host):
+ connection = HTTPConnection(*self.proxy)
+ connection.set_tunnel(host, headers=self.proxy_headers)
+ self._connection = host, connection
+ return connection
diff --git a/src/testlink/testgenreporter.py b/src/testlink/testgenreporter.py
new file mode 100644
index 0000000..d856dd2
--- /dev/null
+++ b/src/testlink/testgenreporter.py
@@ -0,0 +1,50 @@
+#! /usr/bin/python
+# -*- coding: UTF-8 -*-
+
+# Copyright 2017-2019 Brian-Willams, TestLink-API-Python-client developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# ------------------------------------------------------------------------
+
+from .testreporter import AddTestCaseReporter, AddBuildReporter, AddTestPlanReporter, AddPlatformReporter, TestReporter
+
+
+class TestGenReporter(AddTestCaseReporter, AddBuildReporter, AddTestPlanReporter, AddPlatformReporter, TestReporter):
+ """
+ This is the default generate everything it can version of test reporting.
+
+ This class will always try to report a result. It will generate everything possible and will change with additional
+ Add*Reporter's added to the repo. As such you should only use this if you want to always generate everything this
+ repo is capable of. If you want what it does at any specific time you should create this class in your project and
+ use directly.
+
+ If you don't want to generate one of these values you can 'roll your own' version of this class with only the
+ needed features that you want to generate. As stated above if you *only* want to generate what this class currently
+ does. Copying it into your project is the best practice as this class is mutable inside the project!
+
+ For example if you wanted to add platforms and/or tests to testplans, but didn't want to ever make a new testplan
+ you could use a class like:
+ `type('MyOrgTestGenReporter', (AddTestCaseReporter, AddPlatformReporter, TestReporter), {})`
+
+ Example usage with fake testlink server test and a manual project.
+ ```
+ tls = testlink.TestLinkHelper('https://testlink.corp.com/testlink/lib/api/xmlrpc/v1/xmlrpc.php',
+ 'devkeyabc123').connect(testlink.TestlinkAPIClient)
+ tgr = TestGenReporter(tls, ['TEST-123'], testprojectname='MANUALLY_MADE_PROJECT', testplanname='generated',
+ platformname='gend', buildname='8.fake', status='p')
+ tgr.report()
+ ```
+
+ Attention - the list of test case IDs must use full external testcase IDs
+ """
diff --git a/src/testlink/testlinkapi.py b/src/testlink/testlinkapi.py
index 5504298..0ca7e14 100644
--- a/src/testlink/testlinkapi.py
+++ b/src/testlink/testlinkapi.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2011-2015 Luiko Czub, Olivier Renault, James Stock, TestLink-API-Python-client developers
+# Copyright 2011-2019 Luiko Czub, Olivier Renault, James Stock, TestLink-API-Python-client developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@
from __future__ import print_function
from .testlinkapigeneric import TestlinkAPIGeneric, TestLinkHelper
-from .testlinkerrors import TLArgError
+from .testlinkerrors import TLArgError, TLResponseError
import sys
@@ -58,7 +58,7 @@ def __init__(self, server_url, devKey, **kwargs):
# reportTCResult(None, newTestPlanID, None, 'f', '', guess=True,
# testcaseexternalid=tc_aa_full_ext_id)
# otherwise xmlrpclib raise an error, that None values are not allowed
- self.stepsList = []
+ self._emptyStepsList()
self._changePositionalArgConfig()
def _changePositionalArgConfig(self):
@@ -125,12 +125,23 @@ def createTestCase(self, *argsPositional, **argsOptional):
""" createTestCase: Create a test case
positional args: testcasename, testsuiteid, testprojectid, authorlogin,
summary
- optional args : preconditions, importance, executiontype, order,
- internalid, checkduplicatedname, actiononduplicatedname
+ optional args : steps, preconditions, importance, executiontype, order,
+ internalid, checkduplicatedname, actiononduplicatedname,
+ status, estimatedexecduration
argument 'steps' will be set with values from .stepsList,
- when argsOptional does not include a 'steps' item
- .stepsList can be filled before call via .initStep() and .appendStep()
+
+ otherwise, optional arg 'steps' must be defined as a list with
+ dictionaries , example
+ [{'step_number' : 1, 'actions' : "action A" ,
+ 'expected_results' : "result A", 'execution_type' : 0},
+ {'step_number' : 2, 'actions' : "action B" ,
+ 'expected_results' : "result B", 'execution_type' : 1},
+ {'step_number' : 3, 'actions' : "action C" ,
+ 'expected_results' : "result C", 'execution_type' : 0}]
+
"""
# store current stepsList as argument 'steps', when argsOptional defines
@@ -201,7 +212,23 @@ def copyTCnewTestCase(self, origTestCaseId, origVersion=None, **changedAttribute
return self._copyTC(origTestCaseId, changedAttributes, origVersion,
duplicateaction = 'generate_new')
-
+ def getTestCaseByVersion(self, testCaseID, version=None):
+ """
+ Gets testcase information based on the version.
+
+ :param testCaseID: test case to search for
+ :param version: version to search for defaults to None. None searches for latest
+ :return: test case info dictionary
+ """
+ testcases = self.getTestCase(testCaseID, version=version)
+ if version is None:
+ return testcases[0]
+ for testcase in testcases:
+ if str(testcase['version']) == str(version):
+ return testcase
+ else:
+ raise TLArgError("Testcase {} doesn't have version {}.".format(testCaseID, version))
+
def _copyTC(self, origTestCaseId, changedArgs, origVersion=None, **options):
""" creates a copy of test case with id ORIGTESTCASEID
@@ -226,7 +253,7 @@ def _copyTC(self, origTestCaseId, changedArgs, origVersion=None, **options):
"""
# get orig test case content
- origArgItems = self.getTestCase(origTestCaseId, version=origVersion)[0]
+ origArgItems = self.getTestCaseByVersion(origTestCaseId, version=origVersion)
# get orig test case project id
origArgItems['testprojectid'] = self.getProjectIDByNode(origTestCaseId)
@@ -266,7 +293,8 @@ def _copyTCbuildArgs(self, origArgItems, changedArgs, options):
externalArgNames.extend(optArgNames)
externalTointernalNames = {'testcasename' : 'name',
'testsuiteid' : 'testsuite_id', 'authorlogin' : 'author_login',
- 'executiontype' : 'execution_type', 'order' : 'node_order'}
+ 'executiontype' : 'execution_type', 'order' : 'node_order',
+ 'estimatedexecduration' : 'estimated_exec_duration' }
# extend origItems with some values needed in createTestCase
origArgItems['checkduplicatedname'] = 1
@@ -457,14 +485,8 @@ def initStep(self, actions, expected_results, execution_type):
""" initStep :
Initializes the list which stores the Steps of a Test Case to create
"""
- self.stepsList = []
- lst = {}
- lst['step_number'] = '1'
- lst['actions'] = actions
- lst['expected_results'] = expected_results
- lst['execution_type'] = str(execution_type)
- self.stepsList.append(lst)
- return True
+ self._emptyStepsList()
+ return self.appendStep(actions, expected_results, execution_type)
def appendStep(self, actions, expected_results, execution_type):
""" appendStep :
@@ -476,17 +498,71 @@ def appendStep(self, actions, expected_results, execution_type):
lst['expected_results'] = expected_results
lst['execution_type'] = str(execution_type)
self.stepsList.append(lst)
- return True
+ return True
+
+ def _emptyStepsList(self):
+ """ reset .stepsList to an empty List """
+ self.stepsList = []
- def getProjectIDByName(self, projectName):
- projects=self.getProjects()
- result=-1
- for project in projects:
- if (project['name'] == projectName):
- result = project['id']
- break
- return result
+ def getProjectIDByName(self, projectName):
+ try:
+ project=self.getTestProjectByName(projectName)
+ userID = project['id']
+ except KeyError:
+ userID = -1
+
+ return userID
+
+ def ensureUserExist(self, login, **userArgs):
+ """ combines getUserByLogin() + createUser()
+ creates new user only, when login not exist
+
+ returns userID
+
+ userArgs defines optional key value pairs used to create new user
+ - firstname Default 'unknown'
+ - lastname Default 'via pyTLapi'
+ - email Default 'unknown@example.com'
+ - password Default None
+ """
+
+ try:
+ response = self.getUserByLogin(login)
+ userID = response[0]['dbID']
+ except TLResponseError as tl_err:
+ if tl_err.code == 10000:
+ # Cannot Find User Login create new user
+ name1 = userArgs.get('firstname', 'unknown')
+ name2 = userArgs.get('lastname', 'via pyTLapi')
+ mail = userArgs.get('email', 'unknown@example.com')
+ pw = userArgs.get('password')
+ userID = self.createUser(login, name1, name2, mail, password=pw)
+ else:
+ # seems to be another response failure - we forward it
+ raise
+
+ return userID
+ def ensureUserExistWithProjectRole(self, login, rolename, projectname, **userArgs):
+ """ combines ensureUserExist() + setUserRoleOnProject()
+ creates new user only, when login not exist
+
+ returns list with users account details
+
+ rolename
+ - e.g. tester, test designer, senior tester, guest ...
+
+ userArgs defines optional key value pairs used to create new user
+ - firstname Default 'unknown'
+ - lastname Default 'via pyTLapi'
+ - email Default 'unknown@example.com'
+ - password Default None
+ """
+
+ userID = self.ensureUserExist(login, **userArgs)
+ projectID = self.getProjectIDByName(projectname)
+ self.setUserRoleOnProject(userID, rolename, projectID)
+ return self.getUserByID(userID)
if __name__ == "__main__":
tl_helper = TestLinkHelper()
diff --git a/src/testlink/testlinkapigeneric.py b/src/testlink/testlinkapigeneric.py
index 4a670e8..a534fd4 100644
--- a/src/testlink/testlinkapigeneric.py
+++ b/src/testlink/testlinkapigeneric.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2013-2015 Luiko Czub, TestLink-API-Python-client developers
+# Copyright 2013-2019 Luiko Czub, TestLink-API-Python-client developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -18,16 +18,18 @@
# ------------------------------------------------------------------------
import sys, os.path
-IS_PY3 = sys.version_info[0] < 3
-if IS_PY3:
- import xmlrpclib
+IS_PY3 = sys.version_info[0] > 2
+if not IS_PY3:
+ # importing required py2 modules
+ import xmlrpclib as xmlrpc_client
# in py 3 encodestring is deprecated and an alias for encodebytes
# see issue #39 and compare py2 py3 doc
# https://docs.python.org/2/library/base64.html#base64.encodestring
# https://docs.python.org/3/library/base64.html#base64.encodebytes
from base64 import encodestring as encodebytes
else:
- import xmlrpc.client as xmlrpclib
+ # importing required py3 modules
+ import xmlrpc.client as xmlrpc_client
from base64 import encodebytes
from platform import python_version
@@ -41,7 +43,6 @@
decoMakerApiCallReplaceTLResponseError, decoMakerApiCallWithArgs, \
decoMakerApiCallChangePosToOptArg
-
class TestlinkAPIGeneric(object):
""" client for XML-RPC communication between Python and TestLink
Implements the TestLink API methods as generic PY methods with
@@ -63,10 +64,13 @@ class TestlinkAPIGeneric(object):
def __init__(self, server_url, devKey, **args):
transport=args.get('transport')
encoding=args.get('encoding')
- verbose=args.get('verbose',0)
- allow_none=args.get('allow_none',0)
- self.server = xmlrpclib.Server(server_url, transport, encoding,
- verbose, allow_none)
+ verbose=args.get('verbose',False)
+ allow_none=args.get('allow_none',False)
+ use_datetime = args.get('use_datetime', False)
+ context = args.get('context', None)
+ self.server = xmlrpc_client.ServerProxy(server_url, transport, encoding,
+ verbose, allow_none, use_datetime,
+ context=context)
self.devKey = devKey
self._server_url = server_url
self._positionalArgNames = getMethodsWithPositionalArgs()
@@ -147,12 +151,50 @@ def repeat(self):
def about(self):
""" Gives basic information about the API """
+# /**
+# * Creates a new build for a specific test plan
+# *
+# * @param struct $args
+# * @param string $args["devKey"]
+# * @param int $args["testplanid"]
+# * @param string $args["buildname"];
+# * @param string $args["buildnotes"];
+# * @param string $args["active"];
+# * @param string $args["open"];
+# * @param string $args["releasedate"]: YYYY-MM-DD;
+# * @param int $args["copytestersfrombuild"] OPTIONAL,
+# * if > 0 and valid buildid tester assignments will be copied.
+# *
+# * @return mixed $resultInfo
+# *
+# * @access public
+# */
+# public function createBuild($args)
+
@decoApiCallAddDevKey
@decoMakerApiCallWithArgs(['testplanid', 'buildname'],
- ['buildnotes'])
+ ['buildnotes', 'active', 'open', 'releasedate',
+ 'copytestersfrombuild'])
def createBuild(self):
- """ Creates a new build for a specific test plan """
+ """ Creates a new build for a specific test plan
+ active : 1 (default) = activ 0 = inactiv
+ open : 1 (default) = open 0 = closed
+ releasedate : YYYY-MM-DD
+ copytestersfrombuild : valid buildid tester assignments will be copied.
+ """
+
+ @decoMakerApiCallReplaceTLResponseError()
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['buildid'])
+ def closeBuild(self):
+ """ Close build
+
+ buildid - ATTENTION must be an integer
+ - createBuild returns the id as a string
+ - convert it with int() before calling closeBuild()
+ """
+
@decoMakerApiCallReplaceTLResponseError()
@decoApiCallAddDevKey
@decoMakerApiCallWithArgs()
@@ -185,9 +227,32 @@ def getTestSuitesForTestPlan(self):
returns an empty list, if no suite is assigned """
+
+# /**
+# * create a test project
+# *
+# * @param struct $args
+# * @param string $args["devKey"]
+# * @param string $args["testprojectname"]
+# * @param string $args["testcaseprefix"]
+# * @param string $args["notes"] OPTIONAL
+# * @param map $args["options"] OPTIONAL ALL int treated as boolean
+# * keys requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled
+# *
+# * @param int $args["active"] OPTIONAL
+# * @param int $args["public"] OPTIONAL
+# * @param string $args["itsname"] OPTIONAL
+# * @param boolean $args["itsEnabled"] OPTIONAL
+# *
+# *
+# * @return mixed $resultInfo
+# */
+# public function createTestProject($args)
+
@decoApiCallAddDevKey
@decoMakerApiCallWithArgs(['testprojectname', 'testcaseprefix'],
- ['notes', 'active', 'public', 'options'])
+ ['notes', 'active', 'public', 'options',
+ 'itsname', 'itsenabled'])
def createTestProject(self):
""" Create a test project
@@ -201,7 +266,7 @@ def createTestProject(self):
@decoMakerApiCallWithArgs(['testsuiteid'], ['deep', 'details',
'getkeywords'])
def getTestCasesForTestSuite(self):
- """ List test suites within a test plan alphabetically
+ """ List test cases within a test suite alphabetically
details - default is 'simple',
use 'full' if you want to get summary,steps & expected_results
@@ -228,11 +293,50 @@ def getTestCaseIDByName(self):
server return can be a list or a dictionary
- optional arg testprojectname seems to create a dictionary response """
+# /**
+# * createTestCase
+# * @param struct $args
+# * @param string $args["devKey"]
+# * @param string $args["testcasename"]
+# * @param int $args["testsuiteid"]: test case parent test suite id
+# * @param int $args["testprojectid"]: test case parent test suite id
+# *
+# * @param string $args["authorlogin"]: to set test case author
+# * @param string $args["summary"]
+# * @param array $args["steps"]
+# *
+# * @param string $args["preconditions"] - optional
+# * @param int $args["importance"] - optional - see const.inc.php for domain
+# * @param int $args["execution"] - optional - see ... for domain
+# * @param int $args["order'] - optional
+# * @param int $args["internalid"] - optional - do not use
+# * @param string $args["checkduplicatedname"] - optional
+# * @param string $args["actiononduplicatedname"] - optional
+# * @param int $args["status"] - optional - see const.inc.php $tlCfg->testCaseStatus
+# * @param number $args["estimatedexecduration"] - optional
+# *
+# * @return mixed $resultInfo
+# * @return string $resultInfo['operation'] - verbose operation
+# * @return boolean $resultInfo['status'] - verbose operation
+# * @return int $resultInfo['id'] - test case internal ID (Database ID)
+# * @return mixed $resultInfo['additionalInfo']
+# * @return int $resultInfo['additionalInfo']['id'] same as $resultInfo['id']
+# * @return int $resultInfo['additionalInfo']['external_id'] without prefix
+# * @return int $resultInfo['additionalInfo']['status_ok'] 1/0
+# * @return string $resultInfo['additionalInfo']['msg'] - for debug
+# * @return string $resultInfo['additionalInfo']['new_name'] only present if new name generation was needed
+# * @return int $resultInfo['additionalInfo']['version_number']
+# * @return boolean $resultInfo['additionalInfo']['has_duplicate'] - for debug
+# * @return string $resultInfo['message'] operation message
+# */
+# public function createTestCase($args)
+
@decoApiCallAddDevKey
@decoMakerApiCallWithArgs(['testcasename', 'testsuiteid', 'testprojectid',
'authorlogin', 'summary', 'steps'],
['preconditions', 'importance', 'executiontype', 'order',
- 'internalid', 'checkduplicatedname', 'actiononduplicatedname'])
+ 'internalid', 'checkduplicatedname', 'actiononduplicatedname',
+ 'status', 'estimatedexecduration'])
def createTestCase(self):
""" Create a test case
@@ -243,14 +347,93 @@ def createTestCase(self):
'expected_results' : "result B", 'execution_type' : 1},
{'step_number' : 3, 'actions' : "action C" ,
'expected_results' : "result C", 'execution_type' : 0}]
+
+ possible values for optional arguments testlink/cfg/const.inc.php
+ importance: 1 (low) 2 (medium) 3 (high)
+ status: 1 (draft) 2 (readyForReview)
+ 3 (reviewInProgress) 4 (rework)
+ 5 (obsolete) 6 (future)
+ 7 (final)
+ executiontype: 1 (Manual) 2 (Automated)
"""
+# /**
+# * Reports a result for a single test case
+# *
+# * @param struct $args
+# * @param string $args["devKey"]
+# * @param int $args["testcaseid"]: optional, if not present
+# * testcaseexternalid must be present
+# *
+# * @param int $args["testcaseexternalid"]: optional, if does not is present
+# * testcaseid must be present
+# *
+# *
+# *
+# * @param int $args["testplanid"]
+# * @param string $args["status"] - status is {@link $validStatusList}
+# * @param int $args["buildid"] - optional.
+# * if not present and $args["buildname"] exists
+# * then
+# * $args["buildname"] will be checked and used if valid
+# * else
+# * build with HIGHEST ID will be used
+# *
+# * @param int $args["buildname"] - optional.
+# * if not present Build with higher internal ID will be used
+# *
+# *
+# * @param string $args["notes"] - optional
+# * @param string $args["execduration"] - optional
+# *
+# * @param bool $args["guess"] - optional defining whether to guess optinal params or require them
+# * explicitly default is true (guess by default)
+# *
+# * @param string $args["bugid"] - optional
+# *
+# * @param string $args["platformid"] - optional, if not present platformname must be present
+# * @param string $args["platformname"] - optional, if not present platformid must be present
+# *
+# *
+# * @param string $args["customfields"] - optional
+# * contains an map with key:Custom Field Name, value: value for CF.
+# * VERY IMPORTANT: value must be formatted in the way it's written to db,
+# * this is important for types like:
+# *
+# * DATE: strtotime()
+# * DATETIME: mktime()
+# * MULTISELECTION LIST / CHECKBOX / RADIO: se multipli selezione ! come separatore
+# *
+# *
+# * these custom fields must be configured to be writte during execution.
+# * If custom field do not meet condition value will not be written
+# *
+# * @param boolean $args["overwrite"] - optional, if present and true, then last execution
+# * for (testcase,testplan,build,platform) will be overwritten.
+# *
+# * @param boolean $args["user"] - optional, if present and user is a valid login
+# * (no other check will be done) it will be used when writting execution.
+# *
+# * @param string $args["timestamp"] - optional, if not present now is used
+# * format YYYY-MM-DD HH:MM:SS
+# * example 2015-05-22 12:15:45
+# * @return mixed $resultInfo
+# * [status] => true/false of success
+# * [id] => result id or error code
+# * [message] => optional message for error message string
+# * @access public
+# *
+# * @internal revisions
+# *
+# */
+# public function reportTCResult($args)
+
@decoApiCallAddDevKey
@decoMakerApiCallWithArgs(['testplanid', 'status'],
['testcaseid', 'testcaseexternalid', 'buildid', 'buildname',
'platformid', 'platformname', 'notes', 'guess', 'bugid',
'customfields', 'overwrite', 'user', 'execduration',
- 'timestamp'])
+ 'timestamp', 'steps'])
def reportTCResult(self):
""" Reports a result for a single test case
@@ -263,9 +446,11 @@ def reportTCResult(self):
overwrite : if present and true, then last execution for
(testcase,testplan,build,platform) will be overwritten.
user : if present and user is a valid login (no other check will be done)
- it will be used when writting execution.
+ it will be used when writing execution.
execduration : Exec (min) as float (2.5 = 2min 30sec)
- timestamp : 'YYYY-MM-DD hh:mm[:ss]'
+ timestamp : 'YYYY-MM-DD hh:mm[:ss]'#
+ steps : [{'step_number' : 6, 'result' : 'p', 'notes" : 'a_note'},
+ {'step_number' : 7, 'result' : 'f', 'notes" : 'blabla'}]
"""
# /**
@@ -314,6 +499,8 @@ def reportTCResult(self):
# * 'full': (default) get summary,steps,expected_results,test suite name
# * 'simple':
# * 'details':
+# * @param array $args["customfields"]
+# * - optional can be a boolean or an array with the requested fields
# * @return mixed $resultInfo
# *
# * @internal revisions
@@ -327,7 +514,8 @@ def reportTCResult(self):
@decoMakerApiCallWithArgs(['testplanid'],
['buildid', 'platformid',
'testcaseid', 'keywordid', 'keywords', 'executed', 'assignedto',
- 'executestatus', 'executiontype', 'getstepinfo', 'details'])
+ 'executestatus', 'executiontype', 'getstepinfo', 'details',
+ 'customfields'])
def getTestCasesForTestPlan(self):
""" List test cases linked to a test plan
@@ -365,6 +553,7 @@ def getTestCaseCustomFieldDesignValue(self):
# * @param args['platformid'] - OPTIONAL Only if test plan has no platforms
# * @param args['executionorder'] - OPTIONAL
# * @param args['urgency'] - OPTIONAL
+# * @param args['overwrite'] - OPTIONAL
# *
# */
# public function addTestCaseToTestPlan($args)
@@ -372,7 +561,7 @@ def getTestCaseCustomFieldDesignValue(self):
@decoApiCallAddDevKey
@decoMakerApiCallWithArgs(['testprojectid',
'testplanid', 'testcaseexternalid', 'version'],
- ['platformid', 'executionorder', 'urgency'])
+ ['platformid', 'executionorder', 'urgency', 'overwrite'])
def addTestCaseToTestPlan(self):
""" Add a test case version to a test plan """
@@ -418,10 +607,40 @@ def assignRequirements(self):
b) requirements with ID 6735 and 6737 of requirement spec 6733
"""
+# /**
+# * Gets attachments for specified test case VERSION.
+# * The attachment file content is Base64 encoded. To save the file to disk in client,
+# * Base64 decode the content and write file in binary mode.
+# *
+# * @param struct $args
+# * @param string $args["devKey"]
+# * Developer key
+# * @param int $args["testcaseid"]:
+# * optional, if does not is present
+# * testcaseexternalid must be present
+# *
+# * @param int $args["version"]:
+# * optional, if not present, the latest version will be used
+# *
+# * @param int $args["testcaseexternalid"]:
+# * optional, if does not is present
+# * testcaseid must be present
+# *
+# * @return mixed $resultInfo
+# */
+# public function getTestCaseAttachments($args) {
+
@decoApiCallAddDevKey
- @decoMakerApiCallWithArgs([], ['testcaseid', 'testcaseexternalid'])
+ @decoMakerApiCallWithArgs([], ['testcaseid', 'version',
+ 'testcaseexternalid'])
def getTestCaseAttachments(self):
""" Gets attachments for specified test case.
+
+ args variations: testcaseid - testcaseexternalid
+
+ version - optional, if not present, the latest test case version
+ will be used
+
The attachment file content is Base64 encoded. To save the file to disk
in client, Base64 decode the content and write file in binary mode. """
@@ -500,6 +719,10 @@ def getFullPath(self):
def deleteExecution(self):
""" delete an execution
+ executionid - ATTENTION must be an integer
+ - reportTCResult returns the id as a string
+ - convert it with int() before calling deleteExecution()
+
Default TL server configuration does not allow deletion of exections
see Installation & Configuration Manual Version 1.9
chap. 5.8. Test execution settings
@@ -631,14 +854,44 @@ def uploadTestSuiteAttachment(self):
ATTACHMENTFILE, but user could overwrite it, if user want to store the
attachment with a different name
"""
+# /**
+# * Uploads an attachment for a Test Case.
+# *
+# * The attachment content must be Base64 encoded by the client before sending it.
+# *
+# * @param struct $args
+# * @param string $args["devKey"]
+# * Developer key
+# * @param int $args["testcaseid"]
+# * Test Case INTERNAL ID
+# * @param int $args["version"]
+# * version number
+# *
+# * @param string $args["title"]
+# * (Optional) The title of the Attachment
+# * @param string $args["description"]
+# * (Optional) The description of the Attachment
+# * @param string $args["filename"]
+# * The file name of the Attachment(e.g.:notes.txt)
+# * @param string $args["filetype"]
+# * The file type of the Attachment(e.g.: text/plain)
+# * @param string $args["content"]
+# * The content(Base64 encoded) of the Attachment
+# *
+# * @return mixed $resultInfo an array containing the fk_id, fk_table, title,
+# * description, file_name, file_size and file_type. If any errors occur it
+# * returns the erros map.
+# */
+# public function uploadTestCaseAttachment($args) {
@decoApiCallAddAttachment
- @decoMakerApiCallWithArgs(['testcaseid'],
+ @decoMakerApiCallWithArgs(['testcaseid', 'version'],
['title', 'description', 'filename', 'filetype', 'content'])
def uploadTestCaseAttachment(self):
""" Uploads an attachment for a Test Case.
testcaseid - Test Case INTERNAL ID
+ version - Test Case version number
mandatory non api args: attachmentfile
- python file descriptor pointing to the file
@@ -961,10 +1214,25 @@ def setTestCaseExecutionType(self):
@decoMakerApiCallWithArgs(['testplanid'])
def getExecCountersByBuild(self):
""" Gets execution metrics information for a testplan """
-
+ # /**
+ # * create platform
+ # *
+ # * @param struct $args
+ # * @param string $args["devKey"]
+ # * @param string $args["testprojectname"]
+ # * @param string $args["platformname"]
+ # * @param string $args["notes"]
+ # * @param boolean $args["platformondesign"]
+ # * @param boolean $args["platformonexecution"]
+ # * @return mixed $resultInfo
+ # * @internal revisions
+ # */
+ # public function createPlatform($args) {
+
@decoApiCallAddDevKey
@decoMakerApiCallWithArgs(['testprojectname', 'platformname'],
- ['notes'])
+ ['notes',
+ 'platformondesign', 'platformonexecution'])
def createPlatform(self):
""" Creates a platform for test project """
@@ -1071,6 +1339,31 @@ def getUserByID(self):
* ATTENTION: userApiKey will be set to NULL, because is worst that access to user password
"""
+# /**
+# * Update an existing test case
+# * Not all test case attributes will be able to be updated using this method
+# * See details below
+# *
+# * @param struct $args
+# * @param string $args["devKey"]
+# * @param string $args["testcaseexternalid"] format PREFIX-NUMBER
+# * @param int $args["version"] optional version NUMBER (human readable)
+# * @param string $args["testcasename"] - optional
+# * @param string $args["summary"] - optional
+# * @param string $args["preconditions"] - optional
+# * @param array $args["steps"] - optional
+# * each element is a hash with following keys
+# * step_number,actions,expected_results,execution_type
+# *
+# * @param int $args["importance"] - optional - see const.inc.php for domain
+# * @param int $args["executiontype"] - optional - see ... for domain
+# * @param int $args["status'] - optional
+# * @param int $args["estimatedexecduration'] - optional
+# * @param string $args["user'] - login name used as updater - optional
+# * if not provided will be set to user that request update
+# */
+# public function updateTestCase($args)
+
@decoApiCallAddDevKey
@decoMakerApiCallWithArgs(['testcaseexternalid'],
['version', 'testcasename','summary', 'preconditions', 'steps',
@@ -1165,7 +1458,7 @@ def getTestCaseBugs(self):
testplanid test plan id
- args variations: testcaseid - testcaseexternalid (mandatoy!)
+ args variations: testcaseid - testcaseexternalid (mandatory!)
buildid - buildname
platformid - platformname
test case information is general mandatory
@@ -1442,8 +1735,384 @@ def deleteTestProject(self):
def updateTestSuiteCustomFieldDesignValue(self):
""" Update value of Custom Field with scope='design' for a given Test Suite
- customfields : dictionary with customfields names + values
- VERY IMPORTANT: value must be formatted in the way it's written to db
+ customfields : dictionary with customfields names + values
+ VERY IMPORTANT: value must be formatted in the way it's written to db
+ """
+
+# /**
+# * Returns all test suites inside target
+# * test project with target name
+# *
+# * @param
+# * @param struct $args
+# * @param string $args["devKey"]
+# * @param int $args["testsuitename"]
+# * @param string $args["prefix"]
+# * @return mixed $resultInfo
+# *
+# * @access public
+# */
+# public function getTestSuite($args)
+
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['testsuitename', 'prefix'])
+ def getTestSuite(self):
+ """ Returns list with all test suites named TESTUITENAME defined for
+ test project using PREFIX """
+
+
+# /**
+# * update a test suite
+# *
+# * @param struct $args
+# * @param string $args["devKey"]
+# * @param int $args["testprojectid"] OR string $args["prefix"]
+# * @param string $args["testsuitename"] optional
+# * @param string $args["details"] optional
+# * @param int $args["parentid"] optional, if do not provided means test suite must be top level.
+# * @param int $args["order"] optional. Order inside parent container
+# *
+# * @return mixed $resultInfo
+# */
+# public function updateTestSuite($args)
+
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['testsuiteid'],
+ ['testprojectid', 'prefix', 'parentid', 'testsuitename', 'details',
+ 'order'])
+ def updateTestSuite(self):
+ """ update a test suite
+
+ mandatory arg: testsuiteid - identifies the test suite to be change
+
+ mandatory args variations: testprojectid or prefix
+ - test project information is general mandatory
+
+ optional args:
+ - testsuitename - if defined, test suite name will be changed
+ - details - if defined test suite details will be changed
+ - order - if defined, order inside parent container is changed
+ """
+
+# /**
+# * Get Issue Tracker System by name
+# *
+# * @param struct $args
+# * @param string $args["devKey"]
+# * @param string $args["itsname"] ITS name
+# * @return mixed $itsObject
+# * @access public
+# */
+# public function getIssueTrackerSystem($args,$call=null)
+
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['itsname'], [])
+ def getIssueTrackerSystem(self):
+ """ Get Issue Tracker System by name """
+
+# /**
+# * Update value of Custom Field with scope='design'
+# * for a given Build
+# *
+# * @param struct $args
+# * @param string $args["devKey"]: used to check if operation can be done.
+# * if devKey is not valid => abort.
+# *
+# * @param string $args["buildid"]:
+# * @param string $args["testprojectid"]:
+# * @param string $args["customfields"]
+# * contains an map with key:Custom Field Name, value: value for CF.
+# * VERY IMPORTANT: value must be formatted in the way it's written to db,
+# * this is important for types like:
+# *
+# * DATE: strtotime()
+# * DATETIME: mktime()
+# * MULTISELECTION LIST / CHECKBOX / RADIO: se multipli selezione ! come separatore
+# *
+# *
+# * these custom fields must be configured to be writte during execution.
+# * If custom field do not meet condition value will not be written
+# *
+# * @return mixed null if everything ok, else array of IXR_Error objects
+# *
+# * @access public
+# */
+# public function updateBuildCustomFieldsValues($args)
+
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['testprojectid', 'testplanid', 'buildid',
+ 'customfields'])
+ def updateBuildCustomFieldsValues(self):
+ """ Update value of Custom Field with scope='design' for a given Build
+
+ customfields : dictionary with customfields names + values
+ VERY IMPORTANT: value must be formatted in the way it's written to db
+ """
+
+# /**
+# * Gets a set of EXECUTIONS for a particular testcase on a test plan.
+# * If there are no filter criteria regarding platform and build,
+# * result will be get WITHOUT checking for a particular platform and build.
+# *
+# * @param struct $args
+# * @param string $args["devKey"]
+# * @param int $args["tplanid"]
+# * @param int $args["testcaseid"]: Pseudo optional.
+# * if is not present then testcaseexternalid MUST BE present
+# *
+# * @param int $args["testcaseexternalid"]: Pseudo optional.
+# * if is not present then testcaseid MUST BE present
+# *
+# * @param string $args["platformid"]: optional.
+# * ONLY if not present, then $args["platformname"]
+# * will be analized (if exists)
+# *
+# * @param string $args["platformname"]: optional (see $args["platformid"])
+# * @param int $args["buildid"]: optional
+# * ONLY if not present, $args["buildname"] will be analized (if exists)
+# *
+# * @param int $args["buildname"] - optional (see $args["buildid"])
+# * @param int $args["options"] - optional
+# * options['getOrderDescending']
+# * false(=ascending,default)
+# * @return mixed $resultInfo
+# * if execution found
+# * array that contains a map with these keys:
+# * id (execution id),build_id,tester_id,execution_ts,
+# * status,testplan_id,tcversion_id,tcversion_number,
+# * execution_type,notes.
+# *
+# * if test case has not been executed,
+# * array('id' => -1)
+# * @access public
+# */
+# public function getExecutionSet($args)
+
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['testplanid'],
+ ['testcaseid', 'testcaseexternalid',
+ 'buildid', 'buildname', 'platformid', 'platformname',
+ 'options'])
+ def getExecutionSet(self):
+ """ Gets a set of EXECUTIONS for a particular testcase on a test plan.
+ If there are no filter criteria regarding platform and build, result
+ will be get WITHOUT checking for a particular platform and build.
+
+ mandatory arg: testplanid - identifies the test plan
+
+ mandatory args variations: testcaseid - testcaseexternalid
+ - test case information is general mandatory
+
+ optional args variations: buildid - buildname
+ platformid - platformname
+
+ options : dictionary with key 'getOrderDescending' and
+ values 0 (false = default) or 1 (true)
+ """
+
+# /**
+# * Get requirements
+# *
+# * @param string $args["testprojectid"]
+# * @param string $args["testplanid"] OPTIONAL
+# * @param string $args["platformid"] OPTIONAL
+# *
+# * @return mixed error if someting's wrong, else array of test cases
+# *
+# * @access public
+# */
+# public function getRequirements($args)
+
+ @decoMakerApiCallReplaceTLResponseError()
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['testprojectid'], ['testplanid', 'platformid'])
+ def getRequirements(self):
+ """ Get requirements.
+
+ mandatory arg: testprojectid - identifies the test project
+
+ optional args: testplanid, platformid
+ """
+
+# /**
+# * Get requirement coverage
+# *
+# * Retrieve the test cases associated to a requirement
+# *
+# * @param struct $args
+# * @param string $args["devKey"]: used to check if operation can be done.
+# * if devKey is not valid => abort.
+# *
+# * @param string $args["testprojectid"]
+# * @param string $args["requirementdocid"]
+# *
+# * @return mixed error if someting's wrong, else array of test cases
+# *
+# * @access public
+# */
+# public function getReqCoverage($args)
+
+ @decoMakerApiCallReplaceTLResponseError()
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['testprojectid', 'requirementdocid'], [])
+ def getReqCoverage(self):
+ """ Get requirement coverage.
+ Retrieve the test cases associated to a requirement
+
+ mandatory arg:
+ testprojectid - identifies the test project
+ requirementdocid - identifies the requirement
+
+ """
+
+# /**
+# *
+# * @param struct $args
+# * @param string $args["devKey"]
+# * @param string $args["testcaseexternalid"] format PREFIX-NUMBER
+# * @param int $args["testsuiteid"]
+# *
+# */
+# public function setTestCaseTestSuite($args)
+
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['testcaseexternalid', 'testsuiteid'], [])
+ def setTestCaseTestSuite(self):
+ """ move a test case to a different Test Suite
+
+ mandatory arg:
+ testcaseexternalid - identifies the test case
+ testsuiteid - identifies the test suite
+
+ """
+
+# /**
+# * Gets attachments for specified test suite.
+# * The attachment file content is Base64 encoded. To save the file to disk in client,
+# * Base64 decode the content and write file in binary mode.
+# *
+# * @param struct $args
+# * @param string $args["devKey"] Developer key
+# * @param int $args["testsuiteid"]: id of the testsuite
+# *
+# * @return mixed $resultInfo
+# * @author dennis@etern-it.de
+# */
+# public function getTestSuiteAttachments($args)
+
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['testsuiteid'], [])
+ def getTestSuiteAttachments(self):
+ """ Gets attachments for specified test suite.
+ The attachment file content is Base64 encoded. To save the file to disk
+ in client, Base64 decode the content and write file in binary mode. """
+
+# /**
+# * Gets ALL EXECUTIONS for a particular testcase on a test plan.
+# * If there are no filter criteria regarding platform and build,
+# * result will be get WITHOUT checking for a particular platform and build.
+# *
+# * @param struct $args
+# * @param string $args["devKey"]
+# * @param int $args["tplanid"]
+# * @param int $args["testcaseid"]: Pseudo optional.
+# * if does not is present then testcaseexternalid MUST BE present
+# *
+# * @param int $args["testcaseexternalid"]: Pseudo optional.
+# * if does not is present then testcaseid MUST BE present
+# *
+# * @param string $args["platform_id"]: optional.
+# * will be analized ONLY if present and exists
+# *
+# * @param int $args["build_id"]: optional
+# * will be analized ONLY if present and exists
+# *
+# * @param int $args["options"] - optional
+# * options['getBugs'] = true / false
+# *
+# *
+# * @return mixed $resultInfo
+# * if executions found
+# * array that contains a map for each execution with these keys:
+# * id (execution id),build_id,tester_id,execution_ts,
+# * status,testplan_id,tcversion_id,tcversion_number,
+# * execution_type,notes.
+# *
+# * If user has requested getbugs, then a key bugs (that is an array)
+# * will also exists.
+# *
+# * if test case has not been execute, the first map will be returned with -1 as 'id'
+# *
+# * @access public
+# */
+# public function getAllExecutionsResults($args)
+
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['testplanid'],
+ ['testcaseid', 'testcaseexternalid',
+ 'platformid', 'buildid', 'options'])
+ def getAllExecutionsResults(self):
+ """ Gets ALL EXECUTIONS for a particular testcase on a test plan.
+ If there are no filter criteria regarding platform and build,
+ result will be get WITHOUT checking for a particular platform and build.
+
+ mandatory arg: testplanid - identifies the test plan
+
+ mandatory args variations: testcaseid - testcaseexternalid
+ - test case information is general mandatory
+
+ optional args: buildid
+ platformid
+
+ options : dictionary with key 'getBugs' and
+ values 0 (false = default) or 1 (true)
+ """
+
+ # /**
+ # * Create a new user
+ # *
+ # * Restricted to site admin
+ # *
+ # * @param struct $args
+ # * @param string $args["devKey"]
+ # * @param string $args["login"]
+ # * @param string $args["firstname"]
+ # * @param string $args["lastname"]
+ # * @param string $args["email"]
+ # * @param string $args["password"] - OPTIONAL
+ # *
+ # *
+ # * @return ID the new user if OK, otherwise error structure
+ # *
+ # * @access public
+ # */
+ # public function createUser($args) {
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['login', 'firstname', 'lastname', 'email'],
+ ['password'])
+ def createUser(self):
+ """ Create a new user """
+
+ # /**
+ # * Set a role to a user at project level
+ # *
+ # * Restricted to users with System Wide Role Admin
+ # *
+ # * @param struct $args
+ # * @param struct $args["userid"]
+ # * @param struct $args["rolename"]
+ # * @param struct $args["testprojectid"]
+ # *
+ # * @return true if OK, otherwise error structure
+ # *
+ # * @access public
+ # */
+ # public function setUserRoleOnProject($args)
+ @decoApiCallAddDevKey
+ @decoMakerApiCallWithArgs(['userid', 'rolename', 'testprojectid'])
+ def setUserRoleOnProject(self):
+ """ Set a role to a user at project level
+ Restricted to users with System Wide Role Admin
"""
#
@@ -1461,11 +2130,11 @@ def _callServer(self, methodNameAPI, argsAPI=None):
response = getattr(self.server.tl, methodNameAPI)()
else:
response = getattr(self.server.tl, methodNameAPI)(argsAPI)
- except (IOError, xmlrpclib.ProtocolError) as msg:
+ except (IOError, xmlrpc_client.ProtocolError) as msg:
new_msg = 'problems connecting the TestLink Server %s\n%s' %\
(self._server_url, msg)
raise testlinkerrors.TLConnectionError(new_msg)
- except xmlrpclib.Fault as msg:
+ except xmlrpc_client.Fault as msg:
new_msg = 'problems calling the API method %s\n%s' %\
(methodNameAPI, msg)
raise testlinkerrors.TLAPIError(new_msg)
@@ -1529,7 +2198,10 @@ def _getAttachmentArgs(self, attachmentfile):
else:
raise testlinkerrors.TLArgError(
'invalid attachment file: %s' % attachmentfile)
-
+ finally:
+ # ensure file open inside this method call is closed again
+ if not already_file_obj:
+ a_file_obj.close()
return {'filename':os.path.basename(a_file_path),
'filetype':guess_type(a_file_path)[0],
diff --git a/src/testlink/testlinkargs.py b/src/testlink/testlinkargs.py
index c03a2c9..bf40bb7 100644
--- a/src/testlink/testlinkargs.py
+++ b/src/testlink/testlinkargs.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2013-2015 Luiko Czub, TestLink-API-Python-client developers
+# Copyright 2013-2019 Luiko Czub, TestLink-API-Python-client developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/src/testlink/testlinkdecorators.py b/src/testlink/testlinkdecorators.py
index 1dd5c88..0027c06 100644
--- a/src/testlink/testlinkdecorators.py
+++ b/src/testlink/testlinkdecorators.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2013-2015 Luiko Czub, TestLink-API-Python-client developers
+# Copyright 2013-2019 Luiko Czub, TestLink-API-Python-client developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/src/testlink/testlinkerrors.py b/src/testlink/testlinkerrors.py
index 4176c1b..1795d8b 100644
--- a/src/testlink/testlinkerrors.py
+++ b/src/testlink/testlinkerrors.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2012-2015 Patrick Dassier, Luiko Czub, TestLink-API-Python-client developers
+# Copyright 2012-2019 Patrick Dassier, Luiko Czub, TestLink-API-Python-client developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/src/testlink/testlinkhelper.py b/src/testlink/testlinkhelper.py
index c222b78..fb866cc 100644
--- a/src/testlink/testlinkhelper.py
+++ b/src/testlink/testlinkhelper.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2012-2015 Luiko Czub, TestLink-API-Python-client developers
+# Copyright 2012-2020 Luiko Czub, TestLink-API-Python-client developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,10 +17,16 @@
#
# ------------------------------------------------------------------------
-import os
+import os, sys
from argparse import ArgumentParser
from .version import VERSION
+import ssl
+IS_PY3 = sys.version_info[0] > 2
+if IS_PY3:
+ from .proxiedtransport3 import ProxiedTransport
+else:
+ from .proxiedtransport2 import ProxiedTransport
class TestLinkHelper(object):
""" Helper Class to find out the TestLink connection parameters.
@@ -77,8 +83,11 @@ def __init__(self, server_url=None, devkey=None, proxy=None):
self._server_url = server_url
self._devkey = devkey
self._proxy = proxy
+ self._setParams()
+
+ def _setParams(self):
self._setParamsFromEnv()
-
+
def _setParamsFromEnv(self):
""" fill empty slots from environment variables
_server_url <- TESTLINK_API_PYTHON_SERVER_URL
@@ -93,7 +102,7 @@ def _setParamsFromEnv(self):
if self._devkey is None:
self._devkey = os.getenv(self.ENVNAME_DEVKEY, self.DEFAULT_DEVKEY)
- if not self._proxy:
+ if self._proxy is None:
self._proxy = os.getenv(self.ENVNAME_PROXY, self.DEFAULT_PROXY)
def _createArgparser(self, usage):
@@ -128,16 +137,20 @@ def setParamsFromArgs(self, usage=DEFAULT_DESCRIPTION, args=None):
def _getProxiedTransport(self):
""" creates and return a ProxiedTransport with self._proxy settings """
- from .proxiedtransport import ProxiedTransport
a_pt = ProxiedTransport()
a_pt.set_proxy(self._proxy)
return a_pt
- def connect(self, tl_api_class):
+ def connect(self, tl_api_class, **kwargs):
""" returns a new instance of TL_API_CLASS """
- kwargs = {}
if self._proxy:
kwargs['transport'] = self._getProxiedTransport()
+ # must we add an uncertified context for a https connection?
+ # only, if kwargs not includes a context
+ if self._server_url.lower().startswith('https'):
+ if not ('context' in kwargs):
+ kwargs['context'] = ssl._create_unverified_context()
+
return tl_api_class(self._server_url, self._devkey, **kwargs)
diff --git a/src/testlink/testreporter.py b/src/testlink/testreporter.py
new file mode 100644
index 0000000..fed5ee5
--- /dev/null
+++ b/src/testlink/testreporter.py
@@ -0,0 +1,253 @@
+#! /usr/bin/python
+# -*- coding: UTF-8 -*-
+
+# Copyright 2017-2019 Brian-Willams, TestLink-API-Python-client developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# ------------------------------------------------------------------------
+
+
+from testlink.testlinkerrors import TLResponseError, TLArgError
+
+
+class TestReporter(dict):
+ def __init__(self, tls, testcases, *args, **kwargs):
+ """This can be given one or more testcases, but they all must have the
+ same project, plan, and platform.
+
+ TESTCASES must be one or a list of full external testcase id
+ TLS must be an instance of TestlinkAPIClient, defining a
+ XMLRPC connection to a TestLink Server"""
+ super(TestReporter, self).__init__(*args, **kwargs)
+ self.tls = tls
+ # handle single testcase
+ self.testcases = testcases if isinstance(testcases, list) else [testcases]
+ self._plan_testcases = None
+ self.remove_non_report_kwargs()
+ self._platformname_generated = False
+
+ def remove_non_report_kwargs(self):
+ self.buildname = self.pop('buildname')
+
+ default_note = "created automatically with {}".format(self.__class__.__name__)
+ self.buildnotes = self.pop('buildnotes', default_note)
+ self.testplannotes = self.pop('testplannotes', default_note)
+ self.platformnotes = self.pop('platformnotes', default_note)
+
+ def setup_testlink(self):
+ """Call properties that may set report kwarg values."""
+ self.testprojectname
+ self.testprojectid
+ self.testplanid
+ self.testplanname
+ self.platformname
+ self.platformid
+ self.buildid
+
+ def _get_project_name_by_id(self):
+ if self.testprojectid:
+ for project in self.tls.getProjects():
+ if project['id'] == self.testprojectid:
+ return project['name']
+
+ def _projectname_getter(self):
+ if not self.get('testprojectname') and self.testprojectid:
+ self['testprojectname'] = self._get_project_name_by_id()
+ return self.get('testprojectname')
+
+ @property
+ def testprojectname(self):
+ return self._projectname_getter()
+
+ def _get_project_id(self):
+ tpid = self.get('testprojectid')
+ if not tpid and self.testprojectname:
+ self['testprojectid'] = self.tls.getProjectIDByName(self['testprojectname'])
+ return self['testprojectid']
+ return tpid
+
+ def _get_project_id_or_none(self):
+ project_id = self._get_project_id()
+ # If not found the id will return as -1
+ if project_id == -1:
+ project_id = None
+ return project_id
+
+ @property
+ def testprojectid(self):
+ self['testprojectid'] = self._get_project_id_or_none()
+ return self.get('testprojectid')
+
+ @property
+ def testplanid(self):
+ return self.get('testplanid')
+
+ @property
+ def testplanname(self):
+ return self.get('testplanname')
+
+ @property
+ def platformname(self):
+ """Return a platformname added to the testplan if there is one."""
+ return self.get('platformname')
+
+ @property
+ def platformid(self):
+ return self.get('platformid')
+
+ @property
+ def buildid(self):
+ return self.get('buildid')
+
+ @property
+ def plan_tcids(self):
+ if not self._plan_testcases:
+ self._plan_testcases = set()
+ tc_dict = self.tls.getTestCasesForTestPlan(self.testplanid)
+ try:
+ for _, platform in tc_dict.items():
+ for k, v in platform.items():
+ self._plan_testcases.add(v['full_external_id'])
+ except AttributeError:
+ # getTestCasesForTestPlan returns an empty list instead of an empty dict
+ pass
+ return self._plan_testcases
+
+ def reportgen(self):
+ """For use if you need to look at the status returns of individual reporting."""
+ self.setup_testlink()
+ for testcase in self.testcases:
+ yield self.tls.reportTCResult(testcaseexternalid=testcase, **self)
+
+ def report(self):
+ for _ in self.reportgen():
+ pass
+
+
+class AddTestCaseReporter(TestReporter):
+ """Add testcase to testplan if not added."""
+ def setup_testlink(self):
+ super(AddTestCaseReporter, self).setup_testlink()
+ self.ensure_testcases_in_plan()
+
+ def ensure_testcases_in_plan(self):
+ # Get the platformid if possible or else addition will fail
+ self.platformid
+ for testcase in self.testcases:
+ # Can't check if testcase is in plan_tcids, because that won't work if it's there, but of the wrong platform
+ try:
+ self.tls.addTestCaseToTestPlan(
+ self.testprojectid, self.testplanid, testcase, self.get_latest_tc_version(testcase),
+ platformid=self.platformid
+ )
+ except TLResponseError as e:
+ # Test Case version is already linked to Test Plan
+ if e.code == 3045:
+ pass
+ else:
+ raise
+
+ def get_latest_tc_version(self, testcaseexternalid):
+ return int(self.tls.getTestCase(None, testcaseexternalid=testcaseexternalid)[0]['version'])
+
+
+class AddTestPlanReporter(TestReporter):
+ @property
+ def testplanid(self):
+ if not self.get('testplanid'):
+ try:
+ self['testplanid'] = self.tls.getTestPlanByName(self.testprojectname, self.testplanname)[0]['id']
+ except TLResponseError as e:
+ # Name does not exist
+ if e.code == 3033:
+ self['testplanid'] = self._generate_testplanid()
+ else:
+ raise
+ except TypeError:
+ self['testplanid'] = self._generate_testplanid()
+ return self['testplanid']
+
+ def _generate_testplanid(self):
+ """This won't necessarily be able to create a testplanid. It requires a planname and projectname."""
+ if 'testplanname' not in self:
+ raise TLArgError("Need testplanname to generate a testplan for results.")
+
+ tp = self.tls.createTestPlan(self['testplanname'], self.testprojectname,
+ notes=self.testplannotes)
+ self['testplanid'] = tp[0]['id']
+ return self['testplanid']
+
+
+class AddPlatformReporter(TestReporter):
+ @property
+ def platformname(self):
+ """Return a platformname added to the testplan if there is one."""
+ pn_kwarg = self.get('platformname')
+ if pn_kwarg and self._platformname_generated is False:
+ # If we try to create platform and catch platform already exists error (12000) it sometimes duplicates a
+ # platformname
+ try:
+ self.tls.addPlatformToTestPlan(self.testplanid, pn_kwarg)
+ except TLResponseError as e:
+ if int(e.code) == 235:
+ self.tls.createPlatform(self.testprojectname, pn_kwarg,
+ notes=self.platformnotes,
+ platformondesign=True, platformonexecution=True)
+ self.tls.addPlatformToTestPlan(self.testplanid, pn_kwarg)
+ self._platformname_generated = True
+ else:
+ raise
+ return pn_kwarg
+
+ @property
+ def platformid(self):
+ if not self.get('platformid'):
+ self['platformid'] = self.getPlatformID(self.platformname)
+ # This action is idempotent
+ self.tls.addPlatformToTestPlan(self.testplanid, self.platformname)
+ return self['platformid']
+
+ def getPlatformID(self, platformname, _firstrun=True):
+ """
+ This is hardcoded for platformname to always be self.platformname
+ """
+ platforms = self.tls.getTestPlanPlatforms(self.testplanid)
+ for platform in platforms:
+ # https://github.com/Brian-Williams/TestLink-API-Python-client/issues/1
+ if platform['name'].lower() == platformname.lower():
+ return platform['id']
+ # Platformname houses platform creation as platform creation w/o a name isn't possible
+ if not self.platformname:
+ raise TLArgError(
+ "Couldn't find platformid for {}.{}, "
+ "please provide a platformname to generate.".format(self.testplanid, self.platformname)
+ )
+ if _firstrun is True:
+ return self.getPlatformID(self.platformname, _firstrun=False)
+ else:
+ raise TLArgError("PlatformID not found after generated from platformname '{}' "
+ "in test plan {}.".format(self.platformname, self.testplanid))
+
+
+class AddBuildReporter(TestReporter):
+ @property
+ def buildid(self):
+ bid = self.get('buildid')
+ if not bid or bid not in self.tls.getBuildsForTestPlan(self.testplanid):
+ self['buildid'] = self._generate_buildid()
+ return self.get('buildid')
+
+ def _generate_buildid(self):
+ r = self.tls.createBuild(self.testplanid, self.buildname, self.buildnotes)
+ return r[0]['id']
diff --git a/src/testlink/version.py b/src/testlink/version.py
index 884b4e7..4656c6d 100644
--- a/src/testlink/version.py
+++ b/src/testlink/version.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2013-2015 TestLink-API-Python-client developers
+# Copyright 2013-2021 TestLink-API-Python-client developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,6 +17,5 @@
#
# ------------------------------------------------------------------------
-VERSION = '0.6.2'
-TL_RELEASE = '1.9.14'
-
+VERSION = '0.8.2-dev141'
+TL_RELEASE = '1.9.20-fixed_c88e348ce'
diff --git a/test/conftest.py b/test/conftest.py
new file mode 100644
index 0000000..0de5044
--- /dev/null
+++ b/test/conftest.py
@@ -0,0 +1,76 @@
+#! /usr/bin/python
+# -*- coding: UTF-8 -*-
+
+# Copyright 2018-2019 Luiko Czub, TestLink-API-Python-client developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# ------------------------------------------------------------------------
+
+import os.path
+import pytest
+from testlink import TestlinkAPIClient, TestlinkAPIGeneric, TestLinkHelper
+
+# example text file attachment = this python file
+# why not using os.path.realpath(__file__)
+# -> cause __file__ could be compiled python file *.pyc, if the test run is
+# repeated without changing the test code
+ATTACHMENT_EXAMPLE_TEXT= os.path.join(os.path.dirname(__file__),
+ os.path.basename(__file__))
+
+#attachemantFile = open(ATTACHMENT_EXAMPLE_TEXT, 'r')
+
+@pytest.fixture()
+def attachmentFile():
+ ''' open readonly attachment sample before test and close it afterwards '''
+ aFile = open(ATTACHMENT_EXAMPLE_TEXT, 'r')
+ yield aFile
+ aFile.close()
+
+@pytest.fixture(scope='session')
+def api_helper_class():
+ return TestLinkHelper
+
+
+@pytest.fixture(scope='session')
+def api_generic_client(api_helper_class):
+ ''' Init TestlinkAPIGeneric Client with connection parameters defined in
+ environment variables
+ TESTLINK_API_PYTHON_DEVKEY and TESTLINK_API_PYTHON_DEVKEY
+ '''
+ return api_helper_class().connect(TestlinkAPIGeneric)
+
+@pytest.fixture(scope='session')
+def api_general_client(api_helper_class):
+ ''' Init TestlinkAPIClient Client with connection parameters defined in
+ environment variables
+ TESTLINK_API_PYTHON_DEVKEY and TESTLINK_API_PYTHON_DEVKEY
+ '''
+ return api_helper_class().connect(TestlinkAPIClient)
+
+@pytest.fixture(scope='session', params=[TestlinkAPIGeneric, TestlinkAPIClient])
+def api_client_class(request):
+ ''' all variations of Testlink API Client classes '''
+ return request.param
+
+@pytest.fixture(scope='session')
+def api_client(api_client_class, api_helper_class):
+ ''' Init Testlink API Client class defined in fixtures api_client_class with
+ connection parameters defined in environment variables
+ TESTLINK_API_PYTHON_DEVKEY and TESTLINK_API_PYTHON_DEVKEY
+
+ Tests will be call for each Testlink API Client class, defined in
+ fixtures parameter list
+ '''
+ return api_helper_class().connect(api_client_class)
+
diff --git a/test/utest-offline/test_apiClients_whatArgs.py b/test/utest-offline/test_apiClients_whatArgs.py
new file mode 100644
index 0000000..d6fd686
--- /dev/null
+++ b/test/utest-offline/test_apiClients_whatArgs.py
@@ -0,0 +1,152 @@
+#! /usr/bin/python
+# -*- coding: UTF-8 -*-
+
+# Copyright 2018-2021 Luiko Czub, TestLink-API-Python-client developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# ------------------------------------------------------------------------
+
+# TestCases for Testlink API clients whatArgs calls
+# - TestlinkAPIClient, TestlinkAPIGeneric
+#
+
+import pytest
+import re
+
+def test_whatArgs_noArgs(api_client):
+ response = api_client.whatArgs('sayHello')
+ assert re.match('sayHello().*', response)
+
+def test_whatArgs_onlyOptionalArgs(api_client):
+ response = api_client.whatArgs('getTestCaseKeywords')
+ assert re.match(r'getTestCaseKeywords\(\[.*=<.*>\].*\).*',
+ response)
+
+def test_whatArgs_OptionalAndPositionalArgs(api_client):
+ response = api_client.whatArgs('createBuild')
+ assert re.match(r'createBuild\(<.*>.*\).*', response)
+
+def test_whatArgs_MandatoryArgs(api_client):
+ response = api_client.whatArgs('uploadExecutionAttachment')
+ assert re.match(r'uploadExecutionAttachment\(, <.*>.*\).*',
+ response)
+
+def test_whatArgs_unknownMethods(api_client):
+ response = api_client.whatArgs('apiUnknown')
+ assert re.match(r"callServerWithPosArgs\('apiUnknown', \[apiArg=\]\)",
+ response)
+
+test_data_apiCall_descriptions_equal_all = [
+ ('getTestCasesForTestSuite', ['getkeywords=']),
+ ('reportTCResult', ['user=', 'execduration=',
+ 'timestamp=', 'steps=',
+ "[{'step_number' : 6,"]),
+ ('getLastExecutionResult', ['options=','getBugs']),
+ ('getTestCasesForTestPlan', [',',
+ 'buildid=', 'platformid=',
+ 'testcaseid=', 'keywordid=',
+ 'keywords=', 'executed=',
+ 'assignedto=', 'executestatus=',
+ 'executiontype=', 'getstepinfo=',
+ 'details=', 'customfields=',
+ 'keywordid - keywords']),
+ ('createTestCase', [',', ',', ',',
+ ',', ',', #',',
+ 'preconditions=',
+ 'importance=',
+ 'executiontype=', 'order=',
+ 'internalid=',
+ 'checkduplicatedname=',
+ 'actiononduplicatedname=',
+ 'status=',
+ 'estimatedexecduration=']),
+ ('createTestPlan', ['prefix=', 'testprojectname=']),
+ ('getTestSuite',['', '']),
+ ('updateTestSuite',[',', 'testprojectid=',
+ 'prefix=', 'parentid=',
+ 'testsuitename=', 'details=',
+ 'order=']),
+ ('createBuild',[',', ',', 'active=',
+ 'copytestersfrombuild=']),
+ ('addTestCaseToTestPlan',[',', ',',
+ ',', ',',
+ 'platformid=',
+ 'executionorder=',
+ 'urgency=', 'overwrite=']),
+ ('createTestProject',[',', ',',
+ 'notes=', 'active=',
+ 'public=', 'options=',
+ 'itsname=', 'itsenabled=']),
+ ('getIssueTrackerSystem',[',']),
+ ('getExecutionSet',[',', 'testcaseid=',
+ 'testcaseexternalid=',
+ 'buildid=', 'buildname=',
+ 'platformid=',
+ 'platformname=', 'options=']),
+ ('getRequirements',[',', 'testplanid=',
+ 'platformid=']),
+ ('getReqCoverage',[',', ',']),
+ ('setTestCaseTestSuite',[',', ',']),
+ ('getTestSuiteAttachments',[',']),
+ ('getAllExecutionsResults',[',','testcaseid=',
+ 'testcaseexternalid=',
+ 'platformid=', 'buildid=',
+ 'options=']),
+ ('getTestCaseAttachments',['version=',
+ 'testcaseexternalid=']),
+ ('uploadTestCaseAttachment',[',', ',',
+ 'title=', 'description=',
+ 'filename=', 'filetype=',
+ 'content=']),
+ ('createPlatform',[',', ',', 'notes=',
+ 'platformondesign=',
+ 'platformonexecution=']),
+ ('closeBuild', ['']),
+ ('createUser', ['', '', '', '',
+ 'password=']),
+ ('setUserRoleOnProject', ['', '', ''])
+ ]
+
+@pytest.mark.parametrize("apiCall, descriptions",
+ test_data_apiCall_descriptions_equal_all)
+def test_whatArgs_apiCall_descriptions_equal_all(api_client, apiCall, descriptions):
+ argsDescription = api_client.whatArgs(apiCall)
+ for parts in descriptions:
+ assert parts in argsDescription
+
+test_data_apiCall_descriptions_only_generic = [
+ ('createTestCase', [',']),
+ ('createBuild',['buildnotes=']),
+ ('getTestCaseAttachments',['testcaseid='])
+ ]
+@pytest.mark.parametrize("apiCall, descriptions",
+ test_data_apiCall_descriptions_only_generic)
+def test_whatArgs_apiCall_descriptions_only_generic(api_generic_client, apiCall, descriptions):
+ argsDescription = api_generic_client.whatArgs(apiCall)
+ for parts in descriptions:
+ assert parts in argsDescription
+
+test_data_apiCall_descriptions_only_general = [
+ ('createTestCase', ['steps=']),
+ ('createBuild',[',']),
+ ('getTestCaseAttachments',[','])
+ ]
+@pytest.mark.parametrize("apiCall, descriptions",
+ test_data_apiCall_descriptions_only_general)
+def test_whatArgs_apiCall_descriptions_only_general(api_general_client, apiCall, descriptions):
+ argsDescription = api_general_client.whatArgs(apiCall)
+ for parts in descriptions:
+ assert parts in argsDescription
+
+
\ No newline at end of file
diff --git a/test/utest-offline/test_py3_vs_py2.py b/test/utest-offline/test_py3_vs_py2.py
new file mode 100644
index 0000000..aa6c84e
--- /dev/null
+++ b/test/utest-offline/test_py3_vs_py2.py
@@ -0,0 +1,31 @@
+#! /usr/bin/python
+# -*- coding: UTF-8 -*-
+
+# Copyright 2018-2020 Luiko Czub, TestLink-API-Python-client developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# ------------------------------------------------------------------------
+
+# TestCases for Testlink API clients handling py2 and py3 differences
+# - TestlinkAPIClient, TestlinkAPIGeneric
+#
+
+import pytest
+#from conftest import api_general_client, api_generic_client
+
+def test_IS_PY3_same_state():
+ from testlink.testlinkhelper import IS_PY3 as proxie_is_py3
+ from testlink.testlinkapigeneric import IS_PY3 as tl_is_py3
+ assert proxie_is_py3 == tl_is_py3
+
diff --git a/test/utest-offline/testlinkapi_offline_test.py b/test/utest-offline/testlinkapi_offline_test.py
index c8d7c91..0e09db3 100644
--- a/test/utest-offline/testlinkapi_offline_test.py
+++ b/test/utest-offline/testlinkapi_offline_test.py
@@ -1,7 +1,7 @@
#! /usr/bin/python
# -*- coding: UTF-8 -*-
-# Copyright 2012-2015 Luiko Czub, TestLink-API-Python-client developers
+# Copyright 2012-2019 Luiko Czub, TestLink-API-Python-client developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -21,14 +21,7 @@
# no calls are send to a TestLink Server
import sys
-
-IS_PY26 = False
-if sys.version_info[0] == 2 and sys.version_info[1] == 6:
- # py26 needs backport unittest2
- import unittest2 as unittest
- IS_PY26 = True
-else:
- import unittest
+import unittest
if sys.version_info[0] == 2 and sys.version_info[1] == 7:
# py27 and py31 assertRaisesRegexp was renamed in py32 to assertRaisesRegex
@@ -287,6 +280,7 @@ def __init__(self, server_url, devKey, **args):
def loadScenario(self, a_scenario):
self.scenario_data = a_scenario
+ self.callArgs = None
def _callServer(self, methodAPI, argsAPI=None):
self.callArgs = argsAPI
@@ -334,8 +328,12 @@ class TestLinkAPIOfflineTestCase(unittest.TestCase):
{'step_number' : '3', 'actions' : "action C" ,
'expected_results' : "result C", 'execution_type' : "0"}]
+ @classmethod
+ def setUpClass(cls):
+ cls.api = TestLinkHelper().connect(DummyAPIClient)
+
def setUp(self):
- self.api = TestLinkHelper().connect(DummyAPIClient)
+ self.api._emptyStepsList()
# def tearDown(self):
# pass
@@ -564,12 +562,6 @@ def test_reportTCResult_user(self):
self.assertEqual(self.api.devKey, self.api.callArgs['devKey'])
self.assertEqual('a login name', self.api.callArgs['user'])
- def test_whatArgs_reportTCResult(self):
- argsDescription = self.api.whatArgs('reportTCResult')
- self.assertIn('user=', argsDescription)
- self.assertIn('execduration=', argsDescription)
- self.assertIn('timestamp=', argsDescription)
-
def test_getTestCasesForTestSuite_keyWords(self):
self.api.loadScenario(SCENARIO_KEYWORDS)
response = self.api.getTestCasesForTestSuite('deepFalse3', False,
@@ -578,10 +570,6 @@ def test_getTestCasesForTestSuite_keyWords(self):
self.assertNotIn('keywords', response[2])
self.assertEqual(self.api.devKey, self.api.callArgs['devKey'])
- def test_whatArgs_getTestCasesForTestSuite(self):
- argsDescription = self.api.whatArgs('getTestCasesForTestSuite')
- self.assertIn('getkeywords=', argsDescription)
-
def test_listKeywordsForTC_FullExternalId(self):
self.api.loadScenario(SCENARIO_KEYWORDS)
response = self.api.listKeywordsForTC('NPROAPI-2')
@@ -642,30 +630,25 @@ def test_listKeywordsForTS_Multi(self):
self.assertEqual({'8144': set(['KeyWord01', 'KeyWord03']),
'8159': set(['KeyWord02']), '8169': set()}, set_response)
- def test_whatArgs_getLastExecutionResult(self):
- argsDescription = self.api.whatArgs('getLastExecutionResult')
- self.assertIn('options=', argsDescription)
- self.assertIn('getBugs', argsDescription)
-
- def test_whatArgs_createTestCase(self):
- argsDescription = self.api.whatArgs('createTestCase')
- self.assertIn('executiontype=', argsDescription)
- self.assertIn('executiontype, order', argsDescription)
-
def test_connect_with_proxy(self):
""" create a TestLink API dummy with ProxiedTransport"""
self.api = DummyAPIClient('http://SERVER-URL-71', 'DEVKEY-71',
transport='PROXY-71')
- if not IS_PY26:
- # Py 26 does not define a __call__ method and getattr is overriden
- # to created a request and return the response
- # -> so no access to attribute __transport with Py26
- self.assertEqual('PROXY-71', self.api.server.__call__('transport'))
-
- def test_whatArgs_createTestPlan(self):
- argsDescription = self.api.whatArgs('createTestPlan')
- self.assertIn('prefix=', argsDescription)
- self.assertIn('testprojectname=', argsDescription)
+ self.assertEqual('PROXY-71', self.api.server.__call__('transport'))
+
+ def test_connect_with_use_datetime(self):
+ """ create a TestLink Generic API dummy with use_datetime"""
+ self.api = DummyAPIClient('http://SERVER-URL-71', 'DEVKEY-71',
+ use_datetime='datetime?')
+ a_transport = self.api.server.__call__('transport')
+ self.assertEqual('datetime?', a_transport._use_datetime)
+
+ def test_connect_with_context(self):
+ """ create a TestLink Generic API dummy with use_datetime"""
+ self.api = DummyAPIClient('https://SERVER-URL-71', 'DEVKEY-71',
+ context='ssl_context')
+ a_transport = self.api.server.__call__('transport')
+ self.assertEqual('ssl_context', a_transport.context)
if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
diff --git a/test/utest-offline/testlinkapigeneric_offline_test.py b/test/utest-offline/testlinkapigeneric_offline_test.py
index dd2367a..808357e 100644
--- a/test/utest-offline/testlinkapigeneric_offline_test.py
+++ b/test/utest-offline/testlinkapigeneric_offline_test.py
@@ -1,781 +1,786 @@
-#! /usr/bin/python
-# -*- coding: UTF-8 -*-
-
-# Copyright 2013-2015 Luiko Czub, TestLink-API-Python-client developers
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# ------------------------------------------------------------------------
-
-# this test works WITHOUT an online TestLink Server
-# no calls are send to a TestLink Server
-
-import sys, os.path
-
-IS_PY26 = False
-if sys.version_info[0] == 2 and sys.version_info[1] == 6:
- # py26 needs backport unittest2
- import unittest2 as unittest
- IS_PY26 = True
-else:
- import unittest
-
-if sys.version_info[0] == 2 and sys.version_info[1] == 7:
- # py27 and py31 assertRaisesRegexp was renamed in py32 to assertRaisesRegex
- unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
- # py27 and py31 assertRegexpMatches was renamed in py32 to assertRegex
- unittest.TestCase.assertRegex = unittest.TestCase.assertRegexpMatches
-
-
-from testlink import TestlinkAPIGeneric, TestLinkHelper
-from testlink.testlinkerrors import TLArgError, TLResponseError, TLAPIError
-
-
-#from testlink.testlinkapigeneric import positionalArgNamesDefault
-# scenario_a includes response from a testlink 1.9.8 server
-SCENARIO_A = {'repeat' : 'You said: One World',
- 'sayHello' : 'Hey Folks!',
- 'doesUserExist' : {
- 'Big Bird' : [{'message': '(doesUserExist) - Cannot Find User Login provided (Big Bird).',
- 'code': 10000}],
- 'admin' : True },
- 'getProjectTestPlans' : {
- 'onePlan' : [{'name': 'TestPlan_API',
- 'notes': 'New TestPlan created with the API',
- 'active': '1', 'is_public': '1',
- 'testproject_id': '21', 'id': '22'}] ,
- 'noPlan' : '' },
- 'getBuildsForTestPlan' : {'noBuild' : '' },
- 'getTestPlanPlatforms' : {
- 'twoPlatforms' : [{'notes': '', 'id': '1', 'name': 'dutch'},
- {'notes': '', 'id': '2', 'name': 'platt'}],
- 'noPlatform' : [{'message': 'Test plan (noPlatform) has no platforms linked',
- 'code': 3041}]},
- 'getTestSuitesForTestPlan' : {'noSuite' : ''},
- 'getTestSuitesForTestSuite' : {'noSuite' : ''},
- 'getFirstLevelTestSuitesForTestProject' : {
- 'noSuite' : [{'message': 'Test Project (noSuite) is empty.',
- 'code': 7008}]},
- 'getTestCasesForTestSuite' : {'noTestCase' : [] },
- 'getTestCasesForTestPlan' : {'noTestCase' : [] },
- 'getTestCaseIDByName' : {
- 'dictResult' : {'1': {'parent_id': '24', 'tc_external_id': '2',
- 'id': '33', 'tsuite_name': 'B - First Level',
- 'name': 'TESTCASE_B'}},
- 'listResult' : [{'parent_id': '25', 'tc_external_id': '1',
- 'id': '26', 'tsuite_name': 'AA - Second Level',
- 'name': 'TESTCASE_AA'}]},
- 'getProjectPlatforms' : {
- 'twoPlatforms' : {'dutch' : {'id': '1', 'name': 'dutch'},
- 'platt' : {'id': '2', 'name': 'platt'}},
- 'noPlatform' : {}
- },
- 'reportTCResult' : [{'status': True, 'operation': 'reportTCResult',
- 'message': 'Success!', 'overwrite': False, 'id': '773'}],
- 'getProjectKeywords' : {
- 'twoKeywords' : {'25': 'KeyWord01', '26': 'KeyWord02'},
- 'noKeyword' : {}
- },
- 'getTestCaseKeywords' : {
- 'twoKeywords' : {'25': 'KeyWord01', '26': 'KeyWord02'},
- 'noKeyword' : {}
- }
- }
-
-# scenario_tl198 used by test with older responses, changed in TL 1.9.9
-SCENARIO_TL198 = {'testLinkVersion' : 'unknown',
- 'about' : 'Testlink API Version: 1.0 ...'}
-
-# scenario_tl199 used by test with newer responses, changed in TL 1.9.9
-SCENARIO_TL199 = {'testLinkVersion' : '1.9.9',
- 'about' : 'Testlink API Version: 1.0 ...'}
-
-# scenario_custom_fields defines response for custom field request
-# {'default_value': '', 'enable_on_execution': '1', 'name': 'cf_tc_ex_string', 'location': '1', 'enable_on_design': '0', 'valid_regexp': '', 'length_min': '0', 'possible_values': '', 'value': 'a custom string', 'label': 'CF Exec String', 'show_on_testplan_design': '0', 'display_order': '1', 'length_max': '0', 'show_on_design': '0', 'required': '0', 'show_on_execution': '1', 'type': '0', 'id': '24', 'node_id': '7691', 'enable_on_testplan_design': '0'}
-SCENARIO_CUSTOM_FIELDS = {
- 'getTestCaseCustomFieldDesignValue' : {
- 'cf_notAssigned' : [{'message': '(getTestCaseCustomFieldDesignValue) - Custom Field (name:cf_tc_sd_string), is not assigned to Test Project(name=PROJECT_API_GENERIC-8 / id=7760)',
- 'code': 9003}],
- 'cf_full' : {'default_value': '', 'enable_on_execution': '0', 'name': 'cf_tc_sd_string',
- 'location': '1', 'enable_on_design': '1', 'valid_regexp': '', 'length_min': '0', 'possible_values': '',
- 'value': 'a custom spec design string', 'label': 'CF SpecDesign String', 'show_on_testplan_design': '0',
- 'display_order': '1', 'length_max': '0', 'show_on_design': '1', 'required': '0', 'show_on_execution': '1',
- 'type': '0', 'id': '22', 'node_id': '7691', 'enable_on_testplan_design': '0'},
- 'cf_value' : 'a custom spec design string',
- 'cf_valueEmpty' : '',
- 'cf_simple' : {'type': '0', 'name': 'cf_tc_sd_string',
- 'value': 'a custom spec design string', 'label': 'CF SpecDesign String'}
- },
- 'updateTestCaseCustomFieldDesignValue' : {
- 'cf_notAssigned' : '',
- 'a_string' : ''
- },
-
- 'getTestCaseCustomFieldExecutionValue' : {
- 'cf_notAssigned' : '',
- 'cf_full' : {'default_value': '', 'enable_on_execution': '1', 'name': 'cf_tc_ex_string',
- 'location': '1', 'enable_on_design': '0', 'valid_regexp': '', 'length_min': '0', 'possible_values': '',
- 'value': 'a custom exec string', 'label': 'CF Exec String', 'show_on_testplan_design': '0',
- 'display_order': '1', 'length_max': '0', 'show_on_design': '0', 'required': '0', 'show_on_execution': '1',
- 'type': '0', 'id': '24', 'node_id': '7691', 'enable_on_testplan_design': '0'}
- },
-
- 'getTestCaseCustomFieldTestPlanDesignValue' : {
- 'cf_notAssigned' : '',
- 'cf_full' : {'default_value': '', 'enable_on_execution': '0', 'name': 'cf_tc_pd_string',
- 'enable_on_design': '0', 'valid_regexp': '', 'length_min': '0', 'possible_values': '',
- 'value': 'a custom PlanDesign string', 'label': 'CF PlanDesign String', 'show_on_testplan_design': '1',
- 'display_order': '1', 'length_max': '0', 'show_on_design': '0', 'required': '0', 'show_on_execution': '1',
- 'type': '0', 'id': '28', 'node_id': '779', 'enable_on_testplan_design': '1'}
- },
- 'getTestSuiteCustomFieldDesignValue' : {
- 'cf_notAssigned' : '',
- 'cf_full' : {'default_value': '', 'enable_on_execution': '0', 'name': 'cf_ts_string', 'location': '1',
- 'enable_on_design': '1', 'valid_regexp': '', 'length_min': '0', 'possible_values': '',
- 'value': 'a custom TSuite string', 'label': 'CF TestSuite String', 'show_on_testplan_design': '0',
- 'display_order': '1', 'length_max': '0', 'show_on_design': '1', 'required': '0', 'show_on_execution': '1',
- 'type': '0', 'id': '30', 'node_id': '', 'enable_on_testplan_design': '0'}
- },
-
- 'getTestPlanCustomFieldDesignValue' : {
- 'cf_notAssigned' : '',
- 'cf_full' : {'default_value': '', 'enable_on_execution': '0', 'name': 'cf_tp_string', 'location': '1',
- 'enable_on_design': '1', 'valid_regexp': '', 'length_min': '0', 'possible_values': '',
- 'value': 'a custom TPlan string', 'label': 'CF TPlan String', 'show_on_testplan_design': '0',
- 'display_order': '1', 'length_max': '0', 'show_on_design': '1', 'required': '0', 'show_on_execution': '1',
- 'type': '0', 'id': '31', 'node_id': '', 'enable_on_testplan_design': '0'}
- },
-
- 'getReqSpecCustomFieldDesignValue' : {
- 'cf_notAssigned' : '',
- 'cf_full' : {'default_value': '', 'enable_on_execution': '0', 'name': 'cf_req_sd_string', 'location': '1',
- 'enable_on_design': '1', 'valid_regexp': '', 'length_min': '0', 'possible_values': '',
- 'value': 'a custom ReqSpec string', 'label': 'CF ReqSpec String', 'show_on_testplan_design': '0',
- 'display_order': '1', 'length_max': '0', 'show_on_design': '1', 'required': '0', 'show_on_execution': '0',
- 'type': '0', 'id': '32', 'node_id': '', 'enable_on_testplan_design': '0'}
- },
- 'getRequirementCustomFieldDesignValue' : {
- 'cf_notAssigned' : '',
- 'cf_full' : {'default_value': '', 'enable_on_execution': '0', 'name': 'cf_req_string', 'location': '1',
- 'enable_on_design': '1', 'valid_regexp': '', 'length_min': '0', 'possible_values': '',
- 'value': 'a custom Req string', 'label': 'CF Req String', 'show_on_testplan_design': '0',
- 'display_order': '1', 'length_max': '0', 'show_on_design': '1', 'required': '0', 'show_on_execution': '0',
- 'type': '0', 'id': '33', 'node_id': '', 'enable_on_testplan_design': '0'}
- }
-
- }
-
-# scenario_keywords defines response with keywords
-SCENARIO_KEYWORDS = {'getTestCasesForTestSuite' : {
- 'noTestCase' : [] ,
- 'keyWords' : [{'node_order': '0', 'is_open': '1',
- 'keywords': {'1': {'keyword_id': '1', 'notes': 'a key word', 'testcase_id': '8144', 'keyword': 'KeyWord01'},
- '3': {'keyword_id': '3', 'notes': 'a third key word', 'testcase_id': '8144', 'keyword': 'KeyWord03'}},
- 'id': '8144', 'node_type_id': '3', 'layout': '1', 'tc_external_id': '2', 'parent_id': '8134',
- 'version': '1', 'estimated_exec_duration': '3.00', 'updater_id': '2', 'status': '1',
- 'tsuite_name': 'B - First Level', 'importance': '3', 'modification_ts': '2014-06-30 20:45:40',
- 'execution_type': '1', 'preconditions': '