From c5d38ae32a07bc8e6ff3bb92e93cbc8a79ffab39 Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Wed, 14 Mar 2018 15:59:56 -0700 Subject: [PATCH] Run browser functional tests in CI builds (#1487) --- .travis.yml | 1 + build/repo.targets | 34 +- client-ts/FunctionalTests/EchoEndPoint.cs | 16 + client-ts/FunctionalTests/Program.cs | 2 +- client-ts/FunctionalTests/Startup.cs | 17 +- client-ts/FunctionalTests/TestHub.cs | 6 + client-ts/FunctionalTests/package-lock.json | 1605 +++-------------- client-ts/FunctionalTests/package.json | 21 +- client-ts/FunctionalTests/run-tests.ts | 258 --- .../FunctionalTests/run-tests.tsconfig.json | 5 - .../FunctionalTests/selenium/run-ci-tests.ts | 96 + .../FunctionalTests/selenium/run-tests.ts | 162 ++ .../FunctionalTests/selenium/tsconfig.json | 6 + client-ts/FunctionalTests/ts/Common.ts | 4 +- .../FunctionalTests/ts/ConnectionTests.ts | 56 +- .../FunctionalTests/ts/HubConnectionTests.ts | 59 +- .../FunctionalTests/ts/WebDriverReporter.ts | 33 +- .../FunctionalTests/ts/WebSocketTests.ts | 15 +- client-ts/FunctionalTests/ts/index.ts | 11 +- .../FunctionalTests/wwwroot/default.html | 8 +- client-ts/signalr/spec/HttpConnection.spec.ts | 59 +- client-ts/signalr/src/HttpConnection.ts | 29 +- client-ts/signalr/src/HubConnection.ts | 4 +- client-ts/signalr/src/IConnection.ts | 4 +- client-ts/webdriver-tap-runner/bin.ts | 24 + client-ts/webdriver-tap-runner/lib.ts | 202 +++ .../webdriver-tap-runner/package-lock.json | 1358 ++++++++++++++ client-ts/webdriver-tap-runner/package.json | 26 + client-ts/webdriver-tap-runner/tsconfig.json | 7 + client-ts/webdriver-tap-runner/utils.ts | 61 + 30 files changed, 2390 insertions(+), 1799 deletions(-) delete mode 100644 client-ts/FunctionalTests/run-tests.ts delete mode 100644 client-ts/FunctionalTests/run-tests.tsconfig.json create mode 100644 client-ts/FunctionalTests/selenium/run-ci-tests.ts create mode 100644 client-ts/FunctionalTests/selenium/run-tests.ts create mode 100644 client-ts/FunctionalTests/selenium/tsconfig.json create mode 100644 client-ts/webdriver-tap-runner/bin.ts create mode 100644 client-ts/webdriver-tap-runner/lib.ts create mode 100644 client-ts/webdriver-tap-runner/package-lock.json create mode 100644 client-ts/webdriver-tap-runner/package.json create mode 100644 client-ts/webdriver-tap-runner/tsconfig.json create mode 100644 client-ts/webdriver-tap-runner/utils.ts diff --git a/.travis.yml b/.travis.yml index 7624de0c8f..e84ae1c10d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ os: - osx osx_image: xcode8.2 addons: + chrome: stable apt: packages: - libunwind8 diff --git a/build/repo.targets b/build/repo.targets index ea35bb1da6..bac08ff329 100644 --- a/build/repo.targets +++ b/build/repo.targets @@ -10,22 +10,35 @@ - + + $(RestoreDependsOn);RestoreNpm + + + - + + $(TestDependsOn);RunTSClientNodeTests;RunBrowserTests + + + + + + + + - $(PackageDependsOn);PackNPMPackages $(GetArtifactInfoDependsOn);GetNpmArtifactInfo + $(PrepareDependsOn);GetNpmArtifactInfo @@ -49,10 +62,21 @@ - + + Restore;BuildNPMPackages;$(CompileDependsOn) + + + + + + + + Compile;PackNPMPackages;$(PackageDependsOn) + + + - diff --git a/client-ts/FunctionalTests/EchoEndPoint.cs b/client-ts/FunctionalTests/EchoEndPoint.cs index 9a954db666..ed08754845 100644 --- a/client-ts/FunctionalTests/EchoEndPoint.cs +++ b/client-ts/FunctionalTests/EchoEndPoint.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Threading.Tasks; using Microsoft.AspNetCore.Protocols; +using Microsoft.AspNetCore.Protocols.Features; using Microsoft.AspNetCore.Sockets; namespace FunctionalTests @@ -26,6 +27,21 @@ namespace FunctionalTests { connection.Transport.Input.AdvanceTo(result.Buffer.End); } + + // Wait for the user to close + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + connection.Transport.Input.OnWriterCompleted((ex, state) => + { + if (ex != null) + { + ((TaskCompletionSource)state).TrySetException(ex); + } + else + { + ((TaskCompletionSource)state).TrySetResult(null); + } + }, tcs); + await tcs.Task; } } } diff --git a/client-ts/FunctionalTests/Program.cs b/client-ts/FunctionalTests/Program.cs index 2a96ed6242..f281449d7d 100644 --- a/client-ts/FunctionalTests/Program.cs +++ b/client-ts/FunctionalTests/Program.cs @@ -14,7 +14,7 @@ namespace FunctionalTests var host = new WebHostBuilder() .ConfigureLogging(factory => { - factory.AddConsole(); + factory.AddConsole(options => options.IncludeScopes = true); factory.AddFilter("Console", level => level >= LogLevel.Information); factory.AddDebug(); }) diff --git a/client-ts/FunctionalTests/Startup.cs b/client-ts/FunctionalTests/Startup.cs index 982714e46a..a5b77ef60d 100644 --- a/client-ts/FunctionalTests/Startup.cs +++ b/client-ts/FunctionalTests/Startup.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Sockets; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json.Serialization; @@ -80,10 +81,18 @@ namespace FunctionalTests } app.UseFileServer(); - app.UseSockets(options => options.MapEndPoint("/echo")); - app.UseSignalR(options => options.MapHub("/testhub")); - app.UseSignalR(options => options.MapHub("/uncreatable")); - app.UseSignalR(options => options.MapHub("/authorizedhub")); + app.UseSockets(routes => + { + routes.MapEndPoint("/echo"); + }); + + app.UseSignalR(routes => + { + routes.MapHub("/testhub"); + routes.MapHub("/testhub-nowebsockets", options => options.Transports = TransportType.ServerSentEvents | TransportType.LongPolling); + routes.MapHub("/uncreatable"); + routes.MapHub("/authorizedhub"); + }); app.Use(next => async (context) => { diff --git a/client-ts/FunctionalTests/TestHub.cs b/client-ts/FunctionalTests/TestHub.cs index 36a0b96606..6867cb6604 100644 --- a/client-ts/FunctionalTests/TestHub.cs +++ b/client-ts/FunctionalTests/TestHub.cs @@ -5,6 +5,7 @@ using System; using System.Reactive.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; +using Microsoft.AspNetCore.Sockets; namespace FunctionalTests { @@ -52,6 +53,11 @@ namespace FunctionalTests throw new InvalidOperationException(message); } + public string GetActiveTransportName() + { + return Context.Connection.Metadata[ConnectionMetadataNames.Transport].ToString(); + } + public ComplexObject EchoComplexObject(ComplexObject complexObject) { return complexObject; diff --git a/client-ts/FunctionalTests/package-lock.json b/client-ts/FunctionalTests/package-lock.json index bcbe0c69ea..ed6372b193 100644 --- a/client-ts/FunctionalTests/package-lock.json +++ b/client-ts/FunctionalTests/package-lock.json @@ -4,10 +4,22 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@types/selenium-webdriver": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.8.tgz", - "integrity": "sha512-yrqQvb1EZhH+ONMzUmsEnBjzitortVv0lynRe5US2+FofdoMWUE4wf7v4peCd62fqXq8COCVTbpS1/jIg5EbuQ==", + "@std/esm": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@std/esm/-/esm-0.18.0.tgz", + "integrity": "sha512-oeHSSVp/WxC08ngpKgyYR4LcI0+EBwZiJcB58jvIqyJnOGxudSkxTgAQKsVfpNsMXfOoILgu9PWhuzIZ8GQEjw==", + "dev": true + }, + "@types/debug": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-0.0.30.tgz", + "integrity": "sha512-orGL5LXERPYsLov6CWs3Fh6203+dXzJkR7OnddIr2514Hsecwc8xRpzCapshBbKFImCsvS/mk6+FWiN5LyZJAQ==", + "dev": true + }, + "@types/node": { + "version": "9.4.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.4.6.tgz", + "integrity": "sha512-CTUtLb6WqCCgp6P59QintjHWqzf4VL1uPA27bipLAPxFqrtK1gEYllePzTICGqQ8rYsCbpnsNypXjjDzGAAjEQ==", "dev": true }, "@types/strip-bom": { @@ -22,36 +34,6 @@ "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", "dev": true }, - "@types/yargs": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-11.0.0.tgz", - "integrity": "sha512-vFql3tOxs6clgh+WVoLW3nOkNGCdeKsMU6mQZkOerJpV/CR9Xc1c1lZ+kYU+hNSobrQIOcNovWfPFDJIhcG5Pw==", - "dev": true - }, - "adm-zip": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", - "integrity": "sha1-hgbCy/HEJs6MjsABdER/1Jtur8E=", - "dev": true - }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" - } - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, "ansi-styles": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", @@ -61,111 +43,16 @@ "color-convert": "1.9.1" } }, - "argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", - "dev": true, - "requires": { - "sprintf-js": "1.0.3" - } - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "1.0.3" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true - }, - "assert-plus": { + "buffer-shims": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "dev": true, - "requires": { - "hoek": "4.2.0" - } - }, - "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", "dev": true }, "chalk": { @@ -179,29 +66,6 @@ "supports-color": "4.5.0" } }, - "cliui": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.0.0.tgz", - "integrity": "sha512-nY3W5Gu2racvdDk//ELReY+dHjb9PlIcVDFXP72nVIhq2Gy3LuVXYwJoPVudwQnv1shtohpgkdCKT2YaKY0CKw==", - "dev": true, - "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, "color-convert": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", @@ -217,127 +81,21 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "core-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", - "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", - "dev": true - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "4.1.1", - "shebang-command": "1.2.0", - "which": "1.3.0" - } - }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "dev": true, - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "dev": true, - "requires": { - "hoek": "4.2.0" - } - } - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "1.0.0" - } - }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "deep-equal": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz", - "integrity": "sha1-skbCuApXCkfBG+HZvRBw7IeLh84=", - "dev": true - }, - "defined": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz", - "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=", - "dev": true - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.0", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.6.2" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, "diff": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", @@ -350,16 +108,6 @@ "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, "es6-promise": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.2.tgz", @@ -372,169 +120,14 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "events-to-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", - "integrity": "sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y=", - "dev": true - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true, "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "faucet": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/faucet/-/faucet-0.0.1.tgz", - "integrity": "sha1-WX3PHSGJosBiMhtZHo8VHtIDnZw=", - "dev": true, - "requires": { - "defined": "0.0.0", - "duplexer": "0.1.1", - "minimist": "0.0.5", - "sprintf": "0.1.5", - "tap-parser": "0.4.3", - "tape": "2.3.3", - "through2": "0.2.3" - }, - "dependencies": { - "minimist": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", - "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=", - "dev": true - } - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "2.0.0" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", - "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", - "dev": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "1.0.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, - "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" } }, "has-ansi": { @@ -560,24 +153,6 @@ "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "dev": true, - "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.0", - "sntp": "2.1.0" - } - }, - "hoek": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==", - "dev": true - }, "homedir-polyfill": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", @@ -587,276 +162,39 @@ "parse-passwd": "1.0.0" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "is-path-inside": "1.0.1" + "number-is-nan": "1.0.1" } }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "1.0.2" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "js-yaml": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", - "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", - "dev": true, - "requires": { - "argparse": "1.0.9", - "esprima": "4.0.0" - }, - "dependencies": { - "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", - "dev": true - } - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jszip": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", - "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", - "dev": true, - "requires": { - "core-js": "2.3.0", - "es6-promise": "3.0.2", - "lie": "3.1.1", - "pako": "1.0.6", - "readable-stream": "2.0.6" - }, - "dependencies": { - "es6-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", - "dev": true - } - } - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "1.0.0" - } - }, - "lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", - "dev": true, - "requires": { - "immediate": "3.0.6" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" - } - }, - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "dev": true, - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - }, "make-error": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.3.tgz", "integrity": "sha512-j3dZCri3cCd23wgPqK/0/KvTN8R+W6fXDqQe8BNLbTpONjbA8SPaRr+q0BQq9bx3Q/+g68/gDIh9FW3by702Tg==", "dev": true }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "1.2.0" - } - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", - "dev": true - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "dev": true, - "requires": { - "mime-db": "1.30.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.8" - } - }, "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", @@ -886,99 +224,22 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "2.0.1" - } - }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", - "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", - "dev": true, - "requires": { - "p-try": "1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "1.2.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "parse-ms": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz", + "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=", "dev": true }, "parse-passwd": { @@ -987,55 +248,21 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "plur": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-1.0.0.tgz", + "integrity": "sha1-24XGgU9eXlo7Se/CjWBP7GKXUVY=", "dev": true }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "performance-now": { + "pretty-ms": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-2.1.0.tgz", + "integrity": "sha1-QlfCVt8/sLRR1q/6qwIYhBJpgdw=", "dev": true, "requires": { - "pinkie": "2.0.4" + "is-finite": "1.0.2", + "parse-ms": "1.0.1", + "plur": "1.0.0" } }, "process-nextick-args": { @@ -1044,28 +271,10 @@ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", "dev": true }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "re-emitter": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/re-emitter/-/re-emitter-1.1.3.tgz", + "integrity": "sha1-+p4xn/3u6zWycpbvDz03TawvUqc=", "dev": true }, "readable-stream": { @@ -1082,132 +291,18 @@ "util-deprecate": "1.0.2" } }, - "request": { - "version": "2.83.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", - "dev": true, - "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.1", - "har-validator": "5.0.3", - "hawk": "6.0.2", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "resumer": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", - "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", - "dev": true, - "requires": { - "through": "2.3.8" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "selenium-webdriver": { - "version": "4.0.0-alpha.1", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.1.tgz", - "integrity": "sha512-z88rdjHAv3jmTZ7KSGUkTvo4rGzcDGMq0oXWHNIDK96Gs31JKVdu9+FMtT4KBrVoibg8dUicJDok6GnqqttO5Q==", - "dev": true, - "requires": { - "jszip": "3.1.5", - "rimraf": "2.6.2", - "tmp": "0.0.30", - "xml2js": "0.4.19" - } - }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "dev": true, - "requires": { - "hoek": "4.2.0" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1223,42 +318,13 @@ "source-map": "0.6.1" } }, - "sprintf": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/sprintf/-/sprintf-0.1.5.tgz", - "integrity": "sha1-j4PjmpMXwaUCy324BQ5Rxnn27c8=", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", "dev": true, "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "through": "2.3.8" } }, "string_decoder": { @@ -1267,33 +333,12 @@ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -1309,42 +354,70 @@ "has-flag": "2.0.0" } }, - "tap-mocha-reporter": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-3.0.6.tgz", - "integrity": "sha512-UImgw3etckDQCoqZIAIKcQDt0w1JLVs3v0yxLlmwvGLZl6MGFxF7JME5PElXjAoDklVDU42P3vVu5jgr37P4Yg==", + "tap-out": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/tap-out/-/tap-out-1.4.2.tgz", + "integrity": "sha1-yQfsG/lAURHQiCY+kvVgi4jLs3o=", "dev": true, "requires": { - "color-support": "1.1.3", - "debug": "2.6.9", - "diff": "1.4.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "js-yaml": "3.10.0", - "readable-stream": "2.3.4", - "tap-parser": "5.4.0", - "unicode-length": "1.0.3" + "re-emitter": "1.1.3", + "readable-stream": "2.0.6", + "split": "1.0.1", + "trim": "0.0.1" + } + }, + "tap-spec": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tap-spec/-/tap-spec-4.1.1.tgz", + "integrity": "sha1-4unyb1IIIysfViKIyXYk1YqI8Fo=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "duplexer": "0.1.1", + "figures": "1.7.0", + "lodash": "3.10.1", + "pretty-ms": "2.1.0", + "repeat-string": "1.6.1", + "tap-out": "1.4.2", + "through2": "2.0.3" }, "dependencies": { - "diff": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", - "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz", "integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==", "dev": true, - "optional": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", @@ -1360,66 +433,117 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, - "optional": true, "requires": { "safe-buffer": "5.1.1" } }, - "tap-parser": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-5.4.0.tgz", - "integrity": "sha512-BIsIaGqv7uTQgTW1KLTMNPSEQf4zDDPgYOBRdgOfuB+JFOLRBfEu6cLa/KvMvmqggu1FKXDfitjLwsq4827RvA==", + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "events-to-array": "1.1.2", - "js-yaml": "3.10.0", - "readable-stream": "2.3.4" + "ansi-regex": "2.1.1" } - } - } - }, - "tap-parser": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-0.4.3.tgz", - "integrity": "sha1-pOrhkMENdsehEZIf84u+TVjwnuo=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "1.1.14" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { + "readable-stream": "2.3.4", + "xtend": "4.0.1" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + } + } + }, + "tap-teamcity": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tap-teamcity/-/tap-teamcity-3.0.2.tgz", + "integrity": "sha512-FI26a4CGNx9LWx2vRh3fLNrel1GJm5smBVJl2tzabTwGrmX9d+KHWP2O9xdKgMtH5IOBpN6goy9Yh4P2NRaoQw==", + "dev": true, + "requires": { + "@std/esm": "0.18.0", + "duplexer": "0.1.1", + "tap-out": "2.0.0", + "through2": "2.0.3" + }, + "dependencies": { + "readable-stream": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=", + "dev": true, + "requires": { + "buffer-shims": "1.0.0", "core-util-is": "1.0.2", "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "split": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.0.tgz", + "integrity": "sha1-xDlc5oOrzSVLwo/h2rtuXCfc/64=", + "dev": true, + "requires": { + "through": "2.3.8" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tap-out": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tap-out/-/tap-out-2.0.0.tgz", + "integrity": "sha1-4pCSskjFzYme3jSCIZAHl6aLmNI=", + "dev": true, + "requires": { + "re-emitter": "1.1.3", + "readable-stream": "2.2.9", + "split": "1.0.0", + "trim": "0.0.1" } } } }, - "tape": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tape/-/tape-2.3.3.tgz", - "integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=", + "tee": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/tee/-/tee-0.2.0.tgz", + "integrity": "sha1-2HtYIHoMmb1icXnCwsYjXj051yU=", "dev": true, "requires": { - "deep-equal": "0.1.2", - "defined": "0.0.0", - "inherits": "2.0.3", - "jsonify": "0.0.0", - "resumer": "0.0.0", - "through": "2.3.8" + "through": "1.1.2" + }, + "dependencies": { + "through": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/through/-/through-1.1.2.tgz", + "integrity": "sha1-NEpUJaN3MxTKfg62US+6+vdsC/4=", + "dev": true + } } }, "through": { @@ -1429,57 +553,51 @@ "dev": true }, "through2": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", - "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { - "readable-stream": "1.1.14", - "xtend": "2.1.2" + "readable-stream": "2.3.4", + "xtend": "4.0.1" }, "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz", + "integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==", "dev": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" } } } }, - "tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2" - } - }, - "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "dev": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tree-kill": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz", - "integrity": "sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==", + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", "dev": true }, "ts-node": { @@ -1512,61 +630,12 @@ "strip-json-comments": "2.0.1" } }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, - "unicode-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-1.0.3.tgz", - "integrity": "sha1-Wtp6f+1RhBpBijKM8UlHisg1irs=", - "dev": true, - "requires": { - "punycode": "1.4.1", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - } - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", - "dev": true - }, "v8flags": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.1.tgz", @@ -1576,212 +645,12 @@ "homedir-polyfill": "1.0.1" } }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - } - }, - "webdriver-manager": { - "version": "12.0.6", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.0.6.tgz", - "integrity": "sha1-PfGkgZdwELTL+MnYXHpXeCjA5ws=", - "dev": true, - "requires": { - "adm-zip": "0.4.7", - "chalk": "1.1.3", - "del": "2.2.2", - "glob": "7.1.2", - "ini": "1.3.5", - "minimist": "1.2.0", - "q": "1.5.1", - "request": "2.83.0", - "rimraf": "2.6.2", - "semver": "5.5.0", - "xml2js": "0.4.19" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "dev": true, - "requires": { - "isexe": "2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "dev": true, - "requires": { - "sax": "1.2.4", - "xmlbuilder": "9.0.4" - } - }, - "xmlbuilder": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.4.tgz", - "integrity": "sha1-UZy0ymhtAFqEINNJbz8MruzKWA8=", - "dev": true - }, "xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "dev": true, - "requires": { - "object-keys": "0.4.0" - } - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz", - "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", - "dev": true, - "requires": { - "cliui": "4.0.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "9.0.2" - } - }, - "yargs-parser": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", - "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", - "dev": true, - "requires": { - "camelcase": "4.1.0" - } - }, "yn": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", diff --git a/client-ts/FunctionalTests/package.json b/client-ts/FunctionalTests/package.json index 69816a4ec1..2d9d1ade72 100644 --- a/client-ts/FunctionalTests/package.json +++ b/client-ts/FunctionalTests/package.json @@ -6,16 +6,14 @@ "main": "index.js", "dependencies": {}, "devDependencies": { - "@types/selenium-webdriver": "^3.0.8", - "@types/yargs": "^11.0.0", + "@types/debug": "0.0.30", + "@types/node": "^9.4.6", + "debug": "^3.1.0", "es6-promise": "^4.2.2", - "faucet": "0.0.1", - "selenium-webdriver": "^4.0.0-alpha.1", - "tap-mocha-reporter": "^3.0.6", - "tree-kill": "^1.2.0", - "ts-node": "^4.1.0", - "webdriver-manager": "^12.0.6", - "yargs": "^11.0.0" + "tap-spec": "^4.1.1", + "tap-teamcity": "^3.0.2", + "tee": "^0.2.0", + "ts-node": "^4.1.0" }, "scripts": { "clean": "node ../node_modules/rimraf/bin.js ./wwwroot/dist ./obj/js", @@ -23,8 +21,9 @@ "build:lint": "node ../node_modules/tslint/bin/tslint -c ../tslint.json -p ./tsconfig.json", "build:tsc": "node ../node_modules/typescript/bin/tsc --project ./tsconfig.json", "build:rollup": "node ../node_modules/rollup/bin/rollup -c", - "rawtest": "ts-node --project ./run-tests.tsconfig.json ./run-tests.ts --browser chrome -h", - "test": "npm run rawtest | faucet" + "pretest": "npm run build", + "test": "dotnet build && ts-node --project ./selenium/tsconfig.json ./selenium/run-tests.ts", + "ci-test": "ts-node --project ./selenium/tsconfig.json ./selenium/run-ci-tests.ts" }, "author": "", "license": "Apache-2.0" diff --git a/client-ts/FunctionalTests/run-tests.ts b/client-ts/FunctionalTests/run-tests.ts deleted file mode 100644 index c68fdb504c..0000000000 --- a/client-ts/FunctionalTests/run-tests.ts +++ /dev/null @@ -1,258 +0,0 @@ -// console.log messages should be prefixed with "#" to ensure stdout continues to conform to TAP (Test Anything Protocol) -// https://testanything.org/tap-version-13-specification.html - -import { ChildProcess, spawn, spawnSync } from "child_process"; -import * as os from "os"; -import * as path from "path"; -import { Readable } from "stream"; - -import { Builder, By, Capabilities, logging, WebDriver, WebElement } from "selenium-webdriver"; -import * as kill from "tree-kill"; -import { argv } from "yargs"; - -const rootDir = __dirname; - -const verbose = argv.v || argv.verbose || false; -const browser = argv.browser || "chrome"; -const headless = argv.headless || argv.h || false; - -let webDriver: ChildProcess; -let dotnet: ChildProcess; - -console.log("TAP version 13"); - -function logverbose(message: any) { - if (verbose) { - console.log(message); - } -} - -function runCommand(command: string, args: string[]) { - args = args || []; - const result = spawnSync(command, args, { - cwd: rootDir, - }); - if (result.status !== 0) { - console.error("Bail out!"); // Part of the TAP protocol - console.error(`Command ${command} ${args.join(" ")} failed:`); - console.error("stderr:"); - console.error(result.stderr); - console.error("stdout:"); - console.error(result.stdout); - shutdown(1); - } -} - -const logExtractorRegex = /[^ ]+ [^ ]+ "(.*)"/; - -function getMessage(logMessage: string): string { - const r = logExtractorRegex.exec(logMessage); - - // Unescape \" - if (r && r.length >= 2) { - return r[1].replace(/\\"/g, "\""); - } else { - return logMessage; - } -} - -async function waitForElement(driver: WebDriver, id: string): Promise { - while (true) { - const elements = await driver.findElements(By.id(id)); - if (elements && elements.length > 0) { - return elements[0]; - } - } -} - -async function isComplete(element: WebElement): Promise { - return (await element.getAttribute("data-done")) === "1"; -} - -async function getLogEntry(index: number, element: WebElement): Promise { - const elements = await element.findElements(By.id(`__tap_item_${index}`)); - if (elements && elements.length > 0) { - return elements[0]; - } - return null; -} - -async function getEntryContent(element: WebElement): Promise { - return await element.getAttribute("innerHTML"); -} - -async function flushEntries(index: number, element: WebElement): Promise { - let entry = await getLogEntry(index, element); - while (entry) { - index += 1; - console.log(await getEntryContent(entry)); - entry = await getLogEntry(index, element); - } -} - -function applyCapabilities(builder: Builder) { - if (browser === "chrome") { - const caps = Capabilities.chrome(); - const args = []; - - if (headless) { - console.log("# Using Headless Mode"); - args.push("--headless"); - if (process.platform === "win32") { - args.push("--disable-gpu"); - } - } - - caps.set("chromeOptions", { - args, - }); - builder.withCapabilities(caps); - } -} - -async function runTests(port: number, serverUrl: string): Promise { - const webDriverUrl = `http://localhost:${port}/wd/hub`; - console.log(`# Using WebDriver at ${webDriverUrl}`); - console.log(`# Launching ${browser} browser`); - const logPrefs = new logging.Preferences(); - logPrefs.setLevel(logging.Type.BROWSER, logging.Level.INFO); - - const builder = new Builder() - .usingServer(webDriverUrl) - .setLoggingPrefs(logPrefs) - .forBrowser(browser); - - applyCapabilities(builder); - - const driver = await builder.build(); - try { - await driver.get(serverUrl); - - let index = 0; - console.log("# Running tests"); - const element = await waitForElement(driver, "__tap_list"); - const success = true; - while (!await isComplete(element)) { - const entry = await getLogEntry(index, element); - if (entry) { - index += 1; - console.log(await getEntryContent(entry)); - } - } - - // Flush remaining entries - await flushEntries(index, element); - console.log("# End of tests"); - } catch (e) { - console.error("Error: " + e.toString()); - } finally { - await driver.quit(); - } -} - -function waitForMatch(command: string, process: ChildProcess, regex: RegExp): Promise { - return new Promise((resolve, reject) => { - try { - let lastLine = ""; - - async function onData(this: Readable, chunk: string | Buffer): Promise { - try { - chunk = chunk.toString(); - - // Process lines - let lineEnd = chunk.indexOf(os.EOL); - while (lineEnd >= 0) { - const chunkLine = lastLine + chunk.substring(0, lineEnd); - lastLine = ""; - - chunk = chunk.substring(lineEnd + os.EOL.length); - - logverbose(`# ${command}: ${chunkLine}`); - const results = regex.exec(chunkLine); - if (results && results.length > 0) { - this.removeAllListeners("data"); - resolve(results); - return; - } - lineEnd = chunk.indexOf(os.EOL); - } - lastLine = chunk.toString(); - } catch (e) { - this.removeAllListeners("data"); - reject(e); - } - } - - process.on("close", async (code, signal) => { - console.log(`# ${command} process exited with code: ${code}`); - await shutdown(1); - }); - - process.stdout.on("data", onData.bind(process.stdout)); - process.stderr.on("data", onData.bind(process.stderr)); - } catch (e) { - reject(e); - } - }); -} - -async function cleanUpProcess(name: string, process: ChildProcess): Promise { - return new Promise((resolve, reject) => { - try { - if (process && !process.killed) { - console.log(`# Killing ${name} process (PID: ${process.pid})`); - kill(process.pid, "SIGTERM", () => { - console.log("# Killed dotnet process"); - resolve(); - }); - } - else { - resolve(); - } - } catch (e) { - reject(e); - } - }); -} - -async function shutdown(code: number): Promise { - await cleanUpProcess("dotnet", dotnet); - await cleanUpProcess("webDriver", webDriver); - process.exit(code); -} - -// "async main" via IIFE -(async function () { - const webDriverManagerPath = path.resolve(__dirname, "node_modules", "webdriver-manager", "bin", "webdriver-manager"); - - // This script launches the functional test app and then uses Selenium WebDriver to run the tests and verify the results. - console.log("# Updating WebDrivers..."); - runCommand(process.execPath, [webDriverManagerPath, "update"]); - console.log("# Updated WebDrivers"); - - console.log("# Launching WebDriver..."); - webDriver = spawn(process.execPath, [webDriverManagerPath, "start"]); - - const webDriverRegex = /\d+:\d+:\d+.\d+ INFO - Selenium Server is up and running on port (\d+)/; - - // The message we're waiting for is written to stderr for some reason - let results = await waitForMatch("webdriver-server", webDriver, webDriverRegex); - let webDriverPort = Number.parseInt(results[1]); - - console.log("# WebDriver Launched"); - console.log("# Launching Functional Test server..."); - dotnet = spawn("dotnet", [path.resolve(__dirname, "bin", "Debug", "netcoreapp2.1", "FunctionalTests.dll")], { - cwd: rootDir, - }); - - const regex = /Now listening on: (http:\/\/localhost:([\d])+)/; - results = await waitForMatch("dotnet", dotnet, regex); - try { - console.log("# Functional Test server launched."); - await runTests(webDriverPort, results[1]); - await shutdown(0); - } catch (e) { - console.error(`Bail out! Error running tests: ${e}`); - await shutdown(1); - } -})(); \ No newline at end of file diff --git a/client-ts/FunctionalTests/run-tests.tsconfig.json b/client-ts/FunctionalTests/run-tests.tsconfig.json deleted file mode 100644 index f7855d670c..0000000000 --- a/client-ts/FunctionalTests/run-tests.tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs" - } -} \ No newline at end of file diff --git a/client-ts/FunctionalTests/selenium/run-ci-tests.ts b/client-ts/FunctionalTests/selenium/run-ci-tests.ts new file mode 100644 index 0000000000..882365bdfb --- /dev/null +++ b/client-ts/FunctionalTests/selenium/run-ci-tests.ts @@ -0,0 +1,96 @@ +import { ChildProcess, spawn, spawnSync } from "child_process"; +import { existsSync } from "fs"; +import * as path from "path"; + +import * as tapTeamCity from "tap-teamcity"; +import * as tee from "tee"; + +const teamcity = !!process.env.TEAMCITY_VERSION; + +let force = process.env.ASPNETCORE_SIGNALR_FORCE_BROWSER_TESTS === "true"; +let chromePath = process.env.ASPNETCORE_CHROME_PATH; + +let configuration; +let verbose; + +for (let i = 2; i < process.argv.length; i += 1) { + switch (process.argv[i]) { + case "-f": + case "--force": + force = true; + break; + + case "--chrome": + i += 1; + chromePath = process.argv[i]; + break; + + case "--configuration": + i += 1; + configuration = process.argv[i]; + break; + + case "-v": + case "--verbose": + verbose = true; + break; + } +} + +function failPrereq(error: string) { + if (force) { + console.error(`Browser functional tests cannot be run: ${error}`); + process.exit(1); + } else { + console.log(`Skipping browser functional Tests: ${error}`); + + // Zero exit code because we don't want to fail the build. + process.exit(0); + } +} + +function getChromeBinaryPath(): string { + if (chromePath) { + return chromePath; + } else { + switch (process.platform) { + case "win32": + // tslint:disable-next-line:no-string-literal + return path.resolve(process.env.LOCALAPPDATA, "Google", "Chrome", "Application", "chrome.exe"); + case "darwin": + return path.resolve("/", "Applications", "Google Chrome.app", "Contents", "MacOS", "Google Chrome"); + case "linux": + return path.resolve("/", "usr", "bin", "google-chrome"); + } + } +} + +// Check prerequisites +const chromeBinary = getChromeBinaryPath(); +if (!existsSync(chromeBinary)) { + failPrereq(`Unable to locate Google Chrome at '${chromeBinary}'. Use the '--chrome' argument or 'ASPNETCORE_CHROME_PATH' environment variable to specify an alternate path`); +} else { + console.log(`Using Google Chrome Browser from '${chromeBinary}`); +} + +// Launch the tests +const args = ["test", "--", "--chrome", chromeBinary]; +if (configuration) { + args.push("--configuration"); + args.push(configuration); +} +if (verbose) { + args.push("--verbose"); +} +if (teamcity) { + args.push("--raw"); +} + +const testProcess = spawn("npm", args, { cwd: path.resolve(__dirname, "..") }); +testProcess.stderr.pipe(process.stderr); +if (teamcity) { + testProcess.stdout.pipe(tapTeamCity()).pipe(process.stdout); +} + +testProcess.stdout.pipe(process.stdout); +testProcess.on("close", (code) => process.exit(code)); \ No newline at end of file diff --git a/client-ts/FunctionalTests/selenium/run-tests.ts b/client-ts/FunctionalTests/selenium/run-tests.ts new file mode 100644 index 0000000000..a7fea90566 --- /dev/null +++ b/client-ts/FunctionalTests/selenium/run-tests.ts @@ -0,0 +1,162 @@ +import { ChildProcess, spawn } from "child_process"; +import * as fs from "fs"; +import { EOL } from "os"; +import * as path from "path"; +import { PassThrough, Readable } from "stream"; + +import * as tapSpec from "tap-spec"; + +import { run } from "../../webdriver-tap-runner/lib"; + +import * as _debug from "debug"; +const debug = _debug("signalr-functional-tests:run"); + +process.on("unhandledRejection", (reason) => { + console.error(`Unhandled promise rejection: ${reason}`); + process.exit(1); +}); + +// Don't let us hang the build. If this process takes more than 10 minutes, we're outta here +setTimeout(() => { + console.error("Bail out! Tests took more than 10 minutes to run. Aborting."); + process.exit(1); +}, 1000 * 60 * 10); + +function waitForMatch(command: string, process: ChildProcess, regex: RegExp): Promise { + return new Promise((resolve, reject) => { + const commandDebug = _debug(`signalr-functional-tests:${command}`); + try { + let lastLine = ""; + + async function onData(this: Readable, chunk: string | Buffer): Promise { + try { + chunk = chunk.toString(); + + // Process lines + let lineEnd = chunk.indexOf(EOL); + while (lineEnd >= 0) { + const chunkLine = lastLine + chunk.substring(0, lineEnd); + lastLine = ""; + + chunk = chunk.substring(lineEnd + EOL.length); + + const results = regex.exec(chunkLine); + commandDebug(chunkLine); + if (results && results.length > 0) { + resolve(results); + return; + } + lineEnd = chunk.indexOf(EOL); + } + lastLine = chunk.toString(); + } catch (e) { + this.removeAllListeners("data"); + reject(e); + } + } + + process.on("close", async (code, signal) => { + console.log(`${command} process exited with code: ${code}`); + global.process.exit(1); + }); + + process.stdout.on("data", onData.bind(process.stdout)); + process.stderr.on("data", (chunk) => { + onData.bind(process.stderr)(chunk); + console.error(`${command} | ${chunk.toString()}`); + }); + } catch (e) { + reject(e); + } + }); +} + +let raw = false; +let configuration = "Debug"; +let chromePath: string; +let spec: string; + +for (let i = 2; i < process.argv.length; i += 1) { + switch (process.argv[i]) { + case "--raw": + raw = true; + break; + case "--configuration": + i += 1; + configuration = process.argv[i]; + break; + case "-v": + case "--verbose": + _debug.enable("signalr-functional-tests:*"); + break; + case "--chrome": + i += 1; + chromePath = process.argv[i]; + break; + case "--spec": + i += 1; + spec = process.argv[i]; + break; + } +} + +if (chromePath) { + debug(`Using Google Chrome at: '${chromePath}'`); +} + +function createOutput() { + if (raw) { + return process.stdout; + } else { + const output = tapSpec(); + output.pipe(process.stdout); + return output; + } +} + +(async () => { + try { + const serverPath = path.resolve(__dirname, "..", "bin", configuration, "netcoreapp2.1", "FunctionalTests.dll"); + + debug(`Launching Functional Test Server: ${serverPath}`); + const dotnet = spawn("dotnet", [serverPath], { + env: { + ...process.env, + ["ASPNETCORE_URLS"]: "http://127.0.0.1:0" + }, + }); + + function cleanup() { + if (dotnet && !dotnet.killed) { + console.log("Terminating dotnet process"); + dotnet.kill(); + } + } + + process.on("SIGINT", cleanup); + process.on("exit", cleanup); + + debug("Waiting for Functional Test Server to start"); + const results = await waitForMatch("dotnet", dotnet, /Now listening on: (http:\/\/[^\/]+:[\d]+)/); + debug(`Functional Test Server has started at ${results[1]}`); + + let url = results[1] + "?cacheBust=true"; + if (spec) { + url += `&spec=${encodeURI(spec)}`; + } + + debug(`Using server url: ${url}`); + + const failureCount = await run("SignalR Browser Functional Tests", { + browser: "chrome", + chromeBinaryPath: chromePath, + output: createOutput(), + url, + webdriverPort: 9515, + }); + process.exit(failureCount); + } catch (e) { + console.error("Error: " + e.toString()); + process.exit(1); + } +})(); diff --git a/client-ts/FunctionalTests/selenium/tsconfig.json b/client-ts/FunctionalTests/selenium/tsconfig.json new file mode 100644 index 0000000000..74618cd9c5 --- /dev/null +++ b/client-ts/FunctionalTests/selenium/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2015" + } +} \ No newline at end of file diff --git a/client-ts/FunctionalTests/ts/Common.ts b/client-ts/FunctionalTests/ts/Common.ts index a7adf1ef01..7c46dfcc29 100644 --- a/client-ts/FunctionalTests/ts/Common.ts +++ b/client-ts/FunctionalTests/ts/Common.ts @@ -37,7 +37,9 @@ export function eachTransportAndProtocol(action: (transport: TransportType, prot } getTransportTypes().forEach((t) => { return protocols.forEach((p) => { - return action(t, p); + if (t !== TransportType.ServerSentEvents || !(p instanceof MessagePackHubProtocol)) { + return action(t, p); + } }); }); } diff --git a/client-ts/FunctionalTests/ts/ConnectionTests.ts b/client-ts/FunctionalTests/ts/ConnectionTests.ts index ed7f6ca1aa..9744a5c268 100644 --- a/client-ts/FunctionalTests/ts/ConnectionTests.ts +++ b/client-ts/FunctionalTests/ts/ConnectionTests.ts @@ -1,36 +1,36 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -import { HttpConnection, LogLevel, TransportType } from "@aspnet/signalr"; +import { HttpConnection, LogLevel, TransferFormat, TransportType } from "@aspnet/signalr"; import { eachTransport, ECHOENDPOINT_URL } from "./Common"; describe("connection", () => { - if (typeof WebSocket !== "undefined") { - it("can connect to the server without specifying transport explicitly", (done) => { - const message = "Hello World!"; - const connection = new HttpConnection(ECHOENDPOINT_URL); - - let received = ""; - connection.onreceive = (data) => { - received += data; - if (data === message) { - connection.stop(); - } - }; - - connection.onclose = (error) => { - expect(error).toBeUndefined(); - done(); - }; - - connection.start().then(() => { - connection.send(message); - }).catch((e) => { - fail(); - done(); - }); + it("can connect to the server without specifying transport explicitly", (done) => { + const message = "Hello World!"; + const connection = new HttpConnection(ECHOENDPOINT_URL, { + logger: LogLevel.Trace, }); - } + + let received = ""; + connection.onreceive = (data) => { + received += data; + if (data === message) { + connection.stop(); + } + }; + + connection.onclose = (error) => { + expect(error).toBeUndefined(); + done(); + }; + + connection.start(TransferFormat.Text).then(() => { + connection.send(message); + }).catch((e) => { + fail(e); + done(); + }); + }); eachTransport((transportType) => { it("over " + TransportType[transportType] + " can send and receive messages", (done) => { @@ -55,10 +55,10 @@ describe("connection", () => { done(); }; - connection.start().then(() => { + connection.start(TransferFormat.Text).then(() => { connection.send(message); }).catch((e) => { - fail(); + fail(e); done(); }); }); diff --git a/client-ts/FunctionalTests/ts/HubConnectionTests.ts b/client-ts/FunctionalTests/ts/HubConnectionTests.ts index 0607926f99..9651d5c9c0 100644 --- a/client-ts/FunctionalTests/ts/HubConnectionTests.ts +++ b/client-ts/FunctionalTests/ts/HubConnectionTests.ts @@ -1,11 +1,13 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -import { HubConnection, LogLevel, TransportType } from "@aspnet/signalr"; +import { HubConnection, JsonHubProtocol, LogLevel, TransportType } from "@aspnet/signalr"; +import { MessagePackHubProtocol } from "@aspnet/signalr-protocol-msgpack"; import { eachTransport, eachTransportAndProtocol } from "./Common"; const TESTHUBENDPOINT_URL = "/testhub"; +const TESTHUB_NOWEBSOCKETS_ENDPOINT_URL = "/testhub-nowebsockets"; describe("hubConnection", () => { eachTransportAndProtocol((transportType, protocol) => { @@ -394,15 +396,8 @@ describe("hubConnection", () => { // and a Uint8Array even though Buffer instances are also Uint8Array instances value.ByteArray = new Uint8Array(value.ByteArray); - // GUIDs are serialized as raw type which is a string containing bytes which need to - // be extracted. Note that with msgpack5 the original bytes will be encoded with utf8 - // and needs to be decoded. To not go into utf8 encoding intricacies the test uses values - // less than 0x80. - const guidBytes = []; - for (let i = 0; i < value.GUID.length; i++) { - guidBytes.push(value.GUID.charCodeAt(i)); - } - value.GUID = new Uint8Array(guidBytes); + // GUIDs are serialized as Buffer as well. + value.GUID = new Uint8Array(value.GUID); } expect(value).toEqual(complexObject); }) @@ -470,31 +465,29 @@ describe("hubConnection", () => { it("can connect to hub with authorization", async (done) => { const message = "你好,世界!"; - let hubConnection; - getJwtToken("http://" + document.location.host + "/generateJwtToken") - .then((jwtToken) => { - hubConnection = new HubConnection("/authorizedhub", { - accessTokenFactory: () => jwtToken, - logger: LogLevel.Trace, - transport: transportType, - }); - hubConnection.onclose((error) => { - expect(error).toBe(undefined); - done(); - }); - return hubConnection.start(); - }) - .then(() => { - return hubConnection.invoke("Echo", message); - }) - .then((response) => { - expect(response).toEqual(message); - done(); - }) - .catch((err) => { - fail(err); + try { + const jwtToken = await getJwtToken("http://" + document.location.host + "/generateJwtToken"); + const hubConnection = new HubConnection("/authorizedhub", { + accessTokenFactory: () => jwtToken, + logger: LogLevel.Trace, + transport: transportType, + }); + hubConnection.onclose((error) => { + expect(error).toBe(undefined); done(); }); + await hubConnection.start(); + const response = await hubConnection.invoke("Echo", message); + + expect(response).toEqual(message); + + await hubConnection.stop(); + + done(); + } catch (err) { + fail(err); + done(); + } }); if (transportType !== TransportType.LongPolling) { diff --git a/client-ts/FunctionalTests/ts/WebDriverReporter.ts b/client-ts/FunctionalTests/ts/WebDriverReporter.ts index ed56cf3328..85ca1196b9 100644 --- a/client-ts/FunctionalTests/ts/WebDriverReporter.ts +++ b/client-ts/FunctionalTests/ts/WebDriverReporter.ts @@ -13,7 +13,7 @@ function formatValue(v: any): string { } class WebDriverReporter implements jasmine.CustomReporter { - private element: HTMLUListElement; + private element: HTMLDivElement; private specCounter: number = 1; // TAP number start at 1 private recordCounter: number = 0; @@ -22,7 +22,7 @@ class WebDriverReporter implements jasmine.CustomReporter { // For example, Chrome supports scraping console.log from WebDriver which would be ideal, but Firefox does not :( // Create an element for the output - this.element = document.createElement("ul"); + this.element = document.createElement("div"); this.element.setAttribute("id", "__tap_list"); if (!show) { @@ -37,25 +37,27 @@ class WebDriverReporter implements jasmine.CustomReporter { } public specDone(result: jasmine.CustomReporterResult): void { - if (result.status === "failed") { + if (result.status === "disabled") { + return; + } else if (result.status === "failed") { this.taplog(`not ok ${this.specCounter} ${result.fullName}`); - // Include YAML block with failed expectations - this.taplog(" ---"); - this.taplog(" failures:"); + // Just report the first failure + this.taplog(" ---"); for (const expectation of result.failedExpectations) { - this.taplog(` - message: ${expectation.message}`); + // Include YAML block with failed expectations + this.taplog(` - message: ${expectation.message}`); if (expectation.matcherName) { - this.taplog(` matcher: ${expectation.matcherName}`); + this.taplog(` operator: ${expectation.matcherName}`); } if (expectation.expected) { - this.taplog(` expected: ${formatValue(expectation.expected)}`); + this.taplog(` expected: ${formatValue(expectation.expected)}`); } if (expectation.actual) { - this.taplog(` actual: ${formatValue(expectation.actual)}`); + this.taplog(` actual: ${formatValue(expectation.actual)}`); } } - this.taplog(" ..."); + this.taplog(" ..."); } else { this.taplog(`ok ${this.specCounter} ${result.fullName}`); } @@ -69,13 +71,12 @@ class WebDriverReporter implements jasmine.CustomReporter { private taplog(msg: string) { for (const line of msg.split(/\r|\n|\r\n/)) { - const li = this.document.createElement("li"); - li.setAttribute("style", "font-family: monospace; white-space: pre"); - li.setAttribute("id", `__tap_item_${this.recordCounter}`); + const input = this.document.createElement("input"); + input.setAttribute("id", `__tap_item_${this.recordCounter}`); this.recordCounter += 1; - li.innerHTML = line; - this.element.appendChild(li); + input.value = line; + this.element.appendChild(input); } } } diff --git a/client-ts/FunctionalTests/ts/WebSocketTests.ts b/client-ts/FunctionalTests/ts/WebSocketTests.ts index a9aac5643d..e70e322d57 100644 --- a/client-ts/FunctionalTests/ts/WebSocketTests.ts +++ b/client-ts/FunctionalTests/ts/WebSocketTests.ts @@ -14,21 +14,14 @@ if (typeof WebSocket !== "undefined") { webSocket.send(message); }; - let received = ""; webSocket.onmessage = (event) => { - received += event.data; - if (received === message) { - webSocket.close(); - } + expect(event.data).toEqual(message); + webSocket.close(); }; webSocket.onclose = (event) => { - if (!event.wasClean) { - fail("connection closed with unexpected status code: " + event.code + " " + event.reason); - } - - // Jasmine doesn't like tests without expectations - expect(event.wasClean).toBe(true); + expect(event.code).toEqual(1000); + expect(event.wasClean).toBe(true, "WebSocket did not close cleanly"); done(); }; diff --git a/client-ts/FunctionalTests/ts/index.ts b/client-ts/FunctionalTests/ts/index.ts index 5e1c3fbe2b..6a7008345a 100644 --- a/client-ts/FunctionalTests/ts/index.ts +++ b/client-ts/FunctionalTests/ts/index.ts @@ -1,16 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +console.log("SignalR Functional Tests Loaded"); + import "es6-promise/dist/es6-promise.auto.js"; - -// Load SignalR -import { getParameterByName } from "./Utils"; - -const minified = getParameterByName("release") === "true" ? ".min" : ""; -document.write( - ' diff --git a/client-ts/signalr/spec/HttpConnection.spec.ts b/client-ts/signalr/spec/HttpConnection.spec.ts index 79d0acfcf9..5006bce951 100644 --- a/client-ts/signalr/spec/HttpConnection.spec.ts +++ b/client-ts/signalr/spec/HttpConnection.spec.ts @@ -15,13 +15,13 @@ const commonOptions: IHttpConnectionOptions = { describe("HttpConnection", () => { it("cannot be created with relative url if document object is not present", () => { - expect(() => new HttpConnection("/test", TransferFormat.Text, commonOptions)) + expect(() => new HttpConnection("/test", commonOptions)) .toThrow(new Error("Cannot resolve '/test'.")); }); it("cannot be created with relative url if window object is not present", () => { (global as any).window = {}; - expect(() => new HttpConnection("/test", TransferFormat.Text, commonOptions)) + expect(() => new HttpConnection("/test", commonOptions)) .toThrow(new Error("Cannot resolve '/test'.")); delete (global as any).window; }); @@ -34,10 +34,10 @@ describe("HttpConnection", () => { .on("GET", (r) => ""), } as IHttpConnectionOptions; - const connection = new HttpConnection("http://tempuri.org", TransferFormat.Text, options); + const connection = new HttpConnection("http://tempuri.org", options); try { - await connection.start(); + await connection.start(TransferFormat.Text); fail(); done(); } catch (e) { @@ -51,7 +51,7 @@ describe("HttpConnection", () => { ...commonOptions, httpClient: new TestHttpClient() .on("POST", (r) => { - connection.start() + connection.start(TransferFormat.Text) .then(() => { fail(); done(); @@ -64,10 +64,10 @@ describe("HttpConnection", () => { }), } as IHttpConnectionOptions; - const connection = new HttpConnection("http://tempuri.org", TransferFormat.Text, options); + const connection = new HttpConnection("http://tempuri.org", options); try { - await connection.start(); + await connection.start(TransferFormat.Text); } catch (e) { // This exception is thrown after the actual verification is completed. // The connection is not setup to be running so just ignore the error. @@ -86,16 +86,16 @@ describe("HttpConnection", () => { .on("GET", (r) => ""), } as IHttpConnectionOptions; - const connection = new HttpConnection("http://tempuri.org", TransferFormat.Text, options); + const connection = new HttpConnection("http://tempuri.org", options); try { - await connection.start(); + await connection.start(TransferFormat.Text); } catch (e) { expect(e).toBe("reached negotiate"); } try { - await connection.start(); + await connection.start(TransferFormat.Text); } catch (e) { expect(e).toBe("reached negotiate"); } @@ -117,10 +117,10 @@ describe("HttpConnection", () => { }), } as IHttpConnectionOptions; - const connection = new HttpConnection("http://tempuri.org", TransferFormat.Text, options); + const connection = new HttpConnection("http://tempuri.org", options); try { - await connection.start(); + await connection.start(TransferFormat.Text); done(); } catch (e) { fail(); @@ -129,7 +129,7 @@ describe("HttpConnection", () => { }); it("can stop a non-started connection", async (done) => { - const connection = new HttpConnection("http://tempuri.org", TransferFormat.Text, commonOptions); + const connection = new HttpConnection("http://tempuri.org", commonOptions); await connection.stop(); done(); }); @@ -159,10 +159,10 @@ describe("HttpConnection", () => { transport: fakeTransport, } as IHttpConnectionOptions; - const connection = new HttpConnection("http://tempuri.org?q=myData", TransferFormat.Text, options); + const connection = new HttpConnection("http://tempuri.org?q=myData", options); try { - await connection.start(); + await connection.start(TransferFormat.Text); fail(); done(); } catch (e) { @@ -190,10 +190,10 @@ describe("HttpConnection", () => { }), } as IHttpConnectionOptions; - connection = new HttpConnection(givenUrl, TransferFormat.Text, options); + connection = new HttpConnection(givenUrl, options); try { - await connection.start(); + await connection.start(TransferFormat.Text); done(); } catch (e) { fail(); @@ -218,9 +218,9 @@ describe("HttpConnection", () => { transport: requestedTransport, } as IHttpConnectionOptions; - const connection = new HttpConnection("http://tempuri.org", TransferFormat.Text, options); + const connection = new HttpConnection("http://tempuri.org", options); try { - await connection.start(); + await connection.start(TransferFormat.Text); fail(); done(); } catch (e) { @@ -238,9 +238,9 @@ describe("HttpConnection", () => { .on("GET", (r) => ""), } as IHttpConnectionOptions; - const connection = new HttpConnection("http://tempuri.org", TransferFormat.Text, options); + const connection = new HttpConnection("http://tempuri.org", options); try { - await connection.start(); + await connection.start(TransferFormat.Text); fail(); done(); } catch (e) { @@ -256,9 +256,9 @@ describe("HttpConnection", () => { transport: TransportType.WebSockets, } as IHttpConnectionOptions; - const connection = new HttpConnection("http://tempuri.org", TransferFormat.Text, options); + const connection = new HttpConnection("http://tempuri.org", options); try { - await connection.start(); + await connection.start(TransferFormat.Text); fail(); done(); } catch (e) { @@ -274,14 +274,21 @@ describe("HttpConnection", () => { // Force TypeScript to let us call the constructor incorrectly :) expect(() => new (HttpConnection as any)()).toThrowError("The 'url' argument is required."); }); + }); + describe("startAsync", () => { it("throws if no TransferFormat is provided", async () => { - // Force TypeScript to let us call the constructor incorrectly :) - expect(() => new (HttpConnection as any)("http://tempuri.org")).toThrowError("The 'transferFormat' argument is required."); + // Force TypeScript to let us call start incorrectly + const connection: any = new HttpConnection("http://tempuri.org"); + + expect(() => connection.start()).toThrowError("The 'transferFormat' argument is required."); }); it("throws if an unsupported TransferFormat is provided", async () => { - expect(() => new HttpConnection("http://tempuri.org", 42)).toThrowError("Unknown transferFormat value: 42."); + // Force TypeScript to let us call start incorrectly + const connection: any = new HttpConnection("http://tempuri.org"); + + expect(() => connection.start(42)).toThrowError("Unknown transferFormat value: 42."); }); }); }); diff --git a/client-ts/signalr/src/HttpConnection.ts b/client-ts/signalr/src/HttpConnection.ts index 98b97f30ec..178d67ecba 100644 --- a/client-ts/signalr/src/HttpConnection.ts +++ b/client-ts/signalr/src/HttpConnection.ts @@ -39,19 +39,15 @@ export class HttpConnection implements IConnection { private readonly httpClient: HttpClient; private readonly logger: ILogger; private readonly options: IHttpConnectionOptions; - private readonly transferFormat: TransferFormat; private transport: ITransport; private connectionId: string; private startPromise: Promise; public readonly features: any = {}; - constructor(url: string, transferFormat: TransferFormat, options: IHttpConnectionOptions = {}) { + constructor(url: string, options: IHttpConnectionOptions = {}) { Arg.isRequired(url, "url"); - Arg.isRequired(transferFormat, "transferFormat"); - Arg.isIn(transferFormat, TransferFormat, "transferFormat"); - this.transferFormat = transferFormat; this.logger = LoggerFactory.createLogger(options.logger); this.baseUrl = this.resolveUrl(url); @@ -63,18 +59,21 @@ export class HttpConnection implements IConnection { this.options = options; } - public async start(): Promise { + public start(transferFormat: TransferFormat): Promise { + Arg.isRequired(transferFormat, "transferFormat"); + Arg.isIn(transferFormat, TransferFormat, "transferFormat"); + if (this.connectionState !== ConnectionState.Disconnected) { return Promise.reject(new Error("Cannot start a connection that is not in the 'Disconnected' state.")); } this.connectionState = ConnectionState.Connecting; - this.startPromise = this.startInternal(); + this.startPromise = this.startInternal(transferFormat); return this.startPromise; } - private async startInternal(): Promise { + private async startInternal(transferFormat: TransferFormat): Promise { try { if (this.options.transport === TransportType.WebSockets) { // No need to add a connection ID in this case @@ -103,14 +102,14 @@ export class HttpConnection implements IConnection { if (this.connectionId) { this.url = this.baseUrl + (this.baseUrl.indexOf("?") === -1 ? "?" : "&") + `id=${this.connectionId}`; - this.transport = this.createTransport(this.options.transport, negotiateResponse.availableTransports, this.transferFormat); + this.transport = this.createTransport(this.options.transport, negotiateResponse.availableTransports, transferFormat); } } this.transport.onreceive = this.onreceive; this.transport.onclose = (e) => this.stopConnection(true, e); - await this.transport.connect(this.url, this.transferFormat, this); + await this.transport.connect(this.url, transferFormat, this); // only change the state if we were connecting to not overwrite // the state if the connection is already marked as Disconnected @@ -131,7 +130,7 @@ export class HttpConnection implements IConnection { for (const endpoint of availableTransports) { const transport = this.resolveTransport(endpoint, requestedTransport, requestedTransferFormat); - if (transport) { + if (typeof transport === "number") { return this.constructTransport(transport); } } @@ -154,19 +153,19 @@ export class HttpConnection implements IConnection { private resolveTransport(endpoint: IAvailableTransport, requestedTransport: TransportType, requestedTransferFormat: TransferFormat): TransportType | null { const transport = TransportType[endpoint.transport]; - if (!transport) { + if (transport === null || transport === undefined) { this.logger.log(LogLevel.Trace, `Skipping transport '${endpoint.transport}' because it is not supported by this client.`); } else { const transferFormats = endpoint.transferFormats.map((s) => TransferFormat[s]); if (!requestedTransport || transport === requestedTransport) { if (transferFormats.indexOf(requestedTransferFormat) >= 0) { - this.logger.log(LogLevel.Trace, `Selecting transport '${transport}'`); + this.logger.log(LogLevel.Trace, `Selecting transport '${TransportType[transport]}'`); return transport; } else { - this.logger.log(LogLevel.Trace, `Skipping transport '${transport}' because it does not support the requested transfer format '${requestedTransferFormat}'.`); + this.logger.log(LogLevel.Trace, `Skipping transport '${TransportType[transport]}' because it does not support the requested transfer format '${TransferFormat[requestedTransferFormat]}'.`); } } else { - this.logger.log(LogLevel.Trace, `Skipping transport '${transport}' because it was disabled by the client.`); + this.logger.log(LogLevel.Trace, `Skipping transport '${TransportType[transport]}' because it was disabled by the client.`); } } return null; diff --git a/client-ts/signalr/src/HubConnection.ts b/client-ts/signalr/src/HubConnection.ts index ccdb31fabd..fba11d6f0d 100644 --- a/client-ts/signalr/src/HubConnection.ts +++ b/client-ts/signalr/src/HubConnection.ts @@ -42,7 +42,7 @@ export class HubConnection { this.protocol = options.protocol || new JsonHubProtocol(); if (typeof urlOrConnection === "string") { - this.connection = new HttpConnection(urlOrConnection, this.protocol.transferFormat, options); + this.connection = new HttpConnection(urlOrConnection, options); } else { this.connection = urlOrConnection; } @@ -133,7 +133,7 @@ export class HubConnection { } public async start(): Promise { - await this.connection.start(); + await this.connection.start(this.protocol.transferFormat); await this.connection.send( TextMessageFormat.write( diff --git a/client-ts/signalr/src/IConnection.ts b/client-ts/signalr/src/IConnection.ts index 0e8c347996..d7e68c9f03 100644 --- a/client-ts/signalr/src/IConnection.ts +++ b/client-ts/signalr/src/IConnection.ts @@ -2,12 +2,12 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. import { ConnectionClosed, DataReceived } from "./Common"; -import { ITransport, TransportType } from "./Transports"; +import { ITransport, TransferFormat, TransportType } from "./Transports"; export interface IConnection { readonly features: any; - start(): Promise; + start(transferFormat: TransferFormat): Promise; send(data: any): Promise; stop(error?: Error): Promise; diff --git a/client-ts/webdriver-tap-runner/bin.ts b/client-ts/webdriver-tap-runner/bin.ts new file mode 100644 index 0000000000..cd5d2915e4 --- /dev/null +++ b/client-ts/webdriver-tap-runner/bin.ts @@ -0,0 +1,24 @@ +import * as path from "path"; + +import * as yargs from "yargs"; + +import * as _debug from "debug"; +import { run } from "./lib"; +const debug = _debug("webdriver-tap-runner:bin"); + +const argv = yargs + .option("url", { demand: true, description: "The URL of the server to test against" }) + .option("name", { demand: true, description: "The name of the test run" }) + .option("browser", { alias: "b", default: "chrome", description: "The browser to use (only 'chrome' is supported right now)" }) + .option("webdriver-port", { default: 9515, description: "The port on which to launch the WebDriver server", number: true }) + .option("chrome-driver-log", { }) + .option("chrome-driver-log-verbose", { }) + .argv; + +run(argv.name, { + browser: argv.browser, + chromeDriverLogFile: argv["chrome-driver-log"], + chromeVerboseLogging: !!argv["chrome-driver-log-verbose"], + url: argv.url, + webdriverPort: argv["webdriver-port"], +}).then((failures) => process.exit(failures)); diff --git a/client-ts/webdriver-tap-runner/lib.ts b/client-ts/webdriver-tap-runner/lib.ts new file mode 100644 index 0000000000..6c774b602f --- /dev/null +++ b/client-ts/webdriver-tap-runner/lib.ts @@ -0,0 +1,202 @@ +import * as http from "http"; +import * as path from "path"; +import { promisify } from "util"; + +import { ChildProcess, spawn, SpawnOptions } from "child_process"; +import * as chromedriver from "chromedriver"; +import { EOL } from "os"; +import { Builder, logging, WebDriver, WebElement } from "selenium-webdriver"; +import { Options as ChromeOptions } from "selenium-webdriver/chrome"; +import { Readable, Writable } from "stream"; + +import { delay, flushEntries, getEntryContent, getLogEntry, isComplete, waitForElement } from "./utils"; + +import * as _debug from "debug"; +const debug = _debug("webdriver-tap-runner:bin"); + + +export interface RunnerOptions { + browser: string; + url: string; + chromeBinaryPath?: string, + chromeDriverLogFile?: string; + chromeVerboseLogging?: boolean; + output?: Writable; + webdriverPort: number; +} + +function applyBrowserSettings(options: RunnerOptions, builder: Builder) { + if (options.browser === "chrome") { + const chromeOptions = new ChromeOptions(); + chromeOptions.headless(); + + // If we're root, we need to disable the sandbox. + if (process.getuid && process.getuid() === 0) { + chromeOptions.addArguments("--no-sandbox"); + } + + if (options.chromeBinaryPath) { + debug(`Using Chrome Binary Path: ${options.chromeBinaryPath}`); + chromeOptions.setChromeBinaryPath(options.chromeBinaryPath); + } + + builder.setChromeOptions(chromeOptions); + } +} + +function writeToDebug(name: string) { + const writer = _debug(name); + let lastLine: string; + return (chunk: Buffer | string) => { + const str = chunk.toString(); + const lines = str.split(/\r?\n/g); + const lastLineComplete = str.endsWith("\n"); + + if (lines.length > 0 && lastLine) { + lines[0] = lastLine + lines[0]; + } + + const end = lastLineComplete ? lines.length : (lines.length - 1) + for (let i = 0; i < end; i += 1) { + writer(lines[i]); + } + if (lastLineComplete && lines.length > 0) { + lastLine = lines[lines.length - 1]; + } + } +} + +let driverInstance: ChildProcess; +function startDriver(browser: string, port: number) { + let processName: string; + if (browser === "chrome") { + processName = path.basename(chromedriver.path); + driverInstance = spawn(chromedriver.path, [`--port=${port}`]); + } else { + throw new Error(`Unsupported browser: ${browser}`); + } + + // Capture output + driverInstance.stdout.on("data", writeToDebug(`webdriver-tap-runner:${processName}:stdout`)); + driverInstance.stderr.on("data", writeToDebug(`webdriver-tap-runner:${processName}:stderr`)); +} + +function stopDriver(browser: string) { + if (driverInstance && !driverInstance.killed) { + debug("Killing WebDriver..."); + driverInstance.kill(); + debug("Killed WebDriver"); + } +} + +function pingUrl(url: string): Promise { + return new Promise((resolve, reject) => { + const request = http.request(url); + request.on("response", (resp: http.IncomingMessage) => { + if (resp.statusCode >= 400) { + reject(new Error(`Received ${resp.statusCode} ${resp.statusMessage} from server`)); + } else { + resolve(); + } + }); + request.on("error", (error) => reject(error)); + request.end(); + }); +} + +async function pingWithRetry(url: string): Promise { + for (let i = 0; i < 5; i += 1) { + try { + debug(`Pinging URL: ${url}`); + await pingUrl(url); + return true; + } catch (e) { + debug(`Error reaching server: '${e}', retrying...`); + await delay(100); + } + } + debug("Retry limit exhausted"); + return false; +} + +export async function run(runName: string, options: RunnerOptions): Promise { + const output = options.output || (process.stdout as Writable); + + debug(`Using WebDriver port: ${options.webdriverPort}`); + + startDriver(options.browser, options.webdriverPort); + + // Wait for the server to start + const serverUrl = `http://localhost:${options.webdriverPort}`; + if (!await pingWithRetry(`${serverUrl}/status`)) { + console.log("WebDriver did not start in time."); + process.exit(1); + } + + try { + // Shut selenium down when we shut down. + process.on("exit", () => { + stopDriver(options.browser); + }); + + // Build WebDriver + const builder = new Builder() + .usingServer(serverUrl); + + // Set the browser + debug(`Using '${options.browser}' browser`); + builder.forBrowser(options.browser); + + applyBrowserSettings(options, builder); + + // Build driver + const driver = builder.build(); + + let failureCount = 0; + try { + // Navigate to the URL + debug(`Navigating to ${options.url}`); + await driver.get(options.url); + + // Wait for the TAP results list + const listElement = await waitForElement(driver, "__tap_list"); + + output.write(`TAP version 13${EOL}`); + output.write(`# ${runName}${EOL}`); + + // Process messages until the test run is complete + let index = 0; + while (!await isComplete(listElement)) { + const entry = await getLogEntry(index, listElement); + if (entry) { + index += 1; + const content = await getEntryContent(entry); + if (content.startsWith("not ok")) { + failureCount += 1; + } + output.write(content + EOL); + } + } + + // Flush any remaining messages + await flushEntries(index, listElement, (entry) => { + if (entry.startsWith("not ok")) { + failureCount += 1; + } + output.write(entry + EOL); + }); + + } finally { + // Shut down + debug("Shutting WebDriver down..."); + await driver.quit(); + } + + // We're done! + debug("Test run complete"); + return failureCount; + } finally { + debug("Shutting Selenium server down..."); + stopDriver(options.browser); + } +} \ No newline at end of file diff --git a/client-ts/webdriver-tap-runner/package-lock.json b/client-ts/webdriver-tap-runner/package-lock.json new file mode 100644 index 0000000000..d62f19a7de --- /dev/null +++ b/client-ts/webdriver-tap-runner/package-lock.json @@ -0,0 +1,1358 @@ +{ + "name": "selenium-tap-runner", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/debug": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-0.0.30.tgz", + "integrity": "sha512-orGL5LXERPYsLov6CWs3Fh6203+dXzJkR7OnddIr2514Hsecwc8xRpzCapshBbKFImCsvS/mk6+FWiN5LyZJAQ==", + "dev": true + }, + "@types/selenium-webdriver": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.8.tgz", + "integrity": "sha512-yrqQvb1EZhH+ONMzUmsEnBjzitortVv0lynRe5US2+FofdoMWUE4wf7v4peCd62fqXq8COCVTbpS1/jIg5EbuQ==", + "dev": true + }, + "@types/yargs": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-11.0.0.tgz", + "integrity": "sha512-vFql3tOxs6clgh+WVoLW3nOkNGCdeKsMU6mQZkOerJpV/CR9Xc1c1lZ+kYU+hNSobrQIOcNovWfPFDJIhcG5Pw==", + "dev": true + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "chromedriver": { + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.35.0.tgz", + "integrity": "sha512-zqvC/HKybRxiM68GzByvUaXxTmNCmpETvLQIM92IEdrQxPnONKt3ZdTsiwxmGrL2ZIDbr9OEHJljmhZZMEsFPw==", + "requires": { + "del": "3.0.0", + "extract-zip": "1.6.6", + "kew": "0.7.0", + "mkdirp": "0.5.1", + "request": "2.83.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "requires": { + "hoek": "4.2.1" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "requires": { + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "requires": { + "hoek": "4.2.1" + } + } + } + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.18" + } + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "5.5.2", + "har-schema": "2.0.0" + } + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "requires": { + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.1", + "sntp": "2.1.0" + } + }, + "hoek": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "request": { + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.18", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" + } + }, + "sntp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "requires": { + "hoek": "4.2.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "cliui": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.0.0.tgz", + "integrity": "sha512-nY3W5Gu2racvdDk//ELReY+dHjb9PlIcVDFXP72nVIhq2Gy3LuVXYwJoPVudwQnv1shtohpgkdCKT2YaKY0CKw==", + "requires": { + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.4", + "typedarray": "0.0.6" + } + }, + "core-js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", + "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "requires": { + "globby": "6.1.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "p-map": "1.2.0", + "pify": "3.0.0", + "rimraf": "2.6.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "diff": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", + "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "es6-promise": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", + "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extract-zip": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz", + "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=", + "requires": { + "concat-stream": "1.6.0", + "debug": "2.6.9", + "mkdirp": "0.5.0", + "yauzl": "2.4.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", + "requires": { + "minimist": "0.0.8" + } + }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "requires": { + "fd-slicer": "1.0.1" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "requires": { + "pend": "1.2.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "2.0.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "requires": { + "array-union": "1.0.2", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" + }, + "is-path-in-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "requires": { + "is-path-inside": "1.0.1" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "requires": { + "path-is-inside": "1.0.2" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "jszip": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", + "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", + "requires": { + "core-js": "2.3.0", + "es6-promise": "3.0.2", + "lie": "3.1.1", + "pako": "1.0.6", + "readable-stream": "2.0.6" + }, + "dependencies": { + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "kew": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=" + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "1.0.0" + } + }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "requires": { + "immediate": "3.0.6" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + } + }, + "lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "make-error": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz", + "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", + "dev": true + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "requires": { + "mimic-fn": "1.2.0" + } + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "requires": { + "mime-db": "1.33.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "2.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-limit": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "requires": { + "p-try": "1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "1.2.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==" + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "pako": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "2.0.4" + } + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "readable-stream": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz", + "integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "selenium-webdriver": { + "version": "4.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.1.tgz", + "integrity": "sha512-z88rdjHAv3jmTZ7KSGUkTvo4rGzcDGMq0oXWHNIDK96Gs31JKVdu9+FMtT4KBrVoibg8dUicJDok6GnqqttO5Q==", + "requires": { + "jszip": "3.1.5", + "rimraf": "2.6.2", + "tmp": "0.0.30", + "xml2js": "0.4.19" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.3.tgz", + "integrity": "sha512-eKkTgWYeBOQqFGXRfKabMFdnWepo51vWqEdoeikaEPFiJC7MCU5j2h4+6Q8npkZTeLGbSyecZvRxiSoWl3rh+w==", + "dev": true, + "requires": { + "source-map": "0.6.1" + } + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "requires": { + "os-tmpdir": "1.0.2" + } + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "requires": { + "punycode": "1.4.1" + } + }, + "ts-node": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-5.0.0.tgz", + "integrity": "sha512-mlSim/sQS1s5iT3KZEKXRaqsGC7xM2QoxkrhfznZJyou18dl47PTnY7/KMmbGqiVoQrO9Hk53CYpcychF5TNrQ==", + "dev": true, + "requires": { + "arrify": "1.0.1", + "chalk": "2.3.1", + "diff": "3.4.0", + "make-error": "1.3.4", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map-support": "0.5.3", + "yn": "2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", + "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "5.2.0" + } + }, + "supports-color": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", + "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "requires": { + "isexe": "2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": "1.2.4", + "xmlbuilder": "9.0.7" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yargs": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz", + "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", + "requires": { + "cliui": "4.0.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "9.0.2" + } + }, + "yargs-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "requires": { + "camelcase": "4.1.0" + } + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + } + } +} diff --git a/client-ts/webdriver-tap-runner/package.json b/client-ts/webdriver-tap-runner/package.json new file mode 100644 index 0000000000..4cc6a7ff3c --- /dev/null +++ b/client-ts/webdriver-tap-runner/package.json @@ -0,0 +1,26 @@ +{ + "name": "selenium-tap-runner", + "version": "1.0.0", + "description": "Run Browser tests in a Selenium browser and proxy TAP results to the console", + "main": "dist/lib.js", + "scripts": { + "update-selenium": "selenium-standalone install", + "exec": "ts-node ./bin.ts", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "chromedriver": "^2.35.0", + "debug": "^3.1.0", + "selenium-webdriver": "^4.0.0-alpha.1", + "yargs": "^11.0.0" + }, + "devDependencies": { + "@types/debug": "0.0.30", + "@types/selenium-webdriver": "^3.0.8", + "@types/yargs": "^11.0.0", + "ts-node": "^5.0.0" + } +} diff --git a/client-ts/webdriver-tap-runner/tsconfig.json b/client-ts/webdriver-tap-runner/tsconfig.json new file mode 100644 index 0000000000..88aac8eddb --- /dev/null +++ b/client-ts/webdriver-tap-runner/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "lib": [ "es2016" ] + } +} \ No newline at end of file diff --git a/client-ts/webdriver-tap-runner/utils.ts b/client-ts/webdriver-tap-runner/utils.ts new file mode 100644 index 0000000000..eac3801cc4 --- /dev/null +++ b/client-ts/webdriver-tap-runner/utils.ts @@ -0,0 +1,61 @@ +import * as os from "os"; + +import { By, logging, WebDriver, WebElement } from "selenium-webdriver"; + +import * as _debug from "debug"; +const debug = _debug("webdriver-tap-runner:utils"); + +export function delay(ms: number): Promise { + return new Promise((resolve, reject) => { + setTimeout(resolve, ms); + }); +} + +export async function waitForElement(driver: WebDriver, id: string): Promise { + debug(`Waiting for '${id}' element`); + for (let attempts = 0; attempts < 2; attempts += 1) { + const elements = await driver.findElements(By.id(id)); + if (elements && elements.length > 0) { + debug(`Found '${id}' element`); + return elements[0]; + } + + debug(`Waiting 5 sec for '${id}' element to appear...`); + await delay(5 * 1000); + } + + // We failed to find the item + // Collect page source + const source = await driver.getPageSource(); + const logs = await driver.manage().logs().get(logging.Type.BROWSER); + const messages = logs.map((l) => `[${l.level}] ${l.message}`) + .join(os.EOL); + throw new Error( + `Failed to find element '${id}'. Page Source:${os.EOL}${source}${os.EOL}` + + `Browser Logs (${logs.length} messages):${os.EOL}${messages}${os.EOL}`); +} + +export async function isComplete(element: WebElement): Promise { + return (await element.getAttribute("data-done")) === "1"; +} + +export async function getLogEntry(index: number, element: WebElement): Promise { + const elements = await element.findElements(By.id(`__tap_item_${index}`)); + if (elements && elements.length > 0) { + return elements[0]; + } + return null; +} + +export async function getEntryContent(element: WebElement): Promise { + return await element.getAttribute("value"); +} + +export async function flushEntries(index: number, element: WebElement, cb: (entry: string) => void): Promise { + let entry = await getLogEntry(index, element); + while (entry) { + index += 1; + cb(await getEntryContent(entry)); + entry = await getLogEntry(index, element); + } +} \ No newline at end of file