From 3ab7df3afe3d12ad8f4283c191a8d5c211a0d5aa Mon Sep 17 00:00:00 2001 From: Fritz Heiden Date: Wed, 17 Nov 2021 13:24:23 +0000 Subject: [PATCH] Bug 1729274 [wpt PR 30357] - Update WAVE test runner, a=testonly Automatic update from web-platform-tests Update WAVE test runner (#30357) -- wpt-commits: e5c7faa6e8062dbf57eae6f58e99483cd2a4ec4d wpt-pr: 30357 --- .../web-platform/tests/tools/wave/.gitignore | 3 + .../tests/tools/wave/config.default.json | 8 +- .../tests/tools/wave/configuration_loader.py | 24 +- .../tests/tools/wave/data/device.py | 6 + .../tests/tools/wave/data/event_listener.py | 8 + .../wave/data/http_polling_event_listener.py | 11 + .../tests/tools/wave/data/session.py | 24 +- .../tests/tools/wave/docs/README.md | 16 +- .../tests/tools/wave/docs/config.md | 326 + .../tests/tools/wave/docs/rest-api/README.md | 90 +- .../wave/docs/rest-api/devices-api/create.md | 33 + .../docs/rest-api/devices-api/event-types.md | 37 + .../docs/rest-api/devices-api/read-device.md | 41 + .../docs/rest-api/devices-api/read-devices.md | 47 + .../rest-api/devices-api/register-global.md | 54 + .../docs/rest-api/devices-api/register.md | 52 + .../docs/rest-api/devices-api/send-event.md | 43 + .../rest-api/devices-api/send-global-event.md | 46 + .../wave/docs/rest-api/general-api/status.md | 41 + .../tools/wave/docs/rest-api/guides/README.md | 10 + .../docs/rest-api/guides/session-events.md | 52 + .../guides/session-start-devices-api.md | 60 + .../docs/rest-api/results-api/download.md | 2 +- .../wave/docs/rest-api/results-api/import.md | 41 +- .../wave/docs/rest-api/sessions-api/create.md | 6 +- .../docs/rest-api/sessions-api/event-types.md | 27 + .../wave/docs/rest-api/sessions-api/events.md | 88 +- .../wave/docs/rest-api/sessions-api/read.md | 12 +- .../rest-api/sessions-api/read_sessions.md | 123 + .../wave/docs/rest-api/sessions-api/status.md | 10 +- .../tests/tools/wave/docs/usage/usage.md | 29 +- .../tools/wave/network/api/api_handler.py | 44 + .../wave/network/api/devices_api_handler.py | 200 + .../wave/network/api/general_api_handler.py | 77 + .../wave/network/api/results_api_handler.py | 47 +- .../wave/network/api/sessions_api_handler.py | 254 +- .../wave/network/api/tests_api_handler.py | 18 +- .../tests/tools/wave/network/http_handler.py | 45 +- .../tools/wave/network/static_handler.py | 10 +- .../tests/tools/wave/package-lock.json | 35 + .../tests/tools/wave/package.json | 7 + .../tests/tools/wave/requirements.txt | 3 +- .../tools/wave/resources/testharnessreport.js | 480 +- .../test/WAVE Local.postman_environment.json | 34 + ...ver REST API Tests.postman_collection.json | 9833 +++++++++++++++++ .../tools/wave/testing/devices_manager.py | 117 + .../tools/wave/testing/event_dispatcher.py | 153 +- .../tools/wave/testing/results_manager.py | 80 +- .../tools/wave/testing/sessions_manager.py | 99 +- .../tests/tools/wave/testing/test_loader.py | 15 +- .../tests/tools/wave/testing/tests_manager.py | 84 +- ...ver REST API Tests.postman_collection.json | 998 +- .../tests/tools/wave/tests/config.json | 6 + .../tests/tools/wave/tests/test_wave.py | 29 +- .../tests/tools/wave/utils/deserializer.py | 38 +- .../tests/tools/wave/utils/serializer.py | 33 +- .../tests/tools/wave/wave_server.py | 40 +- .../tests/tools/wave/www/comparison.html | 2 +- .../tests/tools/wave/www/configuration.html | 1914 ++-- .../tools/wave/www/css/bulma-0.7.5/bulma.css | 2 +- .../tests/tools/wave/www/index.html | 262 +- .../tests/tools/wave/www/lib/jszip.min.js | 15 + .../tests/tools/wave/www/lib/qrcode.js | 1533 +++ .../tests/tools/wave/www/lib/query-parser.js | 12 + .../tools/wave/www/lib/screen-console.js | 16 + .../tests/tools/wave/www/lib/ui.js | 3 + .../tests/tools/wave/www/lib/wave-service.js | 584 +- .../tests/tools/wave/www/next.html | 36 +- .../tests/tools/wave/www/overview.html | 4 +- .../tests/tools/wave/www/pause.html | 3 +- .../tests/tools/wave/www/results.html | 684 +- .../tests/tools/wave/www/submitresult.html | 1 - .../tests/tools/wave/www/test.html | 155 + 73 files changed, 16543 insertions(+), 2832 deletions(-) create mode 100644 testing/web-platform/tests/tools/wave/.gitignore create mode 100644 testing/web-platform/tests/tools/wave/data/device.py create mode 100644 testing/web-platform/tests/tools/wave/data/event_listener.py create mode 100644 testing/web-platform/tests/tools/wave/data/http_polling_event_listener.py create mode 100644 testing/web-platform/tests/tools/wave/docs/config.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/create.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/event-types.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/read-device.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/read-devices.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/register-global.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/register.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/send-event.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/send-global-event.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/general-api/status.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/guides/README.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/guides/session-events.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/guides/session-start-devices-api.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/event-types.md create mode 100644 testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/read_sessions.md create mode 100644 testing/web-platform/tests/tools/wave/network/api/devices_api_handler.py create mode 100644 testing/web-platform/tests/tools/wave/network/api/general_api_handler.py create mode 100644 testing/web-platform/tests/tools/wave/package-lock.json create mode 100644 testing/web-platform/tests/tools/wave/package.json create mode 100644 testing/web-platform/tests/tools/wave/test/WAVE Local.postman_environment.json create mode 100644 testing/web-platform/tests/tools/wave/test/WAVE Server REST API Tests.postman_collection.json create mode 100644 testing/web-platform/tests/tools/wave/testing/devices_manager.py create mode 100644 testing/web-platform/tests/tools/wave/tests/config.json create mode 100644 testing/web-platform/tests/tools/wave/www/lib/jszip.min.js create mode 100644 testing/web-platform/tests/tools/wave/www/lib/qrcode.js create mode 100644 testing/web-platform/tests/tools/wave/www/lib/query-parser.js create mode 100644 testing/web-platform/tests/tools/wave/www/lib/screen-console.js create mode 100644 testing/web-platform/tests/tools/wave/www/test.html diff --git a/testing/web-platform/tests/tools/wave/.gitignore b/testing/web-platform/tests/tools/wave/.gitignore new file mode 100644 index 000000000000..bf0aae9e13e7 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/.gitignore @@ -0,0 +1,3 @@ +!www/lib +!export/lib +!export/css diff --git a/testing/web-platform/tests/tools/wave/config.default.json b/testing/web-platform/tests/tools/wave/config.default.json index 8a05b91060a5..5b696929bb0f 100644 --- a/testing/web-platform/tests/tools/wave/config.default.json +++ b/testing/web-platform/tests/tools/wave/config.default.json @@ -37,9 +37,13 @@ "automatic": 60000, "manual": 300000 }, - "enable_results_import": false, + "enable_import_results": false, "web_root": "/_wave", "persisting_interval": 20, - "api_titles": [] + "api_titles": [], + "enable_read_sessions": false, + "event_cache_duration": 60000, + "enable_test_type_selection": false, + "enable_test_file_selection": false } } diff --git a/testing/web-platform/tests/tools/wave/configuration_loader.py b/testing/web-platform/tests/tools/wave/configuration_loader.py index 819b414cfaac..e8e0d7448ea7 100644 --- a/testing/web-platform/tests/tools/wave/configuration_loader.py +++ b/testing/web-platform/tests/tools/wave/configuration_loader.py @@ -1,5 +1,6 @@ import json import os +from io import open from tools.wpt import wpt @@ -46,15 +47,24 @@ def load(configuration_file_path): configuration["hostname"] = configuration.get( "browser_host", default_configuration["browser_host"]) - configuration["import_enabled"] = configuration.get( + configuration["import_results_enabled"] = configuration.get( "wave", default_configuration["wave"]).get( - "enable_results_import", - default_configuration["wave"]["enable_results_import"]) + "enable_import_results", + default_configuration["wave"]["enable_import_results"]) + + configuration["read_sessions_enabled"] = configuration.get( + "wave", default_configuration["wave"]).get( + "enable_read_sessions", + default_configuration["wave"]["enable_read_sessions"]) configuration["persisting_interval"] = configuration.get( "wave", default_configuration["wave"]).get( "persisting_interval", default_configuration["wave"]["persisting_interval"]) + configuration["event_cache_duration"] = configuration.get( + "wave", default_configuration["wave"]).get( + "event_cache_duration", default_configuration["wave"]["event_cache_duration"]) + configuration["tests_directory_path"] = os.getcwd() configuration["manifest_file_path"] = os.path.join( @@ -64,6 +74,14 @@ def load(configuration_file_path): "wave", default_configuration["wave"]).get( "api_titles", default_configuration["wave"]["api_titles"]) + configuration["enable_test_type_selection"] = configuration.get( + "wave", default_configuration["wave"]).get( + "enable_test_type_selection", default_configuration["wave"]["enable_test_type_selection"]) + + configuration["enable_test_file_selection"] = configuration.get( + "wave", default_configuration["wave"]).get( + "enable_test_file_selection", default_configuration["wave"]["enable_test_file_selection"]) + return configuration diff --git a/testing/web-platform/tests/tools/wave/data/device.py b/testing/web-platform/tests/tools/wave/data/device.py new file mode 100644 index 000000000000..3b2ccdf4075b --- /dev/null +++ b/testing/web-platform/tests/tools/wave/data/device.py @@ -0,0 +1,6 @@ +class Device(object): + def __init__(self, token, user_agent, name, last_active): + self.token = token + self.user_agent = user_agent + self.name = name + self.last_active = last_active diff --git a/testing/web-platform/tests/tools/wave/data/event_listener.py b/testing/web-platform/tests/tools/wave/data/event_listener.py new file mode 100644 index 000000000000..2695df83cbab --- /dev/null +++ b/testing/web-platform/tests/tools/wave/data/event_listener.py @@ -0,0 +1,8 @@ +class EventListener(object): + def __init__(self, dispatcher_token): + super(EventListener, self).__init__() + self.dispatcher_token = dispatcher_token + self.token = None + + def send_message(self, message): + raise Exception("Client.send_message(message) not implemented!") diff --git a/testing/web-platform/tests/tools/wave/data/http_polling_event_listener.py b/testing/web-platform/tests/tools/wave/data/http_polling_event_listener.py new file mode 100644 index 000000000000..df731d4856c4 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/data/http_polling_event_listener.py @@ -0,0 +1,11 @@ +from .event_listener import EventListener + +class HttpPollingEventListener(EventListener): + def __init__(self, dispatcher_token, event): + super(HttpPollingEventListener, self).__init__(dispatcher_token) + self.event = event + self.message = None + + def send_message(self, message): + self.message = message + self.event.set() diff --git a/testing/web-platform/tests/tools/wave/data/session.py b/testing/web-platform/tests/tools/wave/data/session.py index a4477f66decc..8b5c4a6b38de 100644 --- a/testing/web-platform/tests/tools/wave/data/session.py +++ b/testing/web-platform/tests/tools/wave/data/session.py @@ -12,7 +12,7 @@ class Session(object): def __init__( self, token=None, - types=None, + test_types=None, user_agent=None, labels=None, tests=None, @@ -23,35 +23,30 @@ class Session(object): test_state=None, last_completed_test=None, recent_completed_count=None, + date_created=None, date_started=None, date_finished=None, is_public=None, reference_tokens=None, browser=None, - webhook_urls=None, expiration_date=None, + type=None, malfunctioning_tests=None ): if token is None: token = "" self.token = token - if types is None: - types = [AUTOMATIC, MANUAL] - self.types = types + if test_types is None: + test_types = [AUTOMATIC, MANUAL] + self.test_types = test_types if user_agent is None: user_agent = "" self.user_agent = user_agent if labels is None: labels = [] self.labels = labels - if tests is None: - tests = {} self.tests = tests - if pending_tests is None: - pending_tests = {} self.pending_tests = pending_tests - if running_tests is None: - running_tests = {} self.running_tests = running_tests if timeouts is None: timeouts = {} @@ -59,13 +54,12 @@ class Session(object): if status is None: status = UNKNOWN self.status = status - if test_state is None: - test_state = {} self.test_state = test_state self.last_completed_test = last_completed_test if recent_completed_count is None: recent_completed_count = 0 self.recent_completed_count = recent_completed_count + self.date_created = date_created self.date_started = date_started self.date_finished = date_finished if is_public is None: @@ -75,10 +69,8 @@ class Session(object): reference_tokens = [] self.reference_tokens = reference_tokens self.browser = browser - if webhook_urls is None: - webhook_urls = [] - self.webhook_urls = webhook_urls self.expiration_date = expiration_date + self.type = type if malfunctioning_tests is None: malfunctioning_tests = [] self.malfunctioning_tests = malfunctioning_tests diff --git a/testing/web-platform/tests/tools/wave/docs/README.md b/testing/web-platform/tests/tools/wave/docs/README.md index 6cf7d397f84f..88092b63e774 100644 --- a/testing/web-platform/tests/tools/wave/docs/README.md +++ b/testing/web-platform/tests/tools/wave/docs/README.md @@ -1,4 +1,14 @@ -# WAVE Test Suite Documentation +# WAVE Test Runner Documentation -- [REST API](./rest-api/README.md) -- [Usage Guide](./usage/usage.md) \ No newline at end of file +As part of the [WAVE project](https://cta.tech/Resources/Standards/WAVE-Project) +the WAVE Test Runner was implemented to run tests that confirm proper implementation +of specified features. The code base is used in different subprojects, each of which +may have different requirements and scopes, so some features and screenshots in +this documentation may use specific contexts. + +## Contents + +- [Configuration](./config.md): How to configure the test runner +- [REST API](./rest-api/README.md): Documentation of endpoints, parameters and payloads + - [Guides](./rest-api/guides/README.md): How to use certain API mechanisms +- [Usage Guide](./usage/usage.md): General usage guide diff --git a/testing/web-platform/tests/tools/wave/docs/config.md b/testing/web-platform/tests/tools/wave/docs/config.md new file mode 100644 index 000000000000..afc7c0b05fd2 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/config.md @@ -0,0 +1,326 @@ +# Configuration - [WAVE Test Runner](./README.md) + +Using a configuration file, the WAVE Test Runner can be configured to be more +functional in different use cases. This document lists all configuration +parameters and what they are used for. + +## Contents + +1. [Location and structure](#1-location-and-structure) +2. [Parameters](#2-parameters) + 1. [Results directory](#21-results-directory) + 2. [Test Timeouts](#22-test-timeouts) + 3. [Enable import of results](#23-enable-import-of-results) + 4. [Web namespace](#24-web-namespace) + 5. [Persisting interval](#25-persisting-interval) + 6. [API titles](#26-api-titles) + 7. [Enable listing all sessions](#27-enable-listing-all-sessions) + 8. [Event caching duration](#28-event-caching-duration) + 9. [Enable test type selection](#29-enable-test-type-selection) + +## 1. Location and structure + +Configuration parameters are defined in a JSON file called `config.json` in +the project root of the WPT runner. This configuration file is also used by +the WPT runner, so any WAVE Test Runner related configuration parameters are +wrapped inside a `wave` object. + +``` +/config.json +``` + +```json +{ + "wave": { + "results": "./results" + } +} +``` + +All the default values are stored in a configuration file inside the wave +directory: + +``` +/tools/wave/config.default.json +``` + +```json +{ + "wave": { + "results": "./results", + "timeouts": { + "automatic": 60000, + "manual": 300000 + }, + "enable_import_results": false, + "web_root": "/_wave", + "persisting_interval": 20, + "api_titles": [], + "enable_read_sessions": false, + "event_cache_duration": 60000 + } +} +``` +[🠑 top](#configuration---wave-test-runner) + +## 2. Parameters + +### 2.1 Results directory + +The results parameter sets where results and session information are stored. + +**Parameters**: + +```json +{ + "results": "" +} +``` + +- **results**: Path to the results directory. Can be absolute, or relative to + the project root. + +**Default**: + +```json +{ + "results": "./results" +} +``` + +[🠑 top](#configuration---wave-test-runner) + +### 2.2 Test Timeouts + +The test timeouts set the default test timeout for different test types. + +**Parameters**: + +```json +{ + "timeouts": { + "automatic": "", + "manual": "" + } +} +``` + +- **timeouts**: Holds the key value pairs for different types of tests + - **automatic**: Default time to wait for automatic tests in milliseconds. + - **manual**: Default time to wait for manual tests in milliseconds. + +**Default**: + +```json +{ + "timeouts": { + "automatic": 600000, + "manual": 300000 + } +} +``` + +[🠑 top](#configuration---wave-test-runner) + +### 2.3 Enable import of results + +This parameter enables the capability to import session results from other +WAVE Test Runner instances into the current one. + +**Parameters**: + +```json +{ + "enable_import_results": "" +} +``` + +- **enable_import_results**: Sets whether or not to enable the [REST API endpoint to import results](./rest-api/results-api/import.md) + +**Default**: + +```json +{ + "enable_import_results": "false" +} +``` + +[🠑 top](#configuration---wave-test-runner) + +### 2.4 Web namespace + +All static resources and REST API endpoints are accessible under a +configurable namespace. This namespace can be set using the `web_root` +parameter. + +**Parameters**: + +```json +{ + "web_root": "" +} +``` + +- **web_root**: The namespace to use + +**Default**: + +```json +{ + "web_root": "/_wave" +} +``` + +[🠑 top](#configuration---wave-test-runner) + +### 2.5 Persisting interval + +The persisting interval specifies how many tests have to be completed until +all session information is updated in the results directory. + +For example, if set to 5, then every 5 completed tests the `info.json` in the +results directory is updated with the current state of the session. When +restarting the server, this state is used to reconstruct all sessions testing +state. + +**Parameters**: + +```json +{ + "persisting_interval": "" +} +``` + +- **persisting_interval**: The number of tests to execute until the persisted + session information gets updated + +**Default**: + +```json +{ + "persisting_interval": 20 +} +``` + +[🠑 top](#configuration---wave-test-runner) + +### 2.6 API titles + +The API titles are used to display a more human readible representation of an +API that tests are available for. Using the parameter it is possible to assign +a name to an API subdirectory. + +**Parameters**: + +```json +{ + "api_titles": [ + { + "title": "", + "path": "" + }, + ... + ] +} +``` + +- **api_titles**: An array of titles assigned to paths + - **title**: The displayed title of the API in the UI + - **path**: The path relative to the project root of the tested API + +**Default**: + +```json +{ + "api_titles": [] +} +``` + +**Example**: + +```json +{ + "api_titles": [ + { + "title": "WebGL", + "path": "/webgl" + }, + { + "title": "WebRTC Extensions", + "path": "/webrtc-extensions" + } + ] +} +``` + +[🠑 top](#configuration---wave-test-runner) + +### 2.7 Enable listing all sessions + +This parameter enables the [REST API endpoint to list all available sessions](./rest-api/sessions-api/read_sessions.md). + +**Parameters**: + +```json +{ + "enable_read_sessions": "" +} +``` + +- **enable_import_results**: Sets whether or not to enable the REST API endpoint read all sessions + +**Default**: + +```json +{ + "enable_read_sessions": "false" +} +``` + +[🠑 top](#configuration---wave-test-runner) + +### 2.8 Event caching duration + +This parameters specifies how long events are hold in the cache. Depending on +how fast clients are able to evaluate events, this value may be changed +accordingly. + +**Parameters**: + +```json +{ + "event_cache_duration": "" +} +``` + +- **event_cache_duration**: The duration events are hold in the cache in milliseconds + +**Default**: + +```json +{ + "event_cache_duration": 60000 +} +``` + +[🠑 top](#configuration---wave-test-runner) + +### 2.9 Enable test type selection + +Sets display of test type configuration UI elements. + +**Parameters**: + +```json +{ + "enable_test_type_selection": "" +} +``` + +- **enable_test_type_selection**: Whether or not test type UI controls are displayed + +**Default**: + +False + +[🠑 top](#configuration---wave-test-runner) diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/README.md b/testing/web-platform/tests/tools/wave/docs/rest-api/README.md index 73c24f8ac524..c6d21823a77f 100644 --- a/testing/web-platform/tests/tools/wave/docs/rest-api/README.md +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/README.md @@ -1,26 +1,31 @@ -# REST API - [WAVE Test Suite](../README.md) +# REST API - [WAVE Test Runner](../README.md) -The REST API allows the WAVE server to be integrated into other systems. Every -call must be preceded with a namespace or web root, which is omitted in this -documentation. The default web root is `/_wave`, which can be changed in the +The REST API allows the WAVE server to be integrated into other systems. Every +call must be preceded with a namespace or web root, which is omitted in this +documentation. The default web root is `/_wave`, which can be changed in the config.json using the keyword `web_root`. +Additional [REST API Guides](./guides/README.md) can help to understand how to +use these endpoints in context. + ## Sessions API -| Name | Description | -| ---------------------------------------------- | ---------------------------------------------------- | -| [`create`](./sessions-api/create.md) | Creates a new test session. | -| [`read`](./sessions-api/read.md) | Reads a sessions configuration. | -| [`read public`](./sessions-api/read-public.md) | Reads all public sessions tokens. | -| [`update`](./sessions-api/update.md) | Updates a session configuration. | -| [`delete`](./sessions-api/delete.md) | Deletes a test session. | -| [`status`](./sessions-api/status.md) | Reads the status and progress of a session. | -| [`start`](./sessions-api/control.md#start) | Starts a test session. | -| [`stop`](./sessions-api/control.md#stop) | Stops a test session. | -| [`pause`](./sessions-api/control.md#pause) | Pauses a test session. | -| [`find`](./sessions-api/find.md) | Finds a session token by providing a token fragment. | -| [`labels`](./sessions-api/labels.md) | Attach labels to sessions for organization purposes. | -| [`events`](./sessions-api/events.md) | Register for sessions specific events. | +| Name | Description | +| -------------------------------------------------- | -------------------------------------------------------------- | +| [`create`](./sessions-api/create.md) | Creates a new test session. | +| [`read session`](./sessions-api/read.md) | Reads a sessions configuration. | +| [`read sessions`](./sessions-api/read_sessions.md) | Reads all session tokens, expandable with configs and statuses | +| [`read public`](./sessions-api/read-public.md) | Reads all public sessions tokens. | +| [`update`](./sessions-api/update.md) | Updates a session configuration. | +| [`delete`](./sessions-api/delete.md) | Deletes a test session. | +| [`status`](./sessions-api/status.md) | Reads the status and progress of a session. | +| [`start`](./sessions-api/control.md#start) | Starts a test session. | +| [`stop`](./sessions-api/control.md#stop) | Stops a test session. | +| [`pause`](./sessions-api/control.md#pause) | Pauses a test session. | +| [`find`](./sessions-api/find.md) | Finds a session token by providing a token fragment. | +| [`labels`](./sessions-api/labels.md) | Attach labels to sessions for organization purposes. | +| [`listen events`](./sessions-api/events.md) | Register for sessions specific events. | +| [`push events`](./sessions-api/events.md) | Push session specific events. | ## Tests API @@ -36,19 +41,36 @@ config.json using the keyword `web_root`. ## Results API -| Name | Description | -| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -| [`create`](./results-api/create.md) | Create a new test result for a test in a session. | -| [`read`](./results-api/read.md) | Read all test results of a session. | -| [`read compact`](./results-api/read-compact.md) | Read the number of passed, failed, timed out and not run tests of a session. | -| [`config`](./results-api/config.md) | Read what features of the results API are enabled. | -| [`import`](./results-api/import.md) | Import session results. | -| [`import enabled`](./results-api/import.md#2-import-enabled) | Check whether or not the import feature is enabled. | -| [`download`](./results-api/download.md#1-download) | Download all session results to import into other WMATS instance. | -| [`download api`](./results-api/download.md#2-download-api) | Download all results of an API. | -| [`download all apis`](./results-api/download.md#3-download-all-apis) | Download all results of all APIs. | -| [`view report`](./results-api/download.md#4-download-report) | View the WPT report of an API of a session. | -| [`view multi report`](./results-api/download.md#5-download-multi-report) | View the WPT report of an API of multiple sessions. | -| [`download overview`](./results-api/download.md#6-download-overview) | Download an overview of results of all APIs of a session. | -| [`view report`](./results-api/view.md#1-view-report) | Read an url to a hosted version of a WPT report for an API of a session. | -| [`view multi report`](./results-api/view.md#2-view-multi-report) | Read an url to a hosted version of a WPT report for an API of multiple session. | +| Name | Description | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | +| [`create`](./results-api/create.md) | Create a new test result for a test in a session. | +| [`read`](./results-api/read.md) | Read all test results of a session. | +| [`read compact`](./results-api/read-compact.md) | Read the number of passed, failed, timed out and not run tests of a session. | +| [`import session`](./results-api/import.md#1-import-session) | Import session results. | +| [`import api results`](./results-api/import.md#2-import-api-results) | Import results of a specific API into existing session. | +| [`download`](./results-api/download.md#1-download) | Download all session results to import into other WMATS instance. | +| [`download api`](./results-api/download.md#2-download-api) | Download all results of an API. | +| [`download all apis`](./results-api/download.md#3-download-all-apis) | Download all results of all APIs. | +| [`view report`](./results-api/download.md#4-download-report) | View the WPT report of an API of a session. | +| [`view multi report`](./results-api/download.md#5-download-multi-report) | View the WPT report of an API of multiple sessions. | +| [`download overview`](./results-api/download.md#6-download-overview) | Download an overview of results of all APIs of a session. | +| [`view report`](./results-api/view.md#1-view-report) | Read an url to a hosted version of a WPT report for an API of a session. | +| [`view multi report`](./results-api/view.md#2-view-multi-report) | Read an url to a hosted version of a WPT report for an API of multiple session. | + +## Devices API + +| Name | Description | +| -------------------------------------------------------------------- | -------------------------------------- | +| [`create`](./devices-api/create.md) | Registers a new device. | +| [`read device`](./devices-api/read-device.md) | Read information of a specific device. | +| [`read devices`](./devices-api/read-devices.md) | Read a list of all available devices. | +| [`register event listener`](./devices-api/register.md) | Register for a device specific event. | +| [`send event`](./devices-api/send-event.md) | Sends a device specific event. | +| [`register global event listener`](./devices-api/register-global.md) | Register for a global device event. | +| [`send global event`](./devices-api/send-global-event.md) | Sends a global device event. | + +## General API + +| Name | Description | +| ----------------------------------- | ---------------------------------------------------- | +| [`status`](./general-api/status.md) | Returns information on how the server is configured. | diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/create.md b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/create.md new file mode 100644 index 000000000000..5ed1355ad2f0 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/create.md @@ -0,0 +1,33 @@ +# `create` - [Devices API](../README.md#devices-api) + +The `create` method of the devices API registers a new devices to remotely +start test sessions on. The device will automatically be unregistered if its +not registering for an [event](./register.md) for more than a minute. + +## HTTP Request + +`POST /api/devices` + +## Response Payload + +```json +{ + "token": "" +} +``` + +- **token** specifies the handle to reference the registered device by. + +## Example + +**Request:** + +`POST /api/devices` + +**Response:** + +```json +{ + "token": "e5f0b92e-8309-11ea-a1b1-0021ccd76152" +} +``` diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/event-types.md b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/event-types.md new file mode 100644 index 000000000000..ead1f0695b96 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/event-types.md @@ -0,0 +1,37 @@ +# Device Event Types - [Devices API](../README.md#devices-api) + +Device events are events that are triggered by actions related to devices. +This document specifies what possible events can occur and what they mean. + +## Device specific + +Device specific events are always related to a specific device, referenced by +its token. + +### Start session + +**Type identifier**: `start_session` +**Payload**: +```json +{ + "session_token": "" +} +``` +**Description**: Triggered by a companion device, this event starts a +pre-configured session on the registered device. + +## Global + +Global device events have no special relation to any device. + +### Device added + +**Type identifier**: `device_added` +**Payload**: Same as response of [`read device`](./read-device.md) method. +**Description**: This event is triggered once a new device registers. + +### Device removed + +**Type identifier**: `device_removed` +**Payload**: Same as response of [`read device`](./read-device.md) method. +**Description**: This event is triggered once a device unregisters. diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/read-device.md b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/read-device.md new file mode 100644 index 000000000000..9762b17e71b7 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/read-device.md @@ -0,0 +1,41 @@ +# `read device` - [Devices API](../README.md#devices-api) + +The `read device` method of the devices API fetches available information regarding a +specific device. + +## HTTP Request + +`GET /api/devices/` + +## Response Payload + +```json +{ + "token": "", + "user_agent": "", + "last_active": "", + "name": "" +} +``` + +- **token** is the unique identifier of the device. +- **user_agent** is the user agent of the request the device was registered with. +- **last_active** defines the point in time the device was last active. Expressed as ISO 8601 date and time format. +- **name** the name the device was assign based on its user agent. + +## Example + +**Request:** + +`GET /api/devices/1d9f5d30-830f-11ea-8dcb-0021ccd76152` + +**Response:** + +```json +{ + "token": "1d9f5d30-830f-11ea-8dcb-0021ccd76152", + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36", + "last_active": 1587391153295, + "name": "Chrome 81.0.4044" +} +``` diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/read-devices.md b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/read-devices.md new file mode 100644 index 000000000000..519b06c610d5 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/read-devices.md @@ -0,0 +1,47 @@ +# `read devices` - [Devices API](../README.md#devices-api) + +The `read devices` method of the devices API fetches a list of all registered +devices. + +## HTTP Request + +`GET /api/devices` + +## Response Payload + +```json +[ + { + "token": "", + "user_agent": "", + "last_active": "", + "name": "" + }, + ... +] +``` + +- **token** is the unique identifier of the device. +- **user_agent** is the user agent of the request the device was registered with. +- **last_active** defines the point in time the device was last active. Expressed as ISO 8601 date and time format. +- **name** the name the device was assign based on its user agent. + +## Example + +**Request:** + +`GET /api/devices` + +**Response:** + +```json +[ + { + "token": "1d9f5d30-830f-11ea-8dcb-0021ccd76152", + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36", + "last_active": 1587391153295, + "name": "Chrome 81.0.4044" + }, + ... +] +``` diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/register-global.md b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/register-global.md new file mode 100644 index 000000000000..fd3a2ad99813 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/register-global.md @@ -0,0 +1,54 @@ +# `register global event listener` - [Devices API](../README.md#devices-api) + +The `register global event listener` method of the devices API notifies a +registered listener upon global device events. It uses HTTP long polling in +send the event to this listener in real time, so upon receiving an event, the +connection has to be reestablished by the client to receive further events. + +## HTTP Request + +`GET /api/devices/events` + +## Query Parameters + +| Parameter | Desciption | Example | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | +| `device_token` | The token of the device which performed the request. (Optional) Lets the server know the registered device is still active. | `device_token=7dafeec0-c351-11e9-84c5-3d1ede2e7d2e` | + +## Response Payload + +```json +{ + "type": "", + "data": "" +} +``` + +- **type** defines what type of event has been triggered. +- **data** contains the event specific payload. + +## Event Types + +See [global events](./event-types.md#global) + +## Example + +**Request:** + +`GET /api/devices/events` + +**Response:** + +```json +{ + "type": "device_added", + "data": { + "token": "1d9f5d30-830f-11ea-8dcb-0021ccd76152", + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36", + "last_active": 1587391153295, + "name": "Chrome 81.0.4044" + } +} +``` + + diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/register.md b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/register.md new file mode 100644 index 000000000000..adee3b4742a6 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/register.md @@ -0,0 +1,52 @@ +# `register event listener` - [Devices API](../README.md#devices-api) + +The `register event listener` method of the devices API notifies a registered +listener upon device specific events. It uses HTTP long polling in send the +event to this listener in real time, so upon receiving an event, the +connection has to be reestablished by the client to receive further events. + +## HTTP Request + +`GET /api/devices//events` + +## Query Parameters + +| Parameter | Desciption | Example | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | +| `device_token` | The token of the device which performed the request. (Optional) Lets the server know the registered device is still active. | `device_token=7dafeec0-c351-11e9-84c5-3d1ede2e7d2e` | + +## Response Payload + +```json +{ + "type": "", + "data": "" +} +``` + +- **type** defines what type of event has been triggered. +- **data** contains the event specific payload. + +## Event Types + +### Start session + +See [device specific events](./event-types.md#device-specific) + +## Example + +**Request:** + +`GET /api/devices/1d9f5d30-830f-11ea-8dcb-0021ccd76152/events` + +**Response:** + +```json +{ + "type": "start_session", + "data": { + "session_token": "974c84e0-c35d-11e9-8f8d-47bb5bb0037d" + } +} +``` + diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/send-event.md b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/send-event.md new file mode 100644 index 000000000000..0ec74aec5c5b --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/send-event.md @@ -0,0 +1,43 @@ +# `send event` - [Devices API](../README.md#devices-api) + +The `send event` method of the devices API enables sending an event to +listeners of specific devices events. + +## HTTP Request + +`POST /api/devices//events` + +## Request Payload + +```json +{ + "type": "", + "data": "" +} +``` + +- **type** defines what type of event has been triggered. +- **data** contains the event specific payload. + +## Event Types + +See [device specific events](./event-types.md#device-specific) + +## Example + +**Request:** + +`POST /api/devices/1d9f5d30-830f-11ea-8dcb-0021ccd76152/events` + +```json +{ + "type": "start_session", + "data": { + "session_token": "974c84e0-c35d-11e9-8f8d-47bb5bb0037d" + } +} +``` + +**Response:** + +`200 OK` diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/send-global-event.md b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/send-global-event.md new file mode 100644 index 000000000000..aa5238dc7f75 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/devices-api/send-global-event.md @@ -0,0 +1,46 @@ +# `send global event` - [Devices API](../README.md#devices-api) + +The `send global event` method of the devices API enables sending an event to +listeners of global device events. + +## HTTP Request + +`POST /api/devices/events` + +## Request Payload + +```json +{ + "type": "", + "data": "" +} +``` + +- **type** defines what type of event has been triggered. +- **data** contains the event specific payload. + +## Event Types + +See [global events](./event-types.md#global) + +## Example + +**Request:** + +`POST /api/devices/1d9f5d30-830f-11ea-8dcb-0021ccd76152/events` + +```json +{ + "type": "device_added", + "data": { + "token": "1d9f5d30-830f-11ea-8dcb-0021ccd76152", + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36", + "last_active": 1587391153295, + "name": "Chrome 81.0.4044" + } +} +``` + +**Response:** + +`200 OK` diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/general-api/status.md b/testing/web-platform/tests/tools/wave/docs/rest-api/general-api/status.md new file mode 100644 index 000000000000..8af510066a66 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/general-api/status.md @@ -0,0 +1,41 @@ +# `status` - [General API](../README.md#general-api) + +The `status` method is used to ensure the server is reachable and to determine +what features of different server APIs are enabled. + +## HTTP Request + +``` +GET /api/status +``` + +### Response + +```json +{ + "version_string": "String", + "import_results_enabled": "Boolean", + "reports_enabled": "Boolean", + "read_sessions_enabled": "Boolean" +} +``` + +- **version_string**: The version of the server. +- **import_results_enabled**: If true the [`import result`](../results-api/import.md) endpoint is available +- **reports_enabled**: If true the server will generate reports for completed APIs in a given test session. +- **read_sessions_enabled**: If true it is possible to list all sessions using the [`read sessions`](../sessions-api/read_sessions.md) endpoint of the sessions API + +## Example + +``` +GET /api/status +``` + +```json +{ + "version_string": "v2.0.0", + "import_results_enabled": false, + "reports_enabled": true, + "read_sessions_enabled": false +} +``` diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/guides/README.md b/testing/web-platform/tests/tools/wave/docs/rest-api/guides/README.md new file mode 100644 index 000000000000..c331bb8e6663 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/guides/README.md @@ -0,0 +1,10 @@ +# REST API Guides - [WAVE Test Runner](../../README.md) + +In addition to the [REST API documentation](../README.md), these guide shall +provide a better understanding on how to properly use the different endpoints. + +[Starting sessions on a DUT using the devices API](./session-start-devices-api.md): +How to register a DUT and start a pre-configured session on it. + +[Sending and receiving session events](./session-events.md): +How to register for session events and push events to other listeners. diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/guides/session-events.md b/testing/web-platform/tests/tools/wave/docs/rest-api/guides/session-events.md new file mode 100644 index 000000000000..462737c65220 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/guides/session-events.md @@ -0,0 +1,52 @@ +# Sending and receiving session events + +The session event endpoints allow to listen for events related to a specific +session and to send new events to all registered listeners. + +See all [REST API Guides](./README.md). + +## Register for session specific events + +To receive events of a session, simply perform a GET request to the desired +sessions event endpoint. For example, if we want to receive any events that +are related to the session with token `6fdbd1a0-c339-11e9-b775-6d49dd567772`: + +``` +GET /_wave/api/sessions/6fdbd1a0-c339-11e9-b775-6d49dd567772/events +``` + +```json +{ + "type": "status", + "data": "paused" +} +``` + +As this endpoint makes use of the HTTP long polling, you will not immediately +receive a response. The connection stays open either until an event gets +triggered, in which case the server will respond with that events data, or +there is no event within the timeout, which will return an empty response. + +With one request only one event can be received. To get any further events, +additional requests are necessary. To not miss any events, it is important to +perform the next request immediately after receiving a response. + +## Sending events + +To create a new event, simply send a POST request containing the event data to +the desired sessions event endpoint. For example, if you want to trigger a new +event for a session with token `6fdbd1a0-c339-11e9-b775-6d49dd567772`: + +``` +POST /_wave/api/sessions/6fdbd1a0-c339-11e9-b775-6d49dd567772/events +``` + +```json +{ + "type": "status", + "data": "paused" +} +``` + +This will cause any client, that currently has a connection open as described +in the preceding section, to receive the specified event. diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/guides/session-start-devices-api.md b/testing/web-platform/tests/tools/wave/docs/rest-api/guides/session-start-devices-api.md new file mode 100644 index 000000000000..a376d3161757 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/guides/session-start-devices-api.md @@ -0,0 +1,60 @@ +# Starting sessions on a DUT using the devices API + +See all [REST API Guides](./README.md). + +## Connecting the DUT + +To start a session on a DUT using the devices API, first register the DUT at +the test runner. + +``` +POST /api/devices +``` + +```json +{ + "token": "fa3fb226-98ef-11ea-a21d-0021ccd76152" +} +``` + +Using the device token, you can listen for any events related to the device. + +``` +GET /api/devices/fa3fb226-98ef-11ea-a21d-0021ccd76152/events +``` + +Once an event occurs, the response to this call will contain the event data. +If no event occurs until the request times out, you have to perfom another call. + +```json +{ + "type": "start_session", + "data": { + "session_token": "98ed4b8e-98ed-11ea-9de7-0021ccd76152" + } +} +``` + +Using this data you can start the session and get the URL to the next test to +open. + +## Triggering the session start + +Once a device is registered and waits for events, you can use the device's +event channel to push an event to start a session on it. + +``` +POST /api/devices/fa3fb226-98ef-11ea-a21d-0021ccd76152/events +``` + +```json +{ + "type": "start_session", + "data": { + "session_token": "98ed4b8e-98ed-11ea-9de7-0021ccd76152" + } +} +``` + +The session related to the provided token can be a newly created one or may +already be running. diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/results-api/download.md b/testing/web-platform/tests/tools/wave/docs/rest-api/results-api/download.md index f752dfaa66ff..8cfa7e7531c4 100644 --- a/testing/web-platform/tests/tools/wave/docs/rest-api/results-api/download.md +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/results-api/download.md @@ -7,7 +7,7 @@ the [`create`](./create.md) method of the results API. ## 1. `download` Downloads all results of a session as ZIP, which other instances of the WMAS -Test Suite can import. +Test Runner can import. ### HTTP Request diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/results-api/import.md b/testing/web-platform/tests/tools/wave/docs/rest-api/results-api/import.md index 75026768f87b..08c7de6ff7bb 100644 --- a/testing/web-platform/tests/tools/wave/docs/rest-api/results-api/import.md +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/results-api/import.md @@ -1,16 +1,18 @@ # Import results - [Results API](../README.md#results-api) -If enabled, the WMAS Test Suite can import results exported by any arbitrary other instance. +If enabled, the WMAS Test Runner can import results exported by any arbitrary other instance. -## 1. `import` +## 1. Import session -Import a session's results from a ZIP file. +Upload results and create a new, finished session ### HTTP Request -`POST /api/results/import` +``` +POST /api/results/import +``` -### HTTP Response +#### HTTP Response If successful, the server responds with the token of the imported session: @@ -28,18 +30,37 @@ However, if an error occured, the server responds the error message: } ``` -## 2. `import enabled` +## 2. Import API results -To check whether or not the import features is enabled, the `import enabled` method returns the state as JSON. +Upload a results json file and overwrite results of a specific API. ### HTTP Request -`GET /api/results/import` +``` +POST /api/results///json +``` -### Response +### File structure ```json { - "enabled": "Boolean" + "results": [ + { + "test": "String", + "status": "Enum['OK', 'ERROR', 'TIMEOUT', 'NOT_RUN']", + "message": "String", + "subtests": [ + { + "name": "String", + "status": "Enum['PASS', 'FAIL', 'TIMEOUT', 'NOT_RUN']", + "message": "String" + } + ] + } + ] } ``` + +### HTTP Response + +If successful, the server responds with status code 200. diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/create.md b/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/create.md index eaf00ac16690..86c0674afdea 100644 --- a/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/create.md +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/create.md @@ -21,12 +21,13 @@ The `create` method of the sessions API creates a new session. If provided with "": "Integer" }, "reference_tokens": "Array", - "labels": "Array" + "labels": "Array", + "type": "String" } ``` - **tests** specifies the tests of the session: - - **include** specifies what tests should be selected from all available tests. Can be a path to a test file or directory. + - **include** specifies what tests should be selected from all available tests. Can be a path to a test file or directory. Provided query parameters will be added to all matching test urls. - **exclude** specifies what tests should be removed from the included tests. Can be a path to a test file or directory. - **types** what types of tests should be included. Possible values: - **automatic** tests are tests that execute without user interaction. @@ -37,6 +38,7 @@ The `create` method of the sessions API creates a new session. If provided with - **custom test paths**: Set the timeout for a test file or directory by putting the path with all dots removed as the key. - **reference_tokens** specifies a set of completed sessions that is used to filter out all tests that have not passed in all those sessions from the session that is going to be created. - **labels** specifies the initial set of labels for the session. +- **type** specifies the session type to trigger type specific behaviour like different control pages. ### Default diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/event-types.md b/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/event-types.md new file mode 100644 index 000000000000..455e0a122bb9 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/event-types.md @@ -0,0 +1,27 @@ +# Session Event Types - [Sessions API](../README.md#sessions-api) + +Session events are events that are triggered by actions related to sessions. +The [`event`](./events.md) functions of the sessions API make use of these events. + +## Status change + +**Type identifier**: `status` +**Payload**: `""` +Possible Values: `paused`, `running`, `completed`, `aborted` +**Description**: Triggered once the status of the session changes. + +## Resume + +**Type identifier**: `resume` +**Payload**: `""` +Contains the token of the session to resume. +**Description**: Triggered when a specific session is supposed to be resumed. +This will discard the current session and continue executing the session with +the provided token. + +## Test Completed + +**Type identifier**: `test_completed` +**Payload**: `""` +Contains the test case that completed. +**Description**: Triggered when the test runner received a result for a test. diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/events.md b/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/events.md index e2148b1dd362..c1b693c88c59 100644 --- a/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/events.md +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/events.md @@ -1,12 +1,84 @@ # `events` - [Sessions API](../README.md#sessions-api) +Session events can be used to send messages related to a specific session for +others to receive. This can include status updates or action that running +session react on. + +For possible events see [Session Event Types](./event-types.md) + +## 1. `listen events` + Listen for session specific events by registering on the `events` endpoint using HTTP long polling. -## HTTP Request +### HTTP Request -`GET /api/sessions//events` +``` +GET /api/sessions//events +``` -## Response Payload +### Query Parameters + +| Parameter | Desciption | Default | Example | +| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -------------- | +| `last_event` | The number of the last received event. All events that are newer than `last_event` are returned immediately. If there are no newer events, connection stays open until a new event is triggered. | None | `last_event=5` | + +#### Response Payload + +```json +[ + { + "type": "String", + "data": "String", + "number": "Number" + }, + ... +] +``` + +- **type**: the type of event that occurred. +- **data**: the actual payload of the event +- **number**: the number of the event + +#### Example + +``` +GET /api/sessions/6fdbd1a0-c339-11e9-b775-6d49dd567772/events?last_event=8 +``` + +```json +[ + { + "type": "status", + "data": "paused", + "number": 9 + }, + { + "type": "status", + "data": "running", + "number": 10 + }, + { + "type": "status", + "data": "paused", + "number": 11 + }, + { + "type": "status", + "data": "running", + "number": 12 + } +] +``` + +## 2. `push events` + +Push session specific events for any registered listeners to receive. + +### HTTP Request + +``` +POST /api/sessions//events +``` ```json { @@ -18,13 +90,11 @@ Listen for session specific events by registering on the `events` endpoint using - **type**: the type of event that occurred. - **data**: the actual payload of the event -## Example +#### Example -**Request** - -`GET /api/sessions/6fdbd1a0-c339-11e9-b775-6d49dd567772/events` - -**Response** +``` +POST /api/sessions/6fdbd1a0-c339-11e9-b775-6d49dd567772/events +``` ```json { diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/read.md b/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/read.md index da4b57377a61..4ec9c3f63f5d 100644 --- a/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/read.md +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/read.md @@ -1,4 +1,4 @@ -# `read` - [Sessions API](../README.md#sessions-api) +# `read session` - [Sessions API](../README.md#sessions-api) The `read` method of the sessions API fetches the configuration of a session, including values that can not be set by the user, but are created by the server upon creation. @@ -27,7 +27,9 @@ The `read` method of the sessions API fetches the configuration of a session, in "name": "String", "version": "String" }, - "is_public": "Boolean" + "is_public": "Boolean", + "date_created": "String", + "labels": "Array" } ``` @@ -48,6 +50,8 @@ The `read` method of the sessions API fetches the configuration of a session, in - **name**: The name of the browser. - **version**: The version numbers of the browser. - **is_public** defines whether or not the session is listed when fetching the list of public session using [`read public`](./read-public.md). +- **date_created**: The date the session was created in ISO 8601 format. +- **labels**: A list of the sessions labels. ## Example @@ -79,6 +83,8 @@ The `read` method of the sessions API fetches the configuration of a session, in "name": "Chromium", "version": "76" }, - "is_public": "false" + "is_public": "false", + "date_created": "2020-05-25T11:37:07", + "labels": ["labelA", "labelB"] } ``` diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/read_sessions.md b/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/read_sessions.md new file mode 100644 index 000000000000..d29371f00644 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/read_sessions.md @@ -0,0 +1,123 @@ +# `read sessions` - [Sessions API](../README.md#sessions-api) + +The `read sessions` method of the sessions API fetches a list of all available +session's token with the option expand the returned data by all retrieved +tokens corresponding session configurations or statuses. + +## HTTP Request + +``` +GET /api/sessions +``` + +### Query Parameters + +| Parameter | Description | Default | +| --------- | ---------------------------------------------------------------------------------------------------- | ------- | +| `index` | At what index of all session to start the returned list. | `0` | +| `count` | How many entries to return starting from `index`. | `10` | +| `expand` | Comma separated list of relations from `_links`. Includes additional data in the `_embedded` object. | none | + +### Response Payload + +``` +200 OK +Content-Type: application/json+hal +``` + +```json +{ + "_links": { + "": { + "href": "String" + } + ... + }, + "_embedded": { + "": "Array" + ... + }, + "items": "Array" +} +``` + +- **\_links** contains URLs to related data. For more, see [HAL Specfication](https://tools.ietf.org/html/draft-kelly-json-hal). +- **\_embedded** additional content specified by `expand` query paramater. For more, see [HAL Specfication](https://tools.ietf.org/html/draft-kelly-json-hal). +- **items** contains the returned list of session tokens. + +## Example + +``` +GET /api/sessions?index=0&count=3&expand=status +``` + +``` +200 OK +Content-Type: application/json+hal +``` + +```json +{ + "_links": { + "first": { + "href": "/_wave/api/sessions?index=0&count=3" + }, + "last": { + "href": "/_wave/api/sessions?index=39&count=3" + }, + "self": { + "href": "/_wave/api/sessions?index=0&count=3" + }, + "next": { + "href": "/_wave/api/sessions?index=3&count=3" + }, + "configuration": { + "href": "/_wave/api/sessions/{token}", + "templated": true + }, + "status": { + "href": "/_wave/api/sessions/{token}/status", + "templated": true + } + }, + "items": [ + "13f80c84-9046-11ea-9c80-0021ccd76152", + "34db08e4-903b-11ea-89ce-0021ccd76152", + "a355f846-9465-11ea-ae9e-0021ccd76152" + ], + "_embedded": { + "status": [ + { + "status": "completed", + "expiration_date": null, + "labels": [], + "date_finished": 1588844145897.157, + "token": "13f80c84-9046-11ea-9c80-0021ccd76152", + "date_created": null, + "date_started": 1588844127000, + "type": "wmas" + }, + { + "status": "completed", + "expiration_date": null, + "labels": [], + "date_finished": 1588839822768.9568, + "token": "34db08e4-903b-11ea-89ce-0021ccd76152", + "date_created": null, + "date_started": 1588839522000, + "type": "wmas" + }, + { + "status": "completed", + "expiration_date": null, + "labels": [], + "date_finished": 1589297485065.1802, + "token": "a355f846-9465-11ea-ae9e-0021ccd76152", + "date_created": null, + "date_started": 1589297484000, + "type": "wmas" + } + ] + } +} +``` diff --git a/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/status.md b/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/status.md index 4f50831ae849..b2604a41df86 100644 --- a/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/status.md +++ b/testing/web-platform/tests/tools/wave/docs/rest-api/sessions-api/status.md @@ -25,9 +25,9 @@ The `status` method of the results API returns information about a sessions curr - **paused**: The execution of tests in this session is currently paused. - **completed**: All tests files include in this session were executed and have a result. - **aborted**: The session was finished before all tests were executed. -- **date_started** contains the time the status changed from `PENDING` to `RUNNING` in unix epoch time milliseconds. -- **date_finished** contains the time the status changed to either `COMPLETED` or `ABORTED` in unix epoch time milliseconds. -- **expiration_date** contains the time at which the sessions will be deleted +- **date_started** contains the time the status changed from `PENDING` to `RUNNING` in ISO 8601. +- **date_finished** contains the time the status changed to either `COMPLETED` or `ABORTED` in ISO 8601. +- **expiration_date** contains the time at which the sessions will be deleted in ISO 8601. ## Example @@ -41,8 +41,8 @@ The `status` method of the results API returns information about a sessions curr { "token": "d9caaae0-c362-11e9-943f-eedb305f22f6", "status": "running", - "date_started": "1567606879230", + "date_started": "2019-09-04T14:21:19", "date_finished": null, - "expiration_date": "1567607179230" + "expiration_date": "2019-09-04T14:26:19" } ``` diff --git a/testing/web-platform/tests/tools/wave/docs/usage/usage.md b/testing/web-platform/tests/tools/wave/docs/usage/usage.md index 025eeae424e0..50a99e0d5aa5 100644 --- a/testing/web-platform/tests/tools/wave/docs/usage/usage.md +++ b/testing/web-platform/tests/tools/wave/docs/usage/usage.md @@ -1,4 +1,11 @@ -# Usage Guide - [WAVE Test Suite](../README.md) +# Usage Guide - [WAVE Test Runner](../README.md) + +With WAVE Test Runner v1.0.0 all files and REST API endpoints are served under +a configurable namespace, by default `/_wave/`, which will be used in this +usage guide. + +In this document the usage is explained using screenshots from the context of +the WMAS project. However, practices can be applied to different contexts as well. ## Contents @@ -26,7 +33,7 @@ Each new session is configured using several parameters before the run starts. Every new session is created from the landing page. It is recommended to create a new session from the device that is tested, as the user agent is part of the displayed information, as well as the browser and version, which gets parsed from it. However, this does not influence the execution of tests or the creation of test results. -To create a new session, open the landing page on the URI path `/`. +To create a new session, open the landing page on the URI path `/_wave/index.html`. ![landing_page] @@ -56,7 +63,7 @@ Only tests that have passed the reference test session in all selected browsers The reference browsers represent the status of implementation of all WAVE APIs in modern desktop browsers, at about the time the WAVE specification was published. To start the session press "Start Session", note that the landing page has to stay opened, as the test are going to be execute in the same window. -[To the top](#usage-guide---wave-test-suite) +[To the top](#usage-guide---wave-test-runner) ## 1.3 Exclude tests @@ -96,11 +103,11 @@ Enter the first eight characters or more into the text field labelled "Session T Click "Add" to confirm. The tests should now appear in the list below. -[To the top](#usage-guide---wave-test-suite) +[To the top](#usage-guide---wave-test-runner) # 2. Resuming test sessions -Certain test cases may cause some devices to crash, which makes the test suite unable to automatically run the next test. +Certain test cases may cause some devices to crash, which makes the test runner unable to automatically run the next test. In this case, external interaction is necessary. To alleviate the process of resuming the test session, the are two mechanisms integrated into the web interface that reduce interaction with the device to a minimum. There is also a mechanism that can be useful if a test framework with access to the tested browser is utilized. @@ -132,9 +139,9 @@ To load the next test of a specific session, simply open the following URL: For example: -`/next.html?token=24fcd360-ef4d-11e9-a95f-d6e1ad4c5fdb` +`/_wave/next.html?token=24fcd360-ef4d-11e9-a95f-d6e1ad4c5fdb` -[To the top](#usage-guide---wave-test-suite) +[To the top](#usage-guide---wave-test-runner) # 3. Monitoring test sessions @@ -167,18 +174,18 @@ Once all test files of an API have received a result, it is possible to download Below the table of API results, there are more options to download the results of the session. The first option downloads the results the same way it is persisted on the serverside, along with some meta data. -This form is especially useful if you want to import the session details with the results into other instances of the WAVE Test Suite. +This form is especially useful if you want to import the session details with the results into other instances of the WAVE Test Runner. Furthermore, there is the option to download the raw result in JSON format of all finished APIs. This the same JSON you get by clicking on the "JSON" button in the API results column, but of all finished APIs in a ZIP file. Lastly, you can download a static HTML page, similiar to the results view. Finally, at the bottom of the page you can find the list of malfunctioning tests that have been added from the list of last timed-out test files. Remove tests by clicking their corresponding button with the trashcan icon. -[To the top](#usage-guide---wave-test-suite) +[To the top](#usage-guide---wave-test-runner) # 4. Managing test sessions -The overview page provides features that help to manage and organize multiple sessions. You can access it from the URL `/overview.html`. +The overview page provides features that help to manage and organize multiple sessions. You can access it from the URL `/_wave/overview.html`. ![overview_page] @@ -205,7 +212,7 @@ Sort the list of sessions by clicking on the column to filter them by. Add one or more tags to the filter to conveniently find the sessions you are looking for. Add labels to session when creating them or in their corresponding results page. -[To the top](#usage-guide---wave-test-suite) +[To the top](#usage-guide---wave-test-runner) [landing_page]: ../res/landing_page.jpg "Landing Page" [configuration_page]: ../res/configuration_page_top.jpg "Configuration Page" diff --git a/testing/web-platform/tests/tools/wave/network/api/api_handler.py b/testing/web-platform/tests/tools/wave/network/api/api_handler.py index 9857a7ab73a1..da212378c576 100644 --- a/testing/web-platform/tests/tools/wave/network/api/api_handler.py +++ b/testing/web-platform/tests/tools/wave/network/api/api_handler.py @@ -2,6 +2,7 @@ import json import sys import traceback import logging + from urllib.parse import parse_qsl global logger @@ -51,3 +52,46 @@ class ApiHandler(object): info = sys.exc_info() traceback.print_tb(info[2]) logger.error("{}: {}: {}".format(message, info[0].__name__, info[1].args[0])) + + def create_hal_list(self, items, uris, index, count, total): + hal_list = {} + links = {} + if uris is not None: + for relation in uris: + if relation == "self": + continue + uri = uris[relation] + templated = "{" in uri + links[relation] = {"href": uri, "templated": templated} + + if "self" in uris: + self_uri = uris["self"] + self_uri += "?index={}&count={}".format(index, count) + links["self"] = {"href": self_uri} + + first_uri = uris["self"] + first_uri += "?index={}&count={}".format(0, count) + links["first"] = {"href": first_uri} + + last_uri = uris["self"] + last_uri += "?index={}&count={}".format(total - (total % count), count) + links["last"] = {"href": last_uri} + + if index + count <= total: + next_index = index + count + next_uri = uris["self"] + next_uri += "?index={}&count={}".format(next_index, count) + links["next"] = {"href": next_uri} + + if index != 0: + previous_index = index - count + if previous_index < 0: + previous_index = 0 + previous_uri = uris["self"] + previous_uri += "?index={}&count={}".format(previous_index, count) + links["previous"] = {"href": previous_uri} + + hal_list["_links"] = links + hal_list["items"] = items + + return hal_list diff --git a/testing/web-platform/tests/tools/wave/network/api/devices_api_handler.py b/testing/web-platform/tests/tools/wave/network/api/devices_api_handler.py new file mode 100644 index 000000000000..5e8b02bca1f3 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/network/api/devices_api_handler.py @@ -0,0 +1,200 @@ +import json +import threading + +from .api_handler import ApiHandler +from ...data.http_polling_event_listener import HttpPollingEventListener +from ...testing.event_dispatcher import DEVICES +from ...utils.serializer import serialize_device +from ...testing.devices_manager import DEVICE_TIMEOUT, RECONNECT_TIME +from ...data.exceptions.not_found_exception import NotFoundException + + +class DevicesApiHandler(ApiHandler): + def __init__(self, devices_manager, event_dispatcher, web_root): + super(DevicesApiHandler, self).__init__(web_root) + self._devices_manager = devices_manager + self._event_dispatcher = event_dispatcher + + def create_device(self, request, response): + try: + user_agent = request.headers[b"user-agent"].decode("utf-8") + + device = self._devices_manager.create_device(user_agent) + + self.send_json({"token": device.token}, response) + except Exception: + self.handle_exception("Failed to create device") + response.status = 500 + + def read_device(self, request, response): + try: + uri_parts = self.parse_uri(request) + token = uri_parts[2] + + device = self._devices_manager.read_device(token) + + device_object = serialize_device(device) + + self.send_json(device_object, response) + except NotFoundException: + self.handle_exception("Failed to read device") + response.status = 404 + except Exception: + self.handle_exception("Failed to read device") + response.status = 500 + + def read_devices(self, request, response): + try: + devices = self._devices_manager.read_devices() + + device_objects = [] + for device in devices: + device_object = serialize_device(device) + device_objects.append(device_object) + + self.send_json(device_objects, response) + except Exception: + self.handle_exception("Failed to read devices") + response.status = 500 + + def register_event_listener(self, request, response): + try: + uri_parts = self.parse_uri(request) + token = uri_parts[2] + query = self.parse_query_parameters(request) + + if u"device_token" in query: + self._devices_manager.refresh_device(query["device_token"]) + + event = threading.Event() + timer = threading.Timer( + (DEVICE_TIMEOUT - RECONNECT_TIME) / 1000, + event.set, + []) + timer.start() + http_polling_event_listener = HttpPollingEventListener(token, event) + event_listener_token = self._event_dispatcher.add_event_listener(http_polling_event_listener) + + event.wait() + + message = http_polling_event_listener.message + if message is not None: + self.send_json(data=message, response=response) + self._event_dispatcher.remove_event_listener(event_listener_token) + except Exception: + self.handle_exception("Failed to register event listener") + response.status = 500 + + def register_global_event_listener(self, request, response): + try: + query = self.parse_query_parameters(request) + + if u"device_token" in query: + self._devices_manager.refresh_device(query["device_token"]) + + event = threading.Event() + timer = threading.Timer( + (DEVICE_TIMEOUT - RECONNECT_TIME) / 1000, + event.set, + []) + timer.start() + http_polling_event_listener = HttpPollingEventListener(DEVICES, event) + event_listener_token = self._event_dispatcher.add_event_listener(http_polling_event_listener) + + event.wait() + + message = http_polling_event_listener.message + if message is not None: + self.send_json(data=message, response=response) + self._event_dispatcher.remove_event_listener(event_listener_token) + except Exception: + self.handle_exception(u"Failed to register global event listener") + response.status = 500 + + def post_global_event(self, request, response): + try: + event = {} + body = request.body.decode("utf-8") + if body != "": + event = json.loads(body) + + query = self.parse_query_parameters(request) + if u"device_token" in query: + self._devices_manager.refresh_device(query["device_token"]) + + event_type = None + if "type" in event: + event_type = event["type"] + data = None + if "data" in event: + data = event["data"] + self._devices_manager.post_global_event(event_type, data) + + except Exception: + self.handle_exception("Failed to post global event") + response.status = 500 + + def post_event(self, request, response): + try: + uri_parts = self.parse_uri(request) + token = uri_parts[2] + + event = {} + body = request.body.decode("utf-8") + if body != "": + event = json.loads(body) + + query = self.parse_query_parameters(request) + if u"device_token" in query: + self._devices_manager.refresh_device(query["device_token"]) + + event_type = None + if "type" in event: + event_type = event["type"] + data = None + if "data" in event: + data = event["data"] + self._devices_manager.post_event(token, event_type, data) + + except Exception: + self.handle_exception("Failed to post event") + response.status = 500 + + def handle_request(self, request, response): + method = request.method + uri_parts = self.parse_uri(request) + + # /api/devices + if len(uri_parts) == 2: + if method == u"POST": + self.create_device(request, response) + return + if method == u"GET": + self.read_devices(request, response) + return + + # /api/devices/ + if len(uri_parts) == 3: + function = uri_parts[2] + if method == u"GET": + if function == u"events": + self.register_global_event_listener(request, response) + return + self.read_device(request, response) + return + if method == "POST": + if function == "events": + self.post_global_event(request, response) + return + + # /api/devices// + if len(uri_parts) == 4: + function = uri_parts[3] + if method == u"GET": + if function == u"events": + self.register_event_listener(request, response) + return + if method == "POST": + if function == "events": + self.post_event(request, response) + return diff --git a/testing/web-platform/tests/tools/wave/network/api/general_api_handler.py b/testing/web-platform/tests/tools/wave/network/api/general_api_handler.py new file mode 100644 index 000000000000..bd588d13e85e --- /dev/null +++ b/testing/web-platform/tests/tools/wave/network/api/general_api_handler.py @@ -0,0 +1,77 @@ +from __future__ import absolute_import +from __future__ import unicode_literals + +from .api_handler import ApiHandler + +TOKEN_LENGTH = 36 + + +class GeneralApiHandler(ApiHandler): + def __init__( + self, + web_root, + read_sessions_enabled, + import_results_enabled, + reports_enabled, + version_string, + test_type_selection_enabled, + test_file_selection_enabled + ): + super(GeneralApiHandler, self).__init__(web_root) + self.read_sessions_enabled = read_sessions_enabled + self.import_results_enabled = import_results_enabled + self.reports_enabled = reports_enabled + self.version_string = version_string + self.test_type_selection_enabled = test_type_selection_enabled + self.test_file_selection_enabled = test_file_selection_enabled + + def read_status(self): + try: + return { + "format": "application/json", + "data": { + "version_string": self.version_string, + "read_sessions_enabled": self.read_sessions_enabled, + "import_results_enabled": self.import_results_enabled, + "reports_enabled": self.reports_enabled, + "test_type_selection_enabled": self.test_type_selection_enabled, + "test_file_selection_enabled": self.test_file_selection_enabled + } + } + except Exception: + self.handle_exception("Failed to read server configuration") + return {"status": 500} + + def handle_request(self, request, response): + method = request.method + uri_parts = self.parse_uri(request) + + result = None + # /api/ + if len(uri_parts) == 2: + function = uri_parts[1] + if method == "GET": + if function == "status": + result = self.read_status() + + if result is None: + response.status = 404 + return + + format = None + if "format" in result: + format = result["format"] + if format == "application/json": + data = None + if "data" in result: + data = result["data"] + status = 200 + if "status" in result: + status = result["status"] + self.send_json(data, response, status) + return + + status = 404 + if "status" in result: + status = result["status"] + response.status = status diff --git a/testing/web-platform/tests/tools/wave/network/api/results_api_handler.py b/testing/web-platform/tests/tools/wave/network/api/results_api_handler.py index 285efe56a845..1efa988ae764 100644 --- a/testing/web-platform/tests/tools/wave/network/api/results_api_handler.py +++ b/testing/web-platform/tests/tools/wave/network/api/results_api_handler.py @@ -6,9 +6,10 @@ from ...data.exceptions.invalid_data_exception import InvalidDataException class ResultsApiHandler(ApiHandler): - def __init__(self, results_manager, web_root): + def __init__(self, results_manager, session_manager, web_root): super(ResultsApiHandler, self).__init__(web_root) self._results_manager = results_manager + self._sessions_manager = session_manager def create_result(self, request, response): try: @@ -31,6 +32,11 @@ class ResultsApiHandler(ApiHandler): uri_parts = self.parse_uri(request) token = uri_parts[2] + session = self._sessions_manager.read_session(token) + if session is None: + response.status = 404 + return + results = self._results_manager.read_results(token) self.send_json(response=response, data=results) @@ -52,19 +58,6 @@ class ResultsApiHandler(ApiHandler): self.handle_exception("Failed to read compact results") response.status = 500 - def read_results_config(self, request, response): - try: - import_enabled = self._results_manager.is_import_enabled() - reports_enabled = self._results_manager.are_reports_enabled() - - self.send_json({ - "import_enabled": import_enabled, - "reports_enabled": reports_enabled - }, response) - except Exception: - self.handle_exception("Failed to read results configuration") - response.status = 500 - def read_results_api_wpt_report_url(self, request, response): try: uri_parts = self.parse_uri(request) @@ -112,6 +105,20 @@ class ResultsApiHandler(ApiHandler): self.handle_exception("Failed to download api json") response.status = 500 + def import_results_api_json(self, request, response): + try: + uri_parts = self.parse_uri(request) + token = uri_parts[2] + api = uri_parts[3] + blob = request.body + + self._results_manager.import_results_api_json(token, api, blob) + + response.status = 200 + except Exception: + self.handle_exception("Failed to upload api json") + response.status = 500 + def download_results_all_api_jsons(self, request, response): try: uri_parts = self.parse_uri(request) @@ -182,12 +189,8 @@ class ResultsApiHandler(ApiHandler): return if method == "GET": - if uri_parts[2] == "config": - self.read_results_config(request, response) - return - else: - self.read_results(request, response) - return + self.read_results(request, response) + return # /api/results// if len(uri_parts) == 4: @@ -219,5 +222,9 @@ class ResultsApiHandler(ApiHandler): if function == "json": self.download_results_api_json(request, response) return + if method == "POST": + if function == "json": + self.import_results_api_json(request, response) + return response.status = 404 diff --git a/testing/web-platform/tests/tools/wave/network/api/sessions_api_handler.py b/testing/web-platform/tests/tools/wave/network/api/sessions_api_handler.py index c5040addf70d..dffca43b77c5 100644 --- a/testing/web-platform/tests/tools/wave/network/api/sessions_api_handler.py +++ b/testing/web-platform/tests/tools/wave/network/api/sessions_api_handler.py @@ -6,121 +6,187 @@ from .api_handler import ApiHandler from ...utils.serializer import serialize_session from ...data.exceptions.not_found_exception import NotFoundException from ...data.exceptions.invalid_data_exception import InvalidDataException -from ...data.http_polling_client import HttpPollingClient +from ...data.http_polling_event_listener import HttpPollingEventListener TOKEN_LENGTH = 36 class SessionsApiHandler(ApiHandler): - def __init__(self, sessions_manager, results_manager, event_dispatcher, web_root): + def __init__( + self, + sessions_manager, + results_manager, + event_dispatcher, + web_root, + read_sessions_enabled + ): super(SessionsApiHandler, self).__init__(web_root) self._sessions_manager = sessions_manager self._results_manager = results_manager self._event_dispatcher = event_dispatcher + self._read_sessions_enabled = read_sessions_enabled - def create_session(self, request, response): + def create_session(self, body, headers): try: config = {} - body = request.body.decode("utf-8") + body = body.decode("utf-8") if body != "": config = json.loads(body) tests = {} if "tests" in config: tests = config["tests"] - types = None + test_types = None if "types" in config: - types = config["types"] + test_types = config["types"] timeouts = {} if "timeouts" in config: timeouts = config["timeouts"] reference_tokens = [] if "reference_tokens" in config: reference_tokens = config["reference_tokens"] - webhook_urls = [] - if "webhook_urls" in config: - webhook_urls = config["webhook_urls"] - user_agent = request.headers[b"user-agent"].decode("utf-8") + user_agent = headers[b"user-agent"].decode("utf-8") labels = [] if "labels" in config: labels = config["labels"] expiration_date = None if "expiration_date" in config: expiration_date = config["expiration_date"] + type = None + if "type" in config: + type = config["type"] session = self._sessions_manager.create_session( tests, - types, + test_types, timeouts, reference_tokens, - webhook_urls, user_agent, labels, - expiration_date + expiration_date, + type ) - self.send_json({"token": session.token}, response) + return { + "format": "application/json", + "data": {"token": session.token} + } + except InvalidDataException: self.handle_exception("Failed to create session") - self.send_json({"error": "Invalid input data!"}, response, 400) + return { + "format": "application/json", + "data": {"error": "Invalid input data!"}, + "status": 400 + } except Exception: self.handle_exception("Failed to create session") - response.status = 500 + return {"status": 500} - def read_session(self, request, response): + def read_session(self, token): try: - uri_parts = self.parse_uri(request) - token = uri_parts[2] session = self._sessions_manager.read_session(token) if session is None: - response.status = 404 - return + return {"status": 404} data = serialize_session(session) - del data["pending_tests"] - del data["running_tests"] - del data["malfunctioning_tests"] - del data["test_state"] - del data["date_started"] - del data["date_finished"] - del data["status"] - - self.send_json(data, response) + return { + "format": "application/json", + "data": { + "token": data["token"], + "tests": data["tests"], + "types": data["types"], + "timeouts": data["timeouts"], + "reference_tokens": data["reference_tokens"], + "user_agent": data["user_agent"], + "browser": data["browser"], + "is_public": data["is_public"], + "date_created": data["date_created"], + "labels": data["labels"] + } + } except Exception: self.handle_exception("Failed to read session") - response.status = 500 + return {"status": 500} - def read_session_status(self, request, response): + def read_sessions(self, query_parameters, uri_path): try: - uri_parts = self.parse_uri(request) - token = uri_parts[2] + index = 0 + if "index" in query_parameters: + index = int(query_parameters["index"]) + count = 10 + if "count" in query_parameters: + count = int(query_parameters["count"]) + expand = [] + if "expand" in query_parameters: + expand = query_parameters["expand"].split(",") + session_tokens = self._sessions_manager.read_sessions(index=index, count=count) + total_sessions = self._sessions_manager.get_total_sessions() + + embedded = {} + + for relation in expand: + if relation == "configuration": + configurations = [] + for token in session_tokens: + result = self.read_session(token) + if "status" in result and result["status"] != 200: + continue + configurations.append(result["data"]) + embedded["configuration"] = configurations + + if relation == "status": + statuses = [] + for token in session_tokens: + result = self.read_session_status(token) + if "status" in result and result["status"] != 200: + continue + statuses.append(result["data"]) + embedded["status"] = statuses + + uris = { + "self": uri_path, + "configuration": self._web_root + "api/sessions/{token}", + "status": self._web_root + "api/sessions/{token}/status" + } + + data = self.create_hal_list(session_tokens, uris, index, count, total=total_sessions) + + if len(embedded) > 0: + data["_embedded"] = embedded + + return { + "format": "application/json", + "data": data + } + except Exception: + self.handle_exception("Failed to read session") + return {"status": 500} + + def read_session_status(self, token): + try: session = self._sessions_manager.read_session_status(token) if session is None: - response.status = 404 - return + return {"status": 404} + data = serialize_session(session) - del data["tests"] - del data["pending_tests"] - del data["running_tests"] - del data["malfunctioning_tests"] - del data["types"] - del data["test_state"] - del data["last_completed_test"] - del data["user_agent"] - del data["timeouts"] - del data["browser"] - del data["is_public"] - del data["reference_tokens"] - del data["webhook_urls"] - - self.send_json(data, response) + return { + "format": "application/json", + "data": { + "token": data["token"], + "status": data["status"], + "date_started": data["date_started"], + "date_finished": data["date_finished"], + "expiration_date": data["expiration_date"] + } + } except Exception: self.handle_exception("Failed to read session status") - response.status = 500 + return {"status": 500} def read_public_sessions(self, request, response): try: @@ -144,26 +210,26 @@ class SessionsApiHandler(ApiHandler): tests = {} if "tests" in config: tests = config["tests"] - types = None + test_types = None if "types" in config: - types = config["types"] + test_types = config["types"] timeouts = {} if "timeouts" in config: timeouts = config["timeouts"] reference_tokens = [] if "reference_tokens" in config: reference_tokens = config["reference_tokens"] - webhook_urls = [] - if "webhook_urls" in config: - webhook_urls = config["webhook_urls"] + type = None + if "type" in config: + type = config["type"] self._sessions_manager.update_session_configuration( token, tests, - types, + test_types, timeouts, reference_tokens, - webhook_urls + type ) except NotFoundException: self.handle_exception("Failed to update session configuration") @@ -268,27 +334,56 @@ class SessionsApiHandler(ApiHandler): uri_parts = self.parse_uri(request) token = uri_parts[2] + query_parameters = self.parse_query_parameters(request) + last_event_number = None + if ("last_event" in query_parameters): + last_event_number = int(query_parameters["last_event"]) + event = threading.Event() - http_polling_client = HttpPollingClient(token, event) - self._event_dispatcher.add_session_client(http_polling_client) + http_polling_event_listener = HttpPollingEventListener(token, event) + event_listener_token = self._event_dispatcher.add_event_listener(http_polling_event_listener, last_event_number) event.wait() - message = http_polling_client.message + message = http_polling_event_listener.message self.send_json(data=message, response=response) + self._event_dispatcher.remove_event_listener(event_listener_token) except Exception: self.handle_exception("Failed to register event listener") response.status = 500 + def push_event(self, request, response): + try: + uri_parts = self.parse_uri(request) + token = uri_parts[2] + message = None + body = request.body.decode(u"utf-8") + if body != u"": + message = json.loads(body) + + self._event_dispatcher.dispatch_event( + token, + message["type"], + message["data"]) + except Exception: + self.handle_exception("Failed to push session event") + def handle_request(self, request, response): method = request.method uri_parts = self.parse_uri(request) + body = request.body + headers = request.headers + query_parameters = self.parse_query_parameters(request) + uri_path = request.url_parts.path + result = None # /api/sessions if len(uri_parts) == 2: if method == "POST": - self.create_session(request, response) - return + result = self.create_session(body, headers) + if method == "GET": + if self._read_sessions_enabled: + result = self.read_sessions(query_parameters, uri_path) # /api/sessions/ if len(uri_parts) == 3: @@ -300,8 +395,7 @@ class SessionsApiHandler(ApiHandler): if len(function) != TOKEN_LENGTH: self.find_session(request, response) return - self.read_session(request, response) - return + result = self.read_session(token=uri_parts[2]) if method == "PUT": self.update_session_configuration(request, response) return @@ -314,8 +408,7 @@ class SessionsApiHandler(ApiHandler): function = uri_parts[3] if method == "GET": if function == "status": - self.read_session_status(request, response) - return + result = self.read_session_status(token=uri_parts[2]) if function == "events": self.register_event_listener(request, response) return @@ -332,9 +425,32 @@ class SessionsApiHandler(ApiHandler): if function == "resume": self.resume_session(request, response) return + if function == "events": + self.push_event(request, response) + return if method == "PUT": if function == "labels": self.update_labels(request, response) return - response.status = 404 + if result is None: + response.status = 404 + return + + format = None + if "format" in result: + format = result["format"] + if format == "application/json": + data = None + if "data" in result: + data = result["data"] + status = 200 + if "status" in result: + status = result["status"] + self.send_json(data, response, status) + return + + status = 404 + if "status" in result: + status = result["status"] + response.status = status diff --git a/testing/web-platform/tests/tools/wave/network/api/tests_api_handler.py b/testing/web-platform/tests/tools/wave/network/api/tests_api_handler.py index cf62f706358b..5c797d93a249 100644 --- a/testing/web-platform/tests/tools/wave/network/api/tests_api_handler.py +++ b/testing/web-platform/tests/tools/wave/network/api/tests_api_handler.py @@ -1,4 +1,5 @@ import json + from urllib.parse import urlunsplit from .api_handler import ApiHandler @@ -8,6 +9,10 @@ from ...data.session import PAUSED, COMPLETED, ABORTED, PENDING, RUNNING DEFAULT_LAST_COMPLETED_TESTS_COUNT = 5 DEFAULT_LAST_COMPLETED_TESTS_STATUS = ["ALL"] +EXECUTION_MODE_AUTO = "auto" +EXECUTION_MODE_MANUAL = "manual" +EXECUTION_MODE_PROGRAMMATIC = "programmatic" + class TestsApiHandler(ApiHandler): def __init__( @@ -103,6 +108,8 @@ class TestsApiHandler(ApiHandler): test_timeout = self._tests_manager.get_test_timeout( test=test, session=session) + + test = self._sessions_manager.get_test_path_with_query(test, session) url = self._generate_test_url( test=test, token=token, @@ -250,11 +257,18 @@ class TestsApiHandler(ApiHandler): protocol = "https" port = self._wpt_ssl_port - query = "token={}&timeout={}&https_port={}&web_root={}".format( + test_query = "" + split = test.split("?") + if len(split) > 1: + test = split[0] + test_query = split[1] + + query = "token={}&timeout={}&https_port={}&web_root={}&{}".format( token, test_timeout, self._wpt_ssl_port, - self._web_root + self._web_root, + test_query ) return self._generate_url( diff --git a/testing/web-platform/tests/tools/wave/network/http_handler.py b/testing/web-platform/tests/tools/wave/network/http_handler.py index 0aabf3101dc7..5a663832d243 100644 --- a/testing/web-platform/tests/tools/wave/network/http_handler.py +++ b/testing/web-platform/tests/tools/wave/network/http_handler.py @@ -1,22 +1,30 @@ import http.client as httplib import sys +import logging import traceback +global logger +logger = logging.getLogger("wave-api-handler") + class HttpHandler(object): def __init__( self, - static_handler=None, - sessions_api_handler=None, - tests_api_handler=None, - results_api_handler=None, - http_port=None, - web_root=None + static_handler, + sessions_api_handler, + tests_api_handler, + results_api_handler, + devices_api_handler, + general_api_handler, + http_port, + web_root ): self.static_handler = static_handler self.sessions_api_handler = sessions_api_handler self.tests_api_handler = tests_api_handler self.results_api_handler = results_api_handler + self.general_api_handler = general_api_handler + self.devices_api_handler = devices_api_handler self._http_port = http_port self._web_root = web_root @@ -50,6 +58,7 @@ class HttpHandler(object): def handle_api(self, request, response): path = self._remove_web_root(request.request_path) + path = path.split("?")[0] api_name = path.split("/")[1] if api_name is None: @@ -64,6 +73,11 @@ class HttpHandler(object): if api_name == "results": self.results_api_handler.handle_request(request, response) return + if api_name == "devices": + self.devices_api_handler.handle_request(request, response) + return + + self.general_api_handler.handle_request(request, response) def handle_static_file(self, request, response): self.static_handler.handle_request(request, response) @@ -76,16 +90,19 @@ class HttpHandler(object): def _proxy(self, request, response): host = 'localhost' - port = str(self._http_port) + port = int(self._http_port) uri = request.url_parts.path uri = uri + "?" + request.url_parts.query - data = request.raw_input.read(int(request.headers.get('Content-Length', -1))) + content_length = request.headers.get('Content-Length') + data = "" + if content_length is not None: + data = request.raw_input.read(int(content_length)) method = request.method - # HTTPConnection expects values to be a comma separated string instead - # of a list of values. + headers = {} - for k, v in request.headers.items(): - headers[k] = b",".join(list(v)) + for key in request.headers: + value = request.headers[key] + headers[key.decode("utf-8")] = value.decode("utf-8") try: proxy_connection = httplib.HTTPConnection(host, port) @@ -96,8 +113,8 @@ class HttpHandler(object): response.status = proxy_response.status except IOError: + message = "Failed to perform proxy request" info = sys.exc_info() traceback.print_tb(info[2]) - print("Failed to perform proxy request: " + - info[0].__name__ + ": " + str(info[1].args[0])) + logger.error("{}: {}: {}".format(message, info[0].__name__, info[1].args[0])) response.status = 500 diff --git a/testing/web-platform/tests/tools/wave/network/static_handler.py b/testing/web-platform/tests/tools/wave/network/static_handler.py index 6f8af430604c..fdca361a5678 100644 --- a/testing/web-platform/tests/tools/wave/network/static_handler.py +++ b/testing/web-platform/tests/tools/wave/network/static_handler.py @@ -1,4 +1,5 @@ import os +from io import open class StaticHandler(object): @@ -13,14 +14,21 @@ class StaticHandler(object): file_path = request.request_path if self._web_root is not None: + if not file_path.startswith(self._web_root): + response.status = 404 + return file_path = file_path[len(self._web_root):] - if file_path == "." or file_path == "./" or file_path == "": + if file_path == "": file_path = "index.html" file_path = file_path.split("?")[0] file_path = os.path.join(self.static_dir, file_path) + if not os.path.exists(file_path): + response.status = 404 + return + headers = [] content_types = { diff --git a/testing/web-platform/tests/tools/wave/package-lock.json b/testing/web-platform/tests/tools/wave/package-lock.json new file mode 100644 index 000000000000..cb481388c2f6 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/package-lock.json @@ -0,0 +1,35 @@ +{ + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + } + } +} diff --git a/testing/web-platform/tests/tools/wave/package.json b/testing/web-platform/tests/tools/wave/package.json new file mode 100644 index 000000000000..f365f7c23845 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/package.json @@ -0,0 +1,7 @@ +{ + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "fs-extra": "^7.0.1" + } +} diff --git a/testing/web-platform/tests/tools/wave/requirements.txt b/testing/web-platform/tests/tools/wave/requirements.txt index 070837242fad..ea9f2810db41 100644 --- a/testing/web-platform/tests/tools/wave/requirements.txt +++ b/testing/web-platform/tests/tools/wave/requirements.txt @@ -1 +1,2 @@ -ua-parser==0.10.0 \ No newline at end of file +ua-parser==0.8.0 +python-dateutil==2.8.1 diff --git a/testing/web-platform/tests/tools/wave/resources/testharnessreport.js b/testing/web-platform/tests/tools/wave/resources/testharnessreport.js index a6c392f4b143..394b4bf3a834 100644 --- a/testing/web-platform/tests/tools/wave/resources/testharnessreport.js +++ b/testing/web-platform/tests/tools/wave/resources/testharnessreport.js @@ -15,272 +15,270 @@ */ /* - * If the query parameter token is available means that the test was loaded by + * If the query parameter token is available means that the test was loaded by * the WAVE test runner and the results need to be reported to the server using - * the provided token to identify the session associated this token. + * the provided token to identify the session associated this token. */ -console.log("ARDVAARD") if (location.search && location.search.indexOf("token=") != -1) { - var __WAVE__HOSTNAME = location.hostname; - var __WAVE__PORT = location.port; - var __WAVE__PROTOCOL = location.protocol.replace(/:/, ""); - var __WAVE__QUERY = location.search; - if (!__WAVE__QUERY) __WAVE__QUERY = "?"; - var match = __WAVE__QUERY.match(/https_port=(\d+)/); - var __HTTPS_PORT = parseInt(match && match[1] ? match[1] : 443); - match = __WAVE__QUERY.match(/timeout=(\d+)/); - var __WAVE__TIMEOUT = parseInt(match && match[1] ? match[1] : 65000); - match = __WAVE__QUERY.match(/web_root=(.+)/); - var __WAVE__WEB_ROOT = match && match[1] ? match[1] : "/wave/"; - console.log("\n\n\n\n\n") - console.log(match) - console.log(__WAVE__WEB_ROOT) - match = __WAVE__QUERY.match(/token=([^&]+)/); - var __WAVE__TOKEN = match ? match[1] : null; - var __WAVE__TEST = location.pathname; - var nextUrl = null; - var resultSent = false; - var screenConsole; + var __WAVE__HOSTNAME = location.hostname; + var __WAVE__PORT = location.port; + var __WAVE__PROTOCOL = location.protocol.replace(/:/, ""); + var __WAVE__QUERY = location.search; + var queryParameters = {}; + var keysAndValues = location.search.replace("?", "").split("&"); + for (var i = 0; i < keysAndValues.length; i++) { + var key = keysAndValues[i].split("=")[0]; + var value = keysAndValues[i].split("=")[1]; + queryParameters[key] = value; + } + var __HTTPS_PORT = parseInt(queryParameters["https_port"] || 443); + var __WAVE__TIMEOUT = parseInt(queryParameters["timeout"] || 65000); + var __WAVE__WEB_ROOT = queryParameters["web_root"] || "/_wave/"; + var __WAVE__TOKEN = queryParameters["token"] || null; + var __WAVE__TEST = location.pathname; + var nextUrl = null; + var resultSent = false; + var screenConsole; - try { - var documentRoot = document.body ? document.body : document.documentElement; - documentRoot.style["background-color"] = "#FFF"; - window.open = function () { - logToConsole( - "window.open() is overridden in testharnessreport.js and has not effect" - ); - var dummyWin = { - close: function () { - logToConsole( - "dummyWindow.close() in testharnessreport.js and has not effect" - ); - } - }; - return dummyWin; + try { + var documentRoot = document.body ? document.body : document.documentElement; + documentRoot.style["background-color"] = "#FFF"; + window.open = function () { + logToConsole( + "window.open() is overridden in testharnessreport.js and has not effect" + ); + var dummyWin = { + close: function () { + logToConsole( + "dummyWindow.close() in testharnessreport.js and has not effect" + ); + }, }; - window.close = function () { - logToConsole( - "window.close() is overridden in testharnessreport.js and has not effect" - ); + return dummyWin; + }; + window.close = function () { + logToConsole( + "window.close() is overridden in testharnessreport.js and has not effect" + ); + }; + } catch (err) {} + + setTimeout(function () { + loadNext(); + }, __WAVE__TIMEOUT); + + function logToConsole() { + var text = ""; + for (var i = 0; i < arguments.length; i++) { + text += arguments[i] + " "; + } + if (console && console.log) { + console.log(text); + } + if (screenConsole) { + try { + text = text.replace(/ /gm, " "); + text = text.replace(/\n/gm, "
"); + screenConsole.innerHTML += "
" + text; + } catch (error) { + screenConsole.innerText += "\n" + text; + } + } + } + + function dump_and_report_test_results(tests, status) { + var results_element = document.createElement("script"); + results_element.type = "text/json"; + results_element.id = "__testharness__results__"; + var test_results = tests.map(function (x) { + return { + name: x.name, + status: x.status, + message: x.message, + stack: x.stack, }; - } catch (err) {} + }); + var data = { + test: window.location.href, + tests: test_results, + status: status.status, + message: status.message, + stack: status.stack, + }; + results_element.textContent = JSON.stringify(data); - setTimeout(function () { - loadNext(); - }, __WAVE__TIMEOUT); + // To avoid a HierarchyRequestError with XML documents, ensure that 'results_element' + // is inserted at a location that results in a valid document. + var parent = document.body + ? document.body // is required in XHTML documents + : document.documentElement; // fallback for optional in HTML5, SVG, etc. - function logToConsole() { - var text = ""; - for (var i = 0; i < arguments.length; i++) { - text += arguments[i] + " "; - } - if (console && console.log) { - console.log(text); - } - if (screenConsole) { - try { - text = text.replace(/ /gm, " "); - text = text.replace(/\n/gm, "
"); - screenConsole.innerHTML += "
" + text; - } catch (error) { - screenConsole.innerText += "\n" + text; - } - } - } - - function dump_and_report_test_results(tests, status) { - var results_element = document.createElement("script"); - results_element.type = "text/json"; - results_element.id = "__testharness__results__"; - var test_results = tests.map(function (x) { - return { - name: x.name, - status: x.status, - message: x.message, - stack: x.stack - }; - }); - var data = { - test: window.location.href, - tests: test_results, - status: status.status, - message: status.message, - stack: status.stack - }; - results_element.textContent = JSON.stringify(data); - - // To avoid a HierarchyRequestError with XML documents, ensure that 'results_element' - // is inserted at a location that results in a valid document. - var parent = document.body ? - document.body // is required in XHTML documents - : - document.documentElement; // fallback for optional in HTML5, SVG, etc. - - parent.appendChild(results_element); + parent.appendChild(results_element); + screenConsole = document.getElementById("console"); + if (!screenConsole) { screenConsole = document.createElement("div"); screenConsole.setAttribute("id", "console"); screenConsole.setAttribute("style", "font-family: monospace; padding: 5px"); parent.appendChild(screenConsole); - window.onerror = logToConsole; + } + window.onerror = logToConsole; - finishWptTest(data); - } + finishWptTest(data); + } - function finishWptTest(data) { - logToConsole("Creating result ..."); - data.test = __WAVE__TEST; - createResult( - __WAVE__TOKEN, - data, - function () { - logToConsole("Result created."); - loadNext(); - }, - function () { - logToConsole("Failed to create result."); - logToConsole("Trying alternative method ..."); - createResultAlt(__WAVE__TOKEN, data); - } - ); - } - - function loadNext() { - logToConsole("Loading next test ..."); - if (window.gc) { - // This is using a chromium/v8 feature, enable it by - // --js-flags="expose-gc". - // TODO: Replace this with TestUtils.gc() once/if that is available - // in browsers: - // https://jgraham.github.io/browser-test/#the-testutils-object - logToConsole("Forcing garbage collection using window.gc()"); - window.gc(); + function finishWptTest(data) { + logToConsole("Creating result ..."); + data.test = __WAVE__TEST; + createResult( + __WAVE__TOKEN, + data, + function () { + logToConsole("Result created."); + loadNext(); + }, + function () { + logToConsole("Failed to create result."); + logToConsole("Trying alternative method ..."); + createResultAlt(__WAVE__TOKEN, data); } - readNextTest( - __WAVE__TOKEN, - function (url) { - logToConsole("Redirecting to " + url); - location.href = url; - }, - function () { - logToConsole("Could not load next test."); - logToConsole("Trying alternative method ..."); - readNextAlt(__WAVE__TOKEN); - } - ); - } + ); + } - function readNextTest(token, onSuccess, onError) { - sendRequest( - "GET", - "api/tests/" + token + "/next", - null, - null, - function (response) { - var jsonObject = JSON.parse(response); - onSuccess(jsonObject.next_test); - }, - onError - ); - } - - function readNextAlt(token) { - location.href = getWaveUrl("next.html?token=" + token); - } - - function createResult(token, result, onSuccess, onError) { - sendRequest( - "POST", - "api/results/" + token, { - "Content-Type": "application/json" - }, - JSON.stringify(result), - function () { - onSuccess(); - }, - onError - ); - } - - function createResultAlt(token, result) { - location.href = __WAVE__WEB_ROOT + "submitresult.html" + - "?token=" + token + - "&result=" + encodeURIComponent(JSON.stringify(result)); - } - - function sendRequest(method, uri, headers, data, onSuccess, onError) { - var url = getWaveUrl(uri); - var xhr = new XMLHttpRequest(); - xhr.addEventListener("load", function () { - onSuccess(xhr.response); - }); - xhr.addEventListener("error", function () { - if (onError) onError(); - }); - logToConsole("Sending", method, 'request to "' + url + '"'); - xhr.open(method, url, true); - if (headers) { - for (var header in headers) { - xhr.setRequestHeader(header, headers[header]); - } + function loadNext() { + logToConsole("Loading next test ..."); + readNextTest( + __WAVE__TOKEN, + function (url) { + logToConsole("Redirecting to " + url); + location.href = url; + }, + function () { + logToConsole("Could not load next test."); + logToConsole("Trying alternative method ..."); + readNextAlt(__WAVE__TOKEN); } - xhr.send(data); - } + ); + } - function getWaveUrl(uri) { - var url = __WAVE__WEB_ROOT + uri; - console.log(url) - return url; - } + function readNextTest(token, onSuccess, onError) { + sendRequest( + "GET", + "api/tests/" + token + "/next", + null, + null, + function (response) { + var jsonObject = JSON.parse(response); + onSuccess(jsonObject.next_test); + }, + onError + ); + } - add_completion_callback(dump_and_report_test_results); + function readNextAlt(token) { + location.href = + location.protocol + + "//" + + location.host + + getWaveUrl("next.html?token=" + token); + } + function createResult(token, result, onSuccess, onError) { + sendRequest( + "POST", + "api/results/" + token, + { + "Content-Type": "application/json", + }, + JSON.stringify(result), + function () { + onSuccess(); + }, + onError + ); + } + + function createResultAlt(token, result) { + location.href = + __WAVE__WEB_ROOT + + "submitresult.html" + + "?token=" + + token + + "&result=" + + encodeURIComponent(JSON.stringify(result)); + } + + function sendRequest(method, uri, headers, data, onSuccess, onError) { + var url = getWaveUrl(uri); + url = location.protocol + "//" + location.host + url; + var xhr = new XMLHttpRequest(); + xhr.addEventListener("load", function () { + onSuccess(xhr.response); + }); + xhr.addEventListener("error", function () { + if (onError) onError(); + }); + logToConsole("Sending", method, 'request to "' + url + '"'); + xhr.open(method, url, true); + if (headers) { + for (var header in headers) { + xhr.setRequestHeader(header, headers[header]); + } + } + xhr.send(data); + } + + function getWaveUrl(uri) { + var url = __WAVE__WEB_ROOT + uri; + return url; + } + + add_completion_callback(dump_and_report_test_results); } else { - function dump_test_results(tests, status) { - var results_element = document.createElement("script"); - results_element.type = "text/json"; - results_element.id = "__testharness__results__"; - var test_results = tests.map(function (x) { - return { - name: x.name, - status: x.status, - message: x.message, - stack: x.stack - } - }); - var data = { - test: window.location.href, - tests: test_results, - status: status.status, - message: status.message, - stack: status.stack + function dump_test_results(tests, status) { + var results_element = document.createElement("script"); + results_element.type = "text/json"; + results_element.id = "__testharness__results__"; + var test_results = tests.map(function (x) { + return { + name: x.name, + status: x.status, + message: x.message, + stack: x.stack, }; - results_element.textContent = JSON.stringify(data); + }); + var data = { + test: window.location.href, + tests: test_results, + status: status.status, + message: status.message, + stack: status.stack, + }; + results_element.textContent = JSON.stringify(data); - // To avoid a HierarchyRequestError with XML documents, ensure that 'results_element' - // is inserted at a location that results in a valid document. - var parent = document.body - ? document.body // is required in XHTML documents - : document.documentElement; // fallback for optional in HTML5, SVG, etc. + // To avoid a HierarchyRequestError with XML documents, ensure that 'results_element' + // is inserted at a location that results in a valid document. + var parent = document.body + ? document.body // is required in XHTML documents + : document.documentElement; // fallback for optional in HTML5, SVG, etc. - parent.appendChild(results_element); - } + parent.appendChild(results_element); + } - add_completion_callback(dump_test_results); + add_completion_callback(dump_test_results); - /* If the parent window has a testharness_properties object, - * we use this to provide the test settings. This is used by the - * default in-browser runner to configure the timeout and the - * rendering of results - */ - try { - if (window.opener && "testharness_properties" in window.opener) { - /* If we pass the testharness_properties object as-is here without - * JSON stringifying and reparsing it, IE fails & emits the message - * "Could not complete the operation due to error 80700019". - */ - setup(JSON.parse(JSON.stringify(window.opener.testharness_properties))); - } - } catch (e) {} - // vim: set expandtab shiftwidth=4 tabstop=4: + /* If the parent window has a testharness_properties object, + * we use this to provide the test settings. This is used by the + * default in-browser runner to configure the timeout and the + * rendering of results + */ + try { + if (window.opener && "testharness_properties" in window.opener) { + /* If we pass the testharness_properties object as-is here without + * JSON stringifying and reparsing it, IE fails & emits the message + * "Could not complete the operation due to error 80700019". + */ + setup(JSON.parse(JSON.stringify(window.opener.testharness_properties))); + } + } catch (e) {} } diff --git a/testing/web-platform/tests/tools/wave/test/WAVE Local.postman_environment.json b/testing/web-platform/tests/tools/wave/test/WAVE Local.postman_environment.json new file mode 100644 index 000000000000..b1a6a089ab53 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/test/WAVE Local.postman_environment.json @@ -0,0 +1,34 @@ +{ + "id": "37be8ec4-7855-4554-867e-7a5d2a4f99e6", + "name": "WAVE Local", + "values": [ + { + "key": "host", + "value": "web-platform.test", + "enabled": true + }, + { + "key": "port", + "value": "8000", + "enabled": true + }, + { + "key": "protocol", + "value": "http", + "enabled": true + }, + { + "key": "web_root", + "value": "_wave", + "enabled": true + }, + { + "key": "device_timeout", + "value": "60000", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2020-05-25T12:12:37.098Z", + "_postman_exported_using": "Postman/7.25.0" +} \ No newline at end of file diff --git a/testing/web-platform/tests/tools/wave/test/WAVE Server REST API Tests.postman_collection.json b/testing/web-platform/tests/tools/wave/test/WAVE Server REST API Tests.postman_collection.json new file mode 100644 index 000000000000..93cbedb50486 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/test/WAVE Server REST API Tests.postman_collection.json @@ -0,0 +1,9833 @@ +{ + "info": { + "_postman_id": "ccd6117a-6d61-4617-a6a1-7115db4d4d92", + "name": "WAVE Server REST API Tests", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Read Available Tests", + "item": [ + { + "name": "Read Available Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "051bef94-5544-4ddb-9d85-167677ebecb2", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var availableTests = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(typeof availableTests).to.equal(\"object\");", + " for (var api of Object.keys(availableTests)) {", + " pm.expect(availableTests[api]).to.be.an.instanceof(Array);", + " var apiRegExp = new RegExp(\"^/\" + api, \"i\");", + " for (var test of availableTests[api]) {", + " pm.expect(test).to.match(apiRegExp);", + " }", + " }", + "});", + "", + "var includedTests = [];", + "var excludedTests = [];", + "var specialTimeoutTest = \"\";", + "", + "var apis = Object.keys(availableTests);", + "for(var api of apis) {", + " if (availableTests[api].length > 50) {", + " var subDirs = availableTests[api].map(test => test.split(\"/\").filter(part => !!part).join(\"/\").split(\"/\")[1]).reduce((acc, curr) => acc.indexOf(curr) === -1 ? acc.concat([curr]) : acc, []);", + " if (subDirs.length > 2) {", + " includedTests.push(\"/\" + api);", + " excludedTests.push(\"/\" + api + \"/\" + subDirs[0]);", + " specialTimeoutTest = availableTests[api][availableTests[api].length - 1];", + " break;", + " }", + " ", + " }", + "}", + "", + "pm.globals.set(\"available_tests\", JSON.stringify(availableTests));", + "pm.globals.set(\"included_tests\", JSON.stringify(includedTests));", + "pm.globals.set(\"excluded_tests\", JSON.stringify(excludedTests));", + "pm.globals.set(\"special_timeout_test\", specialTimeoutTest.replace(\".\", \"\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Create and Read Sessions", + "item": [ + { + "name": "Start expiring session remove expiration date", + "item": [ + { + "name": "Create Session With Expiration", + "event": [ + { + "listen": "test", + "script": { + "id": "a8bf3e41-a7df-4c6b-8a20-3a1e6d8a51d9", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "4370546f-b08c-4f77-9bd9-1cd14400665e", + "exec": [ + "var expirationDate = Date.now() + 10000;", + "pm.globals.set(\"expiration_date\", expirationDate);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"expiration_date\": {{expiration_date}}\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "3dcb5b6c-9151-49f7-a1a6-74475927b304", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"JSON structure contains expiration date\", function () {", + " pm.expect(jsonData).to.have.property(\"expiration_date\");", + "});", + "", + "var expirationDate = pm.globals.get(\"expiration_date\");", + "", + "pm.test(\"Expiration date is as specified\", function () {", + " pm.expect(Date.parse(jsonData.expiration_date)).to.equal(expirationDate);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + }, + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "8d05578a-e2d6-41e9-a2a4-aa7d92bfbbce", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "4c5e3ba4-e27b-4341-8cf8-74ed047a8747", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"JSON structure contains expiration date\", function () {", + " pm.expect(jsonData).to.have.property(\"expiration_date\");", + "});", + "", + "pm.test(\"Expiration date is null\", function () {", + " pm.expect(jsonData.expiration_date).to.be.null;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + }, + { + "name": "Clean Up", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Read Public Sessions", + "item": [ + { + "name": "Read Public Sessions", + "event": [ + { + "listen": "test", + "script": { + "id": "1081afd8-a772-4565-b03d-b58f773cbb65", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Response is JSON Array\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.be.an.instanceof(Array);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/public", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "public" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Find Session", + "item": [ + { + "name": "Create Session No Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "268e8a31-87bb-4ec5-81d5-87dc74096828", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Find Session Token", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "625fcc2a-f7b1-403c-b5c5-56db7c5bcee5", + "exec": [ + "const token = pm.globals.get(\"session_token\");", + "pm.globals.set(\"session_token_fragment\", token.split(\"-\").shift());" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "8e04212f-e259-413f-98ee-e366cd3adfdd", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const sessionToken = pm.globals.get(\"session_token\");", + "", + "pm.test(\"Found token is original token\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.token).to.equal(sessionToken);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token_fragment}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token_fragment}}" + ] + } + }, + "response": [] + }, + { + "name": "Clean Up", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Find Session Too Short Fragment", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "6f8429e3-9c12-423c-baaf-8f8de0e4ea49", + "exec": [ + "const token = pm.globals.get(\"session_token\");", + "pm.globals.set(\"session_token_fragment\", token.split(\"-\").shift());" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "id": "7f6ef567-274e-4f9d-b7fc-50f8240547e9", + "exec": [ + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/1234567", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "1234567" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Read Next Test", + "item": [ + { + "name": "Clean Up", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Next Test of Invalid Session", + "event": [ + { + "listen": "test", + "script": { + "id": "11f5c620-dc3b-4a8c-8d2f-f9663025c79f", + "exec": [ + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "next" + ] + } + }, + "response": [] + }, + { + "name": "Create Session \\w Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "c67cde7e-7237-479c-a0c6-b84a533c3b1e", + "exec": [ + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "24b38d64-5c24-415f-b6f6-f8d252d69ac8", + "exec": [ + "var automaticTimeout = 120000;", + "var manualTimeout = 1000000;", + "var specialTimeout = 2000;", + "", + "pm.globals.set(\"automatic_timeout\", automaticTimeout);", + "pm.globals.set(\"manual_timeout\", manualTimeout);", + "pm.globals.set(\"special_timeout\", specialTimeout);", + "", + "const availableTests = JSON.parse(pm.globals.get(\"available_tests\"));", + "const test1 = availableTests[Object.keys(availableTests)[0]][0];", + "", + "pm.globals.set(\"single_test_1\", test1);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tests\": {\n \"include\": [\"{{single_test_1}}\"]\n },\n \"types\": [\n \"automatic\",\n \"manual\"\n ]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read Next Test of Pending Session", + "event": [ + { + "listen": "test", + "script": { + "id": "0806e4e0-bd1b-40ba-a7d5-74d41473a141", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const nextTest = jsonData.next_test;", + "const test = \"/\" + nextTest.split(\"/\").slice(3).join(\"/\").split(\"?\")[0];", + "", + "const web_root = pm.environment.get(\"web_root\");", + "", + "pm.test(\"Returned test is new session page\", function () {", + " pm.expect(test).to.equal(\"/\" + web_root + \"/newsession.html\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "next" + ] + } + }, + "response": [] + }, + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "9b1f2dec-9949-49ff-b466-f602b11dde5d", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Read Next Test of Running Session", + "event": [ + { + "listen": "test", + "script": { + "id": "7fea2977-5ccb-49ee-8f07-79a7f2245f9c", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const nextTest = jsonData.next_test;", + "const parameters = nextTest.split(\"?\")[1].split(\"&\");", + "let test = parameters.find(parameter => parameter.split(\"=\")[0] === \"test_url\").split(\"=\")[1];", + "test = decodeURIComponent(test);", + "test = \"/\" + test.split(\"/\").slice(3).join(\"/\").split(\"?\")[0];", + "const api = test.split(\"/\").filter(part => !!part)[0]", + "const availableTests = JSON.parse(pm.globals.get(\"available_tests\"));", + "", + "pm.test(\"Returned test is valid test\", function () {", + " pm.expect(availableTests).to.have.property(api);", + " pm.expect(availableTests[api]).to.contain(test)", + "});", + "", + "", + "setTimeout(function () {}, 1000);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "next" + ] + } + }, + "response": [] + }, + { + "name": "Create Result", + "event": [ + { + "listen": "test", + "script": { + "id": "7cc27ee8-2f06-4e25-98fb-9872c6cf0d93", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"test\": \"{{current_test}}\",\n \"status\": \"OK\",\n \"message\": null,\n \"subtests\": [\n {\n \"name\": \"Subtest testing feature xy\",\n \"status\": \"FAIL\",\n \"message\": \"Error message\"\n }\n ]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Next Test of Completed Session", + "event": [ + { + "listen": "test", + "script": { + "id": "0a73a5eb-3edb-4d15-924d-260dd22bd831", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const nextTest = jsonData.next_test;", + "const test = \"/\" + nextTest.split(\"/\").slice(3).join(\"/\").split(\"?\")[0];", + "", + "const web_root = pm.environment.get(\"web_root\");", + "", + "pm.test(\"Returned test is new session page\", function () {", + " pm.expect(test).to.equal(\"/\" + web_root + \"/finish.html\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "next" + ] + } + }, + "response": [] + }, + { + "name": "Clean Up", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Create Session \\w Configuration Copy", + "event": [ + { + "listen": "test", + "script": { + "id": "94877ec6-70ea-4c78-acb5-20060a535653", + "exec": [ + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "dcc29092-b7a1-40fc-8359-ecc655dba482", + "exec": [ + "var automaticTimeout = 120000;", + "var manualTimeout = 1000000;", + "var specialTimeout = 2000;", + "", + "pm.globals.set(\"automatic_timeout\", automaticTimeout);", + "pm.globals.set(\"manual_timeout\", manualTimeout);", + "pm.globals.set(\"special_timeout\", specialTimeout);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tests\": {\n \"include\": {{included_tests}},\n \"exclude\": {{excluded_tests}}\n },\n \"types\": [\n \"automatic\",\n \"manual\"\n ],\n \"timeouts\": {\n \"automatic\": 1000\n },\n \"labels\": [\"label1\", \"label2\"]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "8f6e2bf2-6c1e-434e-83c5-9bcc57880692", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Pause Session", + "event": [ + { + "listen": "test", + "script": { + "id": "a0a80c05-a2ca-430a-8bed-46ad7978c684", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/pause", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "pause" + ] + } + }, + "response": [] + }, + { + "name": "Read Next Test of Aborted Session", + "event": [ + { + "listen": "test", + "script": { + "id": "00d823e0-2059-4cab-81d6-8d1de9e5b62a", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const nextTest = jsonData.next_test;", + "const test = \"/\" + nextTest.split(\"/\").slice(3).join(\"/\").split(\"?\")[0];", + "", + "const web_root = pm.environment.get(\"web_root\");", + "", + "pm.test(\"Returned test is new session page\", function () {", + " pm.expect(test).to.equal(\"/\" + web_root + \"/pause.html\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "next" + ] + } + }, + "response": [] + }, + { + "name": "Stop Session", + "event": [ + { + "listen": "test", + "script": { + "id": "b8211c13-667d-4970-aedc-6398c25cc40c", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/stop", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "stop" + ] + } + }, + "response": [] + }, + { + "name": "Read Next Test of Aborted Session", + "event": [ + { + "listen": "test", + "script": { + "id": "8aad813d-53fe-49fd-bdfc-f16ce2233ae2", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const nextTest = jsonData.next_test;", + "const test = \"/\" + nextTest.split(\"/\").slice(3).join(\"/\").split(\"?\")[0];", + "", + "const web_root = pm.environment.get(\"web_root\");", + "", + "pm.test(\"Returned test is new session page\", function () {", + " pm.expect(test).to.equal(\"/\" + web_root + \"/finish.html\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "next" + ] + } + }, + "response": [] + }, + { + "name": "Clean Up", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Control Session", + "item": [ + { + "name": "Setup", + "item": [ + { + "name": "Create Session No Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "305cb915-8496-4577-b17a-e8189d66c3d1", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Pause Pending Session", + "item": [ + { + "name": "Pause Session", + "event": [ + { + "listen": "test", + "script": { + "id": "9d903165-9667-43b0-b210-a950d0fa1fa2", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/pause", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "pause" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "0307919b-6630-48ff-ac18-10c0a47ea254", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is pending\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"pending\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Start Pending Session", + "item": [ + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "f66408d5-5438-4d4f-9bfc-6d24b15e5a90", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "bae5542d-446b-4064-88ff-cc355a8f4f62", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "pm.test(\"Status is running\", function () {", + " pm.expect(jsonData.status).to.equal(\"running\");", + "});", + "", + "pm.test(\"Start date is set\", function () {", + " pm.expect(Date.parse(jsonData.date_started)).to.be.below(Date.now());", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Start Running Session", + "item": [ + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "3b386952-8a37-471a-9192-f28974d975a3", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "8e56d842-325b-4356-ae1a-2465e9efe188", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is running\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"running\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Pause Running Session", + "item": [ + { + "name": "Pause Session", + "event": [ + { + "listen": "test", + "script": { + "id": "a183cb0b-31f0-48f6-9c20-605a16488d7d", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/pause", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "pause" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "f2610594-813a-4079-afae-1510486efab4", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is paused\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"paused\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Pause Paused Session", + "item": [ + { + "name": "Pause Session", + "event": [ + { + "listen": "test", + "script": { + "id": "6b8ec98a-ad91-4d5c-aa07-6b3cc1255902", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/pause", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "pause" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "91a38eb8-a501-457f-a9f4-b63221c957f6", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is paused\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"paused\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Start Paused Session", + "item": [ + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "6e3a8c64-04cb-4c4f-b23d-64655f6e4d22", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "dcf7f6f4-6eef-4a90-a63d-df70446eb209", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is running\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"running\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Stop Running Session", + "item": [ + { + "name": "Stop Session", + "event": [ + { + "listen": "test", + "script": { + "id": "bee7a70d-af1f-4ad1-9c40-7abdbcc983ae", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/stop", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "stop" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "217ed267-1ddb-496a-a7c5-6e29bad97c60", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "pm.test(\"Status is aborted\", function () {", + " pm.expect(jsonData.status).to.equal(\"aborted\");", + "});", + "", + "pm.test(\"Finish date is set\", function () {", + " pm.expect(Date.parse(jsonData.date_finished)).to.be.below(Date.now());", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Stop Aborted Session", + "item": [ + { + "name": "Stop Session", + "event": [ + { + "listen": "test", + "script": { + "id": "a255ea61-5c5a-4f0d-8a67-c244510a608c", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/stop", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "stop" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "9cc7599b-d378-4b04-bada-9d76f6ca610f", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is aborted\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"aborted\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "81e44e3f-5d91-42a7-b0ab-4704eb121868", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "a44180cb-3b13-421b-b146-82901f2b45bd", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Start Aborted Session", + "item": [ + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "47fb1fa9-4849-4ea9-b36e-fcec8decf62e", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "1923ced4-4361-4a4a-bf7c-4fcdf3df50f6", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is aborted\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"aborted\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "226043e5-1a81-4d36-aa7f-67b10bf407af", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "840bf970-0984-452e-9076-b6f66d0b4511", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Pause Aborted Session", + "item": [ + { + "name": "Pause Session", + "event": [ + { + "listen": "test", + "script": { + "id": "88c4072f-538b-4d68-9d61-f0fb017e544c", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/pause", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "pause" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "2ae00eb3-49e8-4487-b27f-e7c7955fe4f4", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is aborted\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"aborted\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "70e0c783-c405-481c-b9c8-5c8bee392f97", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "85c45e3b-fae6-4a90-afd0-97678769e32c", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Setup", + "item": [ + { + "name": "Clean Up", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Create Session No Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "df4a00d1-d4f8-4b0b-892d-21528165a2cf", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "4fa59617-c922-4826-abc6-6d910e918ea4", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "d83f449d-4a2e-41c8-b2cd-5cf0cd1c404e", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Stop Pending Session", + "item": [ + { + "name": "Stop Session", + "event": [ + { + "listen": "test", + "script": { + "id": "98da2b86-4f9f-47dd-afc9-22ce96e73e84", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/stop", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "stop" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "6d8a8202-c85d-4ada-b443-45108a372aea", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is aborted\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"aborted\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Setup", + "item": [ + { + "name": "Clean Up", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Create Session No Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "0f9df486-91cd-46fc-a711-5cf9bebb2706", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "80ee91fd-98ca-4b8e-83b6-32bf3e55a295", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Pause Session", + "event": [ + { + "listen": "test", + "script": { + "id": "47f4d26a-84c2-4980-984d-dac0e95503e1", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/pause", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "pause" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "6bd9e85f-eb73-47be-83b0-01be77868899", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "43ebfadb-dc68-4c41-b34e-462e427dc2ef", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Stop Paused Session Copy", + "item": [ + { + "name": "Stop Session", + "event": [ + { + "listen": "test", + "script": { + "id": "029ec84b-e41b-4744-8745-58c50e52bb11", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/stop", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "stop" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "071d757c-41aa-4b52-9ab4-6c2afd6ed2af", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is aborted\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"aborted\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "305720a0-2c84-43d3-8fe5-55f8fdf511d8", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "84e4ab04-5acc-4428-a81a-c0b0c53b5fd9", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Setup", + "item": [ + { + "name": "Clean Up", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Create Session One Test", + "event": [ + { + "listen": "test", + "script": { + "id": "b7a9d459-9b39-40b2-abdc-fd8fe8b21d61", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "fcbfb0e3-a10d-4ff8-afed-f5f3c9d52090", + "exec": [ + "const availableTests = JSON.parse(pm.globals.get(\"available_tests\"));", + "const test = availableTests[Object.keys(availableTests)[0]][0]", + "", + "pm.globals.set(\"single_test\", test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tests\": {\n \"include\": [\"{{single_test}}\"]\n }\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "937ef61d-d6d6-4e24-bc3a-696a23cc95d6", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Read Next Test of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "29f0554b-66f5-4e26-80ff-39b3faba920a", + "exec": [ + "const response = pm.response.json();", + "const nextTest = response.next_test;", + "pm.globals.set(\"current_test_url\", nextTest);", + "if (!nextTest) return;", + "const parameters = nextTest.split(\"?\")[1].split(\"&\");", + "let test = parameters.find(parameter => parameter.split(\"=\")[0] === \"test_url\").split(\"=\")[1];", + "test = decodeURIComponent(test);", + "test = \"/\" + test.split(\"/\").slice(3).join(\"/\").split(\"?\")[0];", + "pm.globals.set(\"current_test\", test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "next" + ] + } + }, + "response": [] + }, + { + "name": "Create Result", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"test\": \"{{current_test}}\",\n \"status\": \"OK\",\n \"message\": null,\n \"subtests\": [\n {\n \"name\": \"Subtest testing feature xy\",\n \"status\": \"FAIL\",\n \"message\": \"Error message\"\n }\n ]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "9bf66427-6f39-4f0a-b065-1849d679eee6", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "8d1f3837-1f05-47e4-897c-b7c74da9bd43", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Start Completed Session", + "item": [ + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "9bf0fb70-24a0-4136-99fe-8e0e488afd1e", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "7e3184bf-1741-44c8-8cd3-cff30c2deca1", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is completed\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"completed\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Pause Completed Session", + "item": [ + { + "name": "Pause Session", + "event": [ + { + "listen": "test", + "script": { + "id": "e323c6af-d4e5-4a05-b203-09ba67f77d28", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/pause", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "pause" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "40cd04ce-850f-4903-bc24-2e62a4df98fe", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is completed\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"completed\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Stop Completed Session", + "item": [ + { + "name": "Stop Session", + "event": [ + { + "listen": "test", + "script": { + "id": "db7d5209-37b2-46bd-88ab-ce4e241401b2", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/stop", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "stop" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "477dc63d-0b43-4226-b86e-163e2de92b67", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status is completed\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.status).to.equal(\"completed\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Clean Up", + "item": [ + { + "name": "Clean Up", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Update and Read Sessions", + "item": [ + { + "name": "Create Default", + "item": [ + { + "name": "Create Session No Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "b923d95e-a7b4-49d4-ab2a-1f435c454387", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "81f36759-7ae2-42b9-81d2-79f57046b46e", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"tests\");", + " pm.expect(typeof jsonData.tests).to.equal(\"object\");", + " pm.expect(jsonData.tests).to.have.property(\"include\");", + " pm.expect(jsonData.tests.include).to.be.an.instanceof(Array);", + " pm.expect(jsonData.tests).to.have.property(\"exclude\");", + " pm.expect(jsonData.tests.exclude).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"types\");", + " pm.expect(jsonData.types).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"user_agent\");", + " pm.expect(typeof jsonData.user_agent).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"timeouts\");", + " pm.expect(typeof jsonData.timeouts).to.equal(\"object\")", + " pm.expect(jsonData.timeouts).to.have.property(\"automatic\");", + " pm.expect(typeof jsonData.timeouts.automatic).to.equal(\"number\");", + " pm.expect(jsonData.timeouts).to.have.property(\"manual\");", + " pm.expect(typeof jsonData.timeouts.manual).to.equal(\"number\");", + " pm.expect(jsonData).to.have.property(\"browser\");", + " pm.expect(typeof jsonData.browser).to.equal(\"object\");", + " pm.expect(jsonData.browser).to.have.property(\"name\");", + " pm.expect(typeof jsonData.browser.name).to.equal(\"string\");", + " pm.expect(jsonData.browser).to.have.property(\"version\");", + " pm.expect(typeof jsonData.browser.version).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"reference_tokens\");", + " pm.expect(jsonData.reference_tokens).to.be.an.instanceof(Array);", + "});", + "", + "pm.test(\"Configuration is default\", function () {", + " pm.expect(jsonData.token).to.match(tokenRegex);", + " pm.expect(jsonData.tests.include).to.include(\"/\");", + " pm.expect(jsonData.types).to.include(\"automatic\");", + " pm.expect(jsonData.types).to.include(\"manual\");", + " pm.expect(jsonData.user_agent).to.include(\"PostmanRuntime\");", + " pm.expect(jsonData.timeouts.automatic).to.equal(60000);", + " pm.expect(jsonData.timeouts.manual).to.equal(300000);", + " pm.expect(jsonData.browser.name).to.equal(\"Other\");", + " pm.expect(jsonData.browser.version).to.equal(\"0\");", + " pm.expect(jsonData.is_public).to.equal(false);", + " pm.expect(jsonData.reference_tokens).to.be.empty;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "2873d554-9816-41f0-9051-fb2cb1272a76", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"status\");", + " pm.expect(typeof jsonData.status).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"expiration_date\");", + " pm.expect(jsonData.expiration_date).to.be.null;", + " pm.expect(jsonData).to.have.property(\"date_started\");", + " pm.expect(jsonData.date_started).to.satisfy(value => !value || typeof value === \"number\");", + " pm.expect(jsonData).to.have.property(\"date_finished\");", + " pm.expect(jsonData.date_finished).to.satisfy(value => !value || typeof value === \"number\");", + "});", + "", + "pm.test(\"Session status is pending\", function () {", + " pm.expect(jsonData.status).to.equal(\"pending\");", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + }, + { + "name": "Read Tests of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "53605764-d4f0-4b32-9a30-67e84dd104d9", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"pending_tests\");", + " pm.expect(typeof jsonData.pending_tests).to.equal(\"object\");", + " pm.expect(jsonData).to.have.property(\"running_tests\");", + " pm.expect(typeof jsonData.running_tests).to.equal(\"object\");", + "});", + "", + "pm.test(\"All tests are pending tests\", function () {", + " pm.expect(Object.keys(jsonData.pending_tests)).to.not.have.lengthOf(0);", + " pm.expect(Object.keys(jsonData.running_tests)).to.have.lengthOf(0);", + "})", + "", + "console.log(pm.globals.get(\"available_tests\"))", + "const availableTests = JSON.parse(pm.globals.get(\"available_tests\"));", + "", + "pm.test(\"All available tests are part of the session\", function () {", + " for (var api of Object.keys(jsonData.pending_tests)) {", + " for (var test of jsonData.pending_tests[api]) {", + " pm.expect(availableTests[api]).to.include(test);", + " }", + " }", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "cf165ae9-1c44-483c-8fe4-bd7cdaa20710", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "6938d456-afd5-431b-a95f-a2c45a3ac479", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Update With Configuration", + "item": [ + { + "name": "Update Session", + "event": [ + { + "listen": "test", + "script": { + "id": "657be5b3-4a99-4415-a1ef-9dfdcf541e46", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "093a0019-a84c-44fc-b890-36369d51b7d5", + "exec": [ + "var automaticTimeout = 120000;", + "var manualTimeout = 1000000;", + "var specialTimeout = 2000;", + "", + "pm.globals.set(\"automatic_timeout\", automaticTimeout);", + "pm.globals.set(\"manual_timeout\", manualTimeout);", + "pm.globals.set(\"special_timeout\", specialTimeout);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tests\": {\n \"include\": {{included_tests}},\n \"exclude\": {{excluded_tests}}\n },\n \"types\": [\n \"automatic\",\n \"manual\"\n ],\n \"timeouts\": {\n \"automatic\": {{automatic_timeout}},\n \"manual\": {{manual_timeout}},\n \"{{special_timeout_test}}\": {{special_timeout}}\n }\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "4ac55601-9bdf-41f8-9d59-ff1ee20fa471", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"tests\");", + " pm.expect(typeof jsonData.tests).to.equal(\"object\");", + " pm.expect(jsonData.tests).to.have.property(\"include\");", + " pm.expect(jsonData.tests.include).to.be.an.instanceof(Array);", + " pm.expect(jsonData.tests).to.have.property(\"exclude\");", + " pm.expect(jsonData.tests.exclude).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"types\");", + " pm.expect(jsonData.types).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"user_agent\");", + " pm.expect(typeof jsonData.user_agent).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"timeouts\");", + " pm.expect(typeof jsonData.timeouts).to.equal(\"object\")", + " pm.expect(jsonData.timeouts).to.have.property(\"automatic\");", + " pm.expect(typeof jsonData.timeouts.automatic).to.equal(\"number\");", + " pm.expect(jsonData.timeouts).to.have.property(\"manual\");", + " pm.expect(typeof jsonData.timeouts.manual).to.equal(\"number\");", + " pm.expect(jsonData).to.have.property(\"browser\");", + " pm.expect(typeof jsonData.browser).to.equal(\"object\");", + " pm.expect(jsonData.browser).to.have.property(\"name\");", + " pm.expect(typeof jsonData.browser.name).to.equal(\"string\");", + " pm.expect(jsonData.browser).to.have.property(\"version\");", + " pm.expect(typeof jsonData.browser.version).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"reference_tokens\");", + " pm.expect(jsonData.reference_tokens).to.be.an.instanceof(Array);", + "});", + "", + "var includedTests = JSON.parse(pm.globals.get(\"included_tests\"));", + "var excludedTests = JSON.parse(pm.globals.get(\"excluded_tests\"));", + "var automaticTimeout = pm.globals.get(\"automatic_timeout\");", + "var manualTimeout = pm.globals.get(\"manual_timeout\");", + "var specialTimeout = pm.globals.get(\"special_timeout\");", + "var specialTimeoutTest = pm.globals.get(\"special_timeout_test\");", + "", + "pm.test(\"Configuration is as specified\", function () {", + " pm.expect(jsonData.token).to.match(tokenRegex);", + " for (var test of includedTests) {", + " pm.expect(jsonData.tests.include).to.include(test);", + " }", + " for (var test of excludedTests) {", + " pm.expect(jsonData.tests.exclude).to.include(test);", + " }", + " pm.expect(jsonData.types).to.include(\"automatic\");", + " pm.expect(jsonData.types).to.include(\"manual\");", + " pm.expect(jsonData.user_agent).to.include(\"PostmanRuntime\");", + " pm.expect(jsonData.timeouts.automatic).to.equal(automaticTimeout);", + " pm.expect(jsonData.timeouts.manual).to.equal(manualTimeout);", + " pm.expect(jsonData.timeouts[specialTimeoutTest]).to.equal(specialTimeout);", + " pm.expect(jsonData.browser.name).to.equal(\"Other\");", + " pm.expect(jsonData.browser.version).to.equal(\"0\");", + " pm.expect(jsonData.is_public).to.equal(false);", + " pm.expect(jsonData.reference_tokens).to.be.empty;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Tests of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "0b49c01b-e943-4879-b441-4093836d05e9", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"pending_tests\");", + " pm.expect(typeof jsonData.pending_tests).to.equal(\"object\");", + " pm.expect(jsonData).to.have.property(\"running_tests\");", + " pm.expect(typeof jsonData.running_tests).to.equal(\"object\");", + "});", + "", + "pm.test(\"All tests are pending tests\", function () {", + " pm.expect(Object.keys(jsonData.pending_tests)).to.not.have.lengthOf(0);", + " pm.expect(Object.keys(jsonData.running_tests)).to.have.lengthOf(0);", + "})", + "", + "const availableTests = pm.globals.get(\"available_tests\");", + "const includedTests = pm.globals.get(\"included_tests\");", + "const excludedTests = pm.globals.get(\"excluded_tests\");", + "", + "pm.test(\"Selected subset of tests are part of the session\", function () {", + " for (var api of Object.keys(jsonData.pending_tests)) {", + " for (var includedTest of includedTests) {", + " if (includedTest.split(\"/\").find(part => !!part) === api) {", + " var includeRegExp = new RegExp(\"^\" + includedTest, \"i\");", + " for (var test of jsonData.pending_tests[api]) {", + " pm.expect(test).to.match(regex);", + " }", + " break;", + " }", + " }", + " for (var excludedTest of excludedTests) {", + " if (excludedTest.split(\"/\").find(part => !!part) === api) {", + " var excludeRegExp = new RegExp(\"^\" + excludedTest, \"i\");", + " for (var test of jsonData.pending_tests[api]) {", + " pm.expect(test).to.not.match(regex);", + " }", + " break;", + " }", + " }", + " }", + "});", + "", + "const sessionTests = jsonData.pending_tests;", + "", + "pm.globals.set(\"session_tests\", JSON.stringify(sessionTests));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "b9cba19f-88d2-41f6-940a-a0979da05500", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"status\");", + " pm.expect(typeof jsonData.status).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"expiration_date\");", + " pm.expect(jsonData.expiration_date).to.be.null;", + " pm.expect(jsonData).to.have.property(\"date_started\");", + " pm.expect(jsonData.date_started).to.satisfy(value => !value || typeof value === \"number\");", + " pm.expect(jsonData).to.have.property(\"date_finished\");", + " pm.expect(jsonData.date_finished).to.satisfy(value => !value || typeof value === \"number\");", + "});", + "", + "pm.test(\"Session status is pending\", function () {", + " pm.expect(jsonData.status).to.equal(\"pending\");", + "})", + "", + "pm.test(\"Start and Finish date not set\", function () {", + " pm.expect(jsonData.date_started).to.be.null;", + " pm.expect(jsonData.date_finished).to.be.null;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + }, + { + "name": "Clean Up", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "3669c70a-88bc-4854-abac-5cb80a21c392", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "1a81f5c5-28d8-4935-b8ae-822e33c23da9", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Update Session Labels", + "item": [ + { + "name": "Create Session \\w Configuration Copy", + "event": [ + { + "listen": "test", + "script": { + "id": "9de9321c-ae78-4abd-a740-6634e2cbb9b9", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "739284e7-17ac-4c34-baa1-933353eb7ecc", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"labels\": [\"label1\", \"label2\"]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "5b15f338-c773-4f48-8f78-1d7c926beb81", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"tests\");", + " pm.expect(typeof jsonData.tests).to.equal(\"object\");", + " pm.expect(jsonData.tests).to.have.property(\"include\");", + " pm.expect(jsonData.tests.include).to.be.an.instanceof(Array);", + " pm.expect(jsonData.tests).to.have.property(\"exclude\");", + " pm.expect(jsonData.tests.exclude).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"types\");", + " pm.expect(jsonData.types).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"user_agent\");", + " pm.expect(typeof jsonData.user_agent).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"timeouts\");", + " pm.expect(typeof jsonData.timeouts).to.equal(\"object\")", + " pm.expect(jsonData.timeouts).to.have.property(\"automatic\");", + " pm.expect(typeof jsonData.timeouts.automatic).to.equal(\"number\");", + " pm.expect(jsonData.timeouts).to.have.property(\"manual\");", + " pm.expect(typeof jsonData.timeouts.manual).to.equal(\"number\");", + " pm.expect(jsonData).to.have.property(\"browser\");", + " pm.expect(typeof jsonData.browser).to.equal(\"object\");", + " pm.expect(jsonData.browser).to.have.property(\"name\");", + " pm.expect(typeof jsonData.browser.name).to.equal(\"string\");", + " pm.expect(jsonData.browser).to.have.property(\"version\");", + " pm.expect(typeof jsonData.browser.version).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"reference_tokens\");", + " pm.expect(jsonData.reference_tokens).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"labels\");", + " pm.expect(jsonData.labels).to.be.an.instanceof(Array);", + "});", + "", + "pm.test(\"Configuration is default\", function () {", + " pm.expect(jsonData.token).to.match(tokenRegex);", + " pm.expect(jsonData.tests.include).to.include(\"/\");", + " pm.expect(jsonData.types).to.include(\"automatic\");", + " pm.expect(jsonData.types).to.include(\"manual\");", + " pm.expect(jsonData.user_agent).to.include(\"PostmanRuntime\");", + " pm.expect(jsonData.timeouts.automatic).to.equal(60000);", + " pm.expect(jsonData.timeouts.manual).to.equal(300000);", + " pm.expect(jsonData.browser.name).to.equal(\"Other\");", + " pm.expect(jsonData.browser.version).to.equal(\"0\");", + " pm.expect(jsonData.is_public).to.equal(false);", + " pm.expect(jsonData.reference_tokens).to.be.empty;", + " pm.expect(jsonData.labels).to.include(\"label1\");", + " pm.expect(jsonData.labels).to.include(\"label2\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Update Labels Copy", + "event": [ + { + "listen": "test", + "script": { + "id": "e69b7188-b3c9-4f66-8c6c-3f3348f84f6a", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"labels\": [\"new\", \"labels\"]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/labels", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "labels" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "86e20c4d-5797-4b6e-a8cc-14e284e7c491", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"tests\");", + " pm.expect(typeof jsonData.tests).to.equal(\"object\");", + " pm.expect(jsonData.tests).to.have.property(\"include\");", + " pm.expect(jsonData.tests.include).to.be.an.instanceof(Array);", + " pm.expect(jsonData.tests).to.have.property(\"exclude\");", + " pm.expect(jsonData.tests.exclude).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"types\");", + " pm.expect(jsonData.types).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"user_agent\");", + " pm.expect(typeof jsonData.user_agent).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"timeouts\");", + " pm.expect(typeof jsonData.timeouts).to.equal(\"object\")", + " pm.expect(jsonData.timeouts).to.have.property(\"automatic\");", + " pm.expect(typeof jsonData.timeouts.automatic).to.equal(\"number\");", + " pm.expect(jsonData.timeouts).to.have.property(\"manual\");", + " pm.expect(typeof jsonData.timeouts.manual).to.equal(\"number\");", + " pm.expect(jsonData).to.have.property(\"browser\");", + " pm.expect(typeof jsonData.browser).to.equal(\"object\");", + " pm.expect(jsonData.browser).to.have.property(\"name\");", + " pm.expect(typeof jsonData.browser.name).to.equal(\"string\");", + " pm.expect(jsonData.browser).to.have.property(\"version\");", + " pm.expect(typeof jsonData.browser.version).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"reference_tokens\");", + " pm.expect(jsonData.reference_tokens).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"labels\");", + " pm.expect(jsonData.labels).to.be.an.instanceof(Array);", + "});", + "", + "pm.test(\"Configuration is default\", function () {", + " pm.expect(jsonData.token).to.match(tokenRegex);", + " pm.expect(jsonData.tests.include).to.include(\"/\");", + " pm.expect(jsonData.types).to.include(\"automatic\");", + " pm.expect(jsonData.types).to.include(\"manual\");", + " pm.expect(jsonData.user_agent).to.include(\"PostmanRuntime\");", + " pm.expect(jsonData.timeouts.automatic).to.equal(60000);", + " pm.expect(jsonData.timeouts.manual).to.equal(300000);", + " pm.expect(jsonData.browser.name).to.equal(\"Other\");", + " pm.expect(jsonData.browser.version).to.equal(\"0\");", + " pm.expect(jsonData.is_public).to.equal(false);", + " pm.expect(jsonData.reference_tokens).to.be.empty;", + " pm.expect(jsonData.labels).to.include(\"new\");", + " pm.expect(jsonData.labels).to.include(\"labels\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Clean Up", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Delete Session", + "item": [ + { + "name": "Setup", + "item": [ + { + "name": "Create Session No Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "e85fd539-4598-47a9-8768-656656f29fec", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Delete Session", + "event": [ + { + "listen": "test", + "script": { + "id": "8e9fe4af-0d50-49eb-8a4a-00e13021b4a6", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "9418112a-03f6-4641-893e-076c8922c0af", + "exec": [ + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Create and Read Results", + "item": [ + { + "name": "Create Session", + "item": [ + { + "name": "Create Session Two Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "a6995ff1-d626-4f4f-a1e0-567f3429bf52", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "85c7ad8d-5fc9-403d-8715-c65d5ff1323e", + "exec": [ + "const availableTests = JSON.parse(pm.globals.get(\"available_tests\"));", + "const keys = Object.keys(availableTests).sort();", + "const test1 = availableTests[keys[0]][0];", + "const test2 = availableTests[keys[1]][0];", + "", + "console.log(test1, test2)", + "", + "pm.globals.set(\"single_test_1\", test1);", + "pm.globals.set(\"single_test_2\", test2);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tests\": {\n \"include\": [\"{{single_test_1}}\", \"{{single_test_2}}\"]\n }\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "35cf4745-0301-4c9c-b6b3-40945299533f", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Read Results", + "event": [ + { + "listen": "test", + "script": { + "id": "6dc082ef-80ff-407a-a562-e25e37476eee", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Responds with no results\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(typeof jsonData).to.equal(\"object\");", + " pm.expect(Object.keys(jsonData)).to.be.empty;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}" + ], + "query": [ + { + "key": "path", + "value": "/2dcontext/drawing-images-to-the-canvas", + "disabled": true + }, + { + "key": "path", + "value": "/2dcontext/conformance-requirements", + "disabled": true + }, + { + "key": "path", + "value": "/2dcontext/conformance-requirements/2d.missingargs.html", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Read Results Compact", + "event": [ + { + "listen": "test", + "script": { + "id": "fcade663-dbd8-42c9-8344-53ae1feb6fcb", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var singleTest1 = pm.globals.get(\"single_test_1\");", + "var singleTest2 = pm.globals.get(\"single_test_2\");", + "", + "var api1 = singleTest1.split(\"/\").find(part => !!part);", + "var api2 = singleTest2.split(\"/\").find(part => !!part);", + "", + "pm.test(\"Responds with no results\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(typeof jsonData).to.equal(\"object\");", + " pm.expect(jsonData).to.have.property(api1);", + " pm.expect(jsonData).to.have.property(api2);", + " pm.expect(jsonData[api1].complete).to.equal(0);", + " pm.expect(jsonData[api2].complete).to.equal(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}/compact", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}", + "compact" + ] + } + }, + "response": [] + }, + { + "name": "Read Last Completed Tests of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "36d70d08-3caa-4312-9bbd-aed15fd238dd", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + " ", + "pm.test(\"JSON format is as expected\", function () {", + " pm.expect(Object.keys(jsonData)).to.have.lengthOf(3);", + " pm.expect(jsonData).to.have.property(\"pass\");", + " pm.expect(jsonData).to.have.property(\"fail\");", + " pm.expect(jsonData).to.have.property(\"timeout\");", + " for (var key of Object.keys(jsonData)) {", + " pm.expect(jsonData[key]).to.be.an.instanceof(Array);", + " }", + "});", + "", + "pm.test(\"Responds with no last completed tests\", function () {", + " for (var key of Object.keys(jsonData)) {", + " pm.expect(jsonData[key]).to.be.empty;", + " }", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/last_completed", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "last_completed" + ] + } + }, + "response": [] + }, + { + "name": "Read Tests of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "211adcf7-d71a-4b0f-94ab-d285fc8367df", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"pending_tests\");", + " pm.expect(typeof jsonData.pending_tests).to.equal(\"object\");", + " pm.expect(jsonData).to.have.property(\"running_tests\");", + " pm.expect(typeof jsonData.running_tests).to.equal(\"object\");", + "});", + "", + "const sessionTests = jsonData.pending_tests;", + "", + "pm.globals.set(\"session_tests\", JSON.stringify(sessionTests));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Create First Result", + "item": [ + { + "name": "Read Next Test of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "5410c9c2-ebbe-4bc1-ac12-e2602d8ade57", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const response = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(Object.keys(response)).to.have.lengthOf(1);", + " pm.expect(response).to.have.property(\"next_test\");", + " pm.expect(typeof response.next_test).to.equal(\"string\");", + "});", + "", + "const nextTest = response.next_test;", + "pm.globals.set(\"current_test_url\", nextTest);", + "if (!nextTest) return;", + "", + "const parameters = nextTest.split(\"?\")[1].split(\"&\");", + "let test = parameters.find(parameter => parameter.split(\"=\")[0] === \"test_url\").split(\"=\")[1];", + "test = decodeURIComponent(test);", + "test = \"/\" + test.split(\"/\").slice(3).join(\"/\").split(\"?\")[0];", + "", + "pm.globals.set(\"current_test\", test);", + "", + "const test1 = pm.globals.get(\"single_test_1\");", + "", + "pm.test(\"Returned test is first of two specified tests\", function () {", + " console.log(test);", + " console.log(test1);", + " console.log(pm.globals.get(\"single_test_2\"))", + " pm.expect(test).to.equal(test1);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "next" + ] + } + }, + "response": [] + }, + { + "name": "Read Tests of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "e4f9a03f-c731-4192-a9a9-aa9e315c3c44", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"pending_tests\");", + " pm.expect(typeof jsonData.pending_tests).to.equal(\"object\");", + " pm.expect(jsonData).to.have.property(\"running_tests\");", + " pm.expect(typeof jsonData.running_tests).to.equal(\"object\");", + "});", + "", + "const test1 = pm.globals.get(\"single_test_1\");", + "const test2 = pm.globals.get(\"single_test_2\");", + "", + "pm.test(\"One test is pending, one test is running\", function () {", + " pm.expect(Object.keys(jsonData.pending_tests)).to.have.lengthOf(1);", + " var api = Object.keys(jsonData.pending_tests)[0];", + " pm.expect(jsonData.pending_tests[api]).to.have.lengthOf(1);", + " pm.expect(jsonData.pending_tests[api]).to.include(test2);", + " pm.expect(Object.keys(jsonData.running_tests)).to.have.lengthOf(1);", + " api = Object.keys(jsonData.running_tests)[0];", + " pm.expect(jsonData.running_tests[api]).to.have.lengthOf(1);", + " pm.expect(jsonData.running_tests[api]).to.include(test1);", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Create Result", + "event": [ + { + "listen": "test", + "script": { + "id": "9f684f77-4ed0-4cd2-99a4-b1c134432e9f", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"test\": \"{{current_test}}\",\n \"status\": \"OK\",\n \"message\": null,\n \"subtests\": [\n {\n \"name\": \"Subtest testing feature xy\",\n \"status\": \"FAIL\",\n \"message\": \"Error message\"\n }\n ]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Tests of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "33601dd0-6786-4c8a-a6b5-7f1386129de7", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"pending_tests\");", + " pm.expect(typeof jsonData.pending_tests).to.equal(\"object\");", + " pm.expect(jsonData).to.have.property(\"running_tests\");", + " pm.expect(typeof jsonData.running_tests).to.equal(\"object\");", + "});", + "", + "", + "const test1 = pm.globals.get(\"single_test_1\");", + "const test2 = pm.globals.get(\"single_test_2\");", + "", + "pm.test(\"One test is pending, one test is completed\", function () {", + " pm.expect(Object.keys(jsonData.pending_tests)).to.have.lengthOf(1);", + " var api = Object.keys(jsonData.pending_tests)[0];", + " pm.expect(jsonData.pending_tests[api]).to.have.lengthOf(1);", + " pm.expect(jsonData.pending_tests[api]).to.include(test2);", + " pm.expect(Object.keys(jsonData.running_tests)).to.have.lengthOf(0);", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "97be92f1-a8d1-41ea-b41d-0ca08113e6e1", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"status\");", + " pm.expect(typeof jsonData.status).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"expiration_date\");", + " pm.expect(jsonData.expiration_date).to.be.null;", + " pm.expect(jsonData).to.have.property(\"date_started\");", + " pm.expect(jsonData).to.have.property(\"date_finished\");", + "});", + "", + "pm.test(\"Session status is running\", function () {", + " pm.expect(jsonData.status).to.equal(\"running\");", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + }, + { + "name": "Read Last Completed Tests of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "24ec2b79-c266-473f-8579-7b694079d640", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + " ", + "pm.test(\"JSON format is as expected\", function () {", + " pm.expect(Object.keys(jsonData)).to.have.lengthOf(3);", + " pm.expect(jsonData).to.have.property(\"pass\");", + " pm.expect(jsonData).to.have.property(\"fail\");", + " pm.expect(jsonData).to.have.property(\"timeout\");", + " for (var key of Object.keys(jsonData)) {", + " pm.expect(jsonData[key]).to.be.an.instanceof(Array);", + " }", + "});", + "", + "const test1 = pm.globals.get(\"single_test_1\");", + "", + "pm.test(\"Responds with one last completed tests as failed\", function () {", + " pm.expect(jsonData[\"pass\"]).to.be.empty;", + " pm.expect(jsonData[\"fail\"]).to.have.lengthOf(1);", + " pm.expect(jsonData[\"fail\"][0]).to.equal(test1);", + " pm.expect(jsonData[\"timeout\"]).to.be.empty;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/last_completed", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "last_completed" + ] + } + }, + "response": [] + }, + { + "name": "Read Results", + "event": [ + { + "listen": "test", + "script": { + "id": "1f47b39f-727f-43b4-934d-c7f62b268baa", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON format is as expected\", function () {", + " for (var api of Object.keys(jsonData)) {", + " pm.expect(jsonData[api]).to.be.an.instanceof(Array);", + " for (var result of jsonData[api]) {", + " pm.expect(typeof result).to.equal(\"object\");", + " pm.expect(Object.keys(result)).to.have.lengthOf(4);", + " pm.expect(result).to.have.property(\"test\");", + " pm.expect(typeof result.test).to.equal(\"string\");", + " pm.expect(result).to.have.property(\"status\");", + " pm.expect(typeof result.status).to.equal(\"string\");", + " pm.expect(result).to.have.property(\"message\");", + " pm.expect(result.message).to.satisfy(message => !message || typeof message === \"string\");", + " pm.expect(result).to.have.property(\"subtests\");", + " pm.expect(result.subtests).to.be.an.instanceof(Array);", + " for (var subtest of result.subtests) {", + " pm.expect(typeof subtest).to.equal(\"object\");", + " pm.expect(Object.keys(subtest)).to.have.lengthOf(3);", + " pm.expect(subtest).to.have.property(\"name\");", + " pm.expect(typeof subtest.name).to.equal(\"string\");", + " pm.expect(subtest).to.have.property(\"status\");", + " pm.expect(typeof subtest.status).to.equal(\"string\");", + " pm.expect(subtest).to.have.property(\"message\");", + " pm.expect(subtest.message).to.satisfy(message => !message || typeof message === \"string\");", + " }", + " }", + " }", + "});", + "", + "const test1 = pm.globals.get(\"single_test_1\");", + "", + "pm.test(\"Test is first test, successful run and failed\", function () {", + " var api = Object.keys(jsonData)[0];", + " pm.expect(api).to.equal(test1.split(\"/\").find(part => !!part))", + " var result = jsonData[api][0];", + " pm.expect(result.test).to.equal(test1);", + " pm.expect(result.status).to.equal(\"OK\");", + " pm.expect(result.message).to.be.null;", + " var subtest = result.subtests[0];", + " pm.expect(subtest.status).to.equal(\"FAIL\");", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}" + ], + "query": [ + { + "key": "path", + "value": "/2dcontext/drawing-images-to-the-canvas", + "disabled": true + }, + { + "key": "path", + "value": "/2dcontext/conformance-requirements", + "disabled": true + }, + { + "key": "path", + "value": "/2dcontext/conformance-requirements/2d.missingargs.html", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Read Results Compact", + "event": [ + { + "listen": "test", + "script": { + "id": "e734f074-a0c2-4e54-b427-2827fa732843", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "var test = pm.globals.get(\"single_test_1\");", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(typeof jsonData).to.equal(\"object\");", + " for (var api of Object.keys(jsonData)) {", + " pm.expect(jsonData[api]).to.have.property(\"pass\");", + " pm.expect(typeof jsonData[api].pass).to.equal(\"number\");", + " pm.expect(jsonData[api]).to.have.property(\"fail\");", + " pm.expect(typeof jsonData[api].fail).to.equal(\"number\");", + " pm.expect(jsonData[api]).to.have.property(\"timeout\");", + " pm.expect(typeof jsonData[api].timeout).to.equal(\"number\");", + " pm.expect(jsonData[api]).to.have.property(\"not_run\");", + " pm.expect(typeof jsonData[api].not_run).to.equal(\"number\");", + " }", + "})", + "", + "pm.test(\"Responds with one test failed\", function () {", + " var api = test.split(\"/\").find(part => !!part);", + " pm.expect(jsonData[api].pass).to.equal(0);", + " pm.expect(jsonData[api].fail).to.equal(1);", + " pm.expect(jsonData[api].timeout).to.equal(0);", + " pm.expect(jsonData[api].not_run).to.equal(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}/compact", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}", + "compact" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Create Last Result", + "item": [ + { + "name": "Read Next Test of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "fd2c11ee-5eca-43f3-89a7-133602c8034a", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const response = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(Object.keys(response)).to.have.lengthOf(1);", + " pm.expect(response).to.have.property(\"next_test\");", + " pm.expect(typeof response.next_test).to.equal(\"string\");", + "});", + "", + "const nextTest = response.next_test;", + "pm.globals.set(\"current_test_url\", nextTest);", + "if (!nextTest) return;", + "", + "const parameters = nextTest.split(\"?\")[1].split(\"&\");", + "let test = parameters.find(parameter => parameter.split(\"=\")[0] === \"test_url\").split(\"=\")[1];", + "test = decodeURIComponent(test);", + "test = \"/\" + test.split(\"/\").slice(3).join(\"/\").split(\"?\")[0];", + "", + "pm.globals.set(\"current_test\", test);", + "", + "const test2 = pm.globals.get(\"single_test_2\");", + "", + "pm.test(\"Returned test is second of two specified tests\", function () {", + " pm.expect(test).to.equal(test2);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "next" + ] + } + }, + "response": [] + }, + { + "name": "Read Tests of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "112ee3d5-d4e0-4838-879e-f74c38d0218c", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"pending_tests\");", + " pm.expect(typeof jsonData.pending_tests).to.equal(\"object\");", + " pm.expect(jsonData).to.have.property(\"running_tests\");", + " pm.expect(typeof jsonData.running_tests).to.equal(\"object\");", + "});", + "", + "const test1 = pm.globals.get(\"single_test_1\");", + "const test2 = pm.globals.get(\"single_test_2\");", + "", + "pm.test(\"One test is running\", function () {", + " pm.expect(Object.keys(jsonData.pending_tests)).to.have.lengthOf(0);", + " pm.expect(Object.keys(jsonData.running_tests)).to.have.lengthOf(1);", + " var api = Object.keys(jsonData.running_tests)[0];", + " pm.expect(jsonData.running_tests[api]).to.have.lengthOf(1);", + " pm.expect(jsonData.running_tests[api]).to.include(test2);", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Create Result", + "event": [ + { + "listen": "test", + "script": { + "id": "79a84ed9-bb07-493d-ab34-8e7693b3ebbd", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"test\": \"{{current_test}}\",\n \"status\": \"OK\",\n \"message\": null,\n \"subtests\": [\n {\n \"name\": \"Subtest testing feature xy\",\n \"status\": \"PASS\",\n \"message\": \"Error message\"\n }\n ]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "c1829043-18dd-4cc1-8091-41981349f4e3", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"status\");", + " pm.expect(typeof jsonData.status).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"expiration_date\");", + " pm.expect(jsonData.expiration_date).to.be.null;", + " pm.expect(jsonData).to.have.property(\"date_started\");", + " pm.expect(jsonData).to.have.property(\"date_finished\");", + "});", + "", + "pm.test(\"Session status is completed\", function () {", + " pm.expect(jsonData.status).to.equal(\"completed\");", + "})", + "", + "pm.test(\"Finish date is set\", function () {", + " pm.expect(Date.parse(jsonData.date_finished)).to.be.below(Date.now());", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + }, + { + "name": "Read Tests of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "59fee9df-9945-4fdf-a102-9c1b92d915c1", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"pending_tests\");", + " pm.expect(typeof jsonData.pending_tests).to.equal(\"object\");", + " pm.expect(jsonData).to.have.property(\"running_tests\");", + " pm.expect(typeof jsonData.running_tests).to.equal(\"object\");", + "});", + "", + "const test1 = pm.globals.get(\"single_test_1\");", + "const test2 = pm.globals.get(\"single_test_2\");", + "", + "var test1Api = test1.split(\"/\").find(part => !!part);", + "var test2Api = test1.split(\"/\").find(part => !!part);", + "", + "pm.test(\"One test is pending, one test is completed\", function () {", + " pm.expect(Object.keys(jsonData.pending_tests)).to.have.lengthOf(0);", + " pm.expect(Object.keys(jsonData.running_tests)).to.have.lengthOf(0);", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Last Completed Tests of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "91fa38ac-3b06-488a-892e-59458e88a4da", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + " ", + "pm.test(\"JSON format is as expected\", function () {", + " pm.expect(Object.keys(jsonData)).to.have.lengthOf(3);", + " pm.expect(jsonData).to.have.property(\"pass\");", + " pm.expect(jsonData).to.have.property(\"fail\");", + " pm.expect(jsonData).to.have.property(\"timeout\");", + " for (var key of Object.keys(jsonData)) {", + " pm.expect(jsonData[key]).to.be.an.instanceof(Array);", + " }", + "});", + "", + "const test1 = pm.globals.get(\"single_test_1\");", + "const test2 = pm.globals.get(\"single_test_2\");", + "", + "pm.test(\"Responds with one last completed tests as failed and one last completed test as passed\", function () {", + " pm.expect(jsonData[\"pass\"]).to.have.lengthOf(1);", + " pm.expect(jsonData[\"pass\"][0]).to.equal(test2);", + " pm.expect(jsonData[\"fail\"]).to.have.lengthOf(1);", + " pm.expect(jsonData[\"fail\"][0]).to.equal(test1);", + " pm.expect(jsonData[\"timeout\"]).to.be.empty;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/last_completed", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "last_completed" + ] + } + }, + "response": [] + }, + { + "name": "Read Results", + "event": [ + { + "listen": "test", + "script": { + "id": "dca0b42e-9054-4354-87e5-e8d236216050", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON format is as expected\", function () {", + " for (var api of Object.keys(jsonData)) {", + " pm.expect(jsonData[api]).to.be.an.instanceof(Array);", + " for (var result of jsonData[api]) {", + " pm.expect(typeof result).to.equal(\"object\");", + " pm.expect(Object.keys(result)).to.have.lengthOf(4);", + " pm.expect(result).to.have.property(\"test\");", + " pm.expect(typeof result.test).to.equal(\"string\");", + " pm.expect(result).to.have.property(\"status\");", + " pm.expect(typeof result.status).to.equal(\"string\");", + " pm.expect(result).to.have.property(\"message\");", + " pm.expect(result.message).to.satisfy(message => !message || typeof message === \"string\");", + " pm.expect(result).to.have.property(\"subtests\");", + " pm.expect(result.subtests).to.be.an.instanceof(Array);", + " for (var subtest of result.subtests) {", + " pm.expect(typeof subtest).to.equal(\"object\");", + " pm.expect(Object.keys(subtest)).to.have.lengthOf(3);", + " pm.expect(subtest).to.have.property(\"name\");", + " pm.expect(typeof subtest.name).to.equal(\"string\");", + " pm.expect(subtest).to.have.property(\"status\");", + " pm.expect(typeof subtest.status).to.equal(\"string\");", + " pm.expect(subtest).to.have.property(\"message\");", + " pm.expect(subtest.message).to.satisfy(message => !message || typeof message === \"string\");", + " }", + " }", + " }", + "});", + "", + "const test1 = pm.globals.get(\"single_test_1\");", + "const test2 = pm.globals.get(\"single_test_2\");", + "", + "pm.test(\"Test is first and second test, successful run and failed, and successful run and passed\", function () {", + " var api = Object.keys(jsonData)[0];", + " for (var result of jsonData[api]) {", + " if (result.test === test1) {", + " pm.expect(result.test).to.equal(test1); ", + " pm.expect(result.status).to.equal(\"OK\");", + " pm.expect(result.message).to.be.null;", + " var subtest = result.subtests[0];", + " pm.expect(subtest.status).to.equal(\"FAIL\");", + " } else {", + " pm.expect(result.test).to.equal(test2); ", + " pm.expect(result.status).to.equal(\"OK\");", + " pm.expect(result.message).to.be.null;", + " subtest = result.subtests[0];", + " pm.expect(subtest.status).to.equal(\"PASS\");", + " }", + " }", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}" + ], + "query": [ + { + "key": "path", + "value": "/2dcontext/drawing-images-to-the-canvas", + "disabled": true + }, + { + "key": "path", + "value": "/2dcontext/conformance-requirements", + "disabled": true + }, + { + "key": "path", + "value": "/2dcontext/conformance-requirements/2d.missingargs.html", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Read Results Compact", + "event": [ + { + "listen": "test", + "script": { + "id": "14ec9254-5a40-4c86-9a7f-72f43a35a79b", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(typeof jsonData).to.equal(\"object\");", + " for (var api of Object.keys(jsonData)) {", + " pm.expect(jsonData[api]).to.have.property(\"pass\");", + " pm.expect(typeof jsonData[api].pass).to.equal(\"number\");", + " pm.expect(jsonData[api]).to.have.property(\"fail\");", + " pm.expect(typeof jsonData[api].fail).to.equal(\"number\");", + " pm.expect(jsonData[api]).to.have.property(\"timeout\");", + " pm.expect(typeof jsonData[api].timeout).to.equal(\"number\");", + " pm.expect(jsonData[api]).to.have.property(\"not_run\");", + " pm.expect(typeof jsonData[api].not_run).to.equal(\"number\");", + " }", + "})", + "", + "const test1 = pm.globals.get(\"single_test_1\");", + "const test2 = pm.globals.get(\"single_test_2\");", + "", + "var test1Api = test1.split(\"/\").find(part => !!part);", + "var test2Api = test1.split(\"/\").find(part => !!part);", + "", + "pm.test(\"Responds with one test failed\", function () {", + " pm.expect(Object.keys(jsonData)).to.have.lengthOf(2);", + " var api = Object.keys(jsonData)[0];", + " if (api === test1Api) {", + " pm.expect(jsonData[api].pass).to.equal(0);", + " pm.expect(jsonData[api].fail).to.equal(1);", + " } else {", + " pm.expect(jsonData[api].pass).to.equal(1);", + " pm.expect(jsonData[api].fail).to.equal(0);", + " }", + " pm.expect(jsonData[api].timeout).to.equal(0);", + " pm.expect(jsonData[api].not_run).to.equal(0);", + " api = Object.keys(jsonData)[1];", + " if (api === test1Api) {", + " pm.expect(jsonData[api].pass).to.equal(0);", + " pm.expect(jsonData[api].fail).to.equal(1);", + " } else {", + " pm.expect(jsonData[api].pass).to.equal(1);", + " pm.expect(jsonData[api].fail).to.equal(0);", + " }", + " pm.expect(jsonData[api].timeout).to.equal(0);", + " pm.expect(jsonData[api].not_run).to.equal(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}/compact", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}", + "compact" + ] + } + }, + "response": [] + }, + { + "name": "Read Next Test of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "132574dc-3688-47e3-8f50-3235f18806f7", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const response = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(Object.keys(response)).to.have.lengthOf(1);", + " pm.expect(response).to.have.property(\"next_test\");", + " pm.expect(typeof response.next_test).to.equal(\"string\");", + "});", + "", + "const nextTest = response.next_test;", + "pm.globals.set(\"current_test_url\", nextTest);", + "if (!nextTest) return;", + "const test = \"/\" + nextTest.split(\"/\").slice(3).join(\"/\").split(\"?\")[0];", + "pm.globals.set(\"current_test\", test);", + "", + "const web_root = pm.environment.get(\"web_root\");", + "", + "pm.test(\"Returned test finish page\", function () {", + " pm.expect(test).to.equal(\"/\" + web_root + \"/finish.html\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "next" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Clean Up", + "item": [ + { + "name": "Delete Session", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Viewing and Downloading Reports", + "item": [ + { + "name": "Create Sessions", + "item": [ + { + "name": "First Session", + "item": [ + { + "name": "Create Session One Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "2f233dcf-d934-4479-8908-b1349e6ea54d", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "2930a821-2c6e-47b9-89d9-90ebe1cff52a", + "exec": [ + "const availableTests = JSON.parse(pm.globals.get(\"available_tests\"));", + "const keys = Object.keys(availableTests).sort();", + "const test1 = availableTests[keys[0]][0];", + "const test2 = availableTests[keys[1]][0];", + "const apiName = test1.split(\"/\").find(part => !!part);", + "", + "pm.globals.set(\"single_test_1\", test1);", + "pm.globals.set(\"single_test_2\", test2);", + "pm.globals.set(\"api_name\", apiName);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tests\": {\n \"include\": [\"{{single_test_1}}\"]\n }\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "12f3616b-707e-4a71-8db8-89fc195c1fcd", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Read Next Test of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "f35f1509-383f-49af-a30e-41e1df4efa04", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const response = pm.response.json();", + "", + "const nextTest = response.next_test;", + "pm.globals.set(\"current_test_url\", nextTest);", + "if (!nextTest) return;", + "", + "const parameters = nextTest.split(\"?\")[1].split(\"&\");", + "let test = parameters.find(parameter => parameter.split(\"=\")[0] === \"test_url\").split(\"=\")[1];", + "test = decodeURIComponent(test);", + "test = \"/\" + test.split(\"/\").slice(3).join(\"/\").split(\"?\")[0];", + "", + "pm.globals.set(\"current_test\", test);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "next" + ] + } + }, + "response": [] + }, + { + "name": "Create Result", + "event": [ + { + "listen": "test", + "script": { + "id": "b015f161-e390-4224-9f26-92395f44c772", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"test\": \"{{current_test}}\",\n \"status\": \"OK\",\n \"message\": null,\n \"subtests\": [\n {\n \"name\": \"Subtest testing feature xy\",\n \"status\": \"FAIL\",\n \"message\": \"Error message\"\n }\n ]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Second Session", + "item": [ + { + "name": "Create Session One Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "da3d0c17-6b2b-4d61-ad01-dba736425388", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token_comp\", token);" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "7da81e81-d344-4459-b700-19b93d49d3c9", + "exec": [ + "const availableTests = JSON.parse(pm.globals.get(\"available_tests\"));", + "const keys = Object.keys(availableTests).sort();", + "const test1 = availableTests[keys[0]][0];", + "", + "pm.globals.set(\"single_test_1\", test1);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tests\": {\n \"include\": [\"{{single_test_1}}\"]\n }\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "3296c7ba-67a6-405d-a78a-51dd64432301", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token_comp}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token_comp}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Read Next Test of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "72deb55c-4583-4741-9cf8-97713762b894", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const response = pm.response.json();", + "", + "const nextTest = response.next_test;", + "pm.globals.set(\"current_test_url\", nextTest);", + "if (!nextTest) return;", + "", + "const parameters = nextTest.split(\"?\")[1].split(\"&\");", + "let test = parameters.find(parameter => parameter.split(\"=\")[0] === \"test_url\").split(\"=\")[1];", + "test = decodeURIComponent(test);", + "test = \"/\" + test.split(\"/\").slice(3).join(\"/\").split(\"?\")[0];", + "", + "pm.globals.set(\"current_test\", test);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token_comp}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token_comp}}", + "next" + ] + } + }, + "response": [] + }, + { + "name": "Create Result", + "event": [ + { + "listen": "test", + "script": { + "id": "d695f300-a773-4654-b346-95c536cad19e", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"test\": \"{{current_test}}\",\n \"status\": \"OK\",\n \"message\": null,\n \"subtests\": [\n {\n \"name\": \"Subtest testing feature xy\",\n \"status\": \"FAIL\",\n \"message\": \"Error message\"\n }\n ]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token_comp}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token_comp}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Download reports", + "item": [ + { + "name": "Download Results Overview", + "event": [ + { + "listen": "test", + "script": { + "id": "6838965f-7a27-48d5-8c27-f55d4756588f", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}/overview", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}", + "overview" + ] + } + }, + "response": [] + }, + { + "name": "Download All Apis Json", + "event": [ + { + "listen": "test", + "script": { + "id": "bcc2c8c4-94c0-4227-a627-3e0d5be2e7b8", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}/json", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}", + "json" + ] + } + }, + "response": [] + }, + { + "name": "Download WPT Multi Report Url", + "event": [ + { + "listen": "test", + "script": { + "id": "7b370723-0506-4b5e-941a-f31a628a29f6", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Uri returned\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(typeof jsonData.uri).to.equal(\"string\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{api_name}}/reporturl?tokens={{session_token}},{{session_token_comp}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{api_name}}", + "reporturl" + ], + "query": [ + { + "key": "tokens", + "value": "{{session_token}},{{session_token_comp}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Download Results Api Json", + "event": [ + { + "listen": "test", + "script": { + "id": "b5243979-b63a-4bbf-bd51-1529da1e1f6d", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "81f3da29-8a0c-4403-929d-86590b01ef2f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}/{{api_name}}/json", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}", + "{{api_name}}", + "json" + ] + } + }, + "response": [] + }, + { + "name": "Download WPT Report Copy", + "event": [ + { + "listen": "test", + "script": { + "id": "0168ea3f-9bf7-4ea7-a3ad-745c2704d97e", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Uri returned\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(typeof jsonData.uri).to.equal(\"string\");", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "e4efdac9-2d00-4853-b145-18a6d44ff139", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}/{{api_name}}/reporturl", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}", + "{{api_name}}", + "reporturl" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Malfunctioning List", + "item": [ + { + "name": "Create Session \\w Configuration Copy", + "event": [ + { + "listen": "test", + "script": { + "id": "e685a989-8ef7-4e8f-a3c7-b579c66430f9", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "8517360b-2e66-4ca9-9339-017622c39888", + "exec": [ + "var automaticTimeout = 120000;", + "var manualTimeout = 1000000;", + "var specialTimeout = 2000;", + "", + "pm.globals.set(\"automatic_timeout\", automaticTimeout);", + "pm.globals.set(\"manual_timeout\", manualTimeout);", + "pm.globals.set(\"special_timeout\", specialTimeout);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tests\": {\n \"include\": {{included_tests}},\n \"exclude\": {{excluded_tests}}\n },\n \"types\": [\n \"automatic\"\n ],\n \"timeouts\": {\n \"automatic\": {{automatic_timeout}},\n \"manual\": {{manual_timeout}},\n \"{{special_timeout_test}}\": {{special_timeout}}\n },\n \"labels\": [\"label1\", \"label2\"]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Malfunctioning Empty", + "event": [ + { + "listen": "test", + "script": { + "id": "4e85ecaf-4364-4681-ab8a-221d40681c44", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Return empty array\", function() {", + " pm.expect(jsonData).to.be.an.instanceof(Array)", + " pm.expect(jsonData).to.have.length(0)", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/malfunctioning", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "malfunctioning" + ] + } + }, + "response": [] + }, + { + "name": "Update Session Malfunctioning Insert Two", + "event": [ + { + "listen": "test", + "script": { + "id": "a6221560-9eba-4fb4-a7cb-a1570a37425d", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\n\t\"/test/file/one.html\",\n\t\"/test/file/two.html\"\n]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/malfunctioning", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "malfunctioning" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Malfunctioning Two Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "77d14567-4600-44fd-98e4-99e113da6781", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Return array with two tests\", function() {", + " pm.expect(jsonData).to.be.an.instanceof(Array)", + " pm.expect(jsonData).to.have.length(2)", + " pm.expect(jsonData).to.include(\"/test/file/one.html\")", + " pm.expect(jsonData).to.include(\"/test/file/two.html\")", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/malfunctioning", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "malfunctioning" + ] + } + }, + "response": [] + }, + { + "name": "Update Session Malfunctioning Empty Array", + "event": [ + { + "listen": "test", + "script": { + "id": "55b486da-c5ae-49d4-b11a-247387267c9a", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "[]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/malfunctioning", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "malfunctioning" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Malfunctioning Empty", + "event": [ + { + "listen": "test", + "script": { + "id": "953002ec-d8df-477a-a0dc-f4e4a2efd031", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Return empty array\", function() {", + " pm.expect(jsonData).to.be.an.instanceof(Array)", + " pm.expect(jsonData).to.have.length(0)", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/malfunctioning", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "malfunctioning" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Sessions API", + "item": [ + { + "name": "create session", + "item": [ + { + "name": "With Defaults", + "item": [ + { + "name": "Prep: Read Available Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "fc15d329-d132-4abf-90e4-f549eee99b60", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var availableTests = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(typeof availableTests).to.equal(\"object\");", + " for (var api of Object.keys(availableTests)) {", + " pm.expect(availableTests[api]).to.be.an.instanceof(Array);", + " var apiRegExp = new RegExp(\"^/\" + api, \"i\");", + " for (var test of availableTests[api]) {", + " pm.expect(test).to.match(apiRegExp);", + " }", + " }", + "});", + "", + "var includedTests = [];", + "var excludedTests = [];", + "var specialTimeoutTest = \"\";", + "", + "var apis = Object.keys(availableTests);", + "for(var api of apis) {", + " if (availableTests[api].length > 50) {", + " var subDirs = availableTests[api].map(test => test.split(\"/\").filter(part => !!part).join(\"/\").split(\"/\")[1]).reduce((acc, curr) => acc.indexOf(curr) === -1 ? acc.concat([curr]) : acc, []);", + " if (subDirs.length > 2) {", + " includedTests.push(\"/\" + api);", + " excludedTests.push(\"/\" + api + \"/\" + subDirs[0]);", + " specialTimeoutTest = availableTests[api][availableTests[api].length - 1];", + " break;", + " }", + " ", + " }", + "}", + "", + "pm.globals.set(\"available_tests\", availableTests);", + "pm.globals.set(\"included_tests\", JSON.stringify(includedTests));", + "pm.globals.set(\"excluded_tests\", JSON.stringify(excludedTests));", + "pm.globals.set(\"special_timeout_test\", specialTimeoutTest.replace(\".\", \"\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests" + ] + } + }, + "response": [] + }, + { + "name": "Create Session No Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "ebb192ce-48e5-4aac-b99d-68894378eac8", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "159976cd-de00-4f88-8f9f-47db13ca4a30", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Configuration is default\", function () {", + " pm.expect(jsonData.token).to.match(tokenRegex);", + " pm.expect(jsonData.tests.include).to.include(\"/\");", + " pm.expect(jsonData.types).to.include(\"automatic\");", + " pm.expect(jsonData.types).to.include(\"manual\");", + " pm.expect(jsonData.user_agent).to.include(\"PostmanRuntime\");", + " pm.expect(jsonData.timeouts.automatic).to.equal(60000);", + " pm.expect(jsonData.timeouts.manual).to.equal(300000);", + " pm.expect(jsonData.browser.name).to.equal(\"Other\");", + " pm.expect(jsonData.browser.version).to.equal(\"0\");", + " pm.expect(jsonData.is_public).to.equal(false);", + " pm.expect(jsonData.reference_tokens).to.be.empty;", + " pm.expect(jsonData.labels).to.be.empty;", + " pm.expect(new Date(jsonData.date_created).getTime()).to.be.below(Date.now());", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "5058b07b-5e55-44e0-ad88-5c7b5884fa3c", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Session status is pending\", function () {", + " pm.expect(jsonData.status).to.equal(\"pending\");", + "})", + "", + "pm.test(\"Start, Finish and Expiration date not set\", function () {", + " pm.expect(jsonData.date_started).to.be.null;", + " pm.expect(jsonData.date_finished).to.be.null;", + " pm.expect(jsonData.expiration_date).to.be.null;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + }, + { + "name": "Read Tests of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "62a8c853-bf0b-47b0-9cee-7611d2b48cde", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"pending_tests\");", + " pm.expect(typeof jsonData.pending_tests).to.equal(\"object\");", + " pm.expect(jsonData).to.have.property(\"running_tests\");", + " pm.expect(typeof jsonData.running_tests).to.equal(\"object\");", + "});", + "", + "pm.test(\"All tests are pending tests\", function () {", + " pm.expect(Object.keys(jsonData.pending_tests)).to.not.have.lengthOf(0);", + " pm.expect(Object.keys(jsonData.running_tests)).to.have.lengthOf(0);", + "})", + "", + "const availableTests = pm.globals.get(\"available_tests\");", + "", + "pm.test(\"All available tests are part of the session\", function () {", + " for (var api of Object.keys(jsonData.pending_tests)) {", + " for (var test of jsonData.pending_tests[api]) {", + " pm.expect(availableTests[api]).to.include(test);", + " }", + " }", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Clean up: Delete session", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "878420c3-575a-4194-9a72-cebe07823674", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "6672030f-5a8a-4bea-9792-c24b980392e9", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "With Configuration", + "item": [ + { + "name": "Prep: Read Available Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "504b76ca-6870-443c-8c33-0cccc52cddae", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var availableTests = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(typeof availableTests).to.equal(\"object\");", + " for (var api of Object.keys(availableTests)) {", + " pm.expect(availableTests[api]).to.be.an.instanceof(Array);", + " var apiRegExp = new RegExp(\"^/\" + api, \"i\");", + " for (var test of availableTests[api]) {", + " pm.expect(test).to.match(apiRegExp);", + " }", + " }", + "});", + "", + "var includedTests = [];", + "var excludedTests = [];", + "var specialTimeoutTest = \"\";", + "", + "var apis = Object.keys(availableTests).sort();", + "for(var api of apis) {", + " if (availableTests[api].length > 50) {", + " var subDirs = availableTests[api].map(test => test.split(\"/\").filter(part => !!part).join(\"/\").split(\"/\")[1]).reduce((acc, curr) => acc.indexOf(curr) === -1 ? acc.concat([curr]) : acc, []);", + " if (subDirs.length > 2) {", + " includedTests.push(\"/\" + api);", + " excludedTests.push(\"/\" + api + \"/\" + subDirs[0]);", + " specialTimeoutTest = availableTests[api][availableTests[api].length - 1];", + " break;", + " }", + " ", + " }", + "}", + "", + "pm.globals.set(\"available_tests\", availableTests);", + "pm.globals.set(\"included_tests\", JSON.stringify(includedTests));", + "pm.globals.set(\"excluded_tests\", JSON.stringify(excludedTests));", + "pm.globals.set(\"special_timeout_test\", specialTimeoutTest.replace(\".\", \"\"));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests" + ] + } + }, + "response": [] + }, + { + "name": "Create Session \\w Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "fa956871-503a-421c-9822-4e2a11e1cf1c", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "213bdf59-9f98-4b5e-bacc-ae3cb31ae26a", + "exec": [ + "var automaticTimeout = 120000;", + "var manualTimeout = 1000000;", + "var specialTimeout = 2000;", + "", + "pm.globals.set(\"automatic_timeout\", automaticTimeout);", + "pm.globals.set(\"manual_timeout\", manualTimeout);", + "pm.globals.set(\"special_timeout\", specialTimeout);", + "", + "const availableTests = pm.globals.get(\"available_tests\");", + "const apiNames = Object.keys(availableTests).sort();", + "const apiName = apiNames[0];", + "", + "pm.globals.set(\"api_name\", apiName);", + "pm.globals.set(\"special_timeout_test\", \"/\" + apiName);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tests\": {\n \"include\": [\"/{{api_name}}\"]\n },\n \"timeouts\": {\n \"automatic\": {{automatic_timeout}},\n \"manual\": {{manual_timeout}},\n \"{{special_timeout_test}}\": {{special_timeout}}\n },\n \"labels\": [\"label1\", \"label2\"]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "a0434a56-04b8-4987-8f44-2d71b440ef03", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "var includedTests = JSON.parse(pm.globals.get(\"included_tests\"));", + "var excludedTests = JSON.parse(pm.globals.get(\"excluded_tests\"));", + "var automaticTimeout = pm.globals.get(\"automatic_timeout\");", + "var manualTimeout = pm.globals.get(\"manual_timeout\");", + "var specialTimeout = pm.globals.get(\"special_timeout\");", + "var specialTimeoutTest = pm.globals.get(\"special_timeout_test\");", + "", + "pm.test(\"Configuration is as specified\", function () {", + " pm.expect(jsonData.token).to.match(tokenRegex);", + " for (var test of includedTests) {", + " pm.expect(jsonData.tests.include).to.include(test);", + " }", + " for (var test of excludedTests) {", + " pm.expect(jsonData.tests.exclude).to.include(test);", + " }", + " pm.expect(jsonData.types).to.include(\"automatic\");", + " pm.expect(jsonData.types).to.include(\"manual\");", + " pm.expect(jsonData.user_agent).to.include(\"PostmanRuntime\");", + " pm.expect(jsonData.timeouts.automatic).to.equal(automaticTimeout);", + " pm.expect(jsonData.timeouts.manual).to.equal(manualTimeout);", + " pm.expect(jsonData.timeouts[specialTimeoutTest]).to.equal(specialTimeout);", + " pm.expect(jsonData.browser.name).to.equal(\"Other\");", + " pm.expect(jsonData.browser.version).to.equal(\"0\");", + " pm.expect(jsonData.is_public).to.equal(false);", + " pm.expect(jsonData.reference_tokens).to.be.empty;", + " pm.expect(jsonData.labels).to.include(\"label1\");", + " pm.expect(jsonData.labels).to.include(\"label2\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "2a7524e5-7394-4177-8267-42d5b32bc474", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Session status is pending\", function () {", + " pm.expect(jsonData.status).to.equal(\"pending\");", + "})", + "", + "pm.test(\"Start and Finish date not set\", function () {", + " pm.expect(jsonData.date_started).to.be.null;", + " pm.expect(jsonData.date_finished).to.be.null;", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + }, + { + "name": "Read Tests of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "448b6a0c-135d-42ef-8bc0-b4b1ce4ab8a0", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"All tests are pending tests\", function () {", + " pm.expect(Object.keys(jsonData.pending_tests)).to.not.have.lengthOf(0);", + " pm.expect(Object.keys(jsonData.running_tests)).to.have.lengthOf(0);", + "})", + "", + "const availableTests = pm.globals.get(\"available_tests\");", + "const includedTests = pm.globals.get(\"included_tests\");", + "const excludedTests = pm.globals.get(\"excluded_tests\");", + "", + "pm.test(\"Selected subset of tests are part of the session\", function () {", + " for (var api of Object.keys(jsonData.pending_tests)) {", + " for (var includedTest of includedTests) {", + " if (includedTest.split(\"/\").find(part => !!part) === api) {", + " var includeRegExp = new RegExp(\"^\" + includedTest, \"i\");", + " for (var test of jsonData.pending_tests[api]) {", + " pm.expect(test).to.match(regex);", + " }", + " break;", + " }", + " }", + " for (var excludedTest of excludedTests) {", + " if (excludedTest.split(\"/\").find(part => !!part) === api) {", + " var excludeRegExp = new RegExp(\"^\" + excludedTest, \"i\");", + " for (var test of jsonData.pending_tests[api]) {", + " pm.expect(test).to.not.match(regex);", + " }", + " break;", + " }", + " }", + " }", + "});", + "", + "const sessionTests = jsonData.pending_tests;", + "", + "pm.globals.set(\"session_tests\", JSON.stringify(sessionTests));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Clean up: Delete session", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "a16eac8e-9609-4314-abd8-a5c470537b52", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "5df55957-0bbb-4db8-9ab9-d8a70ce37bc5", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "With Expiration", + "item": [ + { + "name": "Create Session With Expiration", + "event": [ + { + "listen": "test", + "script": { + "id": "6c4aa702-ae3a-44a4-b886-b3e883de4d32", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "893672a9-c666-4790-b6a7-e0ce7afda482", + "exec": [ + "var expirationDate = Date.now() + 3000;", + "pm.globals.set(\"expiration_date\", expirationDate);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"expiration_date\": {{expiration_date}}\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read Expired Session", + "event": [ + { + "listen": "test", + "script": { + "id": "f6b31d37-a98b-4110-80a2-f78db6f2e26f", + "exec": [ + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "d41cf31b-c8e8-455f-aa69-527af5fbd66b", + "exec": [ + "var expirationDate = pm.globals.get(\"expiration_date\");", + "", + "var timeout = expirationDate - Date.now() + 1000", + "", + "console.log(timeout)", + "", + "setTimeout(function () {}, timeout);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Clean up: Delete session", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "read session", + "item": [ + { + "name": "Prep: Create Session No Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "090ca1bc-cbee-4d7b-b19f-fc1f4bb879a0", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "232aff11-3cdc-4994-af42-5e3574583814", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"tests\");", + " pm.expect(typeof jsonData.tests).to.equal(\"object\");", + " pm.expect(jsonData.tests).to.have.property(\"include\");", + " pm.expect(jsonData.tests.include).to.be.an.instanceof(Array);", + " pm.expect(jsonData.tests).to.have.property(\"exclude\");", + " pm.expect(jsonData.tests.exclude).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"types\");", + " pm.expect(jsonData.types).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"user_agent\");", + " pm.expect(typeof jsonData.user_agent).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"timeouts\");", + " pm.expect(typeof jsonData.timeouts).to.equal(\"object\")", + " pm.expect(jsonData.timeouts).to.have.property(\"automatic\");", + " pm.expect(typeof jsonData.timeouts.automatic).to.equal(\"number\");", + " pm.expect(jsonData.timeouts).to.have.property(\"manual\");", + " pm.expect(typeof jsonData.timeouts.manual).to.equal(\"number\");", + " pm.expect(jsonData).to.have.property(\"browser\");", + " pm.expect(typeof jsonData.browser).to.equal(\"object\");", + " pm.expect(jsonData.browser).to.have.property(\"name\");", + " pm.expect(typeof jsonData.browser.name).to.equal(\"string\");", + " pm.expect(jsonData.browser).to.have.property(\"version\");", + " pm.expect(typeof jsonData.browser.version).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"reference_tokens\");", + " pm.expect(jsonData.reference_tokens).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"labels\");", + " pm.expect(jsonData.labels).to.be.an.instanceof(Array);", + " pm.expect(jsonData).to.have.property(\"date_created\");", + "});", + "", + "pm.test(\"Configuration is default\", function () {", + " pm.expect(jsonData.token).to.match(tokenRegex);", + " pm.expect(jsonData.tests.include).to.include(\"/\");", + " pm.expect(jsonData.types).to.include(\"automatic\");", + " pm.expect(jsonData.types).to.include(\"manual\");", + " pm.expect(jsonData.user_agent).to.include(\"PostmanRuntime\");", + " pm.expect(jsonData.timeouts.automatic).to.equal(60000);", + " pm.expect(jsonData.timeouts.manual).to.equal(300000);", + " pm.expect(jsonData.browser.name).to.equal(\"Other\");", + " pm.expect(jsonData.browser.version).to.equal(\"0\");", + " pm.expect(jsonData.is_public).to.equal(false);", + " pm.expect(jsonData.reference_tokens).to.be.empty;", + " pm.expect(jsonData.labels).to.be.empty;", + " pm.expect(new Date(jsonData.date_created).getTime()).to.be.below(Date.now());", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Clean up: Delete session", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "read session status", + "item": [ + { + "name": "Prep: Create Session No Configuration", + "event": [ + { + "listen": "test", + "script": { + "id": "73cbeeb0-019b-4331-b0ec-ef9d3f1e0494", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read Session Status", + "event": [ + { + "listen": "test", + "script": { + "id": "ba142b55-2454-4472-bcf9-669b0bb7bab9", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(typeof jsonData.token).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"status\");", + " pm.expect(typeof jsonData.status).to.equal(\"string\");", + " pm.expect(jsonData).to.have.property(\"expiration_date\");", + " pm.expect(jsonData.expiration_date).to.be.null;", + " pm.expect(jsonData).to.have.property(\"date_started\");", + " pm.expect(jsonData).to.have.property(\"date_finished\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "status" + ] + } + }, + "response": [] + }, + { + "name": "Clean up: Delete session", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "read sessions", + "item": [ + { + "name": "Without query parameters", + "item": [ + { + "name": "Read sessions", + "event": [ + { + "listen": "test", + "script": { + "id": "f443955f-b57e-4801-8eac-547597381615", + "exec": [ + "const response = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"JSON structure as expected\", function() {", + " pm.expect(response).to.have.property(\"items\");", + " pm.expect(response[\"items\"]).to.be.instanceof(Array);", + " pm.expect(response).to.have.property(\"_links\");", + " pm.expect(response[\"_links\"]).to.be.instanceof(Object);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/_wave/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "_wave", + "api", + "sessions" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Containing created session", + "item": [ + { + "name": "Prep: Create session", + "event": [ + { + "listen": "test", + "script": { + "id": "2ca80dee-f9a5-4e0b-bead-6ab871d9edf8", + "exec": [ + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read sessions", + "event": [ + { + "listen": "test", + "script": { + "id": "fbf5eafb-0ae7-4105-8df5-51bb53b0c595", + "exec": [ + "const token = pm.globals.get(\"session_token\");", + "const response = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"JSON structure as expected\", function() {", + " pm.expect(response).to.have.property(\"items\");", + " pm.expect(response[\"items\"]).to.be.instanceof(Array);", + " pm.expect(response).to.have.property(\"_links\");", + " pm.expect(response[\"_links\"]).to.be.instanceof(Object);", + "});", + "", + "pm.test(\"Created session's token in response\", function() {", + " pm.expect(response.items).to.contain(token);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/_wave/api/sessions?index=0&count=1000", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "_wave", + "api", + "sessions" + ], + "query": [ + { + "key": "index", + "value": "0" + }, + { + "key": "count", + "value": "1000" + } + ] + } + }, + "response": [] + }, + { + "name": "Clean up: Delete Session", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "With configuration expansion", + "item": [ + { + "name": "Prep: Create session", + "event": [ + { + "listen": "test", + "script": { + "id": "bdd64594-c874-4eb2-8b36-4c37cea0f0cb", + "exec": [ + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read sessions", + "event": [ + { + "listen": "test", + "script": { + "id": "6732aedf-d99d-416f-b93d-98bc0404ef61", + "exec": [ + "const token = pm.globals.get(\"session_token\");", + "const response = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"JSON structure as expected\", function() {", + " pm.expect(response).to.have.property(\"items\");", + " pm.expect(response[\"items\"]).to.be.instanceof(Array);", + " pm.expect(response).to.have.property(\"_links\");", + " pm.expect(response[\"_links\"]).to.be.instanceof(Object);", + " pm.expect(response).to.have.property(\"_embedded\");", + " pm.expect(response[\"_embedded\"]).to.be.instanceof(Object);", + " pm.expect(response[\"_embedded\"]).to.have.property(\"configuration\");", + " pm.expect(response[\"_embedded\"][\"configuration\"]).to.be.instanceof(Array);", + "});", + "", + "pm.test(\"Created session's token in response\", function() {", + " pm.expect(response.items).to.contain(token);", + "});", + "", + "pm.test(\"Created session's token in embedded configuration\", function() {", + " let tokenInConfigurationList = false;", + " let configurations = response._embedded.configuration;", + " for (let configuration of configurations) {", + " if (configuration.token !== token) continue;", + " tokenInConfigurationList = true;", + " }", + " pm.expect(tokenInConfigurationList).to.equal(true);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/_wave/api/sessions?index=0&count=1000&expand=configuration", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "_wave", + "api", + "sessions" + ], + "query": [ + { + "key": "index", + "value": "0" + }, + { + "key": "count", + "value": "1000" + }, + { + "key": "expand", + "value": "configuration" + } + ] + } + }, + "response": [] + }, + { + "name": "Clean up: Delete Session", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "With status expansion", + "item": [ + { + "name": "Prep: Create session", + "event": [ + { + "listen": "test", + "script": { + "id": "3319e417-dd54-495f-a452-5519d2a61082", + "exec": [ + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + }, + { + "name": "Read sessions", + "event": [ + { + "listen": "test", + "script": { + "id": "6ca3480a-3224-4bf8-a466-fdcb06338795", + "exec": [ + "const token = pm.globals.get(\"session_token\");", + "const response = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"JSON structure as expected\", function() {", + " pm.expect(response).to.have.property(\"items\");", + " pm.expect(response[\"items\"]).to.be.instanceof(Array);", + " pm.expect(response).to.have.property(\"_links\");", + " pm.expect(response[\"_links\"]).to.be.instanceof(Object);", + " pm.expect(response).to.have.property(\"_embedded\");", + " pm.expect(response[\"_embedded\"]).to.be.instanceof(Object);", + " pm.expect(response[\"_embedded\"]).to.have.property(\"status\");", + " pm.expect(response[\"_embedded\"][\"status\"]).to.be.instanceof(Array);", + "});", + "", + "pm.test(\"Created session's token in response\", function() {", + " pm.expect(response.items).to.contain(token);", + "});", + "", + "pm.test(\"Created session's token in embedded status\", function() {", + " let tokenInStatusList = false;", + " let statuses = response._embedded.status;", + " for (let status of statuses) {", + " if (status.token !== token) continue;", + " tokenInStatusList = true;", + " }", + " pm.expect(tokenInStatusList).to.equal(true);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/_wave/api/sessions?index=0&count=1000&expand=status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "_wave", + "api", + "sessions" + ], + "query": [ + { + "key": "index", + "value": "0" + }, + { + "key": "count", + "value": "1000" + }, + { + "key": "expand", + "value": "status" + } + ] + } + }, + "response": [] + }, + { + "name": "Clean up: Delete Session", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Results API", + "item": [ + { + "name": "upload api", + "item": [ + { + "name": "create session", + "item": [ + { + "name": "Prep: Read Available Tests", + "event": [ + { + "listen": "test", + "script": { + "id": "1f3d8b67-aab8-44a0-abf4-e320a6b27755", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var availableTests = pm.response.json();", + "", + "var includedTests = [];", + "var excludedTests = [];", + "var specialTimeoutTest = \"\";", + "", + "var apis = Object.keys(availableTests);", + "for(var api of apis) {", + " if (availableTests[api].length > 50) {", + " var subDirs = availableTests[api].map(test => test.split(\"/\").filter(part => !!part)[1]).reduce((acc, curr) => acc.indexOf(curr) === -1 ? acc.concat([curr]) : acc, []);", + " if (subDirs.length > 2) {", + " includedTests.push(\"/\" + api);", + " excludedTests.push(\"/\" + api + \"/\" + subDirs[0]);", + " specialTimeoutTest = availableTests[api][availableTests[api].length - 1];", + " break;", + " }", + " ", + " }", + "}", + "", + "var usedApi = apis[0];", + "", + "pm.globals.set(\"api_name\", usedApi);", + "pm.globals.set(\"test\", availableTests[usedApi][0]);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests" + ] + } + }, + "response": [] + }, + { + "name": "Create Session With One test", + "event": [ + { + "listen": "test", + "script": { + "id": "f42479d4-f723-4843-ba91-f30019ebd975", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const tokenRegex = new RegExp(\"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$\");", + "", + "pm.test(\"Responds with token in JSON format\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property(\"token\");", + " pm.expect(jsonData.token).to.match(tokenRegex);", + "});", + "", + "", + "const response = pm.response.json();", + "const token = response.token;", + "pm.globals.set(\"session_token\", token);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tests\": {\n \"include\": [\"{{test}}\"]\n }\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "9f8fc606-25ee-45de-bcd8-57735900701b", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "be2341a8-1598-41b0-96a6-cfd049badd07", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "create results", + "item": [ + { + "name": "Start Session", + "event": [ + { + "listen": "test", + "script": { + "id": "897e04ae-d0a2-4a67-9488-2bdeb55db06b", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}/start", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}", + "start" + ] + } + }, + "response": [] + }, + { + "name": "Read Next Test of Session", + "event": [ + { + "listen": "test", + "script": { + "id": "95b382c6-9d08-41b9-80d1-40dc0217cc09", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "const response = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(Object.keys(response)).to.have.lengthOf(1);", + " pm.expect(response).to.have.property(\"next_test\");", + " pm.expect(typeof response.next_test).to.equal(\"string\");", + "});", + "", + "const nextTest = response.next_test;", + "pm.globals.set(\"current_test_url\", nextTest);", + "if (!nextTest) return;", + "", + "const parameters = nextTest.split(\"?\")[1].split(\"&\");", + "let test = parameters.find(parameter => parameter.split(\"=\")[0] === \"test_url\").split(\"=\")[1];", + "test = decodeURIComponent(test);", + "test = \"/\" + test.split(\"/\").slice(3).join(\"/\").split(\"?\")[0];", + "", + "pm.globals.set(\"current_test\", test);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/tests/{{session_token}}/next", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "tests", + "{{session_token}}", + "next" + ] + } + }, + "response": [] + }, + { + "name": "Create Result", + "event": [ + { + "listen": "test", + "script": { + "id": "67b23ac9-5254-4b7b-90bf-930a9661a614", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"test\": \"{{current_test}}\",\n \"status\": \"OK\",\n \"message\": null,\n \"subtests\": [\n {\n \"name\": \"Subtest testing feature xy\",\n \"status\": \"FAIL\",\n \"message\": \"Error message\"\n }\n ]\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "upload new results", + "item": [ + { + "name": "Download current results", + "event": [ + { + "listen": "test", + "script": { + "id": "984c79db-5ae1-4c22-8b62-6eccabfaeb98", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "var results = jsonData.results;", + "", + "pm.globals.set(\"results\", results);" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "a9a46b5f-2dd3-4fa7-a482-7186ac01a4d6", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}/{{api_name}}/json", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}", + "{{api_name}}", + "json" + ] + } + }, + "response": [] + }, + { + "name": "Read Current Compact Results", + "event": [ + { + "listen": "test", + "script": { + "id": "f4974495-2e84-4962-9259-1d5501fb7a3f", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.globals.set(\"compact_results\", jsonData);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}/compact", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}", + "compact" + ] + } + }, + "response": [] + }, + { + "name": "Upload Results Api Json", + "event": [ + { + "listen": "test", + "script": { + "id": "4a7df46d-546c-4cf4-8c6a-9b13eaa0be32", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "e2196348-a4a0-48d3-bd72-f591f0b65cf0", + "exec": [ + "var results = pm.globals.get(\"results\");", + "var newSubResult = ", + " {", + " \"name\": \"placeholder sub test\",", + " \"status\": \"PASS\",", + " \"message\": \"this is a placeholder sub test\"", + " };", + "", + "results[0].subtests.push(newSubResult);", + "resultsString = JSON.stringify(results);", + "pm.globals.set(\"resultsString\", resultsString);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"results\": {{resultsString}}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}/{{api_name}}/json", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}", + "{{api_name}}", + "json" + ] + } + }, + "response": [] + }, + { + "name": "Check if changes took effect", + "event": [ + { + "listen": "test", + "script": { + "id": "8c45fd6d-ca98-4393-ace6-c01563d7c1ec", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Returned results contain new result\", function () {", + " var jsonData = pm.response.json();", + " var results = jsonData.results;", + "", + " var oldResults = pm.globals.get(\"results\");", + "", + " pm.expect(oldResults[0].subtests.length + 1).to.equal(results[0].subtests.length);", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "0c9e31fc-1931-4dcd-978f-39e2a38f9a3f", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}/{{api_name}}/json", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}", + "{{api_name}}", + "json" + ] + } + }, + "response": [] + }, + { + "name": "Check changes in Compact Results", + "event": [ + { + "listen": "test", + "script": { + "id": "6e30b533-18f9-41bc-af65-bdde323c215a", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "var test = pm.globals.get(\"test\");", + "var oldCompactResults = pm.globals.get(\"compact_results\");", + "", + "pm.test(\"passed test increased\", function () {", + " var api = test.split(\"/\").find(part => !!part);", + " pm.expect(jsonData[api].pass).to.equal(oldCompactResults[api].pass + 1);", + "})" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/results/{{session_token}}/compact", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "results", + "{{session_token}}", + "compact" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "clean up", + "item": [ + { + "name": "Clean up: Delete session", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/sessions/{{session_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "sessions", + "{{session_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "General API", + "item": [ + { + "name": "server status", + "item": [ + { + "name": "Read server status", + "event": [ + { + "listen": "test", + "script": { + "id": "8b5b4c23-db0a-4b81-9349-ce8ead64bb7a", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"JSON structure is as expected\", function () {", + " pm.expect(jsonData).to.have.property(\"import_results_enabled\");", + " pm.expect(typeof jsonData.import_results_enabled).to.equal(\"boolean\");", + " pm.expect(jsonData).to.have.property(\"reports_enabled\");", + " pm.expect(typeof jsonData.reports_enabled).to.equal(\"boolean\");", + " pm.expect(jsonData).to.have.property(\"read_sessions_enabled\");", + " pm.expect(typeof jsonData.read_sessions_enabled).to.equal(\"boolean\");", + " pm.expect(jsonData).to.have.property(\"version_string\");", + " pm.expect(typeof jsonData.version_string).to.equal(\"string\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/status", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "status" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Devices API", + "item": [ + { + "name": "create", + "item": [ + { + "name": "Create device", + "event": [ + { + "listen": "test", + "script": { + "id": "286426ec-c394-48a4-a90f-c6dd80c7bef1", + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", + "});", + "", + "var response = pm.response.json();", + "", + "pm.test('Schema is valid', function() {", + " pm.expect(response).to.have.property(\"token\");", + "});", + "", + "pm.test('Data is valid', function() {", + " pm.expect(typeof response.token).to.equal(\"string\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "" + ] + } + }, + "response": [] + }, + { + "name": "Clean up: Wait device timeout", + "event": [ + { + "listen": "test", + "script": { + "id": "3bdb0f7c-1261-465e-9b02-0070005f0c43", + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "a1cf5880-7b8e-4bc6-9d92-59632a719c54", + "exec": [ + "var timeout = parseInt(pm.environment.get(\"device_timeout\")) + 500", + "", + "setTimeout(function () {}, timeout);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "read device", + "item": [ + { + "name": "Device not found", + "item": [ + { + "name": "Read device", + "event": [ + { + "listen": "test", + "script": { + "id": "a4401cbc-0a3a-4c2d-b1ed-e7d6867614ff", + "exec": [ + "pm.test(\"Successful GET request\", function () {", + " pm.expect(pm.response.code).to.equal(404);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "1644fd70-b4c4-4207-8e12-67752a984417", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/invalid_token", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "invalid_token" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Device found", + "item": [ + { + "name": "Prep: Create device", + "event": [ + { + "listen": "test", + "script": { + "id": "c4f14137-5ced-449b-a872-b7d541e2f6af", + "exec": [ + "var response = pm.response.json();", + "var token = response.token;", + "", + "pm.globals.set(\"device_token\", token)" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "" + ] + } + }, + "response": [] + }, + { + "name": "Read device", + "event": [ + { + "listen": "test", + "script": { + "id": "61a840f1-d3cd-4d7a-a54f-30da1266e48f", + "exec": [ + "pm.test(\"Successful GET request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});", + "", + "var response = pm.response.json();", + "", + "pm.test('Schema is valid', function() {", + " pm.expect(response).to.have.property(\"token\");", + " pm.expect(response).to.have.property(\"user_agent\");", + " pm.expect(response).to.have.property(\"last_active\");", + " pm.expect(response).to.have.property(\"name\");", + "});", + "", + "pm.test('Data is valid', function() {", + " pm.expect(typeof response.token).to.equal(\"string\");", + " pm.expect(typeof response.user_agent).to.equal(\"string\");", + " pm.expect(typeof response.last_active).to.equal(\"string\");", + " pm.expect(typeof response.name).to.equal(\"string\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/{{device_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "{{device_token}}" + ] + } + }, + "response": [] + }, + { + "name": "Clean up: Wait device timeout", + "event": [ + { + "listen": "test", + "script": { + "id": "17112336-b962-472d-86a8-7872ff9876d1", + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "912939cc-1685-4e1f-b0a6-696b264870ca", + "exec": [ + "var timeout = parseInt(pm.environment.get(\"device_timeout\")) + 500", + "", + "setTimeout(function () {}, timeout);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "Timed out device", + "item": [ + { + "name": "Prep: Create device", + "event": [ + { + "listen": "test", + "script": { + "id": "c99b9318-1e50-4702-9d81-53d87faa1080", + "exec": [ + "var response = pm.response.json();", + "var token = response.token;", + "", + "pm.globals.set(\"device_token\", token)" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "" + ] + } + }, + "response": [] + }, + { + "name": "Wait device timeout", + "event": [ + { + "listen": "test", + "script": { + "id": "89517dc9-31b4-40e7-89e4-99dd6e792e6a", + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "ed20bbf6-4f31-4d91-a07b-7a0b4afcf161", + "exec": [ + "var timeout = parseInt(pm.environment.get(\"device_timeout\")) + 500", + "", + "setTimeout(function () {}, timeout);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "" + ] + } + }, + "response": [] + }, + { + "name": "Read device", + "event": [ + { + "listen": "test", + "script": { + "id": "7b7496f5-2fdc-470c-b394-2a5ae7d29da2", + "exec": [ + "pm.test(\"Successful GET request\", function () {", + " pm.expect(pm.response.code).to.equal(404);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "1807f152-7ef4-4277-9db6-1bff53d6cc8b", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/{{device_token}}", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "{{device_token}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + }, + { + "name": "read devices", + "item": [ + { + "name": "Prep: Create device A", + "event": [ + { + "listen": "test", + "script": { + "id": "a7803c5c-f371-4ae0-8b08-1c06995c9b60", + "exec": [ + "var response = pm.response.json();", + "var token = response.token;", + "", + "pm.globals.set(\"device_token_a\", token)" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "" + ] + } + }, + "response": [] + }, + { + "name": "Prep: Create device B", + "event": [ + { + "listen": "test", + "script": { + "id": "02450903-aef9-4ee6-9d48-94ba9b5fd54c", + "exec": [ + "var response = pm.response.json();", + "var token = response.token;", + "", + "pm.globals.set(\"device_token_b\", token)" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "" + ] + } + }, + "response": [] + }, + { + "name": "Read devices", + "event": [ + { + "listen": "test", + "script": { + "id": "1612417b-bed8-48d5-a6a3-39d0d0fe6932", + "exec": [ + "pm.test(\"Successful GET request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});", + "", + "var response = pm.response.json();", + "", + "pm.test('Schema is valid', function() {", + " pm.expect(response).to.be.instanceof(Array);", + " response.forEach(element => {", + " pm.expect(element).to.have.property(\"token\");", + " pm.expect(element).to.have.property(\"user_agent\");", + " pm.expect(element).to.have.property(\"last_active\");", + " pm.expect(element).to.have.property(\"name\");", + " })", + "});", + "", + "pm.test('Data is valid', function() {", + " pm.expect(response).to.have.lengthOf(2);", + " var devices_left = [", + " pm.globals.get(\"device_token_a\"), ", + " pm.globals.get(\"device_token_b\")", + " ]", + " response.forEach(element => {", + " pm.expect(typeof element.token).to.equal(\"string\");", + " pm.expect(typeof element.user_agent).to.equal(\"string\");", + " pm.expect(typeof element.last_active).to.equal(\"string\");", + " pm.expect(typeof element.name).to.equal(\"string\");", + " pm.expect(devices_left).to.include(element.token);", + " devices_left.splice(devices_left.indexOf(element.token), 1);", + " })", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "" + ] + } + }, + "response": [] + }, + { + "name": "Clean up: Wait device timeout", + "event": [ + { + "listen": "test", + "script": { + "id": "9b7ff623-8aac-46d1-9c56-1ceb8acc2de6", + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "id": "c0fbf742-f75e-4f3b-a576-75c9d5e0f72d", + "exec": [ + "var timeout = parseInt(pm.environment.get(\"device_timeout\")) + 500", + "", + "setTimeout(function () {}, timeout);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "port": "{{port}}", + "path": [ + "{{web_root}}", + "api", + "devices", + "" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {}, + "_postman_isSubFolder": true + } + ], + "protocolProfileBehavior": {} + } + ], + "protocolProfileBehavior": {} +} \ No newline at end of file diff --git a/testing/web-platform/tests/tools/wave/testing/devices_manager.py b/testing/web-platform/tests/tools/wave/testing/devices_manager.py new file mode 100644 index 000000000000..c03572e25903 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/testing/devices_manager.py @@ -0,0 +1,117 @@ +import time +import uuid + +from threading import Timer + +from ..data.device import Device +from ..testing.event_dispatcher import DEVICES, DEVICE_ADDED_EVENT, DEVICE_REMOVED_EVENT +from ..utils.user_agent_parser import parse_user_agent +from ..utils.serializer import serialize_device +from ..data.exceptions.not_found_exception import NotFoundException + + +DEVICE_TIMEOUT = 60000 # 60sec +RECONNECT_TIME = 5000 # 5sec + +class DevicesManager(object): + def initialize(self, event_dispatcher): + self.devices = {} + self._event_dispatcher = event_dispatcher + self._timer = None + + def create_device(self, user_agent): + browser = parse_user_agent(user_agent) + name = "{} {}".format(browser["name"], browser["version"]) + token = str(uuid.uuid1()) + last_active = int(time.time() * 1000) + + device = Device(token, user_agent, name, last_active) + + self._event_dispatcher.dispatch_event( + DEVICES, + DEVICE_ADDED_EVENT, + serialize_device(device)) + self.add_to_cache(device) + + self._set_timer(DEVICE_TIMEOUT) + + return device + + def read_device(self, token): + if token not in self.devices: + raise NotFoundException("Could not find device '{}'".format(token)) + return self.devices[token] + + def read_devices(self): + devices = [] + for key in self.devices: + devices.append(self.devices[key]) + + return devices + + def update_device(self, device): + if device.token not in self.devices: + return + self.devices[device.token] = device + + def delete_device(self, token): + if token not in self.devices: + return + device = self.devices[token] + del self.devices[token] + self._event_dispatcher.dispatch_event( + DEVICES, + DEVICE_REMOVED_EVENT, + serialize_device(device)) + + def refresh_device(self, token): + if token not in self.devices: + return + device = self.devices[token] + device.last_active = int(time.time() * 1000) + self.update_device(device) + + def post_event(self, handle, event_type, data): + if event_type is None: + return + self._event_dispatcher.dispatch_event(handle, event_type, data) + + def post_global_event(self, event_type, data): + self.post_event(DEVICES, event_type, data) + + def _set_timer(self, timeout): + if self._timer is not None: + return + + def handle_timeout(self): + self._timer = None + now = int(time.time() * 1000) + timed_out_devices = [] + for token in self.devices: + device = self.devices[token] + if now - device.last_active < DEVICE_TIMEOUT: + continue + timed_out_devices.append(token) + + for token in timed_out_devices: + self.delete_device(token) + + oldest_active_time = None + for token in self.devices: + device = self.devices[token] + if oldest_active_time is None: + oldest_active_time = device.last_active + else: + if oldest_active_time > device.last_active: + oldest_active_time = device.last_active + if oldest_active_time is not None: + self._set_timer(now - oldest_active_time) + + self._timer = Timer(timeout / 1000.0, handle_timeout, [self]) + self._timer.start() + + def add_to_cache(self, device): + if device.token in self.devices: + return + + self.devices[device.token] = device diff --git a/testing/web-platform/tests/tools/wave/testing/event_dispatcher.py b/testing/web-platform/tests/tools/wave/testing/event_dispatcher.py index bb578d1c6838..09165396ae75 100644 --- a/testing/web-platform/tests/tools/wave/testing/event_dispatcher.py +++ b/testing/web-platform/tests/tools/wave/testing/event_dispatcher.py @@ -1,39 +1,146 @@ +import uuid +import time +from threading import Timer + + STATUS_EVENT = "status" RESUME_EVENT = "resume" TEST_COMPLETED_EVENT = "test_completed" +DEVICES = "devices" +DEVICE_ADDED_EVENT = "device_added" +DEVICE_REMOVED_EVENT = "device_removed" class EventDispatcher(object): - def __init__(self): - self._clients = {} + def __init__(self, event_cache_duration): + self._listeners = {} + self._events = {} + self._current_events = {} + self._cache_duration = event_cache_duration + self._cache_timeout = None - def add_session_client(self, client): - token = client.session_token - if token not in self._clients: - self._clients[token] = [] - self._clients[token].append(client) + def add_event_listener(self, listener, last_event_number=None): + token = listener.dispatcher_token - def remove_session_client(self, client_to_delete): - if client_to_delete is None: - return - token = client_to_delete.session_token - if token not in self._clients: + if last_event_number is not None \ + and token in self._current_events \ + and self._current_events[token] > last_event_number: + diff_events = self._get_diff_events(token, last_event_number) + if len(diff_events) > 0: + listener.send_message(diff_events) + return + + if token not in self._listeners: + self._listeners[token] = [] + self._listeners[token].append(listener) + listener.token = str(uuid.uuid1()) + return listener.token + + def remove_event_listener(self, listener_token): + if listener_token is None: return - for client in self._clients[token]: - if client.session_token == client_to_delete.session_token: - self._clients.remove(client) - break - if len(self._clients[token]) == 0: - del self._clients[token] + for dispatcher_token in self._listeners: + for listener in self._listeners[dispatcher_token]: + if listener.token == listener_token: + self._listeners[dispatcher_token].remove(listener) + if len(self._listeners[dispatcher_token]) == 0: + del self._listeners[dispatcher_token] + return - def dispatch_event(self, token, event_type, data): - if token not in self._clients: + def dispatch_event(self, dispatcher_token, event_type, data=None): + if dispatcher_token not in self._current_events: + self._current_events[dispatcher_token] = -1 + + if dispatcher_token not in self._events: + self._events[dispatcher_token] = [] + + self._add_to_cache(dispatcher_token, event_type, data) + self._set_cache_timer() + + if dispatcher_token not in self._listeners: return + event = { "type": event_type, - "data": data + "data": data, + "number": self._current_events[dispatcher_token] } - for client in self._clients[token]: - client.send_message(event) + for listener in self._listeners[dispatcher_token]: + listener.send_message([event]) + + def _get_diff_events(self, dispatcher_token, last_event_number): + token = dispatcher_token + diff_events = [] + if token not in self._events: + return diff_events + for event in self._events[token]: + if event["number"] <= last_event_number: + continue + diff_events.append({ + "type": event["type"], + "data": event["data"], + "number": event["number"] + }) + return diff_events + + def _set_cache_timer(self): + if self._cache_timeout is not None: + return + + events = self._read_cached_events() + if len(events) == 0: + return + + next_event = events[0] + for event in events: + if next_event["expiration_date"] > event["expiration_date"]: + next_event = event + + timeout = next_event["expiration_date"] / 1000.0 - time.time() + if timeout < 0: + timeout = 0 + + def handle_timeout(self): + self._delete_expired_events() + self._cache_timeout = None + self._set_cache_timer() + + self._cache_timeout = Timer(timeout, handle_timeout, [self]) + self._cache_timeout.start() + + def _delete_expired_events(self): + events = self._read_cached_events() + now = int(time.time() * 1000) + + for event in events: + if event["expiration_date"] < now: + self._remove_from_cache(event) + + def _add_to_cache(self, dispatcher_token, event_type, data): + self._current_events[dispatcher_token] += 1 + current_event_number = self._current_events[dispatcher_token] + event = { + "type": event_type, + "data": data, + "number": current_event_number, + "expiration_date": int(time.time() * 1000) + self._cache_duration + } + self._events[dispatcher_token].append(event) + + def _remove_from_cache(self, event): + for dispatcher_token in self._events: + for cached_event in self._events[dispatcher_token]: + if cached_event is not event: + continue + self._events[dispatcher_token].remove(cached_event) + if len(self._events[dispatcher_token]) == 0: + del self._events[dispatcher_token] + return + + def _read_cached_events(self): + events = [] + for dispatcher_token in self._events: + events = events + self._events[dispatcher_token] + return events diff --git a/testing/web-platform/tests/tools/wave/testing/results_manager.py b/testing/web-platform/tests/tools/wave/testing/results_manager.py index eea3aa9903f4..978c12d75f98 100644 --- a/testing/web-platform/tests/tools/wave/testing/results_manager.py +++ b/testing/web-platform/tests/tools/wave/testing/results_manager.py @@ -5,6 +5,7 @@ import json import hashlib import zipfile import time +from threading import Timer from ..utils.user_agent_parser import parse_user_agent, abbreviate_browser_name from ..utils.serializer import serialize_session @@ -17,6 +18,9 @@ from .wpt_report import generate_report, generate_multi_report from ..data.session import COMPLETED WAVE_SRC_DIR = "./tools/wave" +RESULTS_FILE_REGEX = r"^\w\w\d\d\d?\.json$" +RESULTS_FILE_PATTERN = re.compile(RESULTS_FILE_REGEX) +SESSION_RESULTS_TIMEOUT = 60*30 # 30min class ResultsManager(object): @@ -25,17 +29,18 @@ class ResultsManager(object): results_directory_path, sessions_manager, tests_manager, - import_enabled, + import_results_enabled, reports_enabled, persisting_interval ): self._results_directory_path = results_directory_path self._sessions_manager = sessions_manager self._tests_manager = tests_manager - self._import_enabled = import_enabled + self._import_results_enabled = import_results_enabled self._reports_enabled = reports_enabled self._results = {} self._persisting_interval = persisting_interval + self._timeouts = {} def create_result(self, token, data): result = self.prepare_result(data) @@ -82,10 +87,10 @@ class ResultsManager(object): if filter_path is not None: filter_api = next((p for p in filter_path.split("/") if p is not None), None) - cached_results = self._read_from_cache(token) - persisted_results = self.load_results(token) - results = self._combine_results_by_api(cached_results, - persisted_results) + results = self._read_from_cache(token) + if results == []: + results = self.load_results(token) + self._set_session_cache(token, results) filtered_results = {} @@ -209,6 +214,7 @@ class ResultsManager(object): if test in failed_tests[api]: continue failed_tests[api].append(test) + return passed_tests def read_results_wpt_report_uri(self, token, api): api_directory = os.path.join(self._results_directory_path, token, api) @@ -244,8 +250,7 @@ class ResultsManager(object): return for api in list(self._results[token].keys())[:]: self.save_api_results(token, api) - self.create_info_file(session) - self._clear_cache_api(token, api) + self.create_info_file(session) session.recent_completed_count = 0 self._sessions_manager.update_session(session) @@ -282,22 +287,28 @@ class ResultsManager(object): if api not in self._results[token]: self._results[token][api] = [] self._results[token][api].append(result) + self._set_timeout(token) + + def _set_session_cache(self, token, results): + if token is None: + return + self._results[token] = results + self._set_timeout(token) def _read_from_cache(self, token): if token is None: return [] if token not in self._results: return [] + self._set_timeout(token) return self._results[token] - def _clear_cache_api(self, token, api): + def _clear_session_cache(self, token): if token is None: return if token not in self._results: return - if api not in self._results[token]: - return - del self._results[token][api] + del self._results[token] def _combine_results_by_api(self, result_a, result_b): combined_result = {} @@ -534,7 +545,7 @@ class ResultsManager(object): zip.write(file_path, file_name, zipfile.ZIP_DEFLATED) zip.close() - with open(zip_file_name, "rb") as file: + with open(zip_file_name, "r") as file: blob = file.read() os.remove(zip_file_name) @@ -575,8 +586,8 @@ class ResultsManager(object): return blob - def is_import_enabled(self): - return self._import_enabled + def is_import_results_enabled(self): + return self._import_results_enabled def are_reports_enabled(self): return self._reports_enabled @@ -592,7 +603,7 @@ class ResultsManager(object): return deserialize_session(info) def import_results(self, blob): - if not self.is_import_enabled: + if not self.is_import_results_enabled: raise PermissionDeniedException() tmp_file_name = "{}.zip".format(str(time.time())) @@ -614,9 +625,35 @@ class ResultsManager(object): os.makedirs(destination_path) zip.extractall(destination_path) self.remove_tmp_files() - self.load_results() + self.load_results(token) return token + def import_results_api_json(self, token, api, blob): + if not self.is_import_results_enabled: + raise PermissionDeniedException() + destination_path = os.path.join(self._results_directory_path, token, api) + files = os.listdir(destination_path) + file_name = "" + for file in files: + if RESULTS_FILE_PATTERN.match(file): + file_name = file + break + destination_file_path = os.path.join(destination_path, file_name) + with open(destination_file_path, "wb") as file: + file.write(blob) + + self.generate_report(token, api) + + session = self._sessions_manager.read_session(token) + if session is None: + raise NotFoundException() + + results = self.load_results(token) + test_state = self.parse_test_state(results) + session.test_state = test_state + + self._sessions_manager.update_session(session) + def remove_tmp_files(self): files = os.listdir(".") @@ -624,3 +661,12 @@ class ResultsManager(object): if re.match(r"\d{10}\.\d{2}\.zip", file) is None: continue os.remove(file) + + def _set_timeout(self, token): + if token in self._timeouts: + self._timeouts[token].cancel() + + def handler(self, token): + self._clear_session_cache(token) + + self._timeouts[token] = Timer(SESSION_RESULTS_TIMEOUT, handler, [self, token]) diff --git a/testing/web-platform/tests/tools/wave/testing/sessions_manager.py b/testing/web-platform/tests/tools/wave/testing/sessions_manager.py index b552ac6f2e04..900b7c0dd1ec 100644 --- a/testing/web-platform/tests/tools/wave/testing/sessions_manager.py +++ b/testing/web-platform/tests/tools/wave/testing/sessions_manager.py @@ -2,6 +2,7 @@ import uuid import time import os import json +import re from threading import Timer @@ -25,7 +26,8 @@ class SessionsManager(object): event_dispatcher, tests_manager, results_directory, - results_manager): + results_manager, + configuration): self._test_loader = test_loader self._sessions = {} self._expiration_timeout = None @@ -33,17 +35,18 @@ class SessionsManager(object): self._tests_manager = tests_manager self._results_directory = results_directory self._results_manager = results_manager + self._configuration = configuration def create_session( self, tests=None, - types=None, + test_types=None, timeouts=None, reference_tokens=None, - webhook_urls=None, user_agent=None, labels=None, - expiration_date=None + expiration_date=None, + type=None ): if tests is None: tests = {} @@ -51,8 +54,6 @@ class SessionsManager(object): timeouts = {} if reference_tokens is None: reference_tokens = [] - if webhook_urls is None: - webhook_urls = [] if user_agent is None: user_agent = "" if labels is None: @@ -63,19 +64,19 @@ class SessionsManager(object): if "exclude" not in tests: tests["exclude"] = [] if "automatic" not in timeouts: - timeouts["automatic"] = DEFAULT_TEST_AUTOMATIC_TIMEOUT + timeouts["automatic"] = self._configuration["timeouts"]["automatic"] if "manual" not in timeouts: - timeouts["manual"] = DEFAULT_TEST_MANUAL_TIMEOUT - if types is None: - types = DEFAULT_TEST_TYPES + timeouts["manual"] = self._configuration["timeouts"]["manual"] + if test_types is None: + test_types = DEFAULT_TEST_TYPES - for type in types: - if type != "automatic" and type != "manual": - raise InvalidDataException("Unknown type '{}'".format(type)) + for test_type in test_types: + if test_type != "automatic" and test_type != "manual": + raise InvalidDataException("Unknown type '{}'".format(test_type)) token = str(uuid.uuid1()) pending_tests = self._test_loader.get_tests( - types, + test_types, include_list=tests["include"], exclude_list=tests["exclude"], reference_tokens=reference_tokens) @@ -96,21 +97,24 @@ class SessionsManager(object): "total": test_files_count[api], "complete": 0} + date_created = int(time.time() * 1000) + session = Session( token=token, tests=tests, user_agent=user_agent, browser=browser, - types=types, + test_types=test_types, timeouts=timeouts, pending_tests=pending_tests, running_tests={}, test_state=test_state, status=PENDING, reference_tokens=reference_tokens, - webhook_urls=webhook_urls, labels=labels, - expiration_date=expiration_date + type=type, + expiration_date=expiration_date, + date_created=date_created ) self._push_to_cache(session) @@ -124,11 +128,27 @@ class SessionsManager(object): return None session = self._read_from_cache(token) if session is None or session.test_state is None: + print("loading session from file system") session = self.load_session(token) if session is not None: self._push_to_cache(session) return session + def read_sessions(self, index=None, count=None): + if index is None: + index = 0 + if count is None: + count = 10 + self.load_all_sessions_info() + sessions = [] + for it_index, token in enumerate(self._sessions): + if it_index < index: + continue + if len(sessions) == count: + break + sessions.append(token) + return sessions + def read_session_status(self, token): if token is None: return None @@ -158,7 +178,7 @@ class SessionsManager(object): self._push_to_cache(session) def update_session_configuration( - self, token, tests, types, timeouts, reference_tokens, webhook_urls + self, token, tests, test_types, timeouts, reference_tokens, type ): session = self.read_session(token) if session is None: @@ -173,14 +193,14 @@ class SessionsManager(object): tests["exclude"] = session.tests["exclude"] if reference_tokens is None: reference_tokens = session.reference_tokens - if types is None: - types = session.types + if test_types is None: + test_types = session.test_types pending_tests = self._test_loader.get_tests( include_list=tests["include"], exclude_list=tests["exclude"], reference_tokens=reference_tokens, - types=types + test_types=test_types ) session.pending_tests = pending_tests session.tests = tests @@ -198,8 +218,8 @@ class SessionsManager(object): } session.test_state = test_state - if types is not None: - session.types = types + if test_types is not None: + session.test_types = test_types if timeouts is not None: if AUTOMATIC not in timeouts: timeouts[AUTOMATIC] = session.timeouts[AUTOMATIC] @@ -208,8 +228,8 @@ class SessionsManager(object): session.timeouts = timeouts if reference_tokens is not None: session.reference_tokens = reference_tokens - if webhook_urls is not None: - session.webhook_urls = webhook_urls + if type is not None: + session.type = type self._push_to_cache(session) return session @@ -306,7 +326,7 @@ class SessionsManager(object): if self._expiration_timeout is not None: self._expiration_timeout.cancel() - timeout = next_session.expiration_date / 1000.0 - int(time.time()) + timeout = next_session.expiration_date / 1000 - time.time() if timeout < 0: timeout = 0 @@ -319,10 +339,10 @@ class SessionsManager(object): def _delete_expired_sessions(self): expiring_sessions = self._read_expiring_sessions() - now = int(time.time()) + now = int(time.time() * 1000) for session in expiring_sessions: - if session.expiration_date / 1000.0 < now: + if session.expiration_date < now: self.delete_session(session.token) def _read_expiring_sessions(self): @@ -344,7 +364,7 @@ class SessionsManager(object): return if session.status == PENDING: - session.date_started = int(time.time()) * 1000 + session.date_started = int(time.time() * 1000) session.expiration_date = None session.status = RUNNING @@ -374,7 +394,7 @@ class SessionsManager(object): if session.status == ABORTED or session.status == COMPLETED: return session.status = ABORTED - session.date_finished = time.time() * 1000 + session.date_finished = int(time.time() * 1000) self.update_session(session) self._event_dispatcher.dispatch_event( token, @@ -398,7 +418,7 @@ class SessionsManager(object): if session.status == COMPLETED or session.status == ABORTED: return session.status = COMPLETED - session.date_finished = time.time() * 1000 + session.date_finished = int(time.time() * 1000) self.update_session(session) self._event_dispatcher.dispatch_event( token, @@ -427,6 +447,20 @@ class SessionsManager(object): return api not in session.pending_tests \ and api not in session.running_tests + def get_test_path_with_query(self, test, session): + query_string = "" + include_list = session.tests["include"] + for include_test in include_list: + split = include_test.split("?") + query = "" + if len(split) > 1: + include_test = split[0] + query = split[1] + pattern = re.compile("^" + include_test) + if pattern.match(test) is not None: + query_string += query + "&" + return "{}?{}".format(test, query_string) + def find_token(self, fragment): if len(fragment) < 8: return None @@ -437,3 +471,6 @@ class SessionsManager(object): if len(tokens) != 1: return None return tokens[0] + + def get_total_sessions(self): + return len(self._sessions) diff --git a/testing/web-platform/tests/tools/wave/testing/test_loader.py b/testing/web-platform/tests/tools/wave/testing/test_loader.py index 10de74a4b7ff..d9f06910db2b 100644 --- a/testing/web-platform/tests/tools/wave/testing/test_loader.py +++ b/testing/web-platform/tests/tools/wave/testing/test_loader.py @@ -70,7 +70,7 @@ class TestLoader(object): paths.append(test) continue if test.endswith(".js"): - for element in tests[test]: + for element in tests[test][1:]: paths.append(element[0]) continue return paths @@ -98,6 +98,7 @@ class TestLoader(object): if include_list is not None and len(include_list) > 0: is_valid = False for include_test in include_list: + include_test = include_test.split("?")[0] pattern = re.compile("^" + include_test) if pattern.match(test_path) is not None: is_valid = True @@ -109,6 +110,7 @@ class TestLoader(object): if exclude_list is not None and len(exclude_list) > 0: is_valid = True for exclude_test in exclude_list: + exclude_test = exclude_test.split("?")[0] pattern = re.compile("^" + exclude_test) if pattern.match(test_path) is not None: is_valid = False @@ -136,13 +138,13 @@ class TestLoader(object): def get_tests( self, - types=None, + test_types=None, include_list=None, exclude_list=None, reference_tokens=None ): - if types is None: - types = [AUTOMATIC, MANUAL] + if test_types is None: + test_types = [AUTOMATIC, MANUAL] if include_list is None: include_list = [] if exclude_list is None: @@ -155,7 +157,7 @@ class TestLoader(object): reference_results = self._results_manager.read_common_passed_tests( reference_tokens) - for test_type in types: + for test_type in test_types: if test_type not in TEST_TYPES: continue for api in self._tests[test_type]: @@ -164,7 +166,8 @@ class TestLoader(object): include_list): continue if reference_results is not None and \ - test_path not in reference_results[api]: + (api not in reference_results or + (api in reference_results and test_path not in reference_results[api])): continue if api not in loaded_tests: loaded_tests[api] = [] diff --git a/testing/web-platform/tests/tools/wave/testing/tests_manager.py b/testing/web-platform/tests/tools/wave/testing/tests_manager.py index 26bbdb86526c..99355219bdcd 100644 --- a/testing/web-platform/tests/tools/wave/testing/tests_manager.py +++ b/testing/web-platform/tests/tools/wave/testing/tests_manager.py @@ -6,32 +6,6 @@ from .event_dispatcher import TEST_COMPLETED_EVENT from ..data.exceptions.not_found_exception import NotFoundException from ..data.session import COMPLETED, ABORTED -# Helper class used to adapt a specific compare function to a Python 3 -# key function. -class CmpWrapper: - def __init__(self, test_manager, compare_function, obj): - self.obj = obj - self.test_manager = test_manager - self.compare_function = compare_function - - def __lt__(self, other): - return self.compare_function(self.test_manager, self.obj, other.obj) < 0 - - def __gt__(self, other): - return self.compare_function(self.test_manager, self.obj, other.obj) > 0 - - def __eq__(self, other): - return self.compare_function(self.test_manager, self.obj, other.obj) == 0 - - def __le__(self, other): - return self.compare_function(self.test_manager, self.obj, other.obj) <= 0 - - def __ge__(self, other): - return self.compare_function(self.test_manager, self.obj, other.obj) >= 0 - - def __ne__(self, other): - return self.compare_function(self.test_manager, self.obj, other.obj) != 0 - class TestsManager(object): def initialize( @@ -115,6 +89,8 @@ class TestsManager(object): if potential_result["test"] == test: result = potential_result break + if result is None: + break if result["status"] == "ERROR": if len(tests["fail"]) < count: @@ -144,29 +120,35 @@ class TestsManager(object): for test in tests[api]: sorted_tests.append(test) - def compare(tests_manager, test_a, test_b): - micro_test_list = {} - api_a = "" - for part in test_a.split("/"): - if part != "": - api_a = part - break - api_b = "" - for part in test_b.split("/"): - if part != "": - api_b = part - break - if api_a == api_b: - micro_test_list[api_a] = [test_a, test_b] - else: - micro_test_list[api_a] = [test_a] - micro_test_list[api_b] = [test_b] - next_test = tests_manager._get_next_test_from_list(micro_test_list) - if next_test == test_a: - return -1 - return 1 + class compare(object): + def __init__(self, tests_manager, test): + self.test = test + self.tests_manager = tests_manager - return sorted(sorted_tests, key=lambda x:CmpWrapper(self, compare, x)) + def __lt__(self, test_b): + test_a = self.test + test_b = test_b.test + micro_test_list = {} + api_a = "" + for part in test_a.split("/"): + if part != "": + api_a = part + break + api_b = "" + for part in test_b.split("/"): + if part != "": + api_b = part + break + if api_a == api_b: + micro_test_list[api_a] = [test_a, test_b] + else: + micro_test_list[api_a] = [test_a] + micro_test_list[api_b] = [test_b] + next_test = self.tests_manager._get_next_test_from_list(micro_test_list) + return next_test == test_b + + sorted_tests.sort(key=lambda test: compare(self, test)) + return sorted_tests def _get_next_test_from_list(self, tests): test = None @@ -180,7 +162,7 @@ class TestsManager(object): apis.sort(key=lambda api: api.lower()) for api in apis: - tests[api].sort(key=lambda api: api.replace("/", "").lower()) + tests[api].sort(key=lambda test: test.replace("/", "").lower()) while test is None: if len(apis) <= current_api: @@ -332,7 +314,7 @@ class TestsManager(object): ) self._event_dispatcher.dispatch_event( - token=session.token, + dispatcher_token=session.token, event_type=TEST_COMPLETED_EVENT, data=test ) @@ -377,7 +359,7 @@ class TestsManager(object): def load_tests(self, session): pending_tests = self._test_loader.get_tests( - session.types, + session.test_types, include_list=session.tests["include"], exclude_list=session.tests["exclude"], reference_tokens=session.reference_tokens diff --git a/testing/web-platform/tests/tools/wave/tests/WAVE Server REST API Tests.postman_collection.json b/testing/web-platform/tests/tools/wave/tests/WAVE Server REST API Tests.postman_collection.json index a0801bb77e35..c5a07a335248 100644 --- a/testing/web-platform/tests/tools/wave/tests/WAVE Server REST API Tests.postman_collection.json +++ b/testing/web-platform/tests/tools/wave/tests/WAVE Server REST API Tests.postman_collection.json @@ -1,7 +1,7 @@ { "info": { - "_postman_id": "ccd6117a-6d61-4617-a6a1-7115db4d4d92", - "name": "WAVE Server REST API Tests", + "_postman_id": "69fb733b-51ec-49b2-aa04-e2330c1d26b6", + "name": "WAVE Server REST API Tests copy", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ @@ -14,7 +14,7 @@ { "listen": "test", "script": { - "id": "051bef94-5544-4ddb-9d85-167677ebecb2", + "id": "7c432e7c-d0f9-4583-977a-55ca5d1e42a2", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -94,7 +94,7 @@ { "listen": "test", "script": { - "id": "a8bf3e41-a7df-4c6b-8a20-3a1e6d8a51d9", + "id": "71470bf7-293b-4863-a74e-193d86f2e6ac", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -119,7 +119,7 @@ { "listen": "prerequest", "script": { - "id": "4370546f-b08c-4f77-9bd9-1cd14400665e", + "id": "7d59b7a7-78f8-46fc-a3f1-bd06f7ec31b7", "exec": [ "var expirationDate = Date.now() + 10000;", "pm.globals.set(\"expiration_date\", expirationDate);" @@ -164,7 +164,7 @@ { "listen": "test", "script": { - "id": "3dcb5b6c-9151-49f7-a1a6-74475927b304", + "id": "daaa5a98-f330-4ca5-afc5-c4051e7a94c1", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -214,7 +214,7 @@ { "listen": "test", "script": { - "id": "8d05578a-e2d6-41e9-a2a4-aa7d92bfbbce", + "id": "d6e7a096-5bed-44b0-8772-ab80c1f53447", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -251,7 +251,7 @@ { "listen": "test", "script": { - "id": "4c5e3ba4-e27b-4341-8cf8-74ed047a8747", + "id": "66075d71-55ed-4835-b6b5-ada854653d04", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -331,7 +331,7 @@ { "listen": "test", "script": { - "id": "1081afd8-a772-4565-b03d-b58f773cbb65", + "id": "a365eb0b-ba55-490a-8cd9-7286a1d39cd2", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -378,7 +378,7 @@ { "listen": "test", "script": { - "id": "268e8a31-87bb-4ec5-81d5-87dc74096828", + "id": "52719d31-ef36-4b74-8c5c-05d554e916c0", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -437,7 +437,7 @@ { "listen": "prerequest", "script": { - "id": "625fcc2a-f7b1-403c-b5c5-56db7c5bcee5", + "id": "753a7451-2a92-4de0-a2e2-d0d9c66de134", "exec": [ "const token = pm.globals.get(\"session_token\");", "pm.globals.set(\"session_token_fragment\", token.split(\"-\").shift());" @@ -448,7 +448,7 @@ { "listen": "test", "script": { - "id": "8e04212f-e259-413f-98ee-e366cd3adfdd", + "id": "2a465de6-f66e-4161-ad7e-7c1c3738befd", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -513,7 +513,7 @@ { "listen": "prerequest", "script": { - "id": "6f8429e3-9c12-423c-baaf-8f8de0e4ea49", + "id": "0e7ff96f-8c5a-46f3-a1a0-258ad59b96e0", "exec": [ "const token = pm.globals.get(\"session_token\");", "pm.globals.set(\"session_token_fragment\", token.split(\"-\").shift());" @@ -524,7 +524,7 @@ { "listen": "test", "script": { - "id": "7f6ef567-274e-4f9d-b7fc-50f8240547e9", + "id": "eb36e017-4cdc-458a-b399-8998df800fa1", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -588,7 +588,7 @@ { "listen": "test", "script": { - "id": "11f5c620-dc3b-4a8c-8d2f-f9663025c79f", + "id": "9c8af59c-8d9b-4264-acb5-a0bdf3af434a", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -625,7 +625,7 @@ { "listen": "test", "script": { - "id": "c67cde7e-7237-479c-a0c6-b84a533c3b1e", + "id": "c16bf66e-283e-40b9-b98d-8b058ec1575a", "exec": [ "const response = pm.response.json();", "const token = response.token;", @@ -637,7 +637,7 @@ { "listen": "prerequest", "script": { - "id": "24b38d64-5c24-415f-b6f6-f8d252d69ac8", + "id": "66780bc8-88e6-44d2-a6c8-5c649141accd", "exec": [ "var automaticTimeout = 120000;", "var manualTimeout = 1000000;", @@ -692,7 +692,7 @@ { "listen": "test", "script": { - "id": "0806e4e0-bd1b-40ba-a7d5-74d41473a141", + "id": "82ca12ad-3be5-419a-9a45-14164b0eaf60", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -739,7 +739,7 @@ { "listen": "test", "script": { - "id": "9b1f2dec-9949-49ff-b466-f602b11dde5d", + "id": "135d7d9e-1869-4c02-9c6a-7c0ea522e140", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -776,7 +776,7 @@ { "listen": "test", "script": { - "id": "7fea2977-5ccb-49ee-8f07-79a7f2245f9c", + "id": "1bfe8c41-2144-46de-824d-2d1e3c88205b", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -827,7 +827,7 @@ { "listen": "test", "script": { - "id": "7cc27ee8-2f06-4e25-98fb-9872c6cf0d93", + "id": "02cfcc68-43ed-4533-a80c-699954900390", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -874,7 +874,7 @@ { "listen": "test", "script": { - "id": "0a73a5eb-3edb-4d15-924d-260dd22bd831", + "id": "c8f3d89b-34ca-4d39-977e-638d3630ee5b", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -943,7 +943,7 @@ { "listen": "test", "script": { - "id": "94877ec6-70ea-4c78-acb5-20060a535653", + "id": "bcde140e-51cf-4d7c-b745-5e56f9b59034", "exec": [ "const response = pm.response.json();", "const token = response.token;", @@ -955,7 +955,7 @@ { "listen": "prerequest", "script": { - "id": "dcc29092-b7a1-40fc-8359-ecc655dba482", + "id": "c4354118-88ff-4b5a-9252-dac19264c262", "exec": [ "var automaticTimeout = 120000;", "var manualTimeout = 1000000;", @@ -1005,7 +1005,7 @@ { "listen": "test", "script": { - "id": "8f6e2bf2-6c1e-434e-83c5-9bcc57880692", + "id": "737fc854-7901-4819-96eb-4883f5b15bc9", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1042,7 +1042,7 @@ { "listen": "test", "script": { - "id": "a0a80c05-a2ca-430a-8bed-46ad7978c684", + "id": "a28a515f-0e48-4c23-a418-3b30c1e6dc71", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1079,7 +1079,7 @@ { "listen": "test", "script": { - "id": "00d823e0-2059-4cab-81d6-8d1de9e5b62a", + "id": "1b842c47-c67c-4bfe-a77d-7d5b553e001b", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1126,7 +1126,7 @@ { "listen": "test", "script": { - "id": "b8211c13-667d-4970-aedc-6398c25cc40c", + "id": "e2318e93-7446-43be-ad52-99201a85fcbf", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1163,7 +1163,7 @@ { "listen": "test", "script": { - "id": "8aad813d-53fe-49fd-bdfc-f16ce2233ae2", + "id": "c3bdd37f-c048-4a2a-979b-deca61758292", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1241,7 +1241,7 @@ { "listen": "test", "script": { - "id": "305cb915-8496-4577-b17a-e8189d66c3d1", + "id": "506a2f52-ab98-4e61-943c-ca0f7cab7f28", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1307,7 +1307,7 @@ { "listen": "test", "script": { - "id": "9d903165-9667-43b0-b210-a950d0fa1fa2", + "id": "85ee0aec-9fbd-4544-a938-fb821e576fac", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1344,7 +1344,7 @@ { "listen": "test", "script": { - "id": "0307919b-6630-48ff-ac18-10c0a47ea254", + "id": "f42cdf2c-5304-420d-b79d-f3345ac4455d", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1393,7 +1393,7 @@ { "listen": "test", "script": { - "id": "f66408d5-5438-4d4f-9bfc-6d24b15e5a90", + "id": "01d6bfab-8ec5-4317-8595-667778be42d8", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1430,7 +1430,7 @@ { "listen": "test", "script": { - "id": "bae5542d-446b-4064-88ff-cc355a8f4f62", + "id": "dec9ab62-5014-4b6e-a747-be83dce71a3d", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1483,7 +1483,7 @@ { "listen": "test", "script": { - "id": "3b386952-8a37-471a-9192-f28974d975a3", + "id": "1db3770b-6fa9-40b9-875c-56d113d0ff5b", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1520,7 +1520,7 @@ { "listen": "test", "script": { - "id": "8e56d842-325b-4356-ae1a-2465e9efe188", + "id": "84ccae85-36d6-4226-850c-269cbfddff8e", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1569,7 +1569,7 @@ { "listen": "test", "script": { - "id": "a183cb0b-31f0-48f6-9c20-605a16488d7d", + "id": "29e3a3ed-7b75-43a2-99b3-7300bd39044a", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1606,7 +1606,7 @@ { "listen": "test", "script": { - "id": "f2610594-813a-4079-afae-1510486efab4", + "id": "614689fb-9d26-4ac1-b89c-eca305a957f2", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1655,7 +1655,7 @@ { "listen": "test", "script": { - "id": "6b8ec98a-ad91-4d5c-aa07-6b3cc1255902", + "id": "221d9b75-af5c-4aa9-b13b-6d3af10578aa", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1692,7 +1692,7 @@ { "listen": "test", "script": { - "id": "91a38eb8-a501-457f-a9f4-b63221c957f6", + "id": "34147223-f4f2-4c11-a60b-09396d46c152", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1741,7 +1741,7 @@ { "listen": "test", "script": { - "id": "6e3a8c64-04cb-4c4f-b23d-64655f6e4d22", + "id": "41b686b9-80e9-4c16-906d-ca948c0873a6", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1778,7 +1778,7 @@ { "listen": "test", "script": { - "id": "dcf7f6f4-6eef-4a90-a63d-df70446eb209", + "id": "7a053b4c-87c6-426c-95d6-750c5d478aba", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1827,7 +1827,7 @@ { "listen": "test", "script": { - "id": "bee7a70d-af1f-4ad1-9c40-7abdbcc983ae", + "id": "52600705-47f5-4ec7-bf3c-ff2243022625", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1864,7 +1864,7 @@ { "listen": "test", "script": { - "id": "217ed267-1ddb-496a-a7c5-6e29bad97c60", + "id": "39c0a314-3efe-4977-8c20-33666ea8e0e6", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1917,7 +1917,7 @@ { "listen": "test", "script": { - "id": "a255ea61-5c5a-4f0d-8a67-c244510a608c", + "id": "0d53b83e-5b8b-4d69-980f-ddc2736d2a5f", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1954,7 +1954,7 @@ { "listen": "test", "script": { - "id": "9cc7599b-d378-4b04-bada-9d76f6ca610f", + "id": "4217abb8-3e6e-4125-8aaf-92b378136f87", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -1995,7 +1995,7 @@ { "listen": "prerequest", "script": { - "id": "81e44e3f-5d91-42a7-b0ab-4704eb121868", + "id": "f66a7794-ade7-41e7-abc6-560af7acfd53", "type": "text/javascript", "exec": [ "" @@ -2005,7 +2005,7 @@ { "listen": "test", "script": { - "id": "a44180cb-3b13-421b-b146-82901f2b45bd", + "id": "04ca0247-e30e-470a-8282-d7a5eafc5b92", "type": "text/javascript", "exec": [ "" @@ -2025,7 +2025,7 @@ { "listen": "test", "script": { - "id": "47fb1fa9-4849-4ea9-b36e-fcec8decf62e", + "id": "0a17b42b-dc5e-4588-88c5-b8bc29065dd0", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2062,7 +2062,7 @@ { "listen": "test", "script": { - "id": "1923ced4-4361-4a4a-bf7c-4fcdf3df50f6", + "id": "5b908a57-2fe5-4309-86ab-933c196badfd", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2103,7 +2103,7 @@ { "listen": "prerequest", "script": { - "id": "226043e5-1a81-4d36-aa7f-67b10bf407af", + "id": "fe61b75f-f106-43f6-bcaf-ad4400ae1ef4", "type": "text/javascript", "exec": [ "" @@ -2113,7 +2113,7 @@ { "listen": "test", "script": { - "id": "840bf970-0984-452e-9076-b6f66d0b4511", + "id": "f94a7f25-72eb-4bcf-9ddb-573ca2cfbb1f", "type": "text/javascript", "exec": [ "" @@ -2133,7 +2133,7 @@ { "listen": "test", "script": { - "id": "88c4072f-538b-4d68-9d61-f0fb017e544c", + "id": "c3915b66-bcd4-4a23-8b74-2fd9c16210d8", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2170,7 +2170,7 @@ { "listen": "test", "script": { - "id": "2ae00eb3-49e8-4487-b27f-e7c7955fe4f4", + "id": "c94d14bf-e48f-4a85-8939-e45bc66be6d0", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2211,7 +2211,7 @@ { "listen": "prerequest", "script": { - "id": "70e0c783-c405-481c-b9c8-5c8bee392f97", + "id": "eb6a4051-3cd7-4d75-93ec-8f0c4463cc98", "type": "text/javascript", "exec": [ "" @@ -2221,7 +2221,7 @@ { "listen": "test", "script": { - "id": "85c45e3b-fae6-4a90-afd0-97678769e32c", + "id": "64295661-300d-4773-8856-6c3ecbe158b0", "type": "text/javascript", "exec": [ "" @@ -2263,7 +2263,7 @@ { "listen": "test", "script": { - "id": "df4a00d1-d4f8-4b0b-892d-21528165a2cf", + "id": "4d33ee50-4368-42f3-b8a4-ab0ff27f00f6", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2321,7 +2321,7 @@ { "listen": "prerequest", "script": { - "id": "4fa59617-c922-4826-abc6-6d910e918ea4", + "id": "6a217097-fdf7-4c99-b005-723acf9b8389", "type": "text/javascript", "exec": [ "" @@ -2331,7 +2331,7 @@ { "listen": "test", "script": { - "id": "d83f449d-4a2e-41c8-b2cd-5cf0cd1c404e", + "id": "37c80a2a-9508-4e8d-93d5-dfbfac837bed", "type": "text/javascript", "exec": [ "" @@ -2351,7 +2351,7 @@ { "listen": "test", "script": { - "id": "98da2b86-4f9f-47dd-afc9-22ce96e73e84", + "id": "6d0635aa-2b63-4e0f-89d2-da4d47741160", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2388,7 +2388,7 @@ { "listen": "test", "script": { - "id": "6d8a8202-c85d-4ada-b443-45108a372aea", + "id": "90895f8b-5398-4eeb-9f35-5869736dc46c", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2459,7 +2459,7 @@ { "listen": "test", "script": { - "id": "0f9df486-91cd-46fc-a711-5cf9bebb2706", + "id": "e4929021-a5b2-4839-8537-bfb7ea1935c2", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2518,7 +2518,7 @@ { "listen": "test", "script": { - "id": "80ee91fd-98ca-4b8e-83b6-32bf3e55a295", + "id": "2e2703a2-d74f-46ba-b168-9f3b64888086", "exec": [ "" ], @@ -2553,7 +2553,7 @@ { "listen": "test", "script": { - "id": "47f4d26a-84c2-4980-984d-dac0e95503e1", + "id": "df98f07d-4bb3-467d-92aa-e9122db42e31", "exec": [ "" ], @@ -2587,7 +2587,7 @@ { "listen": "prerequest", "script": { - "id": "6bd9e85f-eb73-47be-83b0-01be77868899", + "id": "c3e166ef-de4b-4c02-89c3-547aaa7a6555", "type": "text/javascript", "exec": [ "" @@ -2597,7 +2597,7 @@ { "listen": "test", "script": { - "id": "43ebfadb-dc68-4c41-b34e-462e427dc2ef", + "id": "a86891f3-8f1d-48c6-9e08-0a792bb1627a", "type": "text/javascript", "exec": [ "" @@ -2617,7 +2617,7 @@ { "listen": "test", "script": { - "id": "029ec84b-e41b-4744-8745-58c50e52bb11", + "id": "3ea151c0-8286-419f-bed6-0c06da912850", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2654,7 +2654,7 @@ { "listen": "test", "script": { - "id": "071d757c-41aa-4b52-9ab4-6c2afd6ed2af", + "id": "f10cf08b-9a1a-4f3e-8b3e-8be52f82734e", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2695,7 +2695,7 @@ { "listen": "prerequest", "script": { - "id": "305720a0-2c84-43d3-8fe5-55f8fdf511d8", + "id": "a1b3bf2e-4d09-4ee1-9063-6c4c842fcd94", "type": "text/javascript", "exec": [ "" @@ -2705,7 +2705,7 @@ { "listen": "test", "script": { - "id": "84e4ab04-5acc-4428-a81a-c0b0c53b5fd9", + "id": "a6745e90-5382-445d-9d65-db74aff7af1b", "type": "text/javascript", "exec": [ "" @@ -2747,7 +2747,7 @@ { "listen": "test", "script": { - "id": "b7a9d459-9b39-40b2-abdc-fd8fe8b21d61", + "id": "da2ec442-44fd-432e-b335-abe4dd73c182", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2772,7 +2772,7 @@ { "listen": "prerequest", "script": { - "id": "fcbfb0e3-a10d-4ff8-afed-f5f3c9d52090", + "id": "47665454-d468-4964-8167-bd80051a3937", "exec": [ "const availableTests = pm.globals.get(\"available_tests\");", "const test = availableTests[Object.keys(availableTests)[0]][0]", @@ -2819,7 +2819,7 @@ { "listen": "test", "script": { - "id": "937ef61d-d6d6-4e24-bc3a-696a23cc95d6", + "id": "8a575b02-40ff-404c-a344-d768c218caba", "exec": [ "" ], @@ -2854,7 +2854,7 @@ { "listen": "test", "script": { - "id": "29f0554b-66f5-4e26-80ff-39b3faba920a", + "id": "e8ab1a02-2be0-43ee-be8b-04967b5aaff3", "exec": [ "const response = pm.response.json();", "const nextTest = response.next_test;", @@ -2926,7 +2926,7 @@ { "listen": "prerequest", "script": { - "id": "9bf66427-6f39-4f0a-b065-1849d679eee6", + "id": "be410b75-d674-4ef3-8dda-5731f4dbcbd5", "type": "text/javascript", "exec": [ "" @@ -2936,7 +2936,7 @@ { "listen": "test", "script": { - "id": "8d1f3837-1f05-47e4-897c-b7c74da9bd43", + "id": "706efcea-90f2-4206-8246-b82240e719fb", "type": "text/javascript", "exec": [ "" @@ -2956,7 +2956,7 @@ { "listen": "test", "script": { - "id": "9bf0fb70-24a0-4136-99fe-8e0e488afd1e", + "id": "a68edf67-0a80-450d-8f4e-921626491163", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -2993,7 +2993,7 @@ { "listen": "test", "script": { - "id": "7e3184bf-1741-44c8-8cd3-cff30c2deca1", + "id": "d3495b7e-e4b7-47a0-9589-f2d145915846", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3042,7 +3042,7 @@ { "listen": "test", "script": { - "id": "e323c6af-d4e5-4a05-b203-09ba67f77d28", + "id": "f1ca8fab-80d3-4d92-887e-018cb6536a9f", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3079,7 +3079,7 @@ { "listen": "test", "script": { - "id": "40cd04ce-850f-4903-bc24-2e62a4df98fe", + "id": "c220fdc3-b483-40fa-ae88-bf7f57415d90", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3128,7 +3128,7 @@ { "listen": "test", "script": { - "id": "db7d5209-37b2-46bd-88ab-ce4e241401b2", + "id": "a9130ab9-562e-48d9-a7c1-f8cb3bb8d85e", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3165,7 +3165,7 @@ { "listen": "test", "script": { - "id": "477dc63d-0b43-4226-b86e-163e2de92b67", + "id": "013fa69e-f98b-4124-ac3f-cbc1bb4c5078", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3249,7 +3249,7 @@ { "listen": "test", "script": { - "id": "b923d95e-a7b4-49d4-ab2a-1f435c454387", + "id": "6ed4bed9-b4d6-4463-beca-4c4c09a23e44", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3308,7 +3308,7 @@ { "listen": "test", "script": { - "id": "81f36759-7ae2-42b9-81d2-79f57046b46e", + "id": "e2eba0d4-9e38-4a5c-9aa0-9874c70f0788", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3390,7 +3390,7 @@ { "listen": "test", "script": { - "id": "2873d554-9816-41f0-9051-fb2cb1272a76", + "id": "22d812a9-5d6f-4cbb-924e-cbe4392072ad", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3447,7 +3447,7 @@ { "listen": "test", "script": { - "id": "53605764-d4f0-4b32-9a30-67e84dd104d9", + "id": "d06b4cb9-f5b9-4a88-8a24-a195e59c298e", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3508,7 +3508,7 @@ { "listen": "prerequest", "script": { - "id": "cf165ae9-1c44-483c-8fe4-bd7cdaa20710", + "id": "bcf419eb-495a-4558-95de-45dea07bd6b0", "type": "text/javascript", "exec": [ "" @@ -3518,7 +3518,7 @@ { "listen": "test", "script": { - "id": "6938d456-afd5-431b-a95f-a2c45a3ac479", + "id": "86795630-9b19-49f3-988e-ac089ef76187", "type": "text/javascript", "exec": [ "" @@ -3538,7 +3538,7 @@ { "listen": "test", "script": { - "id": "657be5b3-4a99-4415-a1ef-9dfdcf541e46", + "id": "181e1f95-34d7-4c2c-bc3c-b6d5f411807d", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3550,7 +3550,7 @@ { "listen": "prerequest", "script": { - "id": "093a0019-a84c-44fc-b890-36369d51b7d5", + "id": "46e33bfe-0783-49f8-a9bc-98e26a34b55e", "exec": [ "var automaticTimeout = 120000;", "var manualTimeout = 1000000;", @@ -3601,7 +3601,7 @@ { "listen": "test", "script": { - "id": "4ac55601-9bdf-41f8-9d59-ff1ee20fa471", + "id": "dd86be15-3e08-491e-b56b-274531c348e6", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3696,7 +3696,7 @@ { "listen": "test", "script": { - "id": "0b49c01b-e943-4879-b441-4093836d05e9", + "id": "569dc8f6-39d4-4ec6-9466-64154671e4bc", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3779,7 +3779,7 @@ { "listen": "test", "script": { - "id": "b9cba19f-88d2-41f6-940a-a0979da05500", + "id": "db83ae93-7a70-4267-8a2f-3d2e7cb40b88", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3862,7 +3862,7 @@ { "listen": "prerequest", "script": { - "id": "3669c70a-88bc-4854-abac-5cb80a21c392", + "id": "52cc597e-30b7-426e-a15e-3a628c66619a", "type": "text/javascript", "exec": [ "" @@ -3872,7 +3872,7 @@ { "listen": "test", "script": { - "id": "1a81f5c5-28d8-4935-b8ae-822e33c23da9", + "id": "60d6def7-cb5b-433f-8a4e-33bd8e20be53", "type": "text/javascript", "exec": [ "" @@ -3895,7 +3895,7 @@ { "listen": "test", "script": { - "id": "9de9321c-ae78-4abd-a740-6634e2cbb9b9", + "id": "a1c2e070-9309-4dee-ae71-6edf4cbe3630", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -3920,7 +3920,7 @@ { "listen": "prerequest", "script": { - "id": "739284e7-17ac-4c34-baa1-933353eb7ecc", + "id": "1f03ad23-73c7-4e64-9de9-abe902e2b90c", "exec": [ "" ], @@ -3964,7 +3964,7 @@ { "listen": "test", "script": { - "id": "5b15f338-c773-4f48-8f78-1d7c926beb81", + "id": "bf76f47c-960c-4007-a59b-92793e470082", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4050,7 +4050,7 @@ { "listen": "test", "script": { - "id": "e69b7188-b3c9-4f66-8c6c-3f3348f84f6a", + "id": "20cc9f7d-b4bb-4bb2-a628-4cfddd86112a", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4098,7 +4098,7 @@ { "listen": "test", "script": { - "id": "86e20c4d-5797-4b6e-a8cc-14e284e7c491", + "id": "11fcc51b-effd-4522-8126-6b6a20435927", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4215,7 +4215,7 @@ { "listen": "test", "script": { - "id": "e85fd539-4598-47a9-8768-656656f29fec", + "id": "2b7198ef-6051-40ee-af4f-466bf41e3768", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4278,7 +4278,7 @@ { "listen": "test", "script": { - "id": "8e9fe4af-0d50-49eb-8a4a-00e13021b4a6", + "id": "cabccfe1-882e-48bf-9946-4b8f67b09f04", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4314,7 +4314,7 @@ { "listen": "test", "script": { - "id": "9418112a-03f6-4641-893e-076c8922c0af", + "id": "cad95ed0-822a-433b-b411-adf3b2e5ea77", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -4359,7 +4359,7 @@ { "listen": "test", "script": { - "id": "a6995ff1-d626-4f4f-a1e0-567f3429bf52", + "id": "3c82b199-e27f-4b86-b98d-9f10ad2b363f", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4384,7 +4384,7 @@ { "listen": "prerequest", "script": { - "id": "85c7ad8d-5fc9-403d-8715-c65d5ff1323e", + "id": "70ad16d3-5df7-4c94-9498-f5abd0b6f76c", "exec": [ "const availableTests = pm.globals.get(\"available_tests\");", "const test1 = availableTests[Object.keys(availableTests)[0]][0];", @@ -4433,7 +4433,7 @@ { "listen": "test", "script": { - "id": "35cf4745-0301-4c9c-b6b3-40945299533f", + "id": "074aff68-931d-4d31-b138-d9068bc57422", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4470,7 +4470,7 @@ { "listen": "test", "script": { - "id": "6dc082ef-80ff-407a-a562-e25e37476eee", + "id": "9440583c-b8a5-4488-93aa-e37daf96485a", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4529,7 +4529,7 @@ { "listen": "test", "script": { - "id": "fcade663-dbd8-42c9-8344-53ae1feb6fcb", + "id": "50b9afe7-63ef-40ae-a6ea-5abbd40f254d", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4581,7 +4581,7 @@ { "listen": "test", "script": { - "id": "36d70d08-3caa-4312-9bbd-aed15fd238dd", + "id": "f0c77dd0-d424-4b6e-8bfa-3e0ea5908514", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4636,7 +4636,7 @@ { "listen": "test", "script": { - "id": "211adcf7-d71a-4b0f-94ab-d285fc8367df", + "id": "bfdb82c2-1411-4e22-810b-a1f7edb8d2f4", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4694,7 +4694,7 @@ { "listen": "test", "script": { - "id": "5410c9c2-ebbe-4bc1-ac12-e2602d8ade57", + "id": "2a60296d-953b-430d-a21d-df99265b90c1", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4751,7 +4751,7 @@ { "listen": "test", "script": { - "id": "e4f9a03f-c731-4192-a9a9-aa9e315c3c44", + "id": "42168b0e-8be9-4211-8c4a-5a0776a3d131", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4812,7 +4812,7 @@ { "listen": "test", "script": { - "id": "9f684f77-4ed0-4cd2-99a4-b1c134432e9f", + "id": "95a23ee1-f9e9-4b6a-abf5-4afa48ba5254", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4859,7 +4859,7 @@ { "listen": "test", "script": { - "id": "33601dd0-6786-4c8a-a6b5-7f1386129de7", + "id": "707c403b-7135-4e0b-aae8-4fa4f07daac9", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4918,7 +4918,7 @@ { "listen": "test", "script": { - "id": "97be92f1-a8d1-41ea-b41d-0ca08113e6e1", + "id": "7b77cb94-4159-462c-877b-23b48446a1bb", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -4973,7 +4973,7 @@ { "listen": "test", "script": { - "id": "24ec2b79-c266-473f-8579-7b694079d640", + "id": "8ba5726b-ed37-48f5-9389-4f6041b1eeaa", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5031,7 +5031,7 @@ { "listen": "test", "script": { - "id": "1f47b39f-727f-43b4-934d-c7f62b268baa", + "id": "5ac4de17-caf3-43b2-8eb8-fee26eed5c96", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5127,7 +5127,7 @@ { "listen": "test", "script": { - "id": "e734f074-a0c2-4e54-b427-2827fa732843", + "id": "38afbc94-502e-4aa7-85c2-70dbbdae7e13", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5195,7 +5195,7 @@ { "listen": "test", "script": { - "id": "fd2c11ee-5eca-43f3-89a7-133602c8034a", + "id": "85cf7940-73d0-4419-81a1-390c9c4c3a1c", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5252,7 +5252,7 @@ { "listen": "test", "script": { - "id": "112ee3d5-d4e0-4838-879e-f74c38d0218c", + "id": "3e288d2c-865a-48ab-ac53-cddd160a9840", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5310,7 +5310,7 @@ { "listen": "test", "script": { - "id": "79a84ed9-bb07-493d-ab34-8e7693b3ebbd", + "id": "786dbb67-47e8-4009-991c-1670d0ab04e1", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5357,7 +5357,7 @@ { "listen": "test", "script": { - "id": "c1829043-18dd-4cc1-8091-41981349f4e3", + "id": "db8fcfe2-89f3-4e1c-be82-059412218073", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5416,7 +5416,7 @@ { "listen": "test", "script": { - "id": "59fee9df-9945-4fdf-a102-9c1b92d915c1", + "id": "f8938178-c924-4038-a1cb-5d6fc88c08d2", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5474,7 +5474,7 @@ { "listen": "test", "script": { - "id": "91fa38ac-3b06-488a-892e-59458e88a4da", + "id": "5ff3fe93-2649-4874-9ae6-ce0bc2887685", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5534,7 +5534,7 @@ { "listen": "test", "script": { - "id": "dca0b42e-9054-4354-87e5-e8d236216050", + "id": "fee15d71-abfc-46f6-ad63-bdf67c42905d", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5639,7 +5639,7 @@ { "listen": "test", "script": { - "id": "14ec9254-5a40-4c86-9a7f-72f43a35a79b", + "id": "9f739832-3e90-4efc-96cf-2889f08af718", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5722,7 +5722,7 @@ { "listen": "test", "script": { - "id": "132574dc-3688-47e3-8f50-3235f18806f7", + "id": "353cde4e-5c57-4df7-b69e-7dac5cf408e3", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5824,7 +5824,7 @@ { "listen": "test", "script": { - "id": "2f233dcf-d934-4479-8908-b1349e6ea54d", + "id": "84a0a44c-fdac-42e5-a3f8-7bbe7fc01fd8", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5840,7 +5840,7 @@ { "listen": "prerequest", "script": { - "id": "2930a821-2c6e-47b9-89d9-90ebe1cff52a", + "id": "6748c537-4286-48bb-9a10-7c1828d7f6e8", "exec": [ "const availableTests = pm.globals.get(\"available_tests\");", "const test1 = availableTests[Object.keys(availableTests)[0]][0];", @@ -5891,7 +5891,7 @@ { "listen": "test", "script": { - "id": "12f3616b-707e-4a71-8db8-89fc195c1fcd", + "id": "579feb39-25c5-482a-95e1-be6861144110", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5928,7 +5928,7 @@ { "listen": "test", "script": { - "id": "f35f1509-383f-49af-a30e-41e1df4efa04", + "id": "2399a9f6-0689-4035-8f01-22ad4726232b", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -5974,7 +5974,7 @@ { "listen": "test", "script": { - "id": "b015f161-e390-4224-9f26-92395f44c772", + "id": "75177890-a772-4bcc-9d94-6a7f485b647b", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6028,7 +6028,7 @@ { "listen": "test", "script": { - "id": "da3d0c17-6b2b-4d61-ad01-dba736425388", + "id": "e35de151-f1d5-4cb4-b403-953bb7dca87f", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6044,7 +6044,7 @@ { "listen": "prerequest", "script": { - "id": "7da81e81-d344-4459-b700-19b93d49d3c9", + "id": "30b29828-cd1c-469a-873c-77a31ce7b7f4", "exec": [ "const availableTests = pm.globals.get(\"available_tests\");", "const test1 = availableTests[Object.keys(availableTests)[0]][0];", @@ -6093,7 +6093,7 @@ { "listen": "test", "script": { - "id": "3296c7ba-67a6-405d-a78a-51dd64432301", + "id": "29cdd92a-28c4-426e-9f53-8563bc3a36e7", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6130,7 +6130,7 @@ { "listen": "test", "script": { - "id": "72deb55c-4583-4741-9cf8-97713762b894", + "id": "8cc61693-a77c-467c-a7df-5c239a3bf569", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6176,7 +6176,7 @@ { "listen": "test", "script": { - "id": "d695f300-a773-4654-b346-95c536cad19e", + "id": "ae30e47e-bb3d-4479-9455-3823bc2adf88", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6234,7 +6234,7 @@ { "listen": "test", "script": { - "id": "6838965f-7a27-48d5-8c27-f55d4756588f", + "id": "e614e97d-15e4-4c9b-987f-bd9f272bd295", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6271,7 +6271,7 @@ { "listen": "test", "script": { - "id": "bcc2c8c4-94c0-4227-a627-3e0d5be2e7b8", + "id": "e72a7387-bd42-4221-be2a-21bca702d1fe", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6308,7 +6308,7 @@ { "listen": "test", "script": { - "id": "7b370723-0506-4b5e-941a-f31a628a29f6", + "id": "dbcd722b-480a-4544-807a-6f7d6edcb441", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6356,7 +6356,7 @@ { "listen": "test", "script": { - "id": "b5243979-b63a-4bbf-bd51-1529da1e1f6d", + "id": "9e302610-c7a7-4de0-989a-9903e77499eb", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6368,7 +6368,7 @@ { "listen": "prerequest", "script": { - "id": "81f3da29-8a0c-4403-929d-86590b01ef2f", + "id": "9d79c233-e3a3-4972-af76-aa3dfa2ade4b", "exec": [ "" ], @@ -6404,7 +6404,7 @@ { "listen": "test", "script": { - "id": "0168ea3f-9bf7-4ea7-a3ad-745c2704d97e", + "id": "0a43dacd-8106-414c-b9b4-750c2a0375a4", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6421,7 +6421,7 @@ { "listen": "prerequest", "script": { - "id": "e4efdac9-2d00-4853-b145-18a6d44ff139", + "id": "4b29e651-47e5-48ae-aa5d-b04b62e47e54", "exec": [ "" ], @@ -6467,7 +6467,7 @@ { "listen": "test", "script": { - "id": "e685a989-8ef7-4e8f-a3c7-b579c66430f9", + "id": "d0ededde-44a0-48f3-bc21-eab3302b7b4d", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6492,7 +6492,7 @@ { "listen": "prerequest", "script": { - "id": "8517360b-2e66-4ca9-9339-017622c39888", + "id": "ef1d181c-10b8-4bdd-9b5c-f14d6d9ca046", "exec": [ "var automaticTimeout = 120000;", "var manualTimeout = 1000000;", @@ -6542,7 +6542,7 @@ { "listen": "test", "script": { - "id": "4e85ecaf-4364-4681-ab8a-221d40681c44", + "id": "e4ed838e-02e3-4fe7-9f27-b9aed6d72d02", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6586,7 +6586,7 @@ { "listen": "test", "script": { - "id": "a6221560-9eba-4fb4-a7cb-a1570a37425d", + "id": "40b5a135-a839-4ffc-98e5-6d8923d53743", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6639,7 +6639,7 @@ { "listen": "test", "script": { - "id": "77d14567-4600-44fd-98e4-99e113da6781", + "id": "32953640-3b13-4df0-8af2-36544ebfe59e", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6685,7 +6685,7 @@ { "listen": "test", "script": { - "id": "55b486da-c5ae-49d4-b11a-247387267c9a", + "id": "784734d2-61db-4ff3-b063-ba9fc07176d3", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6738,7 +6738,7 @@ { "listen": "test", "script": { - "id": "953002ec-d8df-477a-a0dc-f4e4a2efd031", + "id": "75a2bbc5-47aa-4708-b783-2c5e36f59e1a", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -6779,640 +6779,6 @@ ], "protocolProfileBehavior": {} }, - { - "name": "Devices API", - "item": [ - { - "name": "create", - "item": [ - { - "name": "Create device", - "event": [ - { - "listen": "test", - "script": { - "id": "286426ec-c394-48a4-a90f-c6dd80c7bef1", - "exec": [ - "pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", - "});", - "", - "var response = pm.response.json();", - "", - "pm.test('Schema is valid', function() {", - " pm.expect(response).to.have.property(\"token\");", - "});", - "", - "pm.test('Data is valid', function() {", - " pm.expect(typeof response.token).to.equal(\"string\");", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "" - ] - } - }, - "response": [] - }, - { - "name": "Clean up: Wait device timeout", - "event": [ - { - "listen": "test", - "script": { - "id": "3bdb0f7c-1261-465e-9b02-0070005f0c43", - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "a1cf5880-7b8e-4bc6-9d92-59632a719c54", - "exec": [ - "var timeout = parseInt(pm.environment.get(\"device_timeout\")) + 500", - "", - "setTimeout(function () {}, timeout);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true - }, - { - "name": "read device", - "item": [ - { - "name": "Device not found", - "item": [ - { - "name": "Read device", - "event": [ - { - "listen": "test", - "script": { - "id": "a4401cbc-0a3a-4c2d-b1ed-e7d6867614ff", - "exec": [ - "pm.test(\"Successful GET request\", function () {", - " pm.expect(pm.response.code).to.equal(404);", - "});" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "1644fd70-b4c4-4207-8e12-67752a984417", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/invalid_token", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "invalid_token" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true - }, - { - "name": "Device found", - "item": [ - { - "name": "Prep: Create device", - "event": [ - { - "listen": "test", - "script": { - "id": "c4f14137-5ced-449b-a872-b7d541e2f6af", - "exec": [ - "var response = pm.response.json();", - "var token = response.token;", - "", - "pm.globals.set(\"device_token\", token)" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "" - ] - } - }, - "response": [] - }, - { - "name": "Read device", - "event": [ - { - "listen": "test", - "script": { - "id": "61a840f1-d3cd-4d7a-a54f-30da1266e48f", - "exec": [ - "pm.test(\"Successful GET request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});", - "", - "var response = pm.response.json();", - "", - "pm.test('Schema is valid', function() {", - " pm.expect(response).to.have.property(\"token\");", - " pm.expect(response).to.have.property(\"user_agent\");", - " pm.expect(response).to.have.property(\"last_active\");", - " pm.expect(response).to.have.property(\"name\");", - "});", - "", - "pm.test('Data is valid', function() {", - " pm.expect(typeof response.token).to.equal(\"string\");", - " pm.expect(typeof response.user_agent).to.equal(\"string\");", - " pm.expect(typeof response.last_active).to.equal(\"string\");", - " pm.expect(typeof response.name).to.equal(\"string\");", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/{{device_token}}", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "{{device_token}}" - ] - } - }, - "response": [] - }, - { - "name": "Clean up: Wait device timeout", - "event": [ - { - "listen": "test", - "script": { - "id": "17112336-b962-472d-86a8-7872ff9876d1", - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "912939cc-1685-4e1f-b0a6-696b264870ca", - "exec": [ - "var timeout = parseInt(pm.environment.get(\"device_timeout\")) + 500", - "", - "setTimeout(function () {}, timeout);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true - }, - { - "name": "Timed out device", - "item": [ - { - "name": "Prep: Create device", - "event": [ - { - "listen": "test", - "script": { - "id": "c99b9318-1e50-4702-9d81-53d87faa1080", - "exec": [ - "var response = pm.response.json();", - "var token = response.token;", - "", - "pm.globals.set(\"device_token\", token)" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "" - ] - } - }, - "response": [] - }, - { - "name": "Wait device timeout", - "event": [ - { - "listen": "test", - "script": { - "id": "89517dc9-31b4-40e7-89e4-99dd6e792e6a", - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "ed20bbf6-4f31-4d91-a07b-7a0b4afcf161", - "exec": [ - "var timeout = parseInt(pm.environment.get(\"device_timeout\")) + 500", - "", - "setTimeout(function () {}, timeout);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "" - ] - } - }, - "response": [] - }, - { - "name": "Read device", - "event": [ - { - "listen": "test", - "script": { - "id": "7b7496f5-2fdc-470c-b394-2a5ae7d29da2", - "exec": [ - "pm.test(\"Successful GET request\", function () {", - " pm.expect(pm.response.code).to.equal(404);", - "});" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "1807f152-7ef4-4277-9db6-1bff53d6cc8b", - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/{{device_token}}", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "{{device_token}}" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true - } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true - }, - { - "name": "read devices", - "item": [ - { - "name": "Prep: Create device A", - "event": [ - { - "listen": "test", - "script": { - "id": "a7803c5c-f371-4ae0-8b08-1c06995c9b60", - "exec": [ - "var response = pm.response.json();", - "var token = response.token;", - "", - "pm.globals.set(\"device_token_a\", token)" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "" - ] - } - }, - "response": [] - }, - { - "name": "Prep: Create device B", - "event": [ - { - "listen": "test", - "script": { - "id": "02450903-aef9-4ee6-9d48-94ba9b5fd54c", - "exec": [ - "var response = pm.response.json();", - "var token = response.token;", - "", - "pm.globals.set(\"device_token_b\", token)" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "" - ] - } - }, - "response": [] - }, - { - "name": "Read devices", - "event": [ - { - "listen": "test", - "script": { - "id": "1612417b-bed8-48d5-a6a3-39d0d0fe6932", - "exec": [ - "pm.test(\"Successful GET request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});", - "", - "var response = pm.response.json();", - "", - "pm.test('Schema is valid', function() {", - " pm.expect(response).to.be.instanceof(Array);", - " response.forEach(element => {", - " pm.expect(element).to.have.property(\"token\");", - " pm.expect(element).to.have.property(\"user_agent\");", - " pm.expect(element).to.have.property(\"last_active\");", - " pm.expect(element).to.have.property(\"name\");", - " })", - "});", - "", - "pm.test('Data is valid', function() {", - " pm.expect(response).to.have.lengthOf(2);", - " var devices_left = [", - " pm.globals.get(\"device_token_a\"), ", - " pm.globals.get(\"device_token_b\")", - " ]", - " response.forEach(element => {", - " pm.expect(typeof element.token).to.equal(\"string\");", - " pm.expect(typeof element.user_agent).to.equal(\"string\");", - " pm.expect(typeof element.last_active).to.equal(\"string\");", - " pm.expect(typeof element.name).to.equal(\"string\");", - " pm.expect(devices_left).to.include(element.token);", - " devices_left.splice(devices_left.indexOf(element.token), 1);", - " })", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "" - ] - } - }, - "response": [] - }, - { - "name": "Clean up: Wait device timeout", - "event": [ - { - "listen": "test", - "script": { - "id": "9b7ff623-8aac-46d1-9c56-1ceb8acc2de6", - "exec": [ - "" - ], - "type": "text/javascript" - } - }, - { - "listen": "prerequest", - "script": { - "id": "c0fbf742-f75e-4f3b-a576-75c9d5e0f72d", - "exec": [ - "var timeout = parseInt(pm.environment.get(\"device_timeout\")) + 500", - "", - "setTimeout(function () {}, timeout);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{host}}:{{port}}/{{web_root}}/api/devices/", - "protocol": "{{protocol}}", - "host": [ - "{{host}}" - ], - "port": "{{port}}", - "path": [ - "{{web_root}}", - "api", - "devices", - "" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {}, - "_postman_isSubFolder": true - } - ], - "protocolProfileBehavior": {} - }, { "name": "Sessions API", "item": [ @@ -7428,7 +6794,7 @@ { "listen": "test", "script": { - "id": "fc15d329-d132-4abf-90e4-f549eee99b60", + "id": "2f9076bb-59aa-43f9-992d-49723c0487e7", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -7499,7 +6865,7 @@ { "listen": "test", "script": { - "id": "ebb192ce-48e5-4aac-b99d-68894378eac8", + "id": "3279e145-d372-4feb-a496-8e3cce73e839", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -7558,7 +6924,7 @@ { "listen": "test", "script": { - "id": "159976cd-de00-4f88-8f9f-47db13ca4a30", + "id": "2f52b046-49d6-4ae3-bd84-111c5f9e534a", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -7613,7 +6979,7 @@ { "listen": "test", "script": { - "id": "5058b07b-5e55-44e0-ad88-5c7b5884fa3c", + "id": "86966d77-4519-415d-b893-5af6e18a0acf", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -7663,7 +7029,7 @@ { "listen": "test", "script": { - "id": "62a8c853-bf0b-47b0-9cee-7611d2b48cde", + "id": "b67d346a-b2c9-4d67-af7b-27d2f5b8854f", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -7746,7 +7112,7 @@ { "listen": "prerequest", "script": { - "id": "878420c3-575a-4194-9a72-cebe07823674", + "id": "a4ad3a47-e64a-491b-91cf-57b36a8eb88f", "type": "text/javascript", "exec": [ "" @@ -7756,7 +7122,7 @@ { "listen": "test", "script": { - "id": "6672030f-5a8a-4bea-9792-c24b980392e9", + "id": "ee4ad964-e66e-45fd-9c7a-7895fcbc9e10", "type": "text/javascript", "exec": [ "" @@ -7776,7 +7142,7 @@ { "listen": "test", "script": { - "id": "504b76ca-6870-443c-8c33-0cccc52cddae", + "id": "1a955154-d0c2-4d20-84a7-445da8ecc722", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -7847,7 +7213,7 @@ { "listen": "test", "script": { - "id": "fa956871-503a-421c-9822-4e2a11e1cf1c", + "id": "38973bf0-0737-469d-b2ed-819c1acc6cf3", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -7872,7 +7238,7 @@ { "listen": "prerequest", "script": { - "id": "213bdf59-9f98-4b5e-bacc-ae3cb31ae26a", + "id": "cc54eb7d-209f-4659-8119-ade100bb37ef", "exec": [ "var automaticTimeout = 120000;", "var manualTimeout = 1000000;", @@ -7922,7 +7288,7 @@ { "listen": "test", "script": { - "id": "2656b209-57d8-4f39-8db1-ce3376e8a532", + "id": "dbe16a67-70ba-4989-ab50-bafc8acab84c", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -7990,7 +7356,7 @@ { "listen": "test", "script": { - "id": "2a7524e5-7394-4177-8267-42d5b32bc474", + "id": "c1514c3e-fd96-4e9c-b799-6fd04185bb63", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -8039,7 +7405,7 @@ { "listen": "test", "script": { - "id": "448b6a0c-135d-42ef-8bc0-b4b1ce4ab8a0", + "id": "d31b10eb-4a59-4f92-98b2-73707e37a03b", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -8134,7 +7500,7 @@ { "listen": "prerequest", "script": { - "id": "a16eac8e-9609-4314-abd8-a5c470537b52", + "id": "daf759dc-ea36-4d8f-a524-4dae0cc841dc", "type": "text/javascript", "exec": [ "" @@ -8144,7 +7510,7 @@ { "listen": "test", "script": { - "id": "5df55957-0bbb-4db8-9ab9-d8a70ce37bc5", + "id": "b50f4e6b-23b6-41e1-9bab-cd2ca90b7f43", "type": "text/javascript", "exec": [ "" @@ -8164,7 +7530,7 @@ { "listen": "test", "script": { - "id": "6c4aa702-ae3a-44a4-b886-b3e883de4d32", + "id": "53183c44-31d1-48de-89ae-17950ef47363", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -8189,7 +7555,7 @@ { "listen": "prerequest", "script": { - "id": "893672a9-c666-4790-b6a7-e0ce7afda482", + "id": "6dadba7e-5352-4b9c-9601-f0eb429341ce", "exec": [ "var expirationDate = Date.now() + 3000;", "pm.globals.set(\"expiration_date\", expirationDate);" @@ -8234,7 +7600,7 @@ { "listen": "test", "script": { - "id": "f6b31d37-a98b-4110-80a2-f78db6f2e26f", + "id": "f2bc16d6-099b-487c-a7a6-d48d198ff826", "exec": [ "pm.test(\"Status code is 404\", function () {", " pm.response.to.have.status(404);", @@ -8246,7 +7612,7 @@ { "listen": "prerequest", "script": { - "id": "d41cf31b-c8e8-455f-aa69-527af5fbd66b", + "id": "57f858cd-b882-4fc0-9170-60078cfe5af8", "exec": [ "var expirationDate = pm.globals.get(\"expiration_date\");", "", @@ -8319,7 +7685,7 @@ { "listen": "test", "script": { - "id": "090ca1bc-cbee-4d7b-b19f-fc1f4bb879a0", + "id": "b50313e7-7fd2-434b-a86e-7ff5fb0af1d8", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -8378,7 +7744,7 @@ { "listen": "test", "script": { - "id": "232aff11-3cdc-4994-af42-5e3574583814", + "id": "ef9e2d68-6b36-4ce7-8dba-27ebc7153015", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -8494,7 +7860,7 @@ { "listen": "test", "script": { - "id": "73cbeeb0-019b-4331-b0ec-ef9d3f1e0494", + "id": "8c7bcb56-cd2a-4d99-ad6e-ac4bc4767dce", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -8553,7 +7919,7 @@ { "listen": "test", "script": { - "id": "ba142b55-2454-4472-bcf9-669b0bb7bab9", + "id": "0639b5a0-d419-4642-baf1-5aa4aedf33c9", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", @@ -8636,7 +8002,7 @@ { "listen": "test", "script": { - "id": "f443955f-b57e-4801-8eac-547597381615", + "id": "a1fe1580-1807-45d1-93bb-e54596895c00", "exec": [ "const response = pm.response.json();", "", @@ -8687,7 +8053,7 @@ { "listen": "test", "script": { - "id": "2ca80dee-f9a5-4e0b-bead-6ab871d9edf8", + "id": "8c6722cf-7d5c-4b03-8404-3bdf17e543d9", "exec": [ "const response = pm.response.json();", "const token = response.token;", @@ -8731,7 +8097,7 @@ { "listen": "test", "script": { - "id": "fbf5eafb-0ae7-4105-8df5-51bb53b0c595", + "id": "7478f8f8-c838-4ae0-87ef-9889afc1041d", "exec": [ "const token = pm.globals.get(\"session_token\");", "const response = pm.response.json();", @@ -8819,7 +8185,7 @@ { "listen": "test", "script": { - "id": "bdd64594-c874-4eb2-8b36-4c37cea0f0cb", + "id": "9f59683a-df99-4e3c-b461-85c606196218", "exec": [ "const response = pm.response.json();", "const token = response.token;", @@ -8863,7 +8229,7 @@ { "listen": "test", "script": { - "id": "6732aedf-d99d-416f-b93d-98bc0404ef61", + "id": "1cb72090-e989-4f6d-b19a-cd57388b18dc", "exec": [ "const token = pm.globals.get(\"session_token\");", "const response = pm.response.json();", @@ -8969,7 +8335,7 @@ { "listen": "test", "script": { - "id": "3319e417-dd54-495f-a452-5519d2a61082", + "id": "268fd1fa-4dba-401b-8072-8177a0f250c6", "exec": [ "const response = pm.response.json();", "const token = response.token;", @@ -9013,7 +8379,7 @@ { "listen": "test", "script": { - "id": "6ca3480a-3224-4bf8-a466-fdcb06338795", + "id": "93db4741-b574-4433-8dbd-008611311442", "exec": [ "const token = pm.globals.get(\"session_token\");", "const response = pm.response.json();", @@ -9129,7 +8495,7 @@ { "listen": "test", "script": { - "id": "8b5b4c23-db0a-4b81-9349-ce8ead64bb7a", + "id": "d695b371-c742-4eaf-9179-852b04460ecf", "exec": [ "pm.test(\"Status code is 200\", function () {", " pm.response.to.have.status(200);", diff --git a/testing/web-platform/tests/tools/wave/tests/config.json b/testing/web-platform/tests/tools/wave/tests/config.json new file mode 100644 index 000000000000..672ed1aae98d --- /dev/null +++ b/testing/web-platform/tests/tools/wave/tests/config.json @@ -0,0 +1,6 @@ +{ + "ports": { + "http": [8080, "auto"], + "https": [8483] + } +} diff --git a/testing/web-platform/tests/tools/wave/tests/test_wave.py b/testing/web-platform/tests/tools/wave/tests/test_wave.py index 63c8a660a335..ef3d80d10d4e 100644 --- a/testing/web-platform/tests/tools/wave/tests/test_wave.py +++ b/testing/web-platform/tests/tools/wave/tests/test_wave.py @@ -7,15 +7,13 @@ import time from urllib.request import urlopen from urllib.error import URLError -import pytest - from tools.wpt import wpt -def is_port_8000_in_use(): +def is_port_8080_in_use(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: - s.bind(("127.0.0.1", 8000)) - except OSError as e: + s.bind(("127.0.0.1", 8080)) + except socket.error as e: if e.errno == errno.EADDRINUSE: return True else: @@ -25,25 +23,28 @@ def is_port_8000_in_use(): return False def test_serve(): - if is_port_8000_in_use(): - pytest.skip("WAVE Test Runner failed: Port 8000 already in use.") + if is_port_8080_in_use(): + assert False, "WAVE Test Runner failed: Port 8080 already in use." - p = subprocess.Popen([os.path.join(wpt.localpaths.repo_root, "wpt"), "serve-wave"], - preexec_fn=os.setsid) + p = subprocess.Popen([os.path.join(wpt.localpaths.repo_root, "wpt"), + "serve-wave", + "--config", + os.path.join(wpt.localpaths.repo_root, "tools/wave/tests/config.json")], + preexec_fn=os.setsid) start = time.time() try: while True: if p.poll() is not None: assert False, "WAVE Test Runner failed: Server not running." - if time.time() - start > 6 * 60: - assert False, "WAVE Test Runner failed: Server did not start responding within 6m." + if time.time() - start > 60: + assert False, "WAVE Test Runner failed: Server did not start responding within 60s." try: - resp = urlopen("http://web-platform.test:8000/_wave/api/sessions/public") + resp = urlopen("http://web-platform.test:8080/_wave/api/sessions/public") print(resp) except URLError: - print("Server not responding, waiting another 10s.") - time.sleep(10) + print("URLError") + time.sleep(1) else: assert resp.code == 200 break diff --git a/testing/web-platform/tests/tools/wave/utils/deserializer.py b/testing/web-platform/tests/tools/wave/utils/deserializer.py index 36508bf36557..ae794fb7e89b 100644 --- a/testing/web-platform/tests/tools/wave/utils/deserializer.py +++ b/testing/web-platform/tests/tools/wave/utils/deserializer.py @@ -1,4 +1,9 @@ +from __future__ import absolute_import +from __future__ import unicode_literals from ..data.session import Session, UNKNOWN +from datetime import datetime +import dateutil.parser +from dateutil.tz import tzutc def deserialize_sessions(session_dicts): @@ -19,9 +24,9 @@ def deserialize_session(session_dict): if "path" in session_dict: test_paths = session_dict["path"].split(", ") tests["include"] = tests["include"] + test_paths - types = [] + test_types = [] if "types" in session_dict: - types = session_dict["types"] + test_types = session_dict["types"] user_agent = "" if "user_agent" in session_dict: user_agent = session_dict["user_agent"] @@ -46,12 +51,18 @@ def deserialize_session(session_dict): last_completed_test = None if "last_completed_test" in session_dict: last_completed_test = session_dict["last_completed_test"] + date_created = None + if "date_created" in session_dict: + date_created = session_dict["date_created"] + date_created = iso_to_millis(date_created) date_started = None if "date_started" in session_dict: date_started = session_dict["date_started"] + date_started = iso_to_millis(date_started) date_finished = None if "date_finished" in session_dict: date_finished = session_dict["date_finished"] + date_finished = iso_to_millis(date_finished) is_public = False if "is_public" in session_dict: is_public = session_dict["is_public"] @@ -61,12 +72,13 @@ def deserialize_session(session_dict): browser = None if "browser" in session_dict: browser = session_dict["browser"] - webhook_urls = [] - if "webhook_urls" in session_dict: - webhook_urls = session_dict["webhook_urls"] expiration_date = None if "expiration_date" in session_dict: expiration_date = session_dict["expiration_date"] + expiration_date = iso_to_millis(expiration_date) + type = None + if "type" in session_dict: + type = session_dict["type"] malfunctioning_tests = [] if "malfunctioning_tests" in session_dict: malfunctioning_tests = session_dict["malfunctioning_tests"] @@ -74,7 +86,7 @@ def deserialize_session(session_dict): return Session( token=token, tests=tests, - types=types, + test_types=test_types, user_agent=user_agent, labels=labels, timeouts=timeouts, @@ -83,12 +95,24 @@ def deserialize_session(session_dict): status=status, test_state=test_state, last_completed_test=last_completed_test, + date_created=date_created, date_started=date_started, date_finished=date_finished, is_public=is_public, reference_tokens=reference_tokens, browser=browser, - webhook_urls=webhook_urls, expiration_date=expiration_date, + type=type, malfunctioning_tests=malfunctioning_tests ) + +def iso_to_millis(iso_string): + if iso_string is None: + return None + try: + date = dateutil.parser.isoparse(iso_string) + date = date.replace(tzinfo=tzutc()) + epoch = datetime.utcfromtimestamp(0).replace(tzinfo=tzutc()) + return int((date - epoch).total_seconds() * 1000) + except Exception: + return iso_string diff --git a/testing/web-platform/tests/tools/wave/utils/serializer.py b/testing/web-platform/tests/tools/wave/utils/serializer.py index 2adceb0b6de4..3c1b73b34fa5 100644 --- a/testing/web-platform/tests/tools/wave/utils/serializer.py +++ b/testing/web-platform/tests/tools/wave/utils/serializer.py @@ -1,7 +1,10 @@ +from datetime import datetime + + def serialize_session(session): return { "token": session.token, - "types": session.types, + "types": session.test_types, "user_agent": session.user_agent, "labels": session.labels, "timeouts": session.timeouts, @@ -12,11 +15,31 @@ def serialize_session(session): "running_tests": session.running_tests, "status": session.status, "browser": session.browser, - "date_started": session.date_started, - "date_finished": session.date_finished, + "date_created": millis_to_iso(session.date_created), + "date_started": millis_to_iso(session.date_started), + "date_finished": millis_to_iso(session.date_finished), "is_public": session.is_public, "reference_tokens": session.reference_tokens, - "webhook_urls": session.webhook_urls, - "expiration_date": session.expiration_date, + "expiration_date": millis_to_iso(session.expiration_date), + "type": session.type, "malfunctioning_tests": session.malfunctioning_tests } + +def serialize_sessions(sessions): + serialized_sessions = [] + for session in sessions: + serialized_sessions.append(serialize_session(session)) + return serialized_sessions + +def serialize_device(device): + return { + "token": device.token, + "user_agent": device.user_agent, + "name": device.name, + "last_active": millis_to_iso(device.last_active) + } + +def millis_to_iso(millis): + if millis is None: + return None + return datetime.utcfromtimestamp(millis/1000.0).isoformat() + "+00:00" diff --git a/testing/web-platform/tests/tools/wave/wave_server.py b/testing/web-platform/tests/tools/wave/wave_server.py index bc33bfdc6313..45933db8a9a8 100644 --- a/testing/web-platform/tests/tools/wave/wave_server.py +++ b/testing/web-platform/tests/tools/wave/wave_server.py @@ -7,14 +7,19 @@ from .network.http_handler import HttpHandler from .network.api.sessions_api_handler import SessionsApiHandler from .network.api.tests_api_handler import TestsApiHandler from .network.api.results_api_handler import ResultsApiHandler +from .network.api.devices_api_handler import DevicesApiHandler +from .network.api.general_api_handler import GeneralApiHandler from .network.static_handler import StaticHandler from .testing.sessions_manager import SessionsManager from .testing.results_manager import ResultsManager from .testing.tests_manager import TestsManager +from .testing.devices_manager import DevicesManager from .testing.test_loader import TestLoader from .testing.event_dispatcher import EventDispatcher +VERSION_STRING = "v3.3.0" + class WaveServer(object): def initialize(self, @@ -35,10 +40,13 @@ class WaveServer(object): configuration = configuration_loader.load(configuration_file_path) # Initialize Managers - event_dispatcher = EventDispatcher() + event_dispatcher = EventDispatcher( + event_cache_duration=configuration["event_cache_duration"] + ) sessions_manager = SessionsManager() results_manager = ResultsManager() tests_manager = TestsManager() + devices_manager = DevicesManager() test_loader = TestLoader() sessions_manager.initialize( @@ -46,14 +54,15 @@ class WaveServer(object): event_dispatcher=event_dispatcher, tests_manager=tests_manager, results_directory=configuration["results_directory_path"], - results_manager=results_manager + results_manager=results_manager, + configuration=configuration ) results_manager.initialize( results_directory_path=configuration["results_directory_path"], sessions_manager=sessions_manager, tests_manager=tests_manager, - import_enabled=configuration["import_enabled"], + import_results_enabled=configuration["import_results_enabled"], reports_enabled=reports_enabled, persisting_interval=configuration["persisting_interval"] ) @@ -65,6 +74,8 @@ class WaveServer(object): event_dispatcher=event_dispatcher ) + devices_manager.initialize(event_dispatcher) + exclude_list_file_path = os.path.abspath("./excluded.json") include_list_file_path = os.path.abspath("./included.json") test_loader.initialize( @@ -86,7 +97,8 @@ class WaveServer(object): sessions_manager=sessions_manager, results_manager=results_manager, event_dispatcher=event_dispatcher, - web_root=configuration["web_root"] + web_root=configuration["web_root"], + read_sessions_enabled=configuration["read_sessions_enabled"] ) tests_api_handler = TestsApiHandler( tests_manager=tests_manager, @@ -97,9 +109,25 @@ class WaveServer(object): web_root=configuration["web_root"], test_loader=test_loader ) + devices_api_handler = DevicesApiHandler( + devices_manager=devices_manager, + event_dispatcher=event_dispatcher, + web_root=configuration["web_root"] + ) results_api_handler = ResultsApiHandler( results_manager, - web_root=configuration["web_root"]) + sessions_manager, + web_root=configuration["web_root"] + ) + general_api_handler = GeneralApiHandler( + web_root=configuration["web_root"], + read_sessions_enabled=configuration["read_sessions_enabled"], + import_results_enabled=configuration["import_results_enabled"], + reports_enabled=reports_enabled, + version_string=VERSION_STRING, + test_type_selection_enabled=configuration["enable_test_type_selection"], + test_file_selection_enabled=configuration["enable_test_file_selection"] + ) # Initialize HTTP server http_handler = HttpHandler( @@ -107,6 +135,8 @@ class WaveServer(object): sessions_api_handler=sessions_api_handler, tests_api_handler=tests_api_handler, results_api_handler=results_api_handler, + devices_api_handler=devices_api_handler, + general_api_handler=general_api_handler, http_port=configuration["wpt_port"], web_root=configuration["web_root"] ) diff --git a/testing/web-platform/tests/tools/wave/www/comparison.html b/testing/web-platform/tests/tools/wave/www/comparison.html index 5be6da4d647d..debb971bf488 100644 --- a/testing/web-platform/tests/tools/wave/www/comparison.html +++ b/testing/web-platform/tests/tools/wave/www/comparison.html @@ -65,7 +65,7 @@ tokens = tokens ? tokens.split(",") : []; const refTokens = reftokens ? reftokens.split(",") : []; if (tokens) { - WaveService.readResultsConfig(function(config) { + WaveService.readStatus(function(config) { comparisonUi.state.reportsEnabled = config.reportsEnabled; comparisonUi.render(); }); diff --git a/testing/web-platform/tests/tools/wave/www/configuration.html b/testing/web-platform/tests/tools/wave/www/configuration.html index ae174b1a559b..327a155154d0 100644 --- a/testing/web-platform/tests/tools/wave/www/configuration.html +++ b/testing/web-platform/tests/tools/wave/www/configuration.html @@ -18,271 +18,428 @@ diff --git a/testing/web-platform/tests/tools/wave/www/css/bulma-0.7.5/bulma.css b/testing/web-platform/tests/tools/wave/www/css/bulma-0.7.5/bulma.css index 150bbfd34a3a..e793432ae31c 100644 --- a/testing/web-platform/tests/tools/wave/www/css/bulma-0.7.5/bulma.css +++ b/testing/web-platform/tests/tools/wave/www/css/bulma-0.7.5/bulma.css @@ -26,7 +26,7 @@ .pagination-ellipsis, .tabs { -webkit-touch-callout: none; -webkit-user-select: none; - user-select: none; + -moz-user-select: none; -ms-user-select: none; user-select: none; } diff --git a/testing/web-platform/tests/tools/wave/www/index.html b/testing/web-platform/tests/tools/wave/www/index.html index fbfec41d6193..8ba94dfac0c0 100644 --- a/testing/web-platform/tests/tools/wave/www/index.html +++ b/testing/web-platform/tests/tools/wave/www/index.html @@ -23,7 +23,7 @@

WAVE WMAS Test Suite

-

+

GitHub -

@@ -32,23 +32,29 @@

New test session

-
+
- +
- - @@ -87,7 +93,7 @@
+ Token:
+ Expires:
'] + + for (var row = 0; row < nCount; row++) { + aHTML.push('') + + for (var col = 0; col < nCount; col++) { + aHTML.push( + '' + ) + } + + aHTML.push('') + } + + aHTML.push('
') + _el.innerHTML = aHTML.join('') + + // Fix the margin values as real size. + var elTable = _el.childNodes[0] + var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2 + var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2 + + if (nLeftMarginTable > 0 && nTopMarginTable > 0) { + elTable.style.margin = + nTopMarginTable + 'px ' + nLeftMarginTable + 'px' + } + } + + /** + * Clear the QRCode + */ + Drawing.prototype.clear = function () { + this._el.innerHTML = '' + } + + return Drawing + })() + : (function () { + // Drawing in Canvas + function _onMakeImage () { + this._elImage.src = this._elCanvas.toDataURL('image/png') + this._elImage.style.display = 'block' + this._elCanvas.style.display = 'none' + } + + // Android 2.1 bug workaround + // http://code.google.com/p/android/issues/detail?id=5141 + if (this._android && this._android <= 2.1) { + var factor = 1 / window.devicePixelRatio + var drawImage = CanvasRenderingContext2D.prototype.drawImage + CanvasRenderingContext2D.prototype.drawImage = function ( + image, + sx, + sy, + sw, + sh, + dx, + dy, + dw, + dh + ) { + if ('nodeName' in image && /img/i.test(image.nodeName)) { + for (var i = arguments.length - 1; i >= 1; i--) { + arguments[i] = arguments[i] * factor + } + } else if (typeof dw === 'undefined') { + arguments[1] *= factor + arguments[2] *= factor + arguments[3] *= factor + arguments[4] *= factor + } + + drawImage.apply(this, arguments) + } + } + + /** + * Check whether the user's browser supports Data URI or not + * + * @private + * @param {Function} fSuccess Occurs if it supports Data URI + * @param {Function} fFail Occurs if it doesn't support Data URI + */ + function _safeSetDataURI (fSuccess, fFail) { + var self = this + self._fFail = fFail + self._fSuccess = fSuccess + + // Check it just once + if (self._bSupportDataURI === null) { + var el = document.createElement('img') + var fOnError = function () { + self._bSupportDataURI = false + + if (self._fFail) { + self._fFail.call(self) + } + } + var fOnSuccess = function () { + self._bSupportDataURI = true + + if (self._fSuccess) { + self._fSuccess.call(self) + } + } + + el.onabort = fOnError + el.onerror = fOnError + el.onload = fOnSuccess + el.src = + 'data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==' // the Image contains 1px data. + } else if (self._bSupportDataURI === true && self._fSuccess) { + self._fSuccess.call(self) + } else if (self._bSupportDataURI === false && self._fFail) { + self._fFail.call(self) + } + } + + /** + * Drawing QRCode by using canvas + * + * @constructor + * @param {HTMLElement} el + * @param {Object} htOption QRCode Options + */ + var Drawing = function (el, htOption) { + this._bIsPainted = false + this._android = _getAndroid() + + this._htOption = htOption + this._elCanvas = document.createElement('canvas') + this._elCanvas.width = htOption.width + this._elCanvas.height = htOption.height + el.appendChild(this._elCanvas) + this._el = el + this._oContext = this._elCanvas.getContext('2d') + this._bIsPainted = false + this._elImage = document.createElement('img') + this._elImage.alt = 'Scan me!' + this._elImage.style.display = 'none' + this._el.appendChild(this._elImage) + this._bSupportDataURI = null + } + + /** + * Draw the QRCode + * + * @param {QRCode} oQRCode + */ + Drawing.prototype.draw = function (oQRCode) { + var _elImage = this._elImage + var _oContext = this._oContext + var _htOption = this._htOption + + var nCount = oQRCode.getModuleCount() + var nWidth = _htOption.width / nCount + var nHeight = _htOption.height / nCount + var nRoundedWidth = Math.round(nWidth) + var nRoundedHeight = Math.round(nHeight) + + _elImage.style.display = 'none' + this.clear() + + for (var row = 0; row < nCount; row++) { + for (var col = 0; col < nCount; col++) { + var bIsDark = oQRCode.isDark(row, col) + var nLeft = col * nWidth + var nTop = row * nHeight + _oContext.strokeStyle = bIsDark + ? _htOption.colorDark + : _htOption.colorLight + _oContext.lineWidth = 1 + _oContext.fillStyle = bIsDark + ? _htOption.colorDark + : _htOption.colorLight + _oContext.fillRect(nLeft, nTop, nWidth, nHeight) + + // 안티 앨리어싱 방지 처리 + _oContext.strokeRect( + Math.floor(nLeft) + 0.5, + Math.floor(nTop) + 0.5, + nRoundedWidth, + nRoundedHeight + ) + + _oContext.strokeRect( + Math.ceil(nLeft) - 0.5, + Math.ceil(nTop) - 0.5, + nRoundedWidth, + nRoundedHeight + ) + } + } + + this._bIsPainted = true + } + + /** + * Make the image from Canvas if the browser supports Data URI. + */ + Drawing.prototype.makeImage = function () { + if (this._bIsPainted) { + _safeSetDataURI.call(this, _onMakeImage) + } + } + + /** + * Return whether the QRCode is painted or not + * + * @return {Boolean} + */ + Drawing.prototype.isPainted = function () { + return this._bIsPainted + } + + /** + * Clear the QRCode + */ + Drawing.prototype.clear = function () { + this._oContext.clearRect( + 0, + 0, + this._elCanvas.width, + this._elCanvas.height + ) + this._bIsPainted = false + } + + /** + * @private + * @param {Number} nNumber + */ + Drawing.prototype.round = function (nNumber) { + if (!nNumber) { + return nNumber + } + + return Math.floor(nNumber * 1000) / 1000 + } + + return Drawing + })() + + /** + * Get the type by string length + * + * @private + * @param {String} sText + * @param {Number} nCorrectLevel + * @return {Number} type + */ + function _getTypeNumber (sText, nCorrectLevel) { + var nType = 1 + var length = _getUTF8Length(sText) + + for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) { + var nLimit = 0 + + switch (nCorrectLevel) { + case QRErrorCorrectLevel.L: + nLimit = QRCodeLimitLength[i][0] + break + case QRErrorCorrectLevel.M: + nLimit = QRCodeLimitLength[i][1] + break + case QRErrorCorrectLevel.Q: + nLimit = QRCodeLimitLength[i][2] + break + case QRErrorCorrectLevel.H: + nLimit = QRCodeLimitLength[i][3] + break + } + + if (length <= nLimit) { + break + } else { + nType++ + } + } + + if (nType > QRCodeLimitLength.length) { + throw new Error('Too long data') + } + + return nType + } + + function _getUTF8Length (sText) { + var replacedText = encodeURI(sText) + .toString() + .replace(/\%[0-9a-fA-F]{2}/g, 'a') + return replacedText.length + (replacedText.length != sText ? 3 : 0) + } + + /** + * @class QRCode + * @constructor + * @example + * new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie"); + * + * @example + * var oQRCode = new QRCode("test", { + * text : "http://naver.com", + * width : 128, + * height : 128 + * }); + * + * oQRCode.clear(); // Clear the QRCode. + * oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode. + * + * @param {HTMLElement|String} el target element or 'id' attribute of element. + * @param {Object|String} vOption + * @param {String} vOption.text QRCode link data + * @param {Number} [vOption.width=256] + * @param {Number} [vOption.height=256] + * @param {String} [vOption.colorDark="#000000"] + * @param {String} [vOption.colorLight="#ffffff"] + * @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H] + */ + QRCode = function (el, vOption) { + this._htOption = { + width: 256, + height: 256, + typeNumber: 4, + colorDark: '#000000', + colorLight: '#ffffff', + correctLevel: QRErrorCorrectLevel.H + } + + if (typeof vOption === 'string') { + vOption = { + text: vOption + } + } + + // Overwrites options + if (vOption) { + for (var i in vOption) { + this._htOption[i] = vOption[i] + } + } + + if (typeof el === 'string') { + el = document.getElementById(el) + } + + if (this._htOption.useSVG) { + Drawing = svgDrawer + } + + this._android = _getAndroid() + this._el = el + this._oQRCode = null + this._oDrawing = new Drawing(this._el, this._htOption) + + if (this._htOption.text) { + this.makeCode(this._htOption.text) + } + } + + /** + * Make the QRCode + * + * @param {String} sText link data + */ + QRCode.prototype.makeCode = function (sText) { + this._oQRCode = new QRCodeModel( + _getTypeNumber(sText, this._htOption.correctLevel), + this._htOption.correctLevel + ) + this._oQRCode.addData(sText) + this._oQRCode.make() + this._el.title = sText + this._oDrawing.draw(this._oQRCode) + this.makeImage() + } + + /** + * Make the Image from Canvas element + * - It occurs automatically + * - Android below 3 doesn't support Data-URI spec. + * + * @private + */ + QRCode.prototype.makeImage = function () { + if ( + typeof this._oDrawing.makeImage === 'function' && + (!this._android || this._android >= 3) + ) { + this._oDrawing.makeImage() + } + } + + /** + * Clear the QRCode + */ + QRCode.prototype.clear = function () { + this._oDrawing.clear() + } + + /** + * @name QRCode.CorrectLevel + */ + QRCode.CorrectLevel = QRErrorCorrectLevel +})() diff --git a/testing/web-platform/tests/tools/wave/www/lib/query-parser.js b/testing/web-platform/tests/tools/wave/www/lib/query-parser.js new file mode 100644 index 000000000000..c8ab8333d2b0 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/www/lib/query-parser.js @@ -0,0 +1,12 @@ +var QueryParser = {}; + +QueryParser.parseQuery = function () { + var queryParameters = {}; + var keysAndValues = location.search.replace("?", "").split("&"); + for (var i = 0; i < keysAndValues.length; i++) { + var key = keysAndValues[i].split("=")[0]; + var value = keysAndValues[i].split("=")[1]; + queryParameters[key] = value; + } + return queryParameters; +}; diff --git a/testing/web-platform/tests/tools/wave/www/lib/screen-console.js b/testing/web-platform/tests/tools/wave/www/lib/screen-console.js new file mode 100644 index 000000000000..0e13b963a6c3 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/www/lib/screen-console.js @@ -0,0 +1,16 @@ +function ScreenConsole(element) { + this._element = element; +} + +ScreenConsole.prototype.log = function () { + var text = ""; + for (var i = 0; i < arguments.length; i++) { + text += arguments[i] + " "; + } + console.log(text); + this._element.innerText += text + "\n"; +}; + +ScreenConsole.prototype.clear = function () { + this._element.innerText = ""; +}; diff --git a/testing/web-platform/tests/tools/wave/www/lib/ui.js b/testing/web-platform/tests/tools/wave/www/lib/ui.js index 684ed18009e3..4abbece985e4 100644 --- a/testing/web-platform/tests/tools/wave/www/lib/ui.js +++ b/testing/web-platform/tests/tools/wave/www/lib/ui.js @@ -68,6 +68,9 @@ const UI = { case "checked": if (value) element.setAttribute("checked", true); return; + case "indeterminate": + element.indeterminate = value; + return; } }); return element; diff --git a/testing/web-platform/tests/tools/wave/www/lib/wave-service.js b/testing/web-platform/tests/tools/wave/www/lib/wave-service.js index e420290ebc62..f7a60e153dc7 100644 --- a/testing/web-platform/tests/tools/wave/www/lib/wave-service.js +++ b/testing/web-platform/tests/tools/wave/www/lib/wave-service.js @@ -1,13 +1,13 @@ function sendRequest(method, uri, headers, data, onSuccess, onError) { var xhr = new XMLHttpRequest(); - xhr.onload = function() { + xhr.onload = function () { if (xhr.status === 200) { onSuccess(xhr.response); } else { if (onError) onError(xhr.status, xhr.response); } }; - xhr.onerror = function() { + xhr.onerror = function () { if (onError) onError(); }; xhr.open(method, WaveService.uriPrefix + uri, true); @@ -18,9 +18,9 @@ function sendRequest(method, uri, headers, data, onSuccess, onError) { return xhr; } -var WEB_ROOT = "{{WEB_ROOT}}" -var HTTP_PORT = "{{HTTP_PORT}}" -var HTTPS_PORT = "{{HTTPS_PORT}}" +var WEB_ROOT = "{{WEB_ROOT}}"; +var HTTP_PORT = "{{HTTP_PORT}}"; +var HTTPS_PORT = "{{HTTPS_PORT}}"; var OPEN = "open"; var CLOSED = "closed"; @@ -28,42 +28,42 @@ var WaveService = { uriPrefix: WEB_ROOT, socket: { state: CLOSED, - onMessage: function() {}, - onOpen: function() {}, - onClose: function() {}, - send: function() {}, - close: function() {}, - onStateChange: function() {} + onMessage: function () {}, + onOpen: function () {}, + onClose: function () {}, + send: function () {}, + close: function () {}, + onStateChange: function () {}, }, // SESSIONS API - createSession: function(configuration, onSuccess, onError) { + createSession: function (configuration, onSuccess, onError) { var data = JSON.stringify({ tests: configuration.tests, types: configuration.types, timeouts: configuration.timeouts, reference_tokens: configuration.referenceTokens, expiration_date: configuration.expirationDate, - labels: configuration.labels + labels: configuration.labels, }); sendRequest( "POST", "api/sessions", { "Content-Type": "application/json" }, data, - function(response) { + function (response) { var jsonObject = JSON.parse(response); onSuccess(jsonObject.token); }, onError ); }, - readSession: function(token, onSuccess, onError) { + readSession: function (token, onSuccess, onError) { sendRequest( "GET", "api/sessions/" + token, null, null, - function(response) { + function (response) { var jsonObject = JSON.parse(response); onSuccess({ token: jsonObject.token, @@ -75,14 +75,12 @@ var WaveService = { browser: jsonObject.browser, isPublic: jsonObject.is_public, referenceTokens: jsonObject.reference_tokens, - webhookUrls: jsonObject.webhook_urls, - expirationDate: jsonObject.expiration_date }); }, onError ); }, - readMultipleSessions: function(tokens, onSuccess, onError) { + readMultipleSessions: function (tokens, onSuccess, onError) { var requestsLeft = tokens.length; if (requestsLeft === 0) onSuccess([]); var configurations = []; @@ -90,12 +88,12 @@ var WaveService = { var token = tokens[i]; WaveService.readSession( token, - function(configuration) { + function (configuration) { requestsLeft--; configurations.push(configuration); if (requestsLeft === 0) onSuccess(configurations); }, - function(status) { + function (status) { if (status === 404) requestsLeft--; if (status !== 404 && onError) onError(); if (requestsLeft === 0) onSuccess(configurations); @@ -103,29 +101,42 @@ var WaveService = { ); } }, - readSessionStatus: function(token, onSuccess, onError) { + readSessionStatus: function (token, onSuccess, onError) { sendRequest( "GET", "api/sessions/" + token + "/status", null, null, - function(response) { + function (response) { var jsonObject = JSON.parse(response); + var dateStarted = null; + if (jsonObject.date_started) { + dateStarted = new Date(jsonObject.date_started); + } + var dateFinished = null; + if (jsonObject.date_finished) { + dateFinished = new Date(jsonObject.date_finished); + } + var expirationDate = null; + if (jsonObject.expiration_date) { + expirationDate = new Date(jsonObject.expiration_date); + } onSuccess({ token: jsonObject.token, - dateStarted: jsonObject.date_started, - dateFinished: jsonObject.date_finished, + dateStarted: dateStarted, + dateFinished: dateFinished, testFilesCount: jsonObject.test_files_count, testFilesCompleted: jsonObject.test_files_completed, - status: jsonObject.status + status: jsonObject.status, + expirationDate: expirationDate, }); }, - function() { + function () { if (onError) onError(); } ); }, - readMultipleSessionStatuses: function(tokens, onSuccess, onError) { + readMultipleSessionStatuses: function (tokens, onSuccess, onError) { var requestsLeft = tokens.length; if (requestsLeft === 0) onSuccess([]); var statuses = []; @@ -133,134 +144,135 @@ var WaveService = { var token = tokens[i]; WaveService.readSessionStatus( token, - function(status) { + function (status) { requestsLeft--; statuses.push(status); if (requestsLeft === 0) onSuccess(statuses); }, - function() { + function () { requestsLeft--; if (requestsLeft === 0) onSuccess(statuses); } ); } }, - readPublicSessions: function(onSuccess, onError) { + readPublicSessions: function (onSuccess, onError) { sendRequest( "GET", "api/sessions/public", null, null, - function(response) { + function (response) { var jsonObject = JSON.parse(response); onSuccess(jsonObject); }, onError ); }, - updateSession: function(token, configuration, onSuccess, onError) { + updateSession: function (token, configuration, onSuccess, onError) { var data = JSON.stringify({ tests: configuration.tests, types: configuration.types, timeouts: configuration.timeouts, reference_tokens: configuration.referenceTokens, - expiration_date: configuration.expirationDate + expiration_date: configuration.expirationDate, + type: configuration.type, }); sendRequest( "PUT", "api/sessions/" + token, { "Content-Type": "application/json" }, data, - function() { + function () { onSuccess(); }, onError ); }, - updateLabels: function(token, labels, onSuccess, onError) { + updateLabels: function (token, labels, onSuccess, onError) { var data = JSON.stringify({ labels: labels }); sendRequest( "PUT", "api/sessions/" + token + "/labels", { "Content-Type": "application/json" }, data, - function() { + function () { if (onSuccess) onSuccess(); }, onError ); }, - findToken: function(fragment, onSuccess, onError) { + findToken: function (fragment, onSuccess, onError) { sendRequest( "GET", "api/sessions/" + fragment, null, null, - function(response) { + function (response) { var jsonObject = JSON.parse(response); onSuccess(jsonObject.token); }, onError ); }, - startSession: function(token, onSuccess, onError) { + startSession: function (token, onSuccess, onError) { sendRequest( "POST", "api/sessions/" + token + "/start", null, null, - function() { + function () { onSuccess(); }, onError ); }, - pauseSession: function(token, onSuccess, onError) { + pauseSession: function (token, onSuccess, onError) { sendRequest( "POST", "api/sessions/" + token + "/pause", null, null, - function() { + function () { onSuccess(); }, onError ); }, - stopSession: function(token, onSuccess, onError) { + stopSession: function (token, onSuccess, onError) { sendRequest( "POST", "api/sessions/" + token + "/stop", null, null, - function() { + function () { onSuccess(); }, onError ); }, - resumeSession: function(token, resumeToken, onSuccess, onError) { + resumeSession: function (token, resumeToken, onSuccess, onError) { var data = JSON.stringify({ resume_token: resumeToken }); sendRequest( "POST", "api/sessions/" + token + "/resume", { "Content-Type": "application/json" }, data, - function() { + function () { if (onSuccess) onSuccess(); }, - function(response) { + function (response) { if (onError) onError(response); } ); }, - deleteSession: function(token, onSuccess, onError) { + deleteSession: function (token, onSuccess, onError) { sendRequest( "DELETE", "api/sessions/" + token, null, null, - function() { + function () { onSuccess(); }, onError @@ -268,20 +280,33 @@ var WaveService = { }, // TESTS API - readNextTest: function(token, onSuccess, onError) { + readTestList: function (onSuccess, onError) { + sendRequest( + "GET", + "api/tests", + null, + null, + function (response) { + var jsonObject = JSON.parse(response); + onSuccess(jsonObject); + }, + onError + ); + }, + readNextTest: function (token, onSuccess, onError) { sendRequest( "GET", "api/tests/" + token + "/next", null, null, - function(response) { + function (response) { var jsonObject = JSON.parse(response); onSuccess(jsonObject.next_test); }, onError ); }, - readLastCompletedTests: function(token, resultTypes, onSuccess, onError) { + readLastCompletedTests: function (token, resultTypes, onSuccess, onError) { var status = ""; if (resultTypes) { for (var i = 0; i < resultTypes.length; i++) { @@ -294,7 +319,7 @@ var WaveService = { "api/tests/" + token + "/last_completed?status=" + status, null, null, - function(response) { + function (response) { var tests = JSON.parse(response); var parsedTests = []; for (var status in tests) { @@ -308,23 +333,23 @@ var WaveService = { onError ); }, - readMalfunctioningTests: function(token, onSuccess, onError) { + readMalfunctioningTests: function (token, onSuccess, onError) { sendRequest( "GET", "api/tests/" + token + "/malfunctioning", null, null, - function(response) { + function (response) { var tests = JSON.parse(response); onSuccess(tests); }, - function(response) { + function (response) { var errorMessage = JSON.parse(response).error; onError(errorMessage); } ); }, - updateMalfunctioningTests: function( + updateMalfunctioningTests: function ( token, malfunctioningTests, onSuccess, @@ -336,74 +361,74 @@ var WaveService = { "api/tests/" + token + "/malfunctioning", { "Content-Type": "application/json" }, data, - function() { + function () { onSuccess(); }, - function(response) { + function (response) { + var errorMessage = JSON.parse(response).error; + onError(errorMessage); + } + ); + }, + readAvailableApis: function (onSuccess, onError) { + sendRequest( + "GET", + "api/tests/apis", + null, + null, + function (response) { + var apis = JSON.parse(response); + onSuccess(apis); + }, + function (response) { + if (!onError) return; var errorMessage = JSON.parse(response).error; onError(errorMessage); } ); }, - readAvailableApis: function(onSuccess, onError) { - sendRequest( - "GET", - "api/tests/apis", - null, - null, - function(response) { - var apis = JSON.parse(response); - onSuccess(apis); - }, - function(response) { - if(!onError) return; - var errorMessage = JSON.parse(response).error; - onError(errorMessage); - } - ); - }, // RESULTS API - createResult: function(token, result, onSuccess, onError) { + createResult: function (token, result, onSuccess, onError) { sendRequest( "POST", "api/results/" + token, { "Content-Type": "application/json" }, JSON.stringify(result), - function() { + function () { onSuccess(); }, onError ); }, - readResults: function(token, onSuccess, onError) { + readResults: function (token, onSuccess, onError) { sendRequest( "GET", "api/results/" + token, null, null, - function(response) { + function (response) { onSuccess(JSON.parse(response)); }, onError ); }, - readResultsCompact: function(token, onSuccess, onError) { + readResultsCompact: function (token, onSuccess, onError) { sendRequest( "GET", "api/results/" + token + "/compact", null, null, - function(response) { + function (response) { var jsonObject = JSON.parse(response); onSuccess(jsonObject); }, onError ); }, - readResultComparison: function(tokens, onSuccess, onError) { + readResultComparison: function (tokens, onSuccess, onError) { var comparison = {}; - var fetchComplete = function(results) { + var fetchComplete = function (results) { comparison.total = {}; for (var i = 0; i < results.length; i++) { var result = results[i]; @@ -428,16 +453,16 @@ var WaveService = { var results = []; for (var i = 0; i < tokens.length; i++) { var token = tokens[i]; - (function(token) { + (function (token) { WaveService.readResultsCompact( token, - function(result) { + function (result) { requestsLeft--; result.token = token; results.push(result); if (requestsLeft === 0) fetchComplete(results); }, - function(responseStatus) { + function (responseStatus) { if (responseStatus === 404) requestsLeft--; if (status !== 404 && onError) onError(); if (requestsLeft === 0) fetchComplete(results); @@ -446,29 +471,29 @@ var WaveService = { })(token); } }, - downloadResults: function(token) { + downloadResults: function (token) { location.href = "api/results/" + token + "/export"; }, - downloadApiResult: function(token, api) { + downloadApiResult: function (token, api) { location.href = "api/results/" + token + "/" + api + "/json"; }, - downloadAllApiResults: function(token, api) { + downloadAllApiResults: function (token, api) { location.href = "api/results/" + token + "/json"; }, - downloadReport: function(token, api) { + downloadReport: function (token, api) { location.href = "api/results/" + token + "/" + api + "/report"; }, - importResults: function(data, onSuccess, onError) { + importResults: function (data, onSuccess, onError) { sendRequest( "POST", "api/results/import", { "Content-Type": "application/octet-stream" }, data, - function(response) { + function (response) { var token = JSON.parse(response).token; onSuccess(token); }, - function(status, response) { + function (status, response) { var errorMessage; if (status === 500) { errorMessage = "Internal server error."; @@ -479,58 +504,210 @@ var WaveService = { } ); }, - readResultsConfig: function(onSuccess, onError) { - sendRequest( - "GET", - "api/results/config", - null, - null, - function(response) { - var config = JSON.parse(response); - onSuccess({ - importEnabled: config.import_enabled, - reportsEnabled: config.reports_enabled - }); - }, - onError - ); - }, - readReportUri: function(token, api, onSuccess, onError) { + readReportUri: function (token, api, onSuccess, onError) { sendRequest( "GET", "api/results/" + token + "/" + api + "/reporturl", null, null, - function(response) { + function (response) { var jsonObject = JSON.parse(response); onSuccess(jsonObject.uri); }, onError ); }, - downloadMultiReport: function(tokens, api) { - location.href = - "api/results/" + api + "/report?tokens=" + tokens.join(","); + downloadMultiReport: function (tokens, api) { + location.href = "api/results/" + api + "/report?tokens=" + tokens.join(","); }, - readMultiReportUri: function(tokens, api, onSuccess, onError) { + readMultiReportUri: function (tokens, api, onSuccess, onError) { sendRequest( "GET", "api/results/" + api + "/reporturl?tokens=" + tokens.join(","), null, null, - function(response) { + function (response) { var jsonObject = JSON.parse(response); onSuccess(jsonObject.uri); }, onError ); }, - downloadResultsOverview: function(token) { + downloadResultsOverview: function (token) { location.href = "api/results/" + token + "/overview"; }, + // DEVICES API + _device_token: null, + _deviceEventListeners: {}, + _deviceEventNumbers: {}, + registerDevice: function (onSuccess, onError) { + sendRequest( + "POST", + "api/devices", + null, + null, + function (response) { + var data = JSON.parse(response); + WaveService._device_token = data.token; + onSuccess(data.token); + }, + onError + ); + }, + readDevice: function (token, onSuccess, onError) { + sendRequest( + "GET", + "api/devices/" + token, + null, + null, + function (response) { + if (!onSuccess) return; + var data = JSON.parse(response); + onSuccess(data); + }, + function (error) { + if (!onError) return; + onError(error); + } + ); + }, + DEVICE_ADDED_EVENT: "device_added", + DEVICE_REMOVED_EVENT: "device_removed", + START_SESSION: "start_session", + addDeviceEventListener: function (token, callback) { + var listeners = WaveService._deviceEventListeners; + if (!listeners[token]) listeners[token] = []; + listeners[token].push(callback); + WaveService._deviceEventListeners = listeners; + WaveService.listenDeviceEvents(token); + }, + removeDeviceEventListener: function (callback) { + var listeners = WaveService._deviceEventListeners; + for (var token of Object.keys(listeners)) { + var index = listeners[token].indexOf(callback); + if (index === -1) continue; + listeners[token].splice(index, 1); + break; + } + WaveService._deviceEventListeners = listeners; + }, + listenDeviceEvents: function (token) { + var listeners = WaveService._deviceEventListeners; + if (!listeners[token] || listeners.length === 0) return; + var url = "api/devices/" + token + "/events"; + var lastEventNumber = WaveService._deviceEventNumbers[token]; + if (lastEventNumber) { + url += "?last_active=" + lastEventNumber; + } + WaveService.listenHttpPolling( + url, + function (response) { + if (!response) { + WaveService.listenDeviceEvents(token); + return; + } + for (var listener of listeners[token]) { + listener(response); + } + WaveService._deviceEventNumbers[token] = lastEventNumber; + WaveService.listenDeviceEvents(token); + }, + function () { + setTimeout(function () { + WaveService.listenDeviceEvents(); + }, 1000); + } + ); + }, + sendDeviceEvent: function (device_token, event, onSuccess, onError) { + var data = JSON.stringify({ + type: event.type, + data: event.data, + }); + sendRequest( + "POST", + "api/devices/" + device_token + "/events", + { "Content-Type": "application/json" }, + data, + onSuccess, + onError + ); + }, + addGlobalDeviceEventListener: function (callback) { + WaveService._globalDeviceEventListeners.push(callback); + WaveService.listenGlobalDeviceEvents(); + }, + removeGlobalDeviceEventListener: function (callback) { + var index = WaveService._globalDeviceEventListeners.indexOf(callback); + WaveService._globalDeviceEventListeners.splice(index, 1); + }, + listenGlobalDeviceEvents: function () { + var listeners = WaveService._globalDeviceEventListeners; + if (listeners.length === 0) return; + var query = ""; + if (WaveService._device_token) { + query = "?device_token=" + WaveService._device_token; + } + WaveService.listenHttpPolling( + "api/devices/events" + query, + function (response) { + if (!response) { + WaveService.listenGlobalDeviceEvents(); + return; + } + for (var listener of listeners) { + listener(response); + } + WaveService.listenGlobalDeviceEvents(); + }, + function () { + setTimeout(function () { + WaveService.listenGlobalDeviceEvents(); + }, 1000); + } + ); + }, + sendGlobalDeviceEvent: function (event, onSuccess, onError) { + var data = JSON.stringify({ + type: event.type, + data: event.data, + }); + sendRequest( + "POST", + "api/devices/events", + { "Content-Type": "application/json" }, + data, + onSuccess, + onError + ); + }, + + // GENERAL API + readStatus: function (onSuccess, onError) { + sendRequest( + "GET", + "api/status", + null, + null, + function (response) { + var data = JSON.parse(response); + var configuration = { + readSessionsEnabled: data.read_sessions_enabled, + importResultsEnabled: data.import_results_enabled, + reportsEnabled: data.reports_enabled, + versionString: data.version_string, + testTypeSelectionEnabled: data.test_type_selection_enabled, + testFileSelectionEnabled: data.test_file_selection_enabled + }; + onSuccess(configuration); + }, + onError + ); + }, + // UTILITY - addRecentSession: function(token) { + addRecentSession: function (token) { if (!token) return; var state = WaveService.getState(); if (!state.recent_sessions) state.recent_sessions = []; @@ -538,18 +715,18 @@ var WaveService = { state.recent_sessions.unshift(token); WaveService.setState(state); }, - addRecentSessions: function(tokens) { + addRecentSessions: function (tokens) { for (var i = 0; i < tokens.length; i++) { var token = tokens[i]; WaveService.addRecentSession(token); } }, - getPinnedSessions: function() { + getPinnedSessions: function () { var state = WaveService.getState(); if (!state || !state.pinned_sessions) return []; return state.pinned_sessions; }, - addPinnedSession: function(token) { + addPinnedSession: function (token) { if (!token) return; var state = WaveService.getState(); if (!state.pinned_sessions) state.pinned_sessions = []; @@ -557,17 +734,17 @@ var WaveService = { state.pinned_sessions.unshift(token); WaveService.setState(state); }, - getRecentSessions: function() { + getRecentSessions: function () { var state = WaveService.getState(); if (!state || !state.recent_sessions) return []; return state.recent_sessions; }, - setRecentSessions: function(sessionTokens) { + setRecentSessions: function (sessionTokens) { var state = WaveService.getState(); state.recent_sessions = sessionTokens; WaveService.setState(state); }, - removePinnedSession: function(token) { + removePinnedSession: function (token) { if (!token) return; var state = WaveService.getState(); if (!state.pinned_sessions) return; @@ -576,7 +753,7 @@ var WaveService = { state.pinned_sessions.splice(index, 1); WaveService.setState(state); }, - removeRecentSession: function(token) { + removeRecentSession: function (token) { var state = WaveService.getState(); if (!state.recent_sessions) return; var index = state.recent_sessions.indexOf(token); @@ -584,91 +761,106 @@ var WaveService = { state.recent_sessions.splice(index, 1); WaveService.setState(state); }, - getState: function() { + getState: function () { if (!window.localStorage) return null; var storage = window.localStorage; var state = JSON.parse(storage.getItem("wave")); if (!state) return {}; return state; }, - setState: function(state) { + setState: function (state) { if (!window.localStorage) return null; var storage = window.localStorage; storage.setItem("wave", JSON.stringify(state)); }, - connectWebSocket: function(token) { - var protocol; - if (location.protocol === "https:") { - protocol = "wss"; + _globalDeviceEventListeners: [], + _sessionEventListeners: {}, + _sessionEventNumbers: {}, + listenHttpPolling: function (url, onSuccess, onError) { + var uniqueId = new Date().getTime(); + if (url.indexOf("?") === -1) { + url = url + "?id=" + uniqueId; } else { - protocol = "ws"; + url = url + "&id=" + uniqueId; } - var url = protocol + "://" + location.host; - console.log("Connecting web socket to" + url); - var webSocket = new WebSocket(url); - webSocket.onmessage = function(message) { - WaveService.socket.onMessage(JSON.parse(message.data)); - }; - webSocket.onclose = function() { - WaveService.socket.state = CLOSED; - WaveService.socket.onStateChange(CLOSED); - WaveService.socket.onClose(); - }; - webSocket.onopen = function() { - WaveService.socket.state = OPEN; - WaveService.socket.onStateChange(OPEN); - WaveService.socket.onOpen(); - webSocket.send(JSON.stringify({ token: token })); - }; - WaveService.socket.send = function(message) { - webSocket.send(message); - }; - WaveService.socket.close = function() { - webSocket.close(); - }; - }, - connectHttpPolling: function(token) { - var uniqueId = new Date().getTime() - var poll = function() { - var request = sendRequest( - "GET", - "api/sessions/" + token + "/events?id=" + uniqueId, - null, - null, - function(response) { - if (WaveService.socket.state === OPEN) poll(); - WaveService.socket.onMessage(JSON.parse(response)); - }, - function() { - if (WaveService.socket.state === OPEN) setTimeout(poll, 1000); + sendRequest( + "GET", + url, + null, + null, + function (response) { + if (!response) { + onSuccess(null); + return; } - ); - WaveService.socket.close = function() { - request.abort(); - WaveService.socket.state = CLOSED; - WaveService.socket.onStateChange(CLOSED); - WaveService.socket.onClose(); - }; - }; - poll(); - WaveService.socket.onOpen(); - WaveService.socket.state = OPEN; - WaveService.socket.onStateChange(OPEN); + onSuccess(JSON.parse(response)); + }, + onError + ); }, - connect: function(token) { - if (window.WebSocket) { - WaveService.connectWebSocket(token); - } else { - WaveService.connectHttpPolling(token); + addSessionEventListener: function (token, callback) { + var listeners = WaveService._sessionEventListeners; + if (!listeners[token]) listeners[token] = []; + if (listeners[token].indexOf(callback) >= 0) return; + listeners[token].push(callback); + WaveService._sessionEventListeners = listeners; + WaveService.listenSessionEvents(token); + }, + removeSessionEventListener: function (callback) { + var listeners = WaveService._sessionEventListeners; + for (var token of Object.keys(listeners)) { + var index = listeners[token].indexOf(callback); + if (index === -1) continue; + listeners[token].splice(index, 1); + break; } + WaveService._sessionEventListeners = listeners; }, - onMessage: function(callback) { - WaveService.socket.onMessage = callback; + listenSessionEvents: function (token) { + var listeners = WaveService._sessionEventListeners; + if (!listeners[token] || listeners.length === 0) return; + var url = "api/sessions/" + token + "/events"; + var lastEventNumber = WaveService._sessionEventNumbers[token]; + if (lastEventNumber) { + url += "?last_event=" + lastEventNumber; + } + WaveService.listenHttpPolling( + url, + function (response) { + if (!response) { + WaveService.listenSessionEvents(token); + return; + } + var lastEventNumber = 0; + for (var listener of listeners[token]) { + for (var event of response) { + if (event.number > lastEventNumber) { + lastEventNumber = event.number; + } + listener(event); + } + } + WaveService._sessionEventNumbers[token] = lastEventNumber; + WaveService.listenSessionEvents(token); + }, + function () { + setTimeout(function () { + WaveService.listenSessionEvents(); + }, 1000); + } + ); }, - isConnected: function() { - return WaveService.socket.state === OPEN; - }, - openSession: function(token) { + openSession: function (token) { location.href = "/results.html?token=" + token; - } + }, }; + +if (!Object.keys) + Object.keys = function (o) { + if (o !== Object(o)) + throw new TypeError("Object.keys called on a non-object"); + var k = [], + p; + for (p in o) if (Object.prototype.hasOwnProperty.call(o, p)) k.push(p); + return k; + }; diff --git a/testing/web-platform/tests/tools/wave/www/next.html b/testing/web-platform/tests/tools/wave/www/next.html index 277f4afed67f..8412b9234fd8 100644 --- a/testing/web-platform/tests/tools/wave/www/next.html +++ b/testing/web-platform/tests/tools/wave/www/next.html @@ -2,42 +2,30 @@ + + -
+

diff --git a/testing/web-platform/tests/tools/wave/www/overview.html b/testing/web-platform/tests/tools/wave/www/overview.html index cabb158711a4..48ec3a2514b2 100644 --- a/testing/web-platform/tests/tools/wave/www/overview.html +++ b/testing/web-platform/tests/tools/wave/www/overview.html @@ -128,8 +128,8 @@ }) ); }); - WaveService.readResultsConfig(function(config) { - resultsUi.state.importResultsEnabled = config.importEnabled; + WaveService.readStatus(function(config) { + resultsUi.state.importResultsEnabled = config.importResultsEnabled; resultsUi.state.reportsEnabled = config.reportsEnabled; resultsUi.renderManageSessions(); }); diff --git a/testing/web-platform/tests/tools/wave/www/pause.html b/testing/web-platform/tests/tools/wave/www/pause.html index a561968e1125..4e52b6673d4c 100644 --- a/testing/web-platform/tests/tools/wave/www/pause.html +++ b/testing/web-platform/tests/tools/wave/www/pause.html @@ -122,8 +122,7 @@ }); } - WaveService.connectHttpPolling(TOKEN); - WaveService.onMessage(function(message) { + WaveService.addSessionEventListener(TOKEN, function(message) { if (message.type !== "status") return; if (message.data !== "running") return; WaveService.readNextTest(TOKEN, function(url) { diff --git a/testing/web-platform/tests/tools/wave/www/results.html b/testing/web-platform/tests/tools/wave/www/results.html index 839ce05590c9..c9b2c028c197 100644 --- a/testing/web-platform/tests/tools/wave/www/results.html +++ b/testing/web-platform/tests/tools/wave/www/results.html @@ -1,5 +1,5 @@ - + @@ -38,12 +38,12 @@ referenceSessions: [], lastCompletedTests: [], malfunctioningTests: [], - addLabelVisible: false + addLabelVisible: false, }, - refreshData: toUpdate => { - WaveService.readResultsConfig(function(config) { - resultUi.state.reportsEnabled = config.reportsEnabled; - resultUi.renderApiResults(); + refreshData: (toUpdate) => { + WaveService.readStatus(function (config) { + resultUi.state.reportsEnabled = config.reportsEnabled; + resultUi.renderApiResults(); }); switch (toUpdate) { case "test_completed": @@ -88,16 +88,19 @@ } }, refreshSessionConfiguration(callback = () => {}) { - WaveService.readSession(token, configuration => { + WaveService.readSession(token, (configuration) => { resultUi.state.configuration = configuration; callback(configuration); }); }, refreshSessionStatus(callback = () => {}) { - WaveService.readSessionStatus(token, status => { + WaveService.readSessionStatus(token, (status) => { resultUi.state.status = status; if (status.status !== "completed" && status.status !== "aborted") - resultUi.connectWebSocket(); + WaveService.addSessionEventListener( + token, + resultUi.handleSessionEvent + ); callback(status); }); }, @@ -106,38 +109,33 @@ if (!configuration) return; const { referenceTokens } = configuration; if (!referenceTokens) return; - WaveService.readMultipleSessions(referenceTokens, configuration => { + WaveService.readMultipleSessions(referenceTokens, (configuration) => { resultUi.state.referenceSessions = configuration; resultUi.renderReferenceSessions(); callback(configuration); }); }, refreshSessionResults(callback = () => {}) { - WaveService.readResultsCompact(token, results => { + WaveService.readResultsCompact(token, (results) => { resultUi.state.results = results; callback(results); }); }, refreshLastCompletedTests(callback = () => {}) { if (resultUi.state.configuration.isPublic) return; - WaveService.readLastCompletedTests(token, ["timeout"], tests => { + WaveService.readLastCompletedTests(token, ["timeout"], (tests) => { resultUi.state.lastCompletedTests = tests; callback(); }); }, refreshMalfunctioningTests(callback = () => {}) { - WaveService.readMalfunctioningTests(token, tests => { + WaveService.readMalfunctioningTests(token, (tests) => { resultUi.state.malfunctioningTests = tests; callback(); }); }, - connectWebSocket() { - if (!WaveService.isConnected()) { - WaveService.connectHttpPolling(token); - WaveService.onMessage(message => { - resultUi.refreshData(message.type); - }); - } + handleSessionEvent(message) { + resultUi.refreshData(message.type); }, openResultsOverview() { location.href = WEB_ROOT + "overview.html"; @@ -168,7 +166,7 @@ openHtmlReport(api) { const { results } = resultUi.state; if (results[api].complete != results[api].total) return; - WaveService.readReportUri(token, api, function(uri) { + WaveService.readReportUri(token, api, function (uri) { window.open(uri, "_blank"); }); }, @@ -260,9 +258,9 @@ { element: "img", src: "res/wavelogo_2016.jpg", - className: "site-logo" - } - ] + className: "site-logo", + }, + ], }, { className: "column is-narrow", @@ -276,18 +274,18 @@ children: [ { element: "i", - className: "fas fa-arrow-left" - } - ] + className: "fas fa-arrow-left", + }, + ], }, { text: "Results Overview", - element: "span" - } - ] - } - } - ] + element: "span", + }, + ], + }, + }, + ], }, { className: "container", @@ -296,48 +294,48 @@ children: [ { className: "column", - children: { className: "title", text: "Result" } + children: { className: "title", text: "Result" }, }, { className: "column is-narrow", - children: { id: "controls" } - } - ] - } - } - ] + children: { id: "controls" }, + }, + ], + }, + }, + ], }, { id: "session-details", className: "container", - style: "margin-bottom: 2em" + style: "margin-bottom: 2em", }, { id: "last-completed-tests", className: "container", - style: "margin-bottom: 2em" + style: "margin-bottom: 2em", }, { id: "api-results", className: "container", - style: "margin-bottom: 2em" + style: "margin-bottom: 2em", }, { id: "timeout-files", className: "container", - style: "margin-bottom: 2em" + style: "margin-bottom: 2em", }, { id: "export", className: "container", - style: "margin-bottom: 2em" + style: "margin-bottom: 2em", }, { id: "malfunctioning-tests", className: "container", - style: "margin-bottom: 2em" - } - ] + style: "margin-bottom: 2em", + }, + ], }); const root = UI.getRoot(); root.innerHTML = ""; @@ -353,7 +351,7 @@ const { status } = state.status; const { isPublic } = state.configuration; const controlsView = UI.createElement({ - className: "field is-grouped is-grouped-multiline" + className: "field is-grouped is-grouped-multiline", }); if ( status && @@ -364,7 +362,7 @@ const pauseResumeButton = UI.createElement({ id: "pause-resume-button", className: "control button is-dark is-outlined", - onclick: function() { + onclick: function () { if (status === "running") { WaveService.pauseSession(token, resultUi.refreshData); } else { @@ -379,15 +377,15 @@ { element: "i", className: - status === "running" ? "fas fa-pause" : "fas fa-play" - } - ] + status === "running" ? "fas fa-pause" : "fas fa-play", + }, + ], }, { text: status === "running" ? "Pause" : "Resume", - element: "span" - } - ] + element: "span", + }, + ], }); controlsView.appendChild(pauseResumeButton); } @@ -404,15 +402,15 @@ children: [ { element: "i", - className: "fas fa-square" - } - ] + className: "fas fa-square", + }, + ], }, { text: "Stop", - element: "span" - } - ] + element: "span", + }, + ], }); controlsView.appendChild(stopButton); } @@ -428,15 +426,15 @@ children: [ { element: "i", - className: "fas fa-trash-alt" - } - ] + className: "fas fa-trash-alt", + }, + ], }, { text: "Delete", - element: "span" - } - ] + element: "span", + }, + ], }); controlsView.appendChild(deleteButton); } @@ -447,7 +445,7 @@ children: [ { className: "modal-background", - onclick: resultUi.hideDeleteModal + onclick: resultUi.hideDeleteModal, }, { className: "modal-card", @@ -458,19 +456,19 @@ { element: "p", className: "modal-card-title", - text: "Delete Session" - } - ] + text: "Delete Session", + }, + ], }, { className: "modal-card-body", children: [ { element: "p", - text: "Are you sure you want to delete this session?" + text: "Are you sure you want to delete this session?", }, - { element: "p", text: "This action cannot be undone." } - ] + { element: "p", text: "This action cannot be undone." }, + ], }, { className: "modal-card-foot", @@ -478,18 +476,18 @@ { className: "button is-danger", text: "Delete Session", - onclick: resultUi.deleteSession + onclick: resultUi.deleteSession, }, { className: "button", text: "Cancel", - onclick: resultUi.hideDeleteModal - } - ] - } - ] - } - ] + onclick: resultUi.hideDeleteModal, + }, + ], + }, + ], + }, + ], }); controlsView.appendChild(deleteModal); @@ -502,16 +500,16 @@ const { configuration, status, results } = state; if (!configuration || !status) return; const sessionDetailsView = UI.createElement({ - style: "margin-bottom: 20px" + style: "margin-bottom: 20px", }); const heading = UI.createElement({ text: "Session details", - className: "title is-4" + className: "title is-4", }); sessionDetailsView.appendChild(heading); - const getTagStyle = status => { + const getTagStyle = (status) => { switch (status) { case "completed": return "is-success"; @@ -531,7 +529,7 @@ if (!state.durationInterval) state.durationInterval = setInterval(() => { UI.getElement("duration").innerHTML = utils.millisToTimeString( - Date.now() - parseInt(status.dateStarted) + Date.now() - status.dateStarted.getTime() ); }, 1000); } @@ -544,7 +542,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "Token" } + children: { className: "label", text: "Token" }, }, { className: "field-body", @@ -552,11 +550,11 @@ className: "field", children: { className: "control", - text: configuration.token - } - } - } - ] + text: configuration.token, + }, + }, + }, + ], }); sessionDetailsView.appendChild(tokenField); @@ -565,7 +563,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "User Agent" } + children: { className: "label", text: "User Agent" }, }, { className: "field-body", @@ -573,11 +571,11 @@ className: "field", children: { className: "control", - text: configuration.userAgent || "" - } - } - } - ] + text: configuration.userAgent || "", + }, + }, + }, + ], }); sessionDetailsView.appendChild(userAgentField); @@ -586,7 +584,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "Test Paths" } + children: { className: "label", text: "Test Paths" }, }, { className: "field-body", @@ -596,11 +594,11 @@ className: "control", text: configuration.tests.include .reduce((text, test) => text + test + ", ", "") - .slice(0, -2) - } - } - } - ] + .slice(0, -2), + }, + }, + }, + ], }); sessionDetailsView.appendChild(testPathsField); @@ -609,7 +607,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "Excluded Test Paths" } + children: { className: "label", text: "Excluded Test Paths" }, }, { className: "field-body", @@ -620,7 +618,7 @@ children: [ { element: "span", - text: configuration.tests.exclude.length + text: configuration.tests.exclude.length, }, { element: "span", @@ -629,22 +627,24 @@ text: showExcluded ? "hide" : "show", onClick: showExcluded ? resultUi.hideExcluded - : resultUi.showExcluded + : resultUi.showExcluded, }, showExcluded ? { style: "max-height: 250px; overflow: auto; margin-bottom: 10px", - children: configuration.tests.exclude.map(test => ({ - text: test - })) + children: configuration.tests.exclude.map( + (test) => ({ + text: test, + }) + ), } - : null - ] - } - } - } - ] + : null, + ], + }, + }, + }, + ], }); sessionDetailsView.appendChild(excludedTestsField); @@ -655,7 +655,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "Reference Sessions" } + children: { className: "label", text: "Reference Sessions" }, }, { className: "field-body", @@ -663,11 +663,11 @@ className: "field", children: { className: "control", - children: { id: "reference-sessions" } - } - } - } - ] + children: { id: "reference-sessions" }, + }, + }, + }, + ], }); sessionDetailsView.appendChild(referenceSessionField); @@ -676,7 +676,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "Total Test Files" } + children: { className: "label", text: "Total Test Files" }, }, { className: "field-body", @@ -687,11 +687,11 @@ text: Object.keys(results).reduce( (sum, api) => (sum += results[api].total), 0 - ) - } - } - } - ] + ), + }, + }, + }, + ], }); sessionDetailsView.appendChild(totalTestFilesField); @@ -700,7 +700,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "Status" } + children: { className: "label", text: "Status" }, }, { className: "field-body", @@ -708,11 +708,11 @@ className: "field", children: { className: `control tag ${getTagStyle(status.status)}`, - text: status.status - } - } - } - ] + text: status.status, + }, + }, + }, + ], }); sessionDetailsView.appendChild(statusField); @@ -721,7 +721,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "Test Timeouts" } + children: { className: "label", text: "Test Timeouts" }, }, { className: "field-body", @@ -731,14 +731,15 @@ className: `control`, text: Object.keys(configuration.timeouts).reduce( (text, timeout) => - `${text}${timeout}: ${configuration.timeouts[timeout] / - 1000}s\n`, + `${text}${timeout}: ${ + configuration.timeouts[timeout] / 1000 + }s\n`, "" - ) - } - } - } - ] + ), + }, + }, + }, + ], }); sessionDetailsView.appendChild(timeoutsField); @@ -748,7 +749,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "Date Started" } + children: { className: "label", text: "Date Started" }, }, { className: "field-body", @@ -756,11 +757,11 @@ className: "field", children: { className: `control`, - text: new Date(status.dateStarted).toLocaleString() - } - } - } - ] + text: new Date(status.dateStarted).toLocaleString(), + }, + }, + }, + ], }); sessionDetailsView.appendChild(startedField); } @@ -771,7 +772,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "Date Finished" } + children: { className: "label", text: "Date Finished" }, }, { className: "field-body", @@ -779,11 +780,11 @@ className: "field", children: { className: `control`, - text: new Date(status.dateFinished).toLocaleString() - } - } - } - ] + text: new Date(status.dateFinished).toLocaleString(), + }, + }, + }, + ], }); sessionDetailsView.appendChild(finishedField); } @@ -794,7 +795,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "Duration" } + children: { className: "label", text: "Duration" }, }, { className: "field-body", @@ -805,14 +806,14 @@ id: "duration", text: utils.millisToTimeString( status.dateFinished - ? parseInt(status.dateFinished) - - parseInt(status.dateStarted) - : Date.now() - parseInt(status.dateStarted) - ) - } - } - } - ] + ? status.dateFinished.getTime() - + status.dateStarted.getTime() + : Date.now() - status.dateStarted.getTime() + ), + }, + }, + }, + ], }); sessionDetailsView.appendChild(durationField); } @@ -822,7 +823,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "Labels" } + children: { className: "label", text: "Labels" }, }, { className: "field-body", @@ -837,15 +838,15 @@ { element: "span", className: "tag is-info", - text: label + text: label, }, { element: "a", className: "tag is-delete", - onClick: () => resultUi.removeLabel(index) - } - ] - } + onClick: () => resultUi.removeLabel(index), + }, + ], + }, })) .concat( resultUi.state.configuration.isPublic @@ -861,37 +862,37 @@ style: "width: 10rem", id: "session-label-input", type: "text", - onKeyUp: event => + onKeyUp: (event) => event.keyCode === 13 ? resultUi.addLabel() - : null + : null, }, { className: "button is-dark is-outlined is-small is-rounded control", text: "save", - onClick: resultUi.addLabel + onClick: resultUi.addLabel, }, { className: "button is-dark is-outlined is-small is-rounded control", text: "cancel", - onClick: resultUi.hideAddLabel - } - ] - } + onClick: resultUi.hideAddLabel, + }, + ], + }, ] : [ { className: "button is-rounded is-small", text: "Add", - onClick: resultUi.showAddLabel - } + onClick: resultUi.showAddLabel, + }, ] - ) - } - } - ] + ), + }, + }, + ], }); sessionDetailsView.appendChild(labelsField); @@ -904,9 +905,9 @@ const { referenceSessions } = resultUi.state; if (!referenceSessions || referenceSessions.length === 0) return; const referenceSessionsList = UI.createElement({ - className: "field is-grouped is-grouped-multiline" + className: "field is-grouped is-grouped-multiline", }); - const getBrowserIcon = browser => { + const getBrowserIcon = (browser) => { switch (browser.toLowerCase()) { case "firefox": return "fab fa-firefox"; @@ -920,7 +921,7 @@ return "fab fa-safari"; } }; - referenceSessions.forEach(session => { + referenceSessions.forEach((session) => { const { token, browser } = session; const referenceSessionItem = UI.createElement({ className: @@ -932,14 +933,14 @@ className: "icon", children: { element: "i", - className: getBrowserIcon(browser.name) - } + className: getBrowserIcon(browser.name), + }, }, { element: "span", - text: token.split("-").shift() - } - ] + text: token.split("-").shift(), + }, + ], }); referenceSessionsList.appendChild(referenceSessionItem); }); @@ -960,9 +961,9 @@ { element: "span", className: "title is-7", - text: " (most recent first)" - } - ] + text: " (most recent first)", + }, + ], }); lastCompletedTestsView.appendChild(heading); @@ -979,10 +980,10 @@ element: "tr", children: [ { element: "td", text: "Test File" }, - { element: "td", text: "Malfunctioning List" } - ] - } - ] + { element: "td", text: "Malfunctioning List" }, + ], + }, + ], }, { element: "tbody", @@ -1009,18 +1010,18 @@ path ) ? "fas fa-check" - : "fas fa-plus" - } - ] - } - ] - } - ] - } - ] - })) - } - ] + : "fas fa-plus", + }, + ], + }, + ], + }, + ], + }, + ], + })), + }, + ], }); if (lastCompletedTests.length > 0) { lastCompletedTestsView.appendChild( @@ -1028,13 +1029,13 @@ className: "container", style: "overflow-x: auto;", id: "last-completed-overflow", - children: testsTable + children: testsTable, }) ); } else { const noTestsLabel = UI.createElement({ text: "- No Timed-Out Tests -", - style: "text-align: center" + style: "text-align: center", }); lastCompletedTestsView.appendChild(noTestsLabel); } @@ -1053,12 +1054,12 @@ const { results, status } = resultUi.state; const apiResultsView = UI.createElement({ - style: "margin-bottom: 20px" + style: "margin-bottom: 20px", }); const heading = UI.createElement({ text: "API Results", - className: "title is-4" + className: "title is-4", }); apiResultsView.appendChild(heading); @@ -1071,14 +1072,14 @@ children: [ { element: "i", - className: "fas fa-spinner fa-pulse" + className: "fas fa-spinner fa-pulse", }, { style: "margin-left: 0.4em;", - text: "Loading results ..." - } - ] - } + text: "Loading results ...", + }, + ], + }, }); apiResultsView.appendChild(loadingIndicator); @@ -1101,29 +1102,29 @@ { element: "th", text: "Timeout", - style: `min-width: ${width}` + style: `min-width: ${width}`, }, { element: "th", text: "Not Run", - style: `min-width: ${width}` + style: `min-width: ${width}`, }, { element: "th", text: "Test Files Run", - style: `min-width: ${width}` + style: `min-width: ${width}`, }, - { element: "th", text: "Export" } - ] - } - ] + { element: "th", text: "Export" }, + ], + }, + ], }); const apis = Object.keys(results).sort((apiA, apiB) => apiA.toLowerCase() > apiB.toLowerCase() ? 1 : -1 ); - const rows = apis.map(api => { + const rows = apis.map((api) => { const { complete = 0, pass = 0, @@ -1131,9 +1132,9 @@ timeout = 0, timeoutfiles = [], not_run: notRun = 0, - total + total, } = results[api]; - isDone = results[api].complete == results[api].total; + isDone = results[api].complete == results[api].total; const totalTestResults = pass + fail + timeout + notRun; return UI.createElement({ element: "tr", @@ -1144,31 +1145,37 @@ element: "td", children: { style: `color: hsl(141, 71%, 38%); overflow: visible; white-space: nowrap; width: ${width}`, - text: `${pass} (${utils.percent(pass, totalTestResults)}%)` - } + text: `${pass} (${utils.percent(pass, totalTestResults)}%)`, + }, }, { element: "td", children: { className: "has-text-danger", style: `overflow: visible; white-space: nowrap; width: ${width}`, - text: `${fail} (${utils.percent(fail, totalTestResults)}%)` - } + text: `${fail} (${utils.percent(fail, totalTestResults)}%)`, + }, }, { element: "td", children: { style: `color: hsl(48, 100%, 40%); overflow: visible; white-space: nowrap; width: ${width}`, - text: `${timeout} (${utils.percent(timeout, totalTestResults)}%)` - } + text: `${timeout} (${utils.percent( + timeout, + totalTestResults + )}%)`, + }, }, { element: "td", children: { className: "has-text-info", style: `overflow: visible; white-space: nowrap; width: ${width}`, - text: `${notRun} (${utils.percent(notRun, totalTestResults)}%)` - } + text: `${notRun} (${utils.percent( + notRun, + totalTestResults + )}%)`, + }, }, { element: "td", @@ -1177,8 +1184,8 @@ text: `${complete}/${total} (${utils.percent( complete, total - )}%)` - } + )}%)`, + }, }, { element: "td", @@ -1191,31 +1198,32 @@ className: "button is-dark is-outlined is-small", onclick: () => resultUi.downloadApiResultJson(api), text: "json", - title: `Download results of ${api} API as JSON file.` - } + title: `Download results of ${api} API as JSON file.`, + }, }, - resultUi.state.reportsEnabled ? - { - className: "control", - children: { - className: "button is-dark is-outlined is-small", - disabled: !isDone, - onclick: () => resultUi.openHtmlReport(api), - text: "report", - title: `Show results of ${api} API in WPT Report format.` - } - } : null - ] - } - } - ] + resultUi.state.reportsEnabled + ? { + className: "control", + children: { + className: "button is-dark is-outlined is-small", + disabled: !isDone, + onclick: () => resultUi.openHtmlReport(api), + text: "report", + title: `Show results of ${api} API in WPT Report format.`, + }, + } + : null, + ], + }, + }, + ], }); }); const { pass, fail, timeout, not_run, complete, total } = apis.reduce( (sum, api) => { Object.keys(sum).forEach( - key => (sum[key] += results[api][key] ? results[api][key] : 0) + (key) => (sum[key] += results[api][key] ? results[api][key] : 0) ); return sum; }, @@ -1234,31 +1242,43 @@ element: "th", children: { style: `color: hsl(141, 71%, 38%); overflow: visible; white-space: nowrap; width: ${width}`, - text: `${pass} (${utils.percent(pass, totalTestResults)}%)` - } + text: `${pass} (${utils.percent( + pass, + totalTestResults + )}%)`, + }, }, { element: "th", children: { style: `overflow: visible; white-space: nowrap; width: ${width}`, className: "has-text-danger", - text: `${fail} (${utils.percent(fail, totalTestResults)}%)` - } + text: `${fail} (${utils.percent( + fail, + totalTestResults + )}%)`, + }, }, { element: "th", children: { style: `color: hsl(48, 100%, 40%); overflow: visible; white-space: nowrap; width: ${width}`, - text: `${timeout} (${utils.percent(timeout, totalTestResults)}%)` - } + text: `${timeout} (${utils.percent( + timeout, + totalTestResults + )}%)`, + }, }, { element: "th", children: { style: `overflow: visible; white-space: nowrap; width: ${width}`, className: "has-text-info", - text: `${not_run} (${utils.percent(not_run, totalTestResults)}%)` - } + text: `${not_run} (${utils.percent( + not_run, + totalTestResults + )}%)`, + }, }, { element: "th", @@ -1267,13 +1287,13 @@ text: `${complete}/${total} (${utils.percent( complete, total - )}%)` - } + )}%)`, + }, }, - { element: "th" } - ] - } - ] + { element: "th" }, + ], + }, + ], }); const resultsTable = UI.createElement({ @@ -1286,8 +1306,8 @@ id: "results-table", style: "width: 100%; min-width: 30em; border-radius: 3px; border: 2px solid hsl(0, 0%, 86%);", - children: [header, { element: "tbody", children: rows }, footer] - } + children: [header, { element: "tbody", children: rows }, footer], + }, }); apiResultsView.appendChild(resultsTable); @@ -1308,7 +1328,7 @@ const heading = UI.createElement({ className: "title is-4", - text: "Export" + text: "Export", }); exportElement.appendChild(heading); @@ -1317,7 +1337,7 @@ children: [ { className: "field-label", - children: { className: "label", text: "Results" } + children: { className: "label", text: "Results" }, }, { className: "field-body", @@ -1328,7 +1348,7 @@ { className: "column is-9", text: - "Download results for import into other WMAS Test Suite instances." + "Download results for import into other WMAS Test Suite instances.", }, { className: "column is-3", @@ -1343,17 +1363,17 @@ className: "icon", children: { element: "i", - className: "fas fa-file-archive" - } + className: "fas fa-file-archive", + }, }, - { element: "span", text: "Download Zip" } - ] - } - } - ] - } - } - ] + { element: "span", text: "Download Zip" }, + ], + }, + }, + ], + }, + }, + ], }); exportElement.appendChild(resultsField); @@ -1364,8 +1384,8 @@ className: "field-label", children: { className: "label", - text: "All JSON Files" - } + text: "All JSON Files", + }, }, { className: "field-body", @@ -1376,7 +1396,7 @@ { className: "column is-9", text: - "Download JSON files containing results of completed test files." + "Download JSON files containing results of completed test files.", }, { className: "column is-3", @@ -1390,17 +1410,17 @@ className: "icon", children: { element: "i", - className: "fas fa-file-archive" - } + className: "fas fa-file-archive", + }, }, - { element: "span", text: "Download Zip" } - ] - } - } - ] - } - } - ] + { element: "span", text: "Download Zip" }, + ], + }, + }, + ], + }, + }, + ], }); exportElement.appendChild(jsonField); @@ -1411,8 +1431,8 @@ className: "field-label", children: { className: "label", - text: "Session result HTML" - } + text: "Session result HTML", + }, }, { className: "field-body", @@ -1423,7 +1443,7 @@ { className: "column is-9", text: - "Download this sessions result as standalone HTML page, similar to this page." + "Download this sessions result as standalone HTML page, similar to this page.", }, { className: "column is-3", @@ -1437,17 +1457,17 @@ className: "icon", children: { element: "i", - className: "fas fa-code" - } + className: "fas fa-code", + }, }, - { element: "span", text: "Download HTML" } - ] - } - } - ] - } - } - ] + { element: "span", text: "Download HTML" }, + ], + }, + }, + ], + }, + }, + ], }); exportElement.appendChild(htmlField); }, @@ -1455,7 +1475,7 @@ const malfunctioningTestsView = UI.createElement({}); const heading = UI.createElement({ className: "title is-4", - text: "Malfunctioning Tests" + text: "Malfunctioning Tests", }); malfunctioningTestsView.appendChild(heading); @@ -1472,14 +1492,14 @@ element: "tr", children: [ { element: "td", text: "Test File" }, - { element: "td", text: "" } - ] - } - ] + { element: "td", text: "" }, + ], + }, + ], }, { element: "tbody", - children: malfunctioningTests.map(path => ({ + children: malfunctioningTests.map((path) => ({ element: "tr", children: [ { element: "td", text: path }, @@ -1500,17 +1520,17 @@ children: [ { element: "i", - className: "fas fa-trash-alt" - } - ] - } - ] - } - } - ] - })) - } - ] + className: "fas fa-trash-alt", + }, + ], + }, + ], + }, + }, + ], + })), + }, + ], }); if (malfunctioningTests.length > 0) { malfunctioningTestsView.appendChild( @@ -1518,13 +1538,13 @@ className: "container", style: "overflow-x: auto", id: "malfunctioning-overflow", - children: testsTable + children: testsTable, }) ); } else { const noTestsLabel = UI.createElement({ text: "- No Tests Available -", - style: "text-align: center" + style: "text-align: center", }); malfunctioningTestsView.appendChild(noTestsLabel); } @@ -1538,7 +1558,7 @@ malfunctioningTestsElement.appendChild(malfunctioningTestsView); UI.loadScrollPosition("malfunctioning-overflow"); - } + }, }; diff --git a/testing/web-platform/tests/tools/wave/www/submitresult.html b/testing/web-platform/tests/tools/wave/www/submitresult.html index 000c5a1208a6..07e9f3523501 100644 --- a/testing/web-platform/tests/tools/wave/www/submitresult.html +++ b/testing/web-platform/tests/tools/wave/www/submitresult.html @@ -50,7 +50,6 @@ var resultData; var rawResult = parsedQuery.result; if (rawResult) { - console.log(decodeURIComponent(rawResult)); resultData = JSON.parse(decodeURIComponent(rawResult)); } diff --git a/testing/web-platform/tests/tools/wave/www/test.html b/testing/web-platform/tests/tools/wave/www/test.html new file mode 100644 index 000000000000..d06b18fef6d6 --- /dev/null +++ b/testing/web-platform/tests/tools/wave/www/test.html @@ -0,0 +1,155 @@ + + + + + Creating Session - Web Platform Tests + + + + + + + +
+
+ + +

+ + + + Creating Session +

+
+
+
+
Reference Tokens:
+
+
+
+
Test Paths:
+
+
+
+ +
+
+
+
+ + +