From 70fe5505aafed8772a72fb30ee87e6ade921f7dc Mon Sep 17 00:00:00 2001 From: Viktoras Date: Fri, 2 Aug 2013 20:23:13 +0200 Subject: [PATCH 01/28] Fixed DELETE body request warning --- src/HttpLibrary/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index ae11328..8a9e701 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -3,6 +3,7 @@ from base64 import b64encode from functools import wraps from urlparse import urlparse +from webtest import utils import livetest import json @@ -278,7 +279,7 @@ def DELETE(self, url): logger.debug("Performing DELETE request on %s://%s%s" % ( self.context._scheme, self.app.host, url)) self.context.post_process_request( - self.app.delete(path, {}, self.context.request_headers) + self.app.delete(path, utils.NoDefault, self.context.request_headers) ) def follow_response(self): From 536675b1bc400729adce3872eac59dfa111dd547 Mon Sep 17 00:00:00 2001 From: Viktoras Bezaras Date: Thu, 8 Aug 2013 11:42:20 +0200 Subject: [PATCH 02/28] Added Should Not Contain function --- src/HttpLibrary/__init__.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 8a9e701..b85aeed 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -490,6 +490,22 @@ def response_body_should_contain(self, should_contain): '"%s" should have contained "%s", but did not.' % ( self.response.body, should_contain) + def response_body_should_not_contain(self, should_not_contain): + """ + Fails if the response body does contain `should_not_contain` + + Example: + | GET | /foo.xml | + | Response Body Should Not Contain | 404 | + | Response Body Should Not Contain | ERROR | + """ + logger.debug('Testing whether "%s" does not contain "%s".' % ( + self.response.body, should_not_contain)) + + assert should_not_contain not in self.response.body, \ + '"%s" should not have contained "%s", but it did.' % ( + self.response.body, should_not_contain) + def log_response_body(self, log_level='INFO'): """ Logs the response body. From 4857fd423d2344b9e5684f9e733f269cdff45da7 Mon Sep 17 00:00:00 2001 From: Jan Verhoeven Date: Mon, 9 Jun 2014 20:31:05 +0200 Subject: [PATCH 03/28] Added PATCH as main keyword Added a simply echo server to the mock server (copied from POST statement). Added a test in simple.txt test framework. Moved the old 'Custom HTTP' verb test to use OPTIONS instead of PATCH. --- src/HttpLibrary/__init__.py | 19 +++++++++++++++++++ tests/http/mockserver.py | 27 +++++++++++++++++++++++++-- tests/http/simple.txt | 10 ++++++++-- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index ae11328..1b970af 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -267,6 +267,25 @@ def PUT(self, url): self.context.request_headers, **kwargs) ) + def PATCH(self, url): + """ + Issues an HTTP PATCH request. + + `url` is the URL relative to the server root, e.g. '/_utils/config.html' + """ + path = self._path_from_url_or_path(url) + kwargs = {} + if 'Content-Type' in self.context.request_headers: + kwargs[ + 'content_type'] = self.context.request_headers['Content-Type'] + self.context.pre_process_request() + logger.debug("Performing PATCH request on %s://%s%s" % ( + self.context._scheme, self.app.host, url)) + self.context.post_process_request( + self.app.patch(path, self.context.request_body or {}, + self.context.request_headers, **kwargs) + ) + def DELETE(self, url): """ Issues a HTTP DELETE request. diff --git a/tests/http/mockserver.py b/tests/http/mockserver.py index fe2bb8e..bff6ca7 100755 --- a/tests/http/mockserver.py +++ b/tests/http/mockserver.py @@ -22,9 +22,32 @@ def do_DELETE(self): self.finish() def do_PATCH(self): - self.send_response(200, "Patch request ok") + if self.path == '/echo': + data = self.rfile.read(int(self.headers['Content-Length'])) + self.rfile.close() + self.send_response(200, "OK") + self.send_header('Content-Type', 'text/plain; charset=utf-8') + self.end_headers() + self.wfile.write(data) + self.finish() + elif self.path == '/content_type': + self.send_response(200, "OK") + self.wfile.write(self.rfile.read) + self.end_headers() + self.wfile.write(self.headers['Content-Type']) + self.finish() + elif self.path == '/kill': + global server + self.send_response(201, "Killing myself") + server.socket.close() + sys.exit(0) + else: + self.send_error(500) + + def do_OPTIONS(self): + self.send_response(200, "Options request ok") self.end_headers() - self.wfile.write("Got a patch request") + self.wfile.write("Got an OPTIONS request") self.finish() def do_GET(self): diff --git a/tests/http/simple.txt b/tests/http/simple.txt index 827950a..66571d5 100644 --- a/tests/http/simple.txt +++ b/tests/http/simple.txt @@ -102,8 +102,8 @@ Next Request Should Have Status Code w/ Status Line FAIL ... GET /418 HTTP Request with custom HTTP verb should work - HTTP Request PATCH /patch - Response Body Should Contain Got a patch request + HTTP Request OPTIONS /options + Response Body Should Contain Got an OPTIONS request Response Should Have Header OK GET /302 @@ -174,6 +174,12 @@ PUT with two word request body PUT /echo Response Body Should Contain Tooot Tooooot +PATCH with two word request body + Set Request Body Tooot Tooooot + Set Request Header Content-Type text/plain + PATCH /echo + Response Body Should Contain Tooot Tooooot + Get Response Status GET /200 ${status}= Get Response Status From 6a89575e2c5f39b0bebcb9c59e70a2615c80aa29 Mon Sep 17 00:00:00 2001 From: Jan Verhoeven Date: Mon, 13 Oct 2014 11:25:09 +0200 Subject: [PATCH 04/28] GET command takes a string from a string, effectively not allowing to take use a variable as a parameter for the GET command. Remove the quotes if needed. --- src/HttpLibrary/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 1b970af..994fba7 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -138,6 +138,9 @@ def response(self): def _path_from_url_or_path(self, url_or_path): + if url_or_path.startswith("\"") and url_or_path.endswith("\""): + url_or_path = url_or_path[1:-1] + if url_or_path.startswith("/"): return url_or_path From 5046879536ffc48b49b0c59508cbb68e3e0d8b02 Mon Sep 17 00:00:00 2001 From: Yan Bilik Date: Wed, 22 Oct 2014 11:02:17 +0200 Subject: [PATCH 05/28] Added OPTIONS as main keyword --- src/HttpLibrary/__init__.py | 14 ++++++++++++++ tests/http/simple.txt | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 994fba7..82d7f84 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -303,6 +303,20 @@ def DELETE(self, url): self.app.delete(path, {}, self.context.request_headers) ) + def OPTIONS(self, url): + """ + Issues a HTTP OPTIONS request. + + `url` is the URL relative to the server root, e.g. '/_utils/config.html' + """ + path = self._path_from_url_or_path(url) + self.context.pre_process_request() + logger.debug("Performing OPTIONS request on %s://%s%s" % ( + self.context._scheme, self.app.host, path,)) + self.context.post_process_request( + self.app.options(path, self.context.request_headers) + ) + def follow_response(self): """ Follows a HTTP redirect if the previous response status code was a 301 or 302. diff --git a/tests/http/simple.txt b/tests/http/simple.txt index 66571d5..3c97e6b 100644 --- a/tests/http/simple.txt +++ b/tests/http/simple.txt @@ -180,6 +180,10 @@ PATCH with two word request body PATCH /echo Response Body Should Contain Tooot Tooooot +Simple OPTIONS request + OPTIONS /options + Response Body Should Contain Got an OPTIONS request + Get Response Status GET /200 ${status}= Get Response Status From 708d1bccec95308e19fadeacb8fd5de4ef7e1255 Mon Sep 17 00:00:00 2001 From: Oliver Bruns Date: Mon, 9 Feb 2015 21:47:52 +0100 Subject: [PATCH 06/28] replace direct member access with method call --- src/HttpLibrary/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index ae11328..6ee29b5 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -483,11 +483,11 @@ def response_body_should_contain(self, should_contain): | Response Body Should Contain | encoding="UTF-8" | """ logger.debug('Testing whether "%s" contains "%s".' % ( - self.response.body, should_contain)) + self.get_response_body(), should_contain)) - assert should_contain in self.response.body, \ + assert should_contain in self.get_response_body(), \ '"%s" should have contained "%s", but did not.' % ( - self.response.body, should_contain) + self.get_response_body(), should_contain) def log_response_body(self, log_level='INFO'): """ @@ -495,9 +495,9 @@ def log_response_body(self, log_level='INFO'): Specify `log_level` (default: "INFO") to set the log level. """ - if self.response.body: + if self.get_response_body(): logger.write("Response body:", log_level) - logger.write(self.response.body, log_level) + logger.write(self.get_response_body(), log_level) else: logger.debug("No response body received", log_level) From f4cc5b7acca64c1ba7fa4641ce8c3ed98440010f Mon Sep 17 00:00:00 2001 From: Oliver Bruns Date: Mon, 9 Feb 2015 21:28:12 +0100 Subject: [PATCH 07/28] return the response body as a unicode object Keep the symmetry with set_request_body() which encodes the unicode object (Robot Framework defaults to unicode for all strings) into an instance of str by decoding the response into an instance of unicode in get_response_body(). Failure to do so causes UnicodeDecodeErrors in all keywords that compare the response body against a given string if any of the two contains Unicode characters. --- src/HttpLibrary/__init__.py | 2 +- tests/http/simple.txt | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 6ee29b5..6b8bbb3 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -471,7 +471,7 @@ def get_response_body(self): | ${body}= | Get Response Body | | | Should Start With | ${body} | Date: Mon, 9 Feb 2015 21:25:52 +0100 Subject: [PATCH 08/28] convert HTTP header keys from unicode objects into str objects Robot Framework defaults to unicode objects for all strings created. As a result, httplib.py:848 has unicode += str (msg += message_body) which crashes with UnicodeDecodeError when the body contains Unicode characters: File "/usr/lib64/python2.7/httplib.py", line 1001, in request self._send_request(method, url, body, headers) File "/usr/lib64/python2.7/httplib.py", line 1035, in _send_request self.endheaders(body) File "/usr/lib64/python2.7/httplib.py", line 997, in endheaders self._send_output(message_body) File "/usr/lib64/python2.7/httplib.py", line 848, in _send_output msg += message_body UnicodeDecodeError: 'ascii' codec can't decode byte 0xef in position 32: ordinal not in range(128) A similar stack trace has been posted as part of Python bug #11898 [1] (duplicated by #12398 [2]) which has later been closed as a programming error. Both robotframework-httplibrary and webtest treat the 'Content-Type' header special which results in surprising behavior regarding the tests: Had the 'Accept' header been removed from the provided test case no UnicodeDecodeError would have been thrown in httplib.py:848. Turning unicode objects into str objects is the right thing to do according to the HTTP standard ([3], updated by [5]) because it requires conformity to MIME [5] (hint by [6]). [1] http://bugs.python.org/issue11898 [2] http://bugs.python.org/issue12398 [3] https://tools.ietf.org/html/rfc2616 [4] https://tools.ietf.org/html/rfc2047 [5] https://tools.ietf.org/html/rfc7230 [6] https://stackoverflow.com/a/5426648 --- src/HttpLibrary/__init__.py | 2 +- tests/http/simple.txt | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 6b8bbb3..8e860b8 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -431,7 +431,7 @@ def set_request_header(self, header_name, header_value): """ logger.info( 'Set request header "%s" to "%s"' % (header_name, header_value)) - self.context.request_headers[header_name] = header_value + self.context.request_headers[str(header_name)] = header_value def set_basic_auth(self, username, password): """ diff --git a/tests/http/simple.txt b/tests/http/simple.txt index 092fd54..fd08760 100644 --- a/tests/http/simple.txt +++ b/tests/http/simple.txt @@ -176,6 +176,15 @@ POST with Unicode request body POST /echo Response Body Should Contain Tschüss Süße +POST with Unicode request body and Accept Header + [Documentation] Request body and headers must allow to be concatenated + [Tags] unicode + Set Request Body Tschüss Süße + Set Request Header Accept application/json + Set Request Header Content-Type text/plain + POST /echo + Response Body Should Contain Tschüss Süße + PUT with two word request body Set Request Body Tooot Tooooot Set Request Header Content-Type text/plain From e766830d7fd04a752be915a0110c2e1302afddab Mon Sep 17 00:00:00 2001 From: KoleS46 Date: Tue, 9 Feb 2016 14:24:54 +0100 Subject: [PATCH 09/28] http_request passing headers into webtest through keyword argument. --- src/HttpLibrary/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index ae11328..540cdbf 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -197,7 +197,8 @@ def http_request(self, verb, url): logger.debug("Performing %s request on %s://%s%s" % (verb, self.context._scheme, self.app.host, path,)) self.context.post_process_request( - self.context.app.request(path, {}, self.context.request_headers, + self.context.app.request(path, {}, + headers=self.context.request_headers, method=verb.upper(),) ) From 465553b9cde2562e8ab7931e720736c8136a8874 Mon Sep 17 00:00:00 2001 From: KoleS46 Date: Tue, 9 Feb 2016 15:23:16 +0100 Subject: [PATCH 10/28] Update to robotframework 3.0.0 --- .travis.yml | 4 +- bootstrap.py | 251 +++++++++++++++++++++++++++------------ buildout.cfg | 2 +- tests/http/__init__.txt | 3 +- tests/https/__init__.txt | 3 +- tests/json.txt | 2 +- 6 files changed, 181 insertions(+), 84 deletions(-) diff --git a/.travis.yml b/.travis.yml index d1d5fd2..34c6697 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,7 @@ python: - "2.6" - "2.7" env: - - ROBOTFRAMEWORK_VERSION=2.6.3 - - ROBOTFRAMEWORK_VERSION=2.7.7 - - ROBOTFRAMEWORK_VERSION=2.8.1 + - ROBOTFRAMEWORK_VERSION=2.9.2 script: - ./bin/robotframework --monitorwidth 65 tests/ diff --git a/bootstrap.py b/bootstrap.py index 63aebb9..a459921 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -1,6 +1,6 @@ ############################################################################## # -# Copyright (c) 2006 Zope Corporation and Contributors. +# Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, @@ -16,98 +16,195 @@ Simply run this script in a directory containing a buildout.cfg. The script accepts buildout command-line options, so you can use the -c option to specify an alternate configuration file. - -$Id: bootstrap.py 102545 2009-08-06 14:49:47Z chrisw $ """ -import os, shutil, sys, tempfile, urllib2 -from optparse import OptionParser - -tmpeggs = tempfile.mkdtemp() +import os +import shutil +import sys +import tempfile -is_jython = sys.platform.startswith('java') +from optparse import OptionParser -# parsing arguments -parser = OptionParser() -parser.add_option("-v", "--version", dest="version", - help="use a specific zc.buildout version") -parser.add_option("-d", "--distribute", - action="store_true", dest="distribute", default=True, - help="Use Disribute rather than Setuptools.") +__version__ = '2015-07-01' +# See zc.buildout's changelog if this version is up to date. + +tmpeggs = tempfile.mkdtemp(prefix='bootstrap-') + +usage = '''\ +[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options] + +Bootstraps a buildout-based project. + +Simply run this script in a directory containing a buildout.cfg, using the +Python that you want bin/buildout to use. + +Note that by using --find-links to point to local resources, you can keep +this script from going over the network. +''' + +parser = OptionParser(usage=usage) +parser.add_option("--version", + action="store_true", default=False, + help=("Return bootstrap.py version.")) +parser.add_option("-t", "--accept-buildout-test-releases", + dest='accept_buildout_test_releases', + action="store_true", default=False, + help=("Normally, if you do not specify a --version, the " + "bootstrap script and buildout gets the newest " + "*final* versions of zc.buildout and its recipes and " + "extensions for you. If you use this flag, " + "bootstrap and buildout will get the newest releases " + "even if they are alphas or betas.")) +parser.add_option("-c", "--config-file", + help=("Specify the path to the buildout configuration " + "file to be used.")) +parser.add_option("-f", "--find-links", + help=("Specify a URL to search for buildout releases")) +parser.add_option("--allow-site-packages", + action="store_true", default=False, + help=("Let bootstrap.py use existing site packages")) +parser.add_option("--buildout-version", + help="Use a specific zc.buildout version") +parser.add_option("--setuptools-version", + help="Use a specific setuptools version") +parser.add_option("--setuptools-to-dir", + help=("Allow for re-use of existing directory of " + "setuptools versions")) options, args = parser.parse_args() +if options.version: + print("bootstrap.py version %s" % __version__) + sys.exit(0) -if options.version is not None: - VERSION = '==%s' % options.version -else: - VERSION = '' -USE_DISTRIBUTE = options.distribute -args = args + ['bootstrap'] +###################################################################### +# load/install setuptools -to_reload = False try: - import pkg_resources - if not hasattr(pkg_resources, '_distribute'): - to_reload = True - raise ImportError + from urllib.request import urlopen except ImportError: - ez = {} - if USE_DISTRIBUTE: - exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py' - ).read() in ez - ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True) - else: - exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py' - ).read() in ez - ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) - - if to_reload: - reload(pkg_resources) - else: - import pkg_resources - -if sys.platform == 'win32': - def quote(c): - if ' ' in c: - return '"%s"' % c # work around spawn lamosity on windows - else: - return c -else: - def quote (c): - return c + from urllib2 import urlopen -cmd = 'from setuptools.command.easy_install import main; main()' -ws = pkg_resources.working_set - -if USE_DISTRIBUTE: - requirement = 'distribute' +ez = {} +if os.path.exists('ez_setup.py'): + exec(open('ez_setup.py').read(), ez) else: - requirement = 'setuptools' + exec(urlopen('https://bootstrap.pypa.io/ez_setup.py').read(), ez) + +if not options.allow_site_packages: + # ez_setup imports site, which adds site packages + # this will remove them from the path to ensure that incompatible versions + # of setuptools are not in the path + import site + # inside a virtualenv, there is no 'getsitepackages'. + # We can't remove these reliably + if hasattr(site, 'getsitepackages'): + for sitepackage_path in site.getsitepackages(): + # Strip all site-packages directories from sys.path that + # are not sys.prefix; this is because on Windows + # sys.prefix is a site-package directory. + if sitepackage_path != sys.prefix: + sys.path[:] = [x for x in sys.path + if sitepackage_path not in x] + +setup_args = dict(to_dir=tmpeggs, download_delay=0) + +if options.setuptools_version is not None: + setup_args['version'] = options.setuptools_version +if options.setuptools_to_dir is not None: + setup_args['to_dir'] = options.setuptools_to_dir + +ez['use_setuptools'](**setup_args) +import setuptools +import pkg_resources + +# This does not (always?) update the default working set. We will +# do it. +for path in sys.path: + if path not in pkg_resources.working_set.entries: + pkg_resources.working_set.add_entry(path) + +###################################################################### +# Install buildout + +ws = pkg_resources.working_set + +setuptools_path = ws.find( + pkg_resources.Requirement.parse('setuptools')).location + +# Fix sys.path here as easy_install.pth added before PYTHONPATH +cmd = [sys.executable, '-c', + 'import sys; sys.path[0:0] = [%r]; ' % setuptools_path + + 'from setuptools.command.easy_install import main; main()', + '-mZqNxd', tmpeggs] + +find_links = os.environ.get( + 'bootstrap-testing-find-links', + options.find_links or + ('http://downloads.buildout.org/' + if options.accept_buildout_test_releases else None) + ) +if find_links: + cmd.extend(['-f', find_links]) + +requirement = 'zc.buildout' +version = options.buildout_version +if version is None and not options.accept_buildout_test_releases: + # Figure out the most recent final version of zc.buildout. + import setuptools.package_index + _final_parts = '*final-', '*final' + + def _final_version(parsed_version): + try: + return not parsed_version.is_prerelease + except AttributeError: + # Older setuptools + for part in parsed_version: + if (part[:1] == '*') and (part not in _final_parts): + return False + return True + + index = setuptools.package_index.PackageIndex( + search_path=[setuptools_path]) + if find_links: + index.add_find_links((find_links,)) + req = pkg_resources.Requirement.parse(requirement) + if index.obtain(req) is not None: + best = [] + bestv = None + for dist in index[req.project_name]: + distv = dist.parsed_version + if _final_version(distv): + if bestv is None or distv > bestv: + best = [dist] + bestv = distv + elif distv == bestv: + best.append(dist) + if best: + best.sort() + version = best[-1].version +if version: + requirement = '=='.join((requirement, version)) +cmd.append(requirement) + +import subprocess +if subprocess.call(cmd) != 0: + raise Exception( + "Failed to execute command:\n%s" % repr(cmd)[1:-1]) + +###################################################################### +# Import and run buildout -if is_jython: - import subprocess +ws.add_entry(tmpeggs) +ws.require(requirement) +import zc.buildout.buildout - assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd', - quote(tmpeggs), 'zc.buildout' + VERSION], - env=dict(os.environ, - PYTHONPATH= - ws.find(pkg_resources.Requirement.parse(requirement)).location - ), - ).wait() == 0 +if not [a for a in args if '=' not in a]: + args.append('bootstrap') -else: - assert os.spawnle( - os.P_WAIT, sys.executable, quote (sys.executable), - '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION, - dict(os.environ, - PYTHONPATH= - ws.find(pkg_resources.Requirement.parse(requirement)).location - ), - ) == 0 +# if -c was provided, we push it back into args for buildout' main function +if options.config_file is not None: + args[0:0] = ['-c', options.config_file] -ws.add_entry(tmpeggs) -ws.require('zc.buildout' + VERSION) -import zc.buildout.buildout zc.buildout.buildout.main(args) shutil.rmtree(tmpeggs) diff --git a/buildout.cfg b/buildout.cfg index 180f1d0..9b37afb 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -12,7 +12,7 @@ recipe = zc.recipe.egg eggs = robotframework-httplibrary [versions] -robotframework = 2.7.5 +robotframework = 3.0.0 [robotframework] recipe = zc.recipe.egg diff --git a/tests/http/__init__.txt b/tests/http/__init__.txt index 93dcdde..0ed700f 100644 --- a/tests/http/__init__.txt +++ b/tests/http/__init__.txt @@ -2,6 +2,7 @@ Library HttpLibrary.HTTP Library OperatingSystem +Library Process Resource variables.txt @@ -11,7 +12,7 @@ Suite Teardown Stop Mockserver *** Keywords *** Start Mockserver - Start Process tests/http/mockserver.py ${PORT} + Start Process tests/http/mockserver.py ${PORT} Sleep 1 Stop Mockserver diff --git a/tests/https/__init__.txt b/tests/https/__init__.txt index 7cefec4..8038834 100644 --- a/tests/https/__init__.txt +++ b/tests/https/__init__.txt @@ -2,6 +2,7 @@ Library HttpLibrary.HTTP Library OperatingSystem +Library Process Resource variables.txt @@ -11,7 +12,7 @@ Suite Teardown Stop Secure Mockserver *** Keywords *** Start Secure Mockserver - Start Process tests/http/mockserver.py ${PORT} --ssl + Start Process tests/http/mockserver.py ${PORT} --ssl Sleep 1 Stop Secure Mockserver diff --git a/tests/json.txt b/tests/json.txt index 244166c..e3984e1 100644 --- a/tests/json.txt +++ b/tests/json.txt @@ -104,7 +104,7 @@ Stringify Documentation Example Stringify Complex Object ${names} = Create List First Name Family Name Email - ${data} = Create Dictionary names ${names} a 1 b 12 + ${data} = Create Dictionary a=1 b=12 names=${names} ${json_string}= Stringify JSON ${data} Should Be Equal As Strings ${json_string} {"a": "1", "b": "12", "names": ["First Name", "Family Name", "Email"]} From a788b5020fa6195893d298a04f345baa7a1ae642 Mon Sep 17 00:00:00 2001 From: KoleS46 Date: Tue, 9 Feb 2016 15:33:22 +0100 Subject: [PATCH 11/28] Travis CI fix. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 34c6697..e2a33b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: python -install: python bootstrap.py --distribute; ./bin/buildout versions:robotframework=$ROBOTFRAMEWORK_VERSION +install: python bootstrap.py --setuptools-version=19.3; ./bin/buildout versions:robotframework=$ROBOTFRAMEWORK_VERSION python: - "2.6" - "2.7" From f53eaff5752003c2efdb727eee1b3039d44958c0 Mon Sep 17 00:00:00 2001 From: Greg Meece Date: Fri, 4 Mar 2016 12:58:19 -0600 Subject: [PATCH 12/28] Changed extension to .robot for Github formatting. Honors Robot Framework formatting on Github. --- tests/https/{simple.txt => simple.robot} | 1 - 1 file changed, 1 deletion(-) rename tests/https/{simple.txt => simple.robot} (99%) diff --git a/tests/https/simple.txt b/tests/https/simple.robot similarity index 99% rename from tests/https/simple.txt rename to tests/https/simple.robot index 0352d8b..48d10f0 100644 --- a/tests/https/simple.txt +++ b/tests/https/simple.robot @@ -18,4 +18,3 @@ Full-URL GET to https://encrypted.google.com/ Simple GET to https://encrypted.google.com/ Create HTTP Context encrypted.google.com https GET / - From ade400c75897811b2800d53a9d0a5122296e1977 Mon Sep 17 00:00:00 2001 From: Vadym Vikulin Date: Wed, 22 Jun 2016 09:51:10 +0300 Subject: [PATCH 13/28] 1. Added explicit OPTIONS method --- setup.py | 9 ++++----- src/HttpLibrary/__init__.py | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 42ef4c1..9d7ee11 100644 --- a/setup.py +++ b/setup.py @@ -12,19 +12,18 @@ setup( name='robotframework-httplibrary', - version="0.4.2", + version="0.4.3", description='Robot Framework keywords for HTTP requests', long_description=long_description, author='Filip Noetzel', author_email='filip+rfhttplibrary@j03.de', - url='https://github.com/peritus/robotframework-httplibrary', + url='https://github.com/vikulin/robotframework-httplibrary', license='Beerware', - keywords='robotframework testing testautomation web http webtest', + keywords='robotframework testing test automation web http webtest', platforms='any', zip_safe=False, classifiers=CLASSIFIERS.splitlines(), package_dir={'': 'src'}, - install_requires=['robotframework', 'webtest>=2.0', 'jsonpatch', - 'jsonpointer'], + install_requires=['robotframework', 'webtest>=2.0', 'jsonpatch', 'jsonpointer'], packages=['HttpLibrary'] ) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index ae11328..3896d9d 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -35,7 +35,7 @@ class HTTP: Pointer, go to http://tools.ietf.org/html/draft-pbryan-zyp-json-pointer-00. """ - ROBOT_LIBRARY_VERSION = "0.4.2" + ROBOT_LIBRARY_VERSION = "0.4.3" class Context(object): def __init__(self, http, host=None, scheme='http'): @@ -281,6 +281,20 @@ def DELETE(self, url): self.app.delete(path, {}, self.context.request_headers) ) + def OPTIONS(self, url): + """ + Issues a HTTP OPTIONS request. + + `url` is the URL relative to the server root, e.g. '/_utils/config.html' + """ + path = self._path_from_url_or_path(url) + self.context.pre_process_request() + logger.debug("Performing OPTIONS request on %s://%s%s" % ( + self.context._scheme, self.app.host, path)) + self.context.post_process_request( + self.app.options(path, self.context.request_headers) + ) + def follow_response(self): """ Follows a HTTP redirect if the previous response status code was a 301 or 302. From 1ece59e1488725f87a8306911177054ad278e191 Mon Sep 17 00:00:00 2001 From: Vadym Vikulin Date: Wed, 22 Jun 2016 09:54:24 +0300 Subject: [PATCH 14/28] 1. README update --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index 42a10f3..45e07e9 100644 --- a/README.rst +++ b/README.rst @@ -76,6 +76,11 @@ Changelog **v0.4.2** +- Fix: Added explicit OPTIONS method + https://github.com/peritus/robotframework-httplibrary/issues/30 + +**v0.4.2** + - Don't enforce ASCII when converting to JSON (so chinese characters are printed as such and not escaped like \uXXXX). Thanks Terry Yin! From a246781470483f4d34412d997a54093642e86f9a Mon Sep 17 00:00:00 2001 From: Vadym Vikulin Date: Wed, 22 Jun 2016 09:56:02 +0300 Subject: [PATCH 15/28] 1. url rollback --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9d7ee11..f22dcda 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ long_description=long_description, author='Filip Noetzel', author_email='filip+rfhttplibrary@j03.de', - url='https://github.com/vikulin/robotframework-httplibrary', + url='https://github.com/peritus/robotframework-httplibrary', license='Beerware', keywords='robotframework testing test automation web http webtest', platforms='any', From 6d2aafa5ded06d1975ee2edf020088b602ba2393 Mon Sep 17 00:00:00 2001 From: Vadym Vikulin Date: Wed, 22 Jun 2016 10:06:47 +0300 Subject: [PATCH 16/28] 1. Added git ignore file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c88edd9..9c541d8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ output.xml parts/ report.html src/robotframework_httplibrary.egg-info/ +/build/ From 5b6537c48e3253b688a958cdbec83cf254a0234c Mon Sep 17 00:00:00 2001 From: Mohammad Asad Mohammad Date: Wed, 20 Jul 2016 10:40:12 -0700 Subject: [PATCH 17/28] fixed load_json (#1) fixed set_json_value method when json_value has a string --- src/HttpLibrary/__init__.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 3896d9d..c90eaef 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -621,10 +621,18 @@ def set_json_value(self, json_string, json_pointer, json_value): | ${result}= | Set Json Value | {"foo": {"bar": [1,2,3]}} | /foo | 12 | | Should Be Equal | ${result} | {"foo": 12} | | | """ + try: + loaded_json = load_json(json_value) + except ValueError, e: + if isinstance(json_value, basestring): + loaded_json = json_value + else: + raise ValueError("Could not parse '%s' as JSON: %s" % (json_value, e)) + return jsonpatch.apply_patch(json_string, [{ 'op': 'add', 'path': json_pointer, - 'value': load_json(json_value) + 'value': loaded_json }]) @_with_json From 6a07e94ea5aa01cd485827691f7fa19a67a5a1a4 Mon Sep 17 00:00:00 2001 From: Vadym Vikulin Date: Wed, 20 Jul 2016 20:51:16 +0300 Subject: [PATCH 18/28] 1. Fix: Set Json Value keyword. https://github.com/vikulin/robotframework-httplibrary/pull/1 --- README.rst | 6 +++++- setup.py | 2 +- src/HttpLibrary/__init__.py | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 45e07e9..42e5832 100644 --- a/README.rst +++ b/README.rst @@ -74,7 +74,11 @@ mostly a wrapper supposed to have a nice API)! Changelog --------- -**v0.4.2** +**v0.4.4** +- Fix: Set Json Value keyword. + https://github.com/vikulin/robotframework-httplibrary/pull/1 + +**v0.4.3** - Fix: Added explicit OPTIONS method https://github.com/peritus/robotframework-httplibrary/issues/30 diff --git a/setup.py b/setup.py index f22dcda..907d05f 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='robotframework-httplibrary', - version="0.4.3", + version="0.4.4", description='Robot Framework keywords for HTTP requests', long_description=long_description, author='Filip Noetzel', diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index c90eaef..81a5fb0 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -35,7 +35,7 @@ class HTTP: Pointer, go to http://tools.ietf.org/html/draft-pbryan-zyp-json-pointer-00. """ - ROBOT_LIBRARY_VERSION = "0.4.3" + ROBOT_LIBRARY_VERSION = "0.4.4" class Context(object): def __init__(self, http, host=None, scheme='http'): From dc7dddd3501d2dd808d8dce0d093041131cffada Mon Sep 17 00:00:00 2001 From: Vadym Vikulin Date: Thu, 15 Sep 2016 09:31:53 +0300 Subject: [PATCH 19/28] 1. Keep orinigan data type returned by 'Get Json Value' keyword --- README.rst | 5 ++++- setup.py | 2 +- src/HttpLibrary/__init__.py | 8 +++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 42e5832..40386c6 100644 --- a/README.rst +++ b/README.rst @@ -74,8 +74,11 @@ mostly a wrapper supposed to have a nice API)! Changelog --------- +**v0.4.5** +- Fix: Keep original data type returned by 'Get Json Value' keyword + **v0.4.4** -- Fix: Set Json Value keyword. +- Fix: 'Set Json Value' keyword. https://github.com/vikulin/robotframework-httplibrary/pull/1 **v0.4.3** diff --git a/setup.py b/setup.py index 907d05f..bfe10c8 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='robotframework-httplibrary', - version="0.4.4", + version="0.4.5", description='Robot Framework keywords for HTTP requests', long_description=long_description, author='Filip Noetzel', diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 81a5fb0..cd027a9 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -24,6 +24,12 @@ def wrapper(self, json_string, *args, **kwargs): f(self, load_json(json_string), *args, **kwargs), ensure_ascii=False) return wrapper +def _with_native_json(f): + @wraps(f) + def wrapper(self, json_string, *args, **kwargs): + return f(self, load_json(json_string), *args, **kwargs) + return wrapper + class HTTP: """ @@ -566,7 +572,7 @@ def stringify_json(self, data): raise ValueError( "Could not stringify '%r' to JSON: %s" % (data, e)) - @_with_json + @_with_native_json def get_json_value(self, json_string, json_pointer): """ Get the target node of the JSON document `json_string` specified by `json_pointer`. From 17096bfecb122b5ff7aa699a79b2bf9adef9de9c Mon Sep 17 00:00:00 2001 From: Vadym Vikulin Date: Thu, 15 Sep 2016 09:54:05 +0300 Subject: [PATCH 20/28] 1. Split JSON data and string keywords --- src/HttpLibrary/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index cd027a9..efe36dd 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -533,7 +533,16 @@ def log_response_status(self, log_level='INFO'): # json - def should_be_valid_json(self, json_string): + def should_be_valid_json(self, json_data): + """ + Attempts to parse `json_data` as JSON. Fails, if `json_data` is invalid JSON. + + Example: + | Should Be Valid Json | {"foo": "bar"} | + """ + self.parse_json(json.dumps(json_data, ensure_ascii=False)) + + def should_be_valid_json_string(self, json_string): """ Attempts to parse `json_string` as JSON. Fails, if `json_string` is invalid JSON. From 7e4cdd368d6ad6671217f9c8a7428c943714602a Mon Sep 17 00:00:00 2001 From: Vadym Vikulin Date: Thu, 15 Sep 2016 10:22:46 +0300 Subject: [PATCH 21/28] 1. Added stringify parameter --- src/HttpLibrary/__init__.py | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index efe36dd..0f350a6 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -24,12 +24,6 @@ def wrapper(self, json_string, *args, **kwargs): f(self, load_json(json_string), *args, **kwargs), ensure_ascii=False) return wrapper -def _with_native_json(f): - @wraps(f) - def wrapper(self, json_string, *args, **kwargs): - return f(self, load_json(json_string), *args, **kwargs) - return wrapper - class HTTP: """ @@ -533,16 +527,7 @@ def log_response_status(self, log_level='INFO'): # json - def should_be_valid_json(self, json_data): - """ - Attempts to parse `json_data` as JSON. Fails, if `json_data` is invalid JSON. - - Example: - | Should Be Valid Json | {"foo": "bar"} | - """ - self.parse_json(json.dumps(json_data, ensure_ascii=False)) - - def should_be_valid_json_string(self, json_string): + def should_be_valid_json(self, json_string): """ Attempts to parse `json_string` as JSON. Fails, if `json_string` is invalid JSON. @@ -581,8 +566,7 @@ def stringify_json(self, data): raise ValueError( "Could not stringify '%r' to JSON: %s" % (data, e)) - @_with_native_json - def get_json_value(self, json_string, json_pointer): + def get_json_value(self, json_string, json_pointer, stringify=True): """ Get the target node of the JSON document `json_string` specified by `json_pointer`. @@ -590,7 +574,10 @@ def get_json_value(self, json_string, json_pointer): | ${result}= | Get Json Value | {"foo": {"bar": [1,2,3]}} | /foo/bar | | Should Be Equal | ${result} | [1, 2, 3] | | """ - return jsonpointer.resolve_pointer(json_string, json_pointer) + if stringify: + return json.dumps(jsonpointer.resolve_pointer(load_json(json_string)), ensure_ascii=False) + else: + return jsonpointer.resolve_pointer(load_json(json_string)) def json_value_should_equal(self, json_string, json_pointer, expected_value): """ From 41de769260f44403089434fbbca21d12cf526f44 Mon Sep 17 00:00:00 2001 From: Vadym Vikulin Date: Thu, 15 Sep 2016 10:30:17 +0300 Subject: [PATCH 22/28] 1. json_pointer fix --- src/HttpLibrary/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 0f350a6..78b5b17 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -575,9 +575,9 @@ def get_json_value(self, json_string, json_pointer, stringify=True): | Should Be Equal | ${result} | [1, 2, 3] | | """ if stringify: - return json.dumps(jsonpointer.resolve_pointer(load_json(json_string)), ensure_ascii=False) + return json.dumps(jsonpointer.resolve_pointer(load_json(json_string), json_pointer), ensure_ascii=False) else: - return jsonpointer.resolve_pointer(load_json(json_string)) + return jsonpointer.resolve_pointer(load_json(json_string), json_pointer) def json_value_should_equal(self, json_string, json_pointer, expected_value): """ From 337ec7e095ba0992075e257e8ebe2637cbb05f64 Mon Sep 17 00:00:00 2001 From: Vadym Vikulin Date: Fri, 16 Sep 2016 16:02:50 +0300 Subject: [PATCH 23/28] 1. bumped up RF version --- src/HttpLibrary/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 78b5b17..87827f6 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -35,7 +35,7 @@ class HTTP: Pointer, go to http://tools.ietf.org/html/draft-pbryan-zyp-json-pointer-00. """ - ROBOT_LIBRARY_VERSION = "0.4.4" + ROBOT_LIBRARY_VERSION = "0.4.5" class Context(object): def __init__(self, http, host=None, scheme='http'): From d196d524966c293a12fd1971b65b2655bdfd1683 Mon Sep 17 00:00:00 2001 From: Vadym Vikulin Date: Tue, 25 Oct 2016 12:57:49 +0300 Subject: [PATCH 24/28] **v0.4.6** - Improvement: Added stringify parameter to json_value_should_equal and json_value_should_not_equal methods --- README.rst | 3 +++ setup.py | 8 ++++---- src/HttpLibrary/__init__.py | 16 ++++++++-------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 40386c6..783d11c 100644 --- a/README.rst +++ b/README.rst @@ -74,6 +74,9 @@ mostly a wrapper supposed to have a nice API)! Changelog --------- +**v0.4.6** +- Improvement: Added stringify parameter to json_value_should_equal and json_value_should_not_equal methods + **v0.4.5** - Fix: Keep original data type returned by 'Get Json Value' keyword diff --git a/setup.py b/setup.py index bfe10c8..4c161d4 100644 --- a/setup.py +++ b/setup.py @@ -12,12 +12,12 @@ setup( name='robotframework-httplibrary', - version="0.4.5", + version="0.4.6", description='Robot Framework keywords for HTTP requests', long_description=long_description, - author='Filip Noetzel', - author_email='filip+rfhttplibrary@j03.de', - url='https://github.com/peritus/robotframework-httplibrary', + author='Vadym Vikulin', + author_email='vadym.vikulin@gmail.com', + url='https://github.com/vikulin/robotframework-httplibrary', license='Beerware', keywords='robotframework testing test automation web http webtest', platforms='any', diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 87827f6..3721df9 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -35,7 +35,7 @@ class HTTP: Pointer, go to http://tools.ietf.org/html/draft-pbryan-zyp-json-pointer-00. """ - ROBOT_LIBRARY_VERSION = "0.4.5" + ROBOT_LIBRARY_VERSION = "0.4.6" class Context(object): def __init__(self, http, host=None, scheme='http'): @@ -569,7 +569,7 @@ def stringify_json(self, data): def get_json_value(self, json_string, json_pointer, stringify=True): """ Get the target node of the JSON document `json_string` specified by `json_pointer`. - + `stringify` specifies whether JSON data should be transformed to string before assertion. Example: | ${result}= | Get Json Value | {"foo": {"bar": [1,2,3]}} | /foo/bar | | Should Be Equal | ${result} | [1, 2, 3] | | @@ -579,33 +579,33 @@ def get_json_value(self, json_string, json_pointer, stringify=True): else: return jsonpointer.resolve_pointer(load_json(json_string), json_pointer) - def json_value_should_equal(self, json_string, json_pointer, expected_value): + def json_value_should_equal(self, json_string, json_pointer, expected_value, stringify=True): """ Fails if the value of the target node of the JSON document `json_string` specified by JSON Pointer `json_pointer` is not `expected_value`. + `stringify` specifies whether JSON data should be transformed to string before assertion. Example: | Set Test Variable | ${doc} | {"foo": {"bar": [1,2,3]}} | | | Json Value Should Equal | ${doc} | /foo/bar | "[1, 2, 3]" | """ - - got = self.get_json_value(json_string, json_pointer) + got = self.get_json_value(json_string, json_pointer, stringify) assert got == expected_value, \ 'JSON value "%s" does not equal "%s", but should have.' % ( got, expected_value) - def json_value_should_not_equal(self, json_string, json_pointer, expected_value): + def json_value_should_not_equal(self, json_string, json_pointer, expected_value, stringify=True): """ Fails if the value of the target node of the JSON document `json_string` specified by JSON Pointer `json_pointer` is `expected_value`. + `stringify` specifies whether JSON data should be transformed to string before assertion. Example: | Set Test Variable | ${doc} | {"foo": {"bar": [1,2,3]}} | | | Json Value Should Not Equal | ${doc} | /foo/bar | "[1, 2, 3]" | """ - - got = self.get_json_value(json_string, json_pointer) + got = self.get_json_value(json_string, json_pointer, stringify) message = 'JSON value "%s" does not equal "%s"' % (got, expected_value) From dc1effcb5f6e41eadaf1089a1b6b7fd126a0e8ac Mon Sep 17 00:00:00 2001 From: Vadym Date: Mon, 25 Jun 2018 17:06:28 +0300 Subject: [PATCH 25/28] 1. added PATCH method support --- README.rst | 3 +++ setup.py | 2 +- src/HttpLibrary/__init__.py | 20 +++++++++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 783d11c..f961b54 100644 --- a/README.rst +++ b/README.rst @@ -74,6 +74,9 @@ mostly a wrapper supposed to have a nice API)! Changelog --------- +**v0.4.7** +- Improvement: Added PATCH HTTP method + **v0.4.6** - Improvement: Added stringify parameter to json_value_should_equal and json_value_should_not_equal methods diff --git a/setup.py b/setup.py index 4c161d4..b688b97 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='robotframework-httplibrary', - version="0.4.6", + version="0.4.7", description='Robot Framework keywords for HTTP requests', long_description=long_description, author='Vadym Vikulin', diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 3721df9..119ad31 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -35,7 +35,7 @@ class HTTP: Pointer, go to http://tools.ietf.org/html/draft-pbryan-zyp-json-pointer-00. """ - ROBOT_LIBRARY_VERSION = "0.4.6" + ROBOT_LIBRARY_VERSION = "0.4.7" class Context(object): def __init__(self, http, host=None, scheme='http'): @@ -295,6 +295,24 @@ def OPTIONS(self, url): self.app.options(path, self.context.request_headers) ) + def PATCH(self, url): + """ + Issues a HTTP PATCH request. + + `url` is the URL relative to the server root, e.g. '/_utils/config.html' + """ + path = self._path_from_url_or_path(url) + kwargs = {} + if 'Content-Type' in self.context.request_headers: + kwargs[ + 'content_type'] = self.context.request_headers['Content-Type'] + self.context.pre_process_request() + logger.debug("Performing PATCH request on %s://%s%s" % ( + self.context._scheme, self.app.host, url)) + self.context.post_process_request( + self.app.patch(path, self.context.request_body or {}, + self.context.request_headers, **kwargs) + def follow_response(self): """ Follows a HTTP redirect if the previous response status code was a 301 or 302. From 4bb1f9d48a0bbce53478b555728085f9309509a2 Mon Sep 17 00:00:00 2001 From: Vadym Date: Mon, 25 Jun 2018 17:41:26 +0300 Subject: [PATCH 26/28] 1. bracket fix --- src/HttpLibrary/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 119ad31..abaead8 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -312,6 +312,7 @@ def PATCH(self, url): self.context.post_process_request( self.app.patch(path, self.context.request_body or {}, self.context.request_headers, **kwargs) + ) def follow_response(self): """ From bfb97a55a2b27e515f42a708f9b4be85f515005c Mon Sep 17 00:00:00 2001 From: Vinaykumar Patel Date: Mon, 27 Aug 2018 14:58:32 +0530 Subject: [PATCH 27/28] Added function remove_json_key This will remove the specified JSON key from JSON. Till not we had only "set_json_value". --- src/HttpLibrary/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index ae11328..f4b345a 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -621,7 +621,16 @@ def log_json(self, json_string, log_level='INFO'): for line in json.dumps(json_string, indent=2, ensure_ascii=False).split('\n'): logger.write(line, log_level) - # debug + @_with_json + def remove_json_key(self, json_string, json_pointer): + """ + Remove the target key of the JSON document `json_string` specified by + JSON Pointer `json_pointer` to `json_value`. + + Example: + | ${result}= | Remove Json Key | {"foo": {"bar": [1,2,3]}} | /foo/bar | + """ + return jsonpatch.apply_patch(json_string, [{'op': 'remove','path': json_pointer}]) def show_response_body_in_browser(self): """ From 6e5768807f693d33ee30160d328ff471dae5c326 Mon Sep 17 00:00:00 2001 From: Jerry Schneider Date: Thu, 12 Dec 2019 07:59:00 -0700 Subject: [PATCH 28/28] after running 2to3 script --- bootstrap.py | 4 ++-- src/HttpLibrary/__init__.py | 18 +++++++++--------- src/HttpLibrary/livetest.py | 24 ++++++++++++------------ src/rf_httplib_dev_helper.py | 4 ++-- tests/http/mockserver.py | 8 ++++---- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/bootstrap.py b/bootstrap.py index a459921..1594d67 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -73,7 +73,7 @@ options, args = parser.parse_args() if options.version: - print("bootstrap.py version %s" % __version__) + print(("bootstrap.py version %s" % __version__)) sys.exit(0) @@ -83,7 +83,7 @@ try: from urllib.request import urlopen except ImportError: - from urllib2 import urlopen + from urllib.request import urlopen ez = {} if os.path.exists('ez_setup.py'): diff --git a/src/HttpLibrary/__init__.py b/src/HttpLibrary/__init__.py index 9052239..b70750a 100644 --- a/src/HttpLibrary/__init__.py +++ b/src/HttpLibrary/__init__.py @@ -2,10 +2,10 @@ from base64 import b64encode from functools import wraps -from urlparse import urlparse +from urllib.parse import urlparse from webtest import utils -import livetest +from . import livetest import json import jsonpointer import jsonpatch @@ -14,7 +14,7 @@ def load_json(json_string): try: return json.loads(json_string) - except ValueError, e: + except ValueError as e: raise ValueError("Could not parse '%s' as JSON: %s" % (json_string, e)) @@ -65,9 +65,9 @@ def __init__(self, http, host=None, scheme='http'): self.post_process_request(None) def pre_process_request(self): - if len(self.request_headers.items()) > 0: + if len(list(self.request_headers.items())) > 0: logger.debug("Request headers:") - for name, value in self.request_headers.items(): + for name, value in list(self.request_headers.items()): logger.debug("%s: %s" % (name, value)) else: logger.debug("No request headers set") @@ -488,7 +488,7 @@ def log_response_headers(self, log_level='INFO'): Specify `log_level` (default: "INFO") to set the log level. """ logger.write("Response headers:", log_level) - for name, value in self.response.headers.items(): + for name, value in list(self.response.headers.items()): logger.write("%s: %s" % (name, value), log_level) # request headers @@ -635,7 +635,7 @@ def stringify_json(self, data): try: return json.dumps(data, ensure_ascii=False) - except ValueError, e: + except ValueError as e: raise ValueError( "Could not stringify '%r' to JSON: %s" % (data, e)) @@ -698,8 +698,8 @@ def set_json_value(self, json_string, json_pointer, json_value): """ try: loaded_json = load_json(json_value) - except ValueError, e: - if isinstance(json_value, basestring): + except ValueError as e: + if isinstance(json_value, str): loaded_json = json_value else: raise ValueError("Could not parse '%s' as JSON: %s" % (json_value, e)) diff --git a/src/HttpLibrary/livetest.py b/src/HttpLibrary/livetest.py index 9da7b03..a277d1b 100644 --- a/src/HttpLibrary/livetest.py +++ b/src/HttpLibrary/livetest.py @@ -40,13 +40,13 @@ import sys import webtest -import httplib -import urlparse -from Cookie import BaseCookie, CookieError +import http.client +import urllib.parse +from http.cookies import BaseCookie, CookieError from six.moves import http_cookiejar -conn_classes = {'http': httplib.HTTPConnection, - 'https': httplib.HTTPSConnection} +conn_classes = {'http': http.client.HTTPConnection, + 'https': http.client.HTTPSConnection} class RequestCookieAdapter(object): @@ -118,7 +118,7 @@ def __init__(self, host, scheme='http', relative_to=None): def _do_httplib_request(self, req): "Convert WebOb Request to httplib request." - headers = dict((name, val) for name, val in req.headers.iteritems()) + headers = dict((name, val) for name, val in req.headers.items()) if req.scheme not in self.conn: self._load_conn(req.scheme) @@ -130,7 +130,7 @@ def _do_httplib_request(self, req): res.status = '%s %s' % (webresp.status, webresp.reason) res.body = webresp.read() response_headers = [] - for headername in dict(webresp.getheaders()).keys(): + for headername in list(dict(webresp.getheaders()).keys()): for headervalue in webresp.msg.getheaders(headername): response_headers.append((headername, headervalue)) res.headerlist = response_headers @@ -145,9 +145,9 @@ def do_request(self, req, status, expect_errors): headers = {} if self.cookies: c = BaseCookie() - for name, value in self.cookies.items(): + for name, value in list(self.cookies.items()): c[name] = value - hc = '; '.join(['='.join([m.key, m.value]) for m in c.values()]) + hc = '; '.join(['='.join([m.key, m.value]) for m in list(c.values())]) req.headers['Cookie'] = hc res = self._do_httplib_request(req) @@ -172,11 +172,11 @@ def goto(self, href, method='get', **args): Monkeypatch the TestResponse.goto method so that it doesn't wipe out the scheme and host. """ - scheme, host, path, query, fragment = urlparse.urlsplit(href) + scheme, host, path, query, fragment = urllib.parse.urlsplit(href) # We fragment = '' - href = urlparse.urlunsplit((scheme, host, path, query, fragment)) - href = urlparse.urljoin(self.request.url, href) + href = urllib.parse.urlunsplit((scheme, host, path, query, fragment)) + href = urllib.parse.urljoin(self.request.url, href) method = method.lower() assert method in ('get', 'post'), ( 'Only "get" or "post" are allowed for method (you gave %r)' diff --git a/src/rf_httplib_dev_helper.py b/src/rf_httplib_dev_helper.py index 1cecadd..b743591 100644 --- a/src/rf_httplib_dev_helper.py +++ b/src/rf_httplib_dev_helper.py @@ -8,7 +8,7 @@ def run_cli(args): import robot try: robot.run_cli(args) - except Exception, e: - print e + except Exception as e: + print(e) import robot.runner robot.run_from_cli(args, robot.runner.__doc__) diff --git a/tests/http/mockserver.py b/tests/http/mockserver.py index bff6ca7..a70cb75 100755 --- a/tests/http/mockserver.py +++ b/tests/http/mockserver.py @@ -1,12 +1,12 @@ #!/usr/bin/env python -import BaseHTTPServer +import http.server import sys import os import ssl -class WebRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): +class WebRequestHandler(http.server.BaseHTTPRequestHandler): def do_HEAD(self): self.send_response(200, "No payment required") @@ -146,7 +146,7 @@ def do_POST(self, *args, **kwargs): do_PUT = do_POST PORT = int(sys.argv[1]) -server = BaseHTTPServer.HTTPServer(('localhost', PORT), WebRequestHandler) +server = http.server.HTTPServer(('localhost', PORT), WebRequestHandler) scheme = 'http' if '--ssl' in sys.argv: @@ -162,6 +162,6 @@ def do_POST(self, *args, **kwargs): server_side=True, ) -print 'Starting server on %s://localhost:%d/' % (scheme, PORT) +print('Starting server on %s://localhost:%d/' % (scheme, PORT)) server.serve_forever()