From 59b6e10d566887e80c60c8b249c30b64ffe78247 Mon Sep 17 00:00:00 2001 From: Nikolai Markov Date: Wed, 7 Jun 2023 13:43:42 +0300 Subject: [PATCH 1/2] chore: rename some js files to ts --- src/{base-hermione.js => base-hermione.ts} | 0 src/browser-pool/{cancelled-error.js => cancelled-error.ts} | 0 src/browser-pool/{index.js => index.ts} | 0 src/browser/client-bridge/{error.js => error.ts} | 0 .../errors/{assert-view-error.js => assert-view-error.ts} | 0 .../errors/{base-state-error.js => base-state-error.ts} | 0 .../errors/{image-diff-error.js => image-diff-error.ts} | 0 .../errors/{no-ref-image-error.js => no-ref-image-error.ts} | 0 src/browser/{core-error.js => core-error.ts} | 0 .../errors/{height-viewport-error.js => height-viewport-error.ts} | 0 .../errors/{offset-viewport-error.js => offset-viewport-error.ts} | 0 src/config/{browser-config.js => browser-config.ts} | 0 src/config/{index.js => index.ts} | 0 src/constants/{runner-events.js => runner-events.ts} | 0 src/{errors.js => errors.ts} | 0 src/events/async-emitter/{index.js => index.ts} | 0 src/{hermione.js => hermione.ts} | 0 src/runner/{browser-runner.js => browser-runner.ts} | 0 src/runner/{index.js => index.ts} | 0 src/runner/{runner.js => runner.ts} | 0 src/runner/test-runner/{index.js => index.ts} | 0 src/{stats.js => stats.ts} | 0 ...rowser-version-controller.js => browser-version-controller.ts} | 0 .../controllers/{config-controller.js => config-controller.ts} | 0 .../controllers/{only-controller.js => only-controller.ts} | 0 .../controllers/{skip-controller.js => skip-controller.ts} | 0 src/test-reader/{test-parser-api.js => test-parser-api.ts} | 0 src/utils/{worker-process.js => worker-process.ts} | 0 src/worker/{hermione.js => hermione.ts} | 0 29 files changed, 0 insertions(+), 0 deletions(-) rename src/{base-hermione.js => base-hermione.ts} (100%) rename src/browser-pool/{cancelled-error.js => cancelled-error.ts} (100%) rename src/browser-pool/{index.js => index.ts} (100%) rename src/browser/client-bridge/{error.js => error.ts} (100%) rename src/browser/commands/assert-view/errors/{assert-view-error.js => assert-view-error.ts} (100%) rename src/browser/commands/assert-view/errors/{base-state-error.js => base-state-error.ts} (100%) rename src/browser/commands/assert-view/errors/{image-diff-error.js => image-diff-error.ts} (100%) rename src/browser/commands/assert-view/errors/{no-ref-image-error.js => no-ref-image-error.ts} (100%) rename src/browser/{core-error.js => core-error.ts} (100%) rename src/browser/screen-shooter/viewport/coord-validator/errors/{height-viewport-error.js => height-viewport-error.ts} (100%) rename src/browser/screen-shooter/viewport/coord-validator/errors/{offset-viewport-error.js => offset-viewport-error.ts} (100%) rename src/config/{browser-config.js => browser-config.ts} (100%) rename src/config/{index.js => index.ts} (100%) rename src/constants/{runner-events.js => runner-events.ts} (100%) rename src/{errors.js => errors.ts} (100%) rename src/events/async-emitter/{index.js => index.ts} (100%) rename src/{hermione.js => hermione.ts} (100%) rename src/runner/{browser-runner.js => browser-runner.ts} (100%) rename src/runner/{index.js => index.ts} (100%) rename src/runner/{runner.js => runner.ts} (100%) rename src/runner/test-runner/{index.js => index.ts} (100%) rename src/{stats.js => stats.ts} (100%) rename src/test-reader/controllers/{browser-version-controller.js => browser-version-controller.ts} (100%) rename src/test-reader/controllers/{config-controller.js => config-controller.ts} (100%) rename src/test-reader/controllers/{only-controller.js => only-controller.ts} (100%) rename src/test-reader/controllers/{skip-controller.js => skip-controller.ts} (100%) rename src/test-reader/{test-parser-api.js => test-parser-api.ts} (100%) rename src/utils/{worker-process.js => worker-process.ts} (100%) rename src/worker/{hermione.js => hermione.ts} (100%) diff --git a/src/base-hermione.js b/src/base-hermione.ts similarity index 100% rename from src/base-hermione.js rename to src/base-hermione.ts diff --git a/src/browser-pool/cancelled-error.js b/src/browser-pool/cancelled-error.ts similarity index 100% rename from src/browser-pool/cancelled-error.js rename to src/browser-pool/cancelled-error.ts diff --git a/src/browser-pool/index.js b/src/browser-pool/index.ts similarity index 100% rename from src/browser-pool/index.js rename to src/browser-pool/index.ts diff --git a/src/browser/client-bridge/error.js b/src/browser/client-bridge/error.ts similarity index 100% rename from src/browser/client-bridge/error.js rename to src/browser/client-bridge/error.ts diff --git a/src/browser/commands/assert-view/errors/assert-view-error.js b/src/browser/commands/assert-view/errors/assert-view-error.ts similarity index 100% rename from src/browser/commands/assert-view/errors/assert-view-error.js rename to src/browser/commands/assert-view/errors/assert-view-error.ts diff --git a/src/browser/commands/assert-view/errors/base-state-error.js b/src/browser/commands/assert-view/errors/base-state-error.ts similarity index 100% rename from src/browser/commands/assert-view/errors/base-state-error.js rename to src/browser/commands/assert-view/errors/base-state-error.ts diff --git a/src/browser/commands/assert-view/errors/image-diff-error.js b/src/browser/commands/assert-view/errors/image-diff-error.ts similarity index 100% rename from src/browser/commands/assert-view/errors/image-diff-error.js rename to src/browser/commands/assert-view/errors/image-diff-error.ts diff --git a/src/browser/commands/assert-view/errors/no-ref-image-error.js b/src/browser/commands/assert-view/errors/no-ref-image-error.ts similarity index 100% rename from src/browser/commands/assert-view/errors/no-ref-image-error.js rename to src/browser/commands/assert-view/errors/no-ref-image-error.ts diff --git a/src/browser/core-error.js b/src/browser/core-error.ts similarity index 100% rename from src/browser/core-error.js rename to src/browser/core-error.ts diff --git a/src/browser/screen-shooter/viewport/coord-validator/errors/height-viewport-error.js b/src/browser/screen-shooter/viewport/coord-validator/errors/height-viewport-error.ts similarity index 100% rename from src/browser/screen-shooter/viewport/coord-validator/errors/height-viewport-error.js rename to src/browser/screen-shooter/viewport/coord-validator/errors/height-viewport-error.ts diff --git a/src/browser/screen-shooter/viewport/coord-validator/errors/offset-viewport-error.js b/src/browser/screen-shooter/viewport/coord-validator/errors/offset-viewport-error.ts similarity index 100% rename from src/browser/screen-shooter/viewport/coord-validator/errors/offset-viewport-error.js rename to src/browser/screen-shooter/viewport/coord-validator/errors/offset-viewport-error.ts diff --git a/src/config/browser-config.js b/src/config/browser-config.ts similarity index 100% rename from src/config/browser-config.js rename to src/config/browser-config.ts diff --git a/src/config/index.js b/src/config/index.ts similarity index 100% rename from src/config/index.js rename to src/config/index.ts diff --git a/src/constants/runner-events.js b/src/constants/runner-events.ts similarity index 100% rename from src/constants/runner-events.js rename to src/constants/runner-events.ts diff --git a/src/errors.js b/src/errors.ts similarity index 100% rename from src/errors.js rename to src/errors.ts diff --git a/src/events/async-emitter/index.js b/src/events/async-emitter/index.ts similarity index 100% rename from src/events/async-emitter/index.js rename to src/events/async-emitter/index.ts diff --git a/src/hermione.js b/src/hermione.ts similarity index 100% rename from src/hermione.js rename to src/hermione.ts diff --git a/src/runner/browser-runner.js b/src/runner/browser-runner.ts similarity index 100% rename from src/runner/browser-runner.js rename to src/runner/browser-runner.ts diff --git a/src/runner/index.js b/src/runner/index.ts similarity index 100% rename from src/runner/index.js rename to src/runner/index.ts diff --git a/src/runner/runner.js b/src/runner/runner.ts similarity index 100% rename from src/runner/runner.js rename to src/runner/runner.ts diff --git a/src/runner/test-runner/index.js b/src/runner/test-runner/index.ts similarity index 100% rename from src/runner/test-runner/index.js rename to src/runner/test-runner/index.ts diff --git a/src/stats.js b/src/stats.ts similarity index 100% rename from src/stats.js rename to src/stats.ts diff --git a/src/test-reader/controllers/browser-version-controller.js b/src/test-reader/controllers/browser-version-controller.ts similarity index 100% rename from src/test-reader/controllers/browser-version-controller.js rename to src/test-reader/controllers/browser-version-controller.ts diff --git a/src/test-reader/controllers/config-controller.js b/src/test-reader/controllers/config-controller.ts similarity index 100% rename from src/test-reader/controllers/config-controller.js rename to src/test-reader/controllers/config-controller.ts diff --git a/src/test-reader/controllers/only-controller.js b/src/test-reader/controllers/only-controller.ts similarity index 100% rename from src/test-reader/controllers/only-controller.js rename to src/test-reader/controllers/only-controller.ts diff --git a/src/test-reader/controllers/skip-controller.js b/src/test-reader/controllers/skip-controller.ts similarity index 100% rename from src/test-reader/controllers/skip-controller.js rename to src/test-reader/controllers/skip-controller.ts diff --git a/src/test-reader/test-parser-api.js b/src/test-reader/test-parser-api.ts similarity index 100% rename from src/test-reader/test-parser-api.js rename to src/test-reader/test-parser-api.ts diff --git a/src/utils/worker-process.js b/src/utils/worker-process.ts similarity index 100% rename from src/utils/worker-process.js rename to src/utils/worker-process.ts diff --git a/src/worker/hermione.js b/src/worker/hermione.ts similarity index 100% rename from src/worker/hermione.js rename to src/worker/hermione.ts From c02e716d62e3acd4537b0fb5f937b1a783c15d3d Mon Sep 17 00:00:00 2001 From: Nikolai Markov Date: Wed, 7 Jun 2023 13:57:43 +0300 Subject: [PATCH 2/2] feat: re-write major parts of hermione to typescript and get rid of typings BREAKING CHANGE: package exports have changed --- .eslintrc.js | 6 + bin/hermione | 2 +- package-lock.json | 98 ++- package.json | 16 +- src/base-hermione.ts | 70 +- src/browser-pool/basic-pool.js | 8 +- src/browser-pool/cancelled-error.ts | 10 +- src/browser-pool/index.ts | 18 +- src/browser-pool/limited-pool.js | 2 +- src/browser/calibrator.js | 2 +- src/browser/client-bridge/client-bridge.js | 2 +- src/browser/client-bridge/error.ts | 9 +- .../assert-view/assert-view-results.js | 4 +- .../capture-processors/assert-refs.js | 4 +- .../capture-processors/update-refs.js | 4 +- .../assert-view/errors/assert-view-error.ts | 9 +- .../assert-view/errors/base-state-error.ts | 11 +- .../assert-view/errors/image-diff-error.ts | 68 +- .../assert-view/errors/no-ref-image-error.ts | 30 +- src/browser/commands/assert-view/index.js | 4 +- src/browser/commands/scrollIntoView.ts | 2 +- src/browser/commands/types.ts | 60 ++ src/browser/core-error.ts | 10 +- src/browser/history/commands.ts | 4 +- .../errors/height-viewport-error.ts | 8 +- .../errors/offset-viewport-error.ts | 8 +- .../viewport/coord-validator/index.js | 4 +- src/browser/types.ts | 109 +++ src/cli/index.js | 2 +- src/config/browser-config.ts | 18 +- src/config/index.ts | 48 +- src/config/types.ts | 151 ++++ src/constants/runner-events.ts | 57 -- src/errors.ts | 30 +- src/events/async-emitter/index.ts | 22 +- src/events/index.ts | 117 +++ src/events/types.ts | 14 + src/hermione.ts | 135 ++- src/index.ts | 17 + src/reporters/base.js | 20 +- src/runner/browser-runner.ts | 116 +-- src/runner/index.ts | 186 ++-- src/runner/runner.ts | 18 +- src/runner/suite-monitor.js | 6 +- src/runner/test-runner/index.ts | 11 +- .../test-runner/insistant-test-runner.js | 14 +- src/runner/test-runner/regular-test-runner.js | 12 +- src/runner/test-runner/skipped-test-runner.js | 10 +- src/signal-handler.js | 2 +- src/stats.ts | 64 +- src/test-collection.ts | 11 +- .../controllers/browser-version-controller.ts | 37 +- .../controllers/config-controller.ts | 25 +- .../controllers/only-controller.ts | 33 +- .../controllers/skip-controller.ts | 39 +- src/test-reader/index.js | 4 +- src/test-reader/mocha-reader/index.js | 16 +- src/test-reader/read-events.js | 3 - src/test-reader/test-parser-api.ts | 38 +- src/test-reader/test-parser.js | 16 +- src/types/index.ts | 151 ++++ src/utils/worker-process.ts | 22 +- src/utils/workers-registry.js | 8 +- src/worker/constants/runner-events.js | 19 - src/worker/hermione-facade.js | 2 +- src/worker/hermione.ts | 67 +- src/worker/runner/browser-pool.js | 4 +- src/worker/runner/caching-test-parser.js | 8 +- src/worker/runner/index.js | 10 +- src/worker/runner/sequence-test-parser.js | 4 +- src/worker/runner/simple-test-parser.js | 4 +- src/worker/runner/test-runner/index.js | 2 +- test/src/browser-pool/basic-pool.js | 6 +- test/src/browser-pool/limited-pool.js | 2 +- test/src/browser/calibrator.js | 2 +- .../browser/client-bridge/client-bridge.js | 2 +- .../assert-view/assert-view-results.js | 10 +- .../capture-processors/assert-refs.js | 2 +- .../capture-processors/update-refs.js | 4 +- .../assert-view/errors/assert-view-error.js | 2 +- .../assert-view/errors/image-diff-error.js | 4 +- .../assert-view/errors/no-ref-image-error.js | 4 +- .../src/browser/commands/assert-view/index.js | 6 +- .../viewport/coord-validator.js | 8 +- test/src/cli/index.js | 2 +- test/src/config/browser-config.js | 2 +- test/src/config/browser-options.js | 2 +- test/src/config/index.js | 4 +- test/src/config/options.js | 2 +- test/src/events/async-emitter/index.js | 2 +- test/src/events/utils.js | 2 +- test/src/hermione.js | 24 +- test/src/reporters/flat.js | 2 +- test/src/reporters/jsonl.js | 2 +- test/src/reporters/plain.js | 2 +- test/src/runner/browser-runner.js | 15 +- test/src/runner/index.js | 33 +- test/src/runner/suite-monitor.js | 2 +- .../test-runner/insistant-test-runner.js | 10 +- .../runner/test-runner/regular-test-runner.js | 2 +- .../runner/test-runner/skipped-test-runner.js | 2 +- test/src/stats.js | 4 +- test/src/test-collection.ts | 2 +- .../controllers/browser-version-controller.js | 2 +- .../controllers/config-controller.js | 2 +- .../controllers/only-controller.js | 2 +- .../controllers/skip-controller.js | 2 +- test/src/test-reader/index.js | 2 +- test/src/test-reader/mocha-reader/index.js | 4 +- test/src/test-reader/test-parser-api.js | 4 +- test/src/test-reader/test-parser.js | 15 +- test/src/utils/worker-process.js | 2 +- test/src/utils/workers-registry.js | 4 +- test/src/worker/hermione-facade.js | 4 +- test/src/worker/hermione.js | 12 +- test/src/worker/runner/browser-pool.js | 2 +- test/src/worker/runner/caching-test-parser.js | 4 +- test/src/worker/runner/index.js | 2 +- .../src/worker/runner/sequence-test-parser.js | 2 +- test/src/worker/runner/simple-test-parser.js | 2 +- test/src/worker/runner/test-runner/index.js | 2 +- tsconfig.common.json | 4 +- typings/api.d.ts | 4 + typings/global.d.ts | 34 +- typings/index.d.ts | 817 ------------------ typings/mocha/index.d.ts | 9 - typings/webdriverio/index.d.ts | 103 --- 127 files changed, 1685 insertions(+), 1698 deletions(-) create mode 100644 src/browser/commands/types.ts create mode 100644 src/browser/types.ts create mode 100644 src/config/types.ts delete mode 100644 src/constants/runner-events.ts create mode 100644 src/events/index.ts create mode 100644 src/events/types.ts create mode 100644 src/index.ts delete mode 100644 src/test-reader/read-events.js create mode 100644 src/types/index.ts delete mode 100644 src/worker/constants/runner-events.js create mode 100644 typings/api.d.ts delete mode 100644 typings/index.d.ts delete mode 100644 typings/mocha/index.d.ts delete mode 100644 typings/webdriverio/index.d.ts diff --git a/.eslintrc.js b/.eslintrc.js index baa339b84..095a22400 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,6 +7,12 @@ module.exports = { ecmaVersion: 2022, }, overrides: [ + { + files: ["*.ts"], + rules: { + "@typescript-eslint/explicit-function-return-type": "error", + }, + }, { files: ["*.js"], rules: { diff --git a/bin/hermione b/bin/hermione index 6e3519577..e0a06e07b 100755 --- a/bin/hermione +++ b/bin/hermione @@ -1,4 +1,4 @@ #!/usr/bin/env node 'use strict'; -var cli = require('../build/cli').run(); +var cli = require('../build/src/cli').run(); diff --git a/package-lock.json b/package-lock.json index b7f9c87af..b02cfe773 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "looks-same": "^8.1.0", "micromatch": "^4.0.5", "mocha": "^10.2.0", - "plugins-loader": "^1.1.0", + "plugins-loader": "^1.2.0", "png-validator": "1.1.0", "sharp": "~0.30.7", "sizzle": "^2.3.6", @@ -50,6 +50,7 @@ "@commitlint/cli": "^17.1.2", "@commitlint/config-conventional": "^17.1.0", "@swc/core": "^1.3.40", + "@types/bluebird": "^3.5.38", "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", "@types/lodash": "^4.14.191", @@ -80,6 +81,7 @@ "sinon-chai": "^2.12.0", "standard-version": "^9.5.0", "ts-node": "^10.9.1", + "type-fest": "^3.10.0", "typescript": "^4.9.5" }, "engines": { @@ -1272,6 +1274,18 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@gemini-testing/commander": { "version": "2.15.3", "resolved": "https://registry.npmjs.org/@gemini-testing/commander/-/commander-2.15.3.tgz", @@ -1843,6 +1857,12 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, + "node_modules/@types/bluebird": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.38.tgz", + "integrity": "sha512-yR/Kxc0dd4FfwtEoLZMoqJbM/VE/W7hXn/MIjb+axcwag0iFmSPK7OBUZq1YWLynJUoWQkfUrI7T0HDqGApNSg==", + "dev": true + }, "node_modules/@types/cacheable-request": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", @@ -6323,6 +6343,18 @@ "node": ">= 0.8.0" } }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/espree": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", @@ -10446,9 +10478,9 @@ } }, "node_modules/plugins-loader": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/plugins-loader/-/plugins-loader-1.1.0.tgz", - "integrity": "sha512-AVLb8tpRhQn3buabonz19+VMBc2CW1iLFuZzEiIqHqsCdBgE6PRZ/YtPyKtYv91Esp7WRFAH+ruUtRnkQwrxwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/plugins-loader/-/plugins-loader-1.2.0.tgz", + "integrity": "sha512-mElmcsDnP5jW0kHGa9RQLROHPk+Txq5foGFeoQDjvMpSf9X4OCxAoOrhsB0eTRWIA2Zgz40+bFUi/dyRTa4nTQ==", "dependencies": { "lodash": "^4.16.4" } @@ -11263,6 +11295,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -12339,11 +12382,12 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.11.1.tgz", + "integrity": "sha512-aCuRNRERRVh33lgQaJRlUxZqzfhzwTrsE98Mc3o3VXqmiaQdHacgUtJ0esp+7MvZ92qhtzKPeusaX6vIEcoreA==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -14451,6 +14495,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true } } }, @@ -14849,6 +14899,12 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, + "@types/bluebird": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.38.tgz", + "integrity": "sha512-yR/Kxc0dd4FfwtEoLZMoqJbM/VE/W7hXn/MIjb+axcwag0iFmSPK7OBUZq1YWLynJUoWQkfUrI7T0HDqGApNSg==", + "dev": true + }, "@types/cacheable-request": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", @@ -18276,6 +18332,12 @@ "requires": { "prelude-ls": "^1.2.1" } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true } } }, @@ -21453,9 +21515,9 @@ } }, "plugins-loader": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/plugins-loader/-/plugins-loader-1.1.0.tgz", - "integrity": "sha512-AVLb8tpRhQn3buabonz19+VMBc2CW1iLFuZzEiIqHqsCdBgE6PRZ/YtPyKtYv91Esp7WRFAH+ruUtRnkQwrxwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/plugins-loader/-/plugins-loader-1.2.0.tgz", + "integrity": "sha512-mElmcsDnP5jW0kHGa9RQLROHPk+Txq5foGFeoQDjvMpSf9X4OCxAoOrhsB0eTRWIA2Zgz40+bFUi/dyRTa4nTQ==", "requires": { "lodash": "^4.16.4" } @@ -22056,6 +22118,13 @@ "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", "requires": { "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + } } }, "serialize-javascript": { @@ -22894,9 +22963,10 @@ "dev": true }, "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.11.1.tgz", + "integrity": "sha512-aCuRNRERRVh33lgQaJRlUxZqzfhzwTrsE98Mc3o3VXqmiaQdHacgUtJ0esp+7MvZ92qhtzKPeusaX6vIEcoreA==", + "dev": true }, "typedarray": { "version": "0.0.6", diff --git a/package.json b/package.json index 81f853d6f..617d9da84 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,13 @@ "name": "hermione", "version": "7.1.0", "description": "Tests framework based on mocha and wdio", - "main": "build/hermione.js", + "main": "build/src/index.js", "files": [ - "build", - "typings" + "build" ], - "types": "./typings/index.d.ts", "scripts": { - "build": "tsc && copyfiles --up 1 'src/browser/client-scripts/*' build", + "build": "tsc && npm run copy-static", + "copy-static": "copyfiles 'src/browser/client-scripts/*' 'typings/*' build", "check-types": "tsc --project tsconfig.spec.json", "clean": "rimraf build/ *.tsbuildinfo", "coverage": "nyc --reporter=text npm run test-unit", @@ -23,7 +22,8 @@ "prepack": "npm run clean && npm run build", "preversion": "npm run lint && npm test", "commitmsg": "commitlint -e", - "release": "standard-version" + "release": "standard-version", + "watch": "npm run copy-static && tsc --watch" }, "repository": { "type": "git", @@ -65,7 +65,7 @@ "looks-same": "^8.1.0", "micromatch": "^4.0.5", "mocha": "^10.2.0", - "plugins-loader": "^1.1.0", + "plugins-loader": "^1.2.0", "png-validator": "1.1.0", "sharp": "~0.30.7", "sizzle": "^2.3.6", @@ -81,6 +81,7 @@ "@commitlint/cli": "^17.1.2", "@commitlint/config-conventional": "^17.1.0", "@swc/core": "^1.3.40", + "@types/bluebird": "^3.5.38", "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", "@types/lodash": "^4.14.191", @@ -111,6 +112,7 @@ "sinon-chai": "^2.12.0", "standard-version": "^9.5.0", "ts-node": "^10.9.1", + "type-fest": "^3.10.0", "typescript": "^4.9.5" }, "peerDependencies": { diff --git a/src/base-hermione.ts b/src/base-hermione.ts index c2e1d81a9..5d4daf435 100644 --- a/src/base-hermione.ts +++ b/src/base-hermione.ts @@ -1,24 +1,34 @@ -"use strict"; - -const _ = require("lodash"); -const Promise = require("bluebird"); -const pluginsLoader = require("plugins-loader"); - -const Config = require("./config"); -const RunnerEvents = require("./constants/runner-events"); -const AsyncEmitter = require("./events/async-emitter"); -const Errors = require("./errors"); -const WorkerRunnerEvents = require("./worker/constants/runner-events"); -const { tryToRegisterTsNode } = require("./utils/typescript"); - -const PREFIX = require("../package").name + "-"; - -module.exports = class BaseHermione extends AsyncEmitter { - static create(config) { +import _ from "lodash"; +import pluginsLoader from "plugins-loader"; +import { Config } from "./config"; +import { + AsyncEmitter, + InterceptedEvent, + MasterEvents, + WorkerEvents, + Events, + InterceptHandler, + Interceptor, +} from "./events"; +import Errors from "./errors"; +import { tryToRegisterTsNode } from "./utils/typescript"; +import * as packageJson from "../package.json"; +import { ConfigInput } from "./config/types"; + +const PREFIX = packageJson.name + "-"; + +export abstract class BaseHermione extends AsyncEmitter { + protected _interceptors: Interceptor[] = []; + protected _config: Config; + + static create( + this: new (config?: string | ConfigInput) => T, + config?: string | ConfigInput, + ): T { return new this(config); } - constructor(config) { + protected constructor(config?: string | ConfigInput) { super(); this._interceptors = []; @@ -29,34 +39,32 @@ module.exports = class BaseHermione extends AsyncEmitter { this._loadPlugins(); } - _init() { - this._init = () => Promise.resolve(); // init only once - return this.emitAndWait(RunnerEvents.INIT); + protected async _init(): Promise { + this._init = (): Promise => Promise.resolve(); // init only once + await this.emitAndWait(MasterEvents.INIT); } - get config() { + get config(): Config { return this._config; } - get events() { - return _.extend({}, RunnerEvents, WorkerRunnerEvents); + get events(): Events { + return _.extend({}, MasterEvents, WorkerEvents); } - get errors() { + get errors(): typeof Errors { return Errors; } - intercept(event, handler) { + intercept(event: InterceptedEvent, handler: InterceptHandler): this { this._interceptors.push({ event, handler }); return this; } - isWorker() { - throw new Error("Method must be implemented in child classes"); - } + abstract isWorker(): boolean; - _loadPlugins() { + protected _loadPlugins(): void { pluginsLoader.load(this, this.config.plugins, PREFIX); } -}; +} diff --git a/src/browser-pool/basic-pool.js b/src/browser-pool/basic-pool.js index 700e9afa4..495ae8444 100644 --- a/src/browser-pool/basic-pool.js +++ b/src/browser-pool/basic-pool.js @@ -2,8 +2,8 @@ const _ = require("lodash"); const Browser = require("../browser/new-browser"); -const CancelledError = require("./cancelled-error"); -const Events = require("../constants/runner-events"); +const { CancelledError } = require("./cancelled-error"); +const { MasterEvents } = require("../events"); const Pool = require("./pool"); const debug = require("debug"); @@ -30,7 +30,7 @@ module.exports = class BasicPool extends Pool { await browser.init(); this.log(`browser ${browser.fullId} started`); - await this._emit(Events.SESSION_START, browser); + await this._emit(MasterEvents.SESSION_START, browser); if (this._cancelled) { throw new CancelledError(); @@ -55,7 +55,7 @@ module.exports = class BasicPool extends Pool { this.log(`stop browser ${browser.fullId}`); try { - await this._emit(Events.SESSION_END, browser); + await this._emit(MasterEvents.SESSION_END, browser); } catch (err) { console.warn((err && err.stack) || err); } diff --git a/src/browser-pool/cancelled-error.ts b/src/browser-pool/cancelled-error.ts index 1ac0167de..fd7f931d9 100644 --- a/src/browser-pool/cancelled-error.ts +++ b/src/browser-pool/cancelled-error.ts @@ -1,11 +1,9 @@ -"use strict"; +export class CancelledError extends Error { + name = "CancelledError"; + message = "Browser request was cancelled"; -module.exports = class CancelledError extends Error { constructor() { super(); - - this.name = "CancelledError"; - this.message = "Browser request was cancelled"; Error.captureStackTrace(this, CancelledError); } -}; +} diff --git a/src/browser-pool/index.ts b/src/browser-pool/index.ts index b511d17a1..9797288e0 100644 --- a/src/browser-pool/index.ts +++ b/src/browser-pool/index.ts @@ -1,13 +1,15 @@ -"use strict"; +import _ from "lodash"; +import BasicPool from "./basic-pool"; +import LimitedPool from "./limited-pool"; +import PerBrowserLimitedPool from "./per-browser-limited-pool"; +import CachingPool from "./caching-pool"; +import { Config } from "../config"; +import { AsyncEmitter } from "../events"; -const _ = require("lodash"); -const BasicPool = require("./basic-pool"); -const LimitedPool = require("./limited-pool"); -const PerBrowserLimitedPool = require("./per-browser-limited-pool"); -const CachingPool = require("./caching-pool"); +export type BrowserPool = LimitedPool | PerBrowserLimitedPool; -exports.create = function (config, emitter) { - var pool = BasicPool.create(config, emitter); +export const create = function (config: Config, emitter: AsyncEmitter): BrowserPool { + let pool: BasicPool | CachingPool | PerBrowserLimitedPool | LimitedPool = BasicPool.create(config, emitter); pool = new CachingPool(pool, config); pool = new PerBrowserLimitedPool(pool, config); diff --git a/src/browser-pool/limited-pool.js b/src/browser-pool/limited-pool.js index 51f50b3a5..318d23168 100644 --- a/src/browser-pool/limited-pool.js +++ b/src/browser-pool/limited-pool.js @@ -4,7 +4,7 @@ const _ = require("lodash"); const Promise = require("bluebird"); const yallist = require("yallist"); const Pool = require("./pool"); -const CancelledError = require("./cancelled-error"); +const { CancelledError } = require("./cancelled-error"); const debug = require("debug"); const { buildCompositeBrowserId } = require("./utils"); diff --git a/src/browser/calibrator.js b/src/browser/calibrator.js index b92038ea4..0e58729fb 100644 --- a/src/browser/calibrator.js +++ b/src/browser/calibrator.js @@ -5,7 +5,7 @@ const path = require("path"); const Promise = require("bluebird"); const _ = require("lodash"); const looksSame = require("looks-same"); -const CoreError = require("./core-error"); +const { CoreError } = require("./core-error"); const clientScriptCalibrate = fs.readFileSync(path.join(__dirname, "client-scripts", "calibrate.js"), "utf8"); const DIRECTION = { FORWARD: "forward", REVERSE: "reverse" }; diff --git a/src/browser/client-bridge/client-bridge.js b/src/browser/client-bridge/client-bridge.js index a57327d07..d0b5b7e66 100644 --- a/src/browser/client-bridge/client-bridge.js +++ b/src/browser/client-bridge/client-bridge.js @@ -1,7 +1,7 @@ "use strict"; const Promise = require("bluebird"); -const ClientBridgeError = require("./error"); +const { ClientBridgeError } = require("./error"); module.exports = class ClientBridge { static create(browser, script) { diff --git a/src/browser/client-bridge/error.ts b/src/browser/client-bridge/error.ts index 67a56b56a..8e373825a 100644 --- a/src/browser/client-bridge/error.ts +++ b/src/browser/client-bridge/error.ts @@ -1,9 +1,6 @@ -"use strict"; - -module.exports = class ClientBridgeError extends Error { - constructor(message) { +export class ClientBridgeError extends Error { + constructor(message: string) { super(message); - this.name = this.constructor.name; } -}; +} diff --git a/src/browser/commands/assert-view/assert-view-results.js b/src/browser/commands/assert-view/assert-view-results.js index 8220f043c..5b8933a70 100644 --- a/src/browser/commands/assert-view/assert-view-results.js +++ b/src/browser/commands/assert-view/assert-view-results.js @@ -1,7 +1,7 @@ "use strict"; -const ImageDiffError = require("./errors/image-diff-error"); -const NoRefImageError = require("./errors/no-ref-image-error"); +const { ImageDiffError } = require("./errors/image-diff-error"); +const { NoRefImageError } = require("./errors/no-ref-image-error"); module.exports = class AssertViewResults { static fromRawObject(results) { diff --git a/src/browser/commands/assert-view/capture-processors/assert-refs.js b/src/browser/commands/assert-view/capture-processors/assert-refs.js index 3af8b302f..ce460165a 100644 --- a/src/browser/commands/assert-view/capture-processors/assert-refs.js +++ b/src/browser/commands/assert-view/capture-processors/assert-refs.js @@ -1,8 +1,8 @@ "use strict"; const Promise = require("bluebird"); -const ImageDiffError = require("../errors/image-diff-error"); -const NoRefImageError = require("../errors/no-ref-image-error"); +const { ImageDiffError } = require("../errors/image-diff-error"); +const { NoRefImageError } = require("../errors/no-ref-image-error"); exports.handleNoRefImage = (currImg, refImg, state) => { return Promise.reject(NoRefImageError.create(state, currImg, refImg)); diff --git a/src/browser/commands/assert-view/capture-processors/update-refs.js b/src/browser/commands/assert-view/capture-processors/update-refs.js index 027e006f3..df77941ee 100644 --- a/src/browser/commands/assert-view/capture-processors/update-refs.js +++ b/src/browser/commands/assert-view/capture-processors/update-refs.js @@ -1,9 +1,9 @@ "use strict"; const fs = require("fs-extra"); -const WorkerRunnerEvents = require("../../../../worker/constants/runner-events"); +const { WorkerEvents } = require("../../../../events"); exports.handleNoRefImage = exports.handleImageDiff = async (currImg, refImg, state, { emitter }) => { await fs.copy(currImg.path, refImg.path); - emitter.emit(WorkerRunnerEvents.UPDATE_REFERENCE, { state, refImg }); + emitter.emit(WorkerEvents.UPDATE_REFERENCE, { state, refImg }); }; diff --git a/src/browser/commands/assert-view/errors/assert-view-error.ts b/src/browser/commands/assert-view/errors/assert-view-error.ts index a0835582f..8c2b8e729 100644 --- a/src/browser/commands/assert-view/errors/assert-view-error.ts +++ b/src/browser/commands/assert-view/errors/assert-view-error.ts @@ -1,10 +1,7 @@ -"use strict"; - -module.exports = class AssertViewError extends Error { - constructor(message) { +export class AssertViewError extends Error { + constructor(public message: string = "image comparison failed") { super(); this.name = this.constructor.name; - this.message = message || "image comparison failed"; } -}; +} diff --git a/src/browser/commands/assert-view/errors/base-state-error.ts b/src/browser/commands/assert-view/errors/base-state-error.ts index cdf072385..26969fc07 100644 --- a/src/browser/commands/assert-view/errors/base-state-error.ts +++ b/src/browser/commands/assert-view/errors/base-state-error.ts @@ -1,12 +1,9 @@ -"use strict"; +import { ImageInfo } from "../../../../types"; -module.exports = class BaseStateError extends Error { - constructor(stateName, currImg = {}, refImg = {}) { +export class BaseStateError extends Error { + constructor(public stateName: string, public currImg: ImageInfo, public refImg: ImageInfo) { super(); this.name = this.constructor.name; - this.stateName = stateName; - this.currImg = currImg; - this.refImg = refImg; } -}; +} diff --git a/src/browser/commands/assert-view/errors/image-diff-error.ts b/src/browser/commands/assert-view/errors/image-diff-error.ts index be95e1b4d..79281df41 100644 --- a/src/browser/commands/assert-view/errors/image-diff-error.ts +++ b/src/browser/commands/assert-view/errors/image-diff-error.ts @@ -1,22 +1,68 @@ -"use strict"; +import { ImageInfo } from "../../../../types"; -const Image = require("../../../../image"); -const BaseStateError = require("./base-state-error"); +import Image from "../../../../image"; +import { BaseStateError } from "./base-state-error"; -module.exports = class ImageDiffError extends BaseStateError { - static create(...args) { - return new this(...args); +import type { LooksSameOptions, LooksSameResult } from "looks-same"; + +interface DiffOptions extends LooksSameOptions { + /** Path to the current screenshot */ + current: string; + reference: string; + diffColor: string; +} + +type DiffAreas = Pick; + +type ImageDiffErrorConstructor = new ( + stateName: string, + currImg: ImageInfo, + refImg: ImageInfo, + diffOpts: DiffOptions, + diffAreas: DiffAreas, +) => T; + +interface ImageDiffErrorData { + stateName: string; + currImg: ImageInfo; + refImg: ImageInfo; + diffOpts: DiffOptions; + diffBounds: LooksSameResult["diffBounds"]; + diffClusters: LooksSameResult["diffClusters"]; +} + +export class ImageDiffError extends BaseStateError { + message: string; + diffOpts: DiffOptions; + diffBounds?: DiffAreas["diffBounds"]; + diffClusters?: DiffAreas["diffClusters"]; + + static create( + this: ImageDiffErrorConstructor, + stateName: string, + currImg: ImageInfo, + refImg: ImageInfo, + diffOpts: DiffOptions, + diffAreas: DiffAreas = {}, + ): T { + return new this(stateName, currImg, refImg, diffOpts, diffAreas); } - static fromObject(data) { + static fromObject(this: ImageDiffErrorConstructor, data: ImageDiffErrorData): T { const { diffBounds, diffClusters } = data; - return new ImageDiffError(data.stateName, data.currImg, data.refImg, data.diffOpts, { + return new this(data.stateName, data.currImg, data.refImg, data.diffOpts, { diffBounds, diffClusters, }); } - constructor(stateName, currImg, refImg, diffOpts, { diffBounds, diffClusters } = {}) { + constructor( + stateName: string, + currImg: ImageInfo, + refImg: ImageInfo, + diffOpts: DiffOptions, + { diffBounds, diffClusters }: DiffAreas = {}, + ) { super(stateName, currImg, refImg); this.message = `images are different for "${stateName}" state`; @@ -25,7 +71,7 @@ module.exports = class ImageDiffError extends BaseStateError { this.diffClusters = diffClusters; } - saveDiffTo(diffPath) { + saveDiffTo(diffPath: string): Promise { return Image.buildDiff({ diff: diffPath, ...this.diffOpts }); } -}; +} diff --git a/src/browser/commands/assert-view/errors/no-ref-image-error.ts b/src/browser/commands/assert-view/errors/no-ref-image-error.ts index 6e4acd959..518e9a1a5 100644 --- a/src/browser/commands/assert-view/errors/no-ref-image-error.ts +++ b/src/browser/commands/assert-view/errors/no-ref-image-error.ts @@ -1,19 +1,31 @@ -"use strict"; +import { BaseStateError } from "./base-state-error"; +import { ImageInfo } from "../../../../types"; -const BaseStateError = require("./base-state-error"); +type NoRefImageErrorConstructor = new (stateName: string, currImg: ImageInfo, refImg: ImageInfo) => T; -module.exports = class NoRefImageError extends BaseStateError { - static create(...args) { - return new this(...args); +interface NoRefImageErrorData { + stateName: string; + currImg: ImageInfo; + refImg: ImageInfo; +} + +export class NoRefImageError extends BaseStateError { + static create( + this: NoRefImageErrorConstructor, + stateName: string, + currImg: ImageInfo, + refImg: ImageInfo, + ): T { + return new this(stateName, currImg, refImg); } - static fromObject(data) { - return new NoRefImageError(data.stateName, data.currImg, data.refImg); + static fromObject(this: NoRefImageErrorConstructor, data: NoRefImageErrorData): T { + return new this(data.stateName, data.currImg, data.refImg); } - constructor(stateName, currImg, refImg) { + constructor(stateName: string, currImg: ImageInfo, refImg: ImageInfo) { super(stateName, currImg, refImg); this.message = `can not find reference image at ${this.refImg.path} for "${stateName}" state`; } -}; +} diff --git a/src/browser/commands/assert-view/index.js b/src/browser/commands/assert-view/index.js index 411014f8e..45b06a843 100644 --- a/src/browser/commands/assert-view/index.js +++ b/src/browser/commands/assert-view/index.js @@ -10,8 +10,8 @@ const temp = require("../../../temp"); const { getCaptureProcessors } = require("./capture-processors"); const RuntimeConfig = require("../../../config/runtime-config"); const AssertViewResults = require("./assert-view-results"); -const BaseStateError = require("./errors/base-state-error"); -const AssertViewError = require("./errors/assert-view-error"); +const { BaseStateError } = require("./errors/base-state-error"); +const { AssertViewError } = require("./errors/assert-view-error"); const InvalidPngError = require("./errors/invalid-png-error"); module.exports = browser => { diff --git a/src/browser/commands/scrollIntoView.ts b/src/browser/commands/scrollIntoView.ts index 2a87918d3..6fb1d915d 100644 --- a/src/browser/commands/scrollIntoView.ts +++ b/src/browser/commands/scrollIntoView.ts @@ -3,7 +3,7 @@ type Browser = { }; // TODO: remove after fix https://github.com/webdriverio/webdriverio/issues/9620 -export = async (browser: Browser) => { +export = async (browser: Browser): Promise => { const { publicAPI: session } = browser; session.overwriteCommand( diff --git a/src/browser/commands/types.ts b/src/browser/commands/types.ts new file mode 100644 index 000000000..a9225c280 --- /dev/null +++ b/src/browser/commands/types.ts @@ -0,0 +1,60 @@ +import { AssertViewOptsConfig } from "../../config/types"; + +export interface AssertViewOpts extends Partial { + /** + * Maximum allowed difference between colors. + * Overrides config {@link https://github.com/gemini-testing/hermione#browsers browsers}.{@link https://github.com/gemini-testing/hermione#tolerance tolerance} value. + * + * @remarks + * Indicates maximum allowed CIEDE2000 difference between colors. Used only in non-strict mode. + * Increasing global default is not recommended, prefer changing tolerance for particular suites or states instead. + * By default it's 2.3 which should be enough for the most cases. + * + * @defaultValue `2.3` + */ + tolerance?: number; + /** + * Minimum difference in brightness (zero by default) between the darkest/lightest pixel (which is adjacent to the antialiasing pixel) and theirs adjacent pixels. + * Overrides config {@link https://github.com/gemini-testing/hermione#browsers browsers}.{@link https://github.com/gemini-testing/hermione#antialiasingTolerance antialiasingTolerance} value. + * + * @remarks + * Read more about this option in {@link https://github.com/gemini-testing/looks-same#comparing-images-with-ignoring-antialiasing looks-same} + * + * @defaultValue `4` + */ + antialiasingTolerance?: number; + /** + * Allows testing of regions which bottom bounds are outside of a viewport height. + * Overrides config {@link https://github.com/gemini-testing/hermione#browsers browsers}.{@link https://github.com/gemini-testing/hermione#compositeImage compositeImage} value. + * + * @remarks + * In the resulting screenshot the area which fits the viewport bounds will be joined with the area which is outside of the viewport height. + * + * @defaultValue `true` + */ + compositeImage?: boolean; + /** + * Allows to specify a delay (in milliseconds) before making any screenshot. + * Overrides config {@link https://github.com/gemini-testing/hermione#browsers browsers}.{@link https://github.com/gemini-testing/hermione#screenshotDelay screenshotDelay} value. + * + * @remarks + * This is useful when the page has elements which are animated or if you do not want to screen a scrollbar. + * + * @defaultValue `0` + */ + screenshotDelay?: number; + /** + * Ability to set DOM-node selector which should be scroll when the captured element does not completely fit on the screen. + * + * @remarks + * Useful when you capture the modal (popup). In this case a duplicate of the modal appears on the screenshot. + * That happens because we scroll the page using `window` selector, which scroll only the background of the modal, and the modal itself remains in place. + * Default value is `undefined` (it means scroll relative to `window`). Works only when `compositeImage` is `true` (default). + * + * @defaultValue `undefined` + */ + selectorToScroll?: string; +} + +export type AssertViewCommand = (state: string, selectors: string | string[], opts?: AssertViewOpts) => Promise; +export type AssertViewElementCommand = (state: string, opts?: AssertViewOpts) => Promise; diff --git a/src/browser/core-error.ts b/src/browser/core-error.ts index b33b6ccf1..ad373ba5b 100644 --- a/src/browser/core-error.ts +++ b/src/browser/core-error.ts @@ -1,9 +1,7 @@ -"use strict"; +export class CoreError extends Error { + name = "CoreError"; -module.exports = class CoreError extends Error { - constructor(message) { + constructor(message: string) { super(message); - - this.name = "CoreError"; } -}; +} diff --git a/src/browser/history/commands.ts b/src/browser/history/commands.ts index 82d639130..1bcf3abc4 100644 --- a/src/browser/history/commands.ts +++ b/src/browser/history/commands.ts @@ -93,8 +93,8 @@ const wdioElementCommands = [ "waitUntil", ]; -export const getBrowserCommands = () => wdioBrowserCommands; -export const getElementCommands = () => wdioElementCommands; +export const getBrowserCommands = (): typeof wdioBrowserCommands => wdioBrowserCommands; +export const getElementCommands = (): typeof wdioElementCommands => wdioElementCommands; export const createScope = (elementScope: boolean): `${Scopes}` => { return elementScope ? Scopes.ELEMENT : Scopes.BROWSER; }; diff --git a/src/browser/screen-shooter/viewport/coord-validator/errors/height-viewport-error.ts b/src/browser/screen-shooter/viewport/coord-validator/errors/height-viewport-error.ts index 0f76a9e18..cdca55a67 100644 --- a/src/browser/screen-shooter/viewport/coord-validator/errors/height-viewport-error.ts +++ b/src/browser/screen-shooter/viewport/coord-validator/errors/height-viewport-error.ts @@ -1,12 +1,10 @@ -"use strict"; - /** * Height of the element is larger than viewport */ -module.exports = class HeightViewportError extends Error { - constructor(message) { +export class HeightViewportError extends Error { + constructor(message: string) { super(message); this.name = this.constructor.name; } -}; +} diff --git a/src/browser/screen-shooter/viewport/coord-validator/errors/offset-viewport-error.ts b/src/browser/screen-shooter/viewport/coord-validator/errors/offset-viewport-error.ts index 79abb210f..909e860a2 100644 --- a/src/browser/screen-shooter/viewport/coord-validator/errors/offset-viewport-error.ts +++ b/src/browser/screen-shooter/viewport/coord-validator/errors/offset-viewport-error.ts @@ -1,12 +1,10 @@ -"use strict"; - /** * Position of an element is outside of a viewport left, top or right bounds */ -module.exports = class OffsetViewportError extends Error { - constructor(message) { +export class OffsetViewportError extends Error { + constructor(message: string) { super(message); this.name = this.constructor.name; } -}; +} diff --git a/src/browser/screen-shooter/viewport/coord-validator/index.js b/src/browser/screen-shooter/viewport/coord-validator/index.js index 0a323ff95..c5ba7c609 100644 --- a/src/browser/screen-shooter/viewport/coord-validator/index.js +++ b/src/browser/screen-shooter/viewport/coord-validator/index.js @@ -1,8 +1,8 @@ "use strict"; const debug = require("debug"); -const HeightViewportError = require("./errors/height-viewport-error"); -const OffsetViewportError = require("./errors/offset-viewport-error"); +const { HeightViewportError } = require("./errors/height-viewport-error"); +const { OffsetViewportError } = require("./errors/offset-viewport-error"); const isOutsideOfViewport = (viewport, cropArea) => cropArea.top < 0 || cropArea.left < 0 || cropArea.left + cropArea.width > viewport.width; diff --git a/src/browser/types.ts b/src/browser/types.ts new file mode 100644 index 000000000..36b5ff8b1 --- /dev/null +++ b/src/browser/types.ts @@ -0,0 +1,109 @@ +import type { AssertViewCommand, AssertViewElementCommand } from "./commands/types"; + +export interface BrowserMeta { + pid: number; + browserVersion: string; + [name: string]: unknown; +} + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace WebdriverIO { + interface Browser { + getMeta(): Promise; + getMeta(key: string): Promise; + + setMeta(key: string, value: unknown): Promise; + + extendOptions(opts: { [name: string]: unknown }): Promise; + + /** + * Takes a screenshot of the passed selector and compares the received screenshot with the reference. + * + * @remarks + * For more details, see {@link https://github.com/gemini-testing/hermione#assertview documentation}. + * + * @example + * ```ts + * + * it('some test', async function() { + * await this.browser.url('some/url'); + * await this.browser.assertView('plain', '.button', { + * ignoreElements: ['.link'], + * tolerance: 2.3, + * antialiasingTolerance: 4, + * allowViewportOverflow: true, + * captureElementFromTop: true, + * compositeImage: true, + * screenshotDelay: 600, + * selectorToScroll: '.modal' + * }); + *}); + * ``` + * + * @param state state name, should be unique within one test + * @param selectors DOM-node selector that you need to capture + * @param opts additional options, currently available: + * "ignoreElements", "tolerance", "antialiasingTolerance", "allowViewportOverflow", "captureElementFromTop", + * "compositeImage", "screenshotDelay", "selectorToScroll" + */ + assertView: AssertViewCommand; + + /** + * Command that allows to add human-readable description for commands in history + * + * @remarks + * For more details, see {@link https://github.com/gemini-testing/hermione#runstep documentation} + * + * @example + * ```ts + * it('some test', async function () { + * await this.browser.runStep('step name', async () => { + * await this.browser.url('some/url'); + * ... + * }); + * }) + * ``` + * + * @param stepName step name + * @param stepCb step callback + * @returns {Promise} value, returned by `stepCb` + */ + runStep(stepName: string, stepCb: () => Promise | unknown): Promise; + } + + interface Element { + /** + * Takes a screenshot of the element and compares the received screenshot with the reference. + * + * @remarks + * For more details, see {@link https://github.com/gemini-testing/hermione#assertview documentation}. + * + * @example + * ```ts + * + * it('some test', async function() { + * await this.browser.url('some/url'); + * const button = await this.browser.$('.button'); + * await button.assertView('plain', { + * ignoreElements: ['.link'], + * tolerance: 2.3, + * antialiasingTolerance: 4, + * allowViewportOverflow: true, + * captureElementFromTop: true, + * compositeImage: true, + * screenshotDelay: 600, + * selectorToScroll: '.modal' + * }); + *}); + * ``` + * + * @param state state name, should be unique within one test + * @param opts additional options, currently available: + * "ignoreElements", "tolerance", "antialiasingTolerance", "allowViewportOverflow", "captureElementFromTop", + * "compositeImage", "screenshotDelay", "selectorToScroll" + */ + assertView: AssertViewElementCommand; + } + } +} diff --git a/src/cli/index.js b/src/cli/index.js index ffd01ebd7..96f92bfc9 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -5,7 +5,7 @@ const escapeRe = require("escape-string-regexp"); const defaults = require("../config/defaults"); const info = require("./info"); -const Hermione = require("../hermione"); +const { Hermione } = require("../hermione"); const pkg = require("../../package.json"); const logger = require("../utils/logger"); diff --git a/src/config/browser-config.ts b/src/config/browser-config.ts index 17c44fb4c..952ebb809 100644 --- a/src/config/browser-config.ts +++ b/src/config/browser-config.ts @@ -1,14 +1,14 @@ -"use strict"; +import * as path from "path"; +import * as _ from "lodash"; +import type { Test } from "../types"; +import type { CommonConfig } from "./types"; -const path = require("path"); -const _ = require("lodash"); - -module.exports = class BrowserConfig { - constructor(browserOptions) { +export class BrowserConfig { + constructor(browserOptions: CommonConfig) { _.extend(this, browserOptions); } - getScreenshotPath(test, stateName) { + getScreenshotPath(test: Test, stateName: string): string { const filename = `${stateName}.png`; const { screenshotsDir } = this; @@ -17,7 +17,7 @@ module.exports = class BrowserConfig { : path.resolve(process.cwd(), screenshotsDir, test.id, this.id, filename); } - serialize() { + serialize(): Omit { return _.omit(this, ["system"]); } -}; +} diff --git a/src/config/index.ts b/src/config/index.ts index 52f178a73..384bf6b6f 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,18 +1,19 @@ -"use strict"; - -const path = require("path"); -const _ = require("lodash"); -const BrowserConfig = require("./browser-config"); -const defaults = require("./defaults"); -const parseOptions = require("./options"); -const logger = require("../utils/logger"); - -module.exports = class Config { - static create(config) { +import * as path from "path"; +import * as _ from "lodash"; +import defaults from "./defaults"; +import { BrowserConfig } from "./browser-config"; +import parseOptions from "./options"; +import logger from "../utils/logger"; +import { ConfigInput } from "./types"; + +export class Config { + configPath!: string; + + static create(config?: string | ConfigInput): Config { return new Config(config); } - static read(configPath) { + static read(configPath: string): unknown { try { return require(path.resolve(process.cwd(), configPath)); } catch (e) { @@ -21,13 +22,13 @@ module.exports = class Config { } } - constructor(config) { - let options; + constructor(config?: string | ConfigInput) { + let options: ConfigInput; if (_.isObjectLike(config)) { - options = config; + options = config as ConfigInput; } else if (typeof config === "string") { this.configPath = config; - options = Config.read(config); + options = Config.read(config) as ConfigInput; } else { for (const configPath of defaults.configPaths) { try { @@ -36,7 +37,8 @@ module.exports = class Config { this.configPath = resolvedConfigPath; break; - } catch (err) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err: any) { if (err.code !== "MODULE_NOT_FOUND") { throw err; } @@ -47,7 +49,7 @@ module.exports = class Config { throw new Error(`Unable to read config from paths: ${defaults.configPaths.join(", ")}`); } - options = Config.read(this.configPath); + options = Config.read(this.configPath) as ConfigInput; } if (_.isFunction(options.prepareEnvironment)) { @@ -73,15 +75,15 @@ module.exports = class Config { }); } - forBrowser(id) { + forBrowser(id: string): BrowserConfig { return this.browsers[id]; } - getBrowserIds() { + getBrowserIds(): Array { return _.keys(this.browsers); } - serialize() { + serialize(): Omit { return _.extend({}, this, { browsers: _.mapValues(this.browsers, broConf => broConf.serialize()), }); @@ -91,7 +93,7 @@ module.exports = class Config { * This method is used in subrocesses to merge a created config * in a subrocess with a config from the main process */ - mergeWith(config) { + mergeWith(config: Config): void { _.mergeWith(this, config, (l, r) => { if (_.isObjectLike(l)) { return; @@ -102,4 +104,4 @@ module.exports = class Config { return typeof l === typeof r ? r : l; }); } -}; +} diff --git a/src/config/types.ts b/src/config/types.ts new file mode 100644 index 000000000..de50da035 --- /dev/null +++ b/src/config/types.ts @@ -0,0 +1,151 @@ +import type { SetRequired } from "type-fest"; +import type { BrowserConfig } from "./browser-config"; +import type { Test } from "../types"; + +export interface CompareOptsConfig { + shouldCluster: boolean; + clustersSize: number; + stopOnFirstFail: boolean; +} + +export interface BuildDiffOptsConfig { + ignoreAntialiasing: boolean; + ignoreCaret: boolean; +} + +export interface AssertViewOptsConfig { + /** + * DOM-node selectors which will be ignored (painted with a black rectangle) when comparing images. + * + * @defaultValue `[]` + */ + ignoreElements: string | Array; + /** + * Ability to set capture element from the top area or from current position. + * + * @remarks + * In the first case viewport will be scrolled to the top of the element. + * + * @defaultValue `true` + */ + captureElementFromTop: boolean; + /** + * Disables check that element is outside of the viewport left, top, right or bottom bounds. + * + * @remarks + * By default Hermione throws an error if element is outside the viewport bounds. + * This option disables check that element is outside of the viewport left, top, right or bottom bounds. + * And in this case if browser option {@link https://github.com/gemini-testing/hermione#compositeimage compositeImage} set to `false`, then only visible part of the element will be captured. + * But if {@link https://github.com/gemini-testing/hermione#compositeimage compositeImage} set to `true` (default), then in the resulting screenshot will appear the whole element with not visible parts outside of the bottom bounds of viewport. + * + * @defaultValue `false` + */ + allowViewportOverflow: boolean; +} + +export interface ExpectOptsConfig { + wait: number; + interval: number; +} + +export interface MochaOpts { + /** milliseconds to wait before considering a test slow. */ + slow?: number; + + /** timeout in milliseconds or time string like '1s'. */ + timeout?: number; + + /** string or regexp to filter tests with. */ + grep?: string | RegExp; +} + +export interface SystemConfig { + debug: boolean; + mochaOpts: MochaOpts; + expectOpts: ExpectOptsConfig; + ctx: { [name: string]: unknown }; + patternsOnReject: Array; + workers: number; + testsPerWorker: number; + diffColor: string; + tempDir: string; + parallelLimit: number; + fileExtensions: Array; +} + +export interface CommonConfig { + configPath?: string; + automationProtocol: "webdriver" | "devtools"; + desiredCapabilities: WebDriver.DesiredCapabilities | null; + gridUrl: string; + baseUrl: string; + sessionsPerBrowser: number; + testsPerSession: number; + retry: number; + shouldRetry(testInfo: { ctx: Test; retriesLeft: number }): boolean | null; + httpTimeout: number; + urlHttpTimeout: number | null; + pageLoadTimeout: number | null; + sessionRequestTimeout: number | null; + sessionQuitTimeout: number | null; + testTimeout: number | null; + waitTimeout: number; + saveHistoryMode: "all" | "none" | "onlyFailed"; + takeScreenshotOnFails: { + testFail: boolean; + assertViewFail: boolean; + }; + takeScreenshotOnFailsTimeout: number | null; + takeScreenshotOnFailsMode: "fullpage" | "viewport"; + prepareBrowser(browser: WebdriverIO.Browser): void | null; + screenshotPath: string | null; + screenshotsDir(test: Test): string; + calibrate: boolean; + compositeImage: boolean; + strictTestsOrder: boolean; + screenshotMode: "fullpage" | "viewport" | "auto"; + screenshotDelay: number; + tolerance: number; + antialiasingTolerance: number; + compareOpts: CompareOptsConfig; + buildDiffOpts: BuildDiffOptsConfig; + assertViewOpts: AssertViewOptsConfig; + expectOpts: ExpectOptsConfig; + meta: { [name: string]: unknown }; + windowSize: string | { width: number; height: number } | null; + orientation: "landscape" | "portrait" | null; + resetCursor: boolean; + headers: Record | null; + + system: SystemConfig; + headless: boolean | null; +} + +export interface SetsConfig { + files: string | Array; + ignoreFiles?: Array; + browsers?: Array; +} + +// Only browsers desiredCapabilities are required in input config +export type ConfigInput = { + browsers: Record, "desiredCapabilities">>; + plugins?: Record; + sets?: Record; + prepareEnvironment?: () => void | null; +}; + +declare module "." { + export interface Config extends CommonConfig { + browsers: Record; + plugins: Record>; + sets: Record; + prepareEnvironment?: () => void | null; + } +} + +declare module "./browser-config" { + export interface BrowserConfig extends CommonConfig { + id: string; + } +} diff --git a/src/constants/runner-events.ts b/src/constants/runner-events.ts deleted file mode 100644 index d7a271fa5..000000000 --- a/src/constants/runner-events.ts +++ /dev/null @@ -1,57 +0,0 @@ -// TODO: refactor this file because it contains not only runner events -"use strict"; - -const _ = require("lodash"); - -const getAsyncEvents = () => ({ - INIT: "init", - - RUNNER_START: "startRunner", - RUNNER_END: "endRunner", - - SESSION_START: "startSession", - SESSION_END: "endSession", - - EXIT: "exit", -}); - -const getRunnerSyncEvents = () => ({ - NEW_WORKER_PROCESS: "newWorkerProcess", - - SUITE_BEGIN: "beginSuite", - SUITE_END: "endSuite", - - TEST_BEGIN: "beginTest", - TEST_END: "endTest", - - TEST_PASS: "passTest", - TEST_FAIL: "failTest", - TEST_PENDING: "pendingTest", - - RETRY: "retry", -}); - -const getSyncEvents = () => - _.extend({}, getRunnerSyncEvents(), { - CLI: "cli", - - BEGIN: "begin", - END: "end", - - BEFORE_FILE_READ: "beforeFileRead", - AFTER_FILE_READ: "afterFileRead", - - AFTER_TESTS_READ: "afterTestsRead", - - INFO: "info", - WARNING: "warning", - ERROR: "err", - }); - -let events = _.extend(getSyncEvents(), getAsyncEvents()); - -Object.defineProperty(events, "getSync", { value: getSyncEvents, enumerable: false }); -Object.defineProperty(events, "getAsync", { value: getAsyncEvents, enumerable: false }); -Object.defineProperty(events, "getRunnerSync", { value: getRunnerSyncEvents, enumerable: false }); - -module.exports = events; diff --git a/src/errors.ts b/src/errors.ts index 9cd963bd9..f5fc397d8 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,10 +1,22 @@ -"use strict"; +import { CoreError } from "./browser/core-error"; +import { CancelledError } from "./browser-pool/cancelled-error"; +import { ClientBridgeError } from "./browser/client-bridge/error"; +import { HeightViewportError } from "./browser/screen-shooter/viewport/coord-validator/errors/height-viewport-error"; +import { OffsetViewportError } from "./browser/screen-shooter/viewport/coord-validator/errors/offset-viewport-error"; +import { AssertViewError } from "./browser/commands/assert-view/errors/assert-view-error"; +import { ImageDiffError } from "./browser/commands/assert-view/errors/image-diff-error"; +import { NoRefImageError } from "./browser/commands/assert-view/errors/no-ref-image-error"; +import { Constructor } from "type-fest"; -exports.CoreError = require("./browser/core-error"); -exports.CancelledError = require("./browser-pool/cancelled-error"); -exports.ClientBridgeError = require("./browser/client-bridge/error"); -exports.HeightViewportError = require("./browser/screen-shooter/viewport/coord-validator/errors/height-viewport-error"); -exports.OffsetViewportError = require("./browser/screen-shooter/viewport/coord-validator/errors/offset-viewport-error"); -exports.AssertViewError = require("./browser/commands/assert-view/errors/assert-view-error"); -exports.ImageDiffError = require("./browser/commands/assert-view/errors/image-diff-error"); -exports.NoRefImageError = require("./browser/commands/assert-view/errors/no-ref-image-error"); +const Errors = { + CoreError, + CancelledError, + ClientBridgeError, + HeightViewportError, + OffsetViewportError, + AssertViewError, + ImageDiffError, + NoRefImageError, +} as const satisfies Record>; + +export default Errors; diff --git a/src/events/async-emitter/index.ts b/src/events/async-emitter/index.ts index 2480a0ad4..79d223719 100644 --- a/src/events/async-emitter/index.ts +++ b/src/events/async-emitter/index.ts @@ -1,13 +1,17 @@ -"use strict"; +import { EventEmitter } from "events"; +import BluebirdPromise from "bluebird"; -const { EventEmitter } = require("events"); -const { method } = require("bluebird"); - -module.exports = class AsyncEmitter extends EventEmitter { - async emitAndWait(event, ...args) { - const results = await Promise.allSettled(this.listeners(event).map(l => method(l).apply(this, args))); +export class AsyncEmitter extends EventEmitter { + async emitAndWait(event: string | symbol, ...args: unknown[]): Promise { + const results = await Promise.allSettled( + this.listeners(event).map(l => + BluebirdPromise.method(l as (...args: unknown[]) => unknown).apply(this, args as []), + ), + ); const rejected = results.find(({ status }) => status === "rejected"); - return rejected ? Promise.reject(rejected.reason) : results.map(r => r.value); + return rejected + ? Promise.reject((rejected as PromiseRejectedResult).reason) + : results.map(r => (r as PromiseFulfilledResult).value); } -}; +} diff --git a/src/events/index.ts b/src/events/index.ts new file mode 100644 index 000000000..80eb5c15a --- /dev/null +++ b/src/events/index.ts @@ -0,0 +1,117 @@ +import { ValueOf } from "type-fest"; + +export * from "./async-emitter"; +export * from "./types"; + +export const TestReaderEvents = { + NEW_BUILD_INSTRUCTION: "newBuildInstruction", +} as const; + +export type TestReaderEvents = typeof TestReaderEvents; + +export type TestReaderEvent = ValueOf; + +export const MasterAsyncEvents = { + INIT: "init", + + RUNNER_START: "startRunner", + RUNNER_END: "endRunner", + + SESSION_START: "startSession", + SESSION_END: "endSession", + + EXIT: "exit", +} as const; + +export type MasterAsyncEvents = typeof MasterAsyncEvents; + +export type MasterAsyncEvent = ValueOf; + +export const RunnerSyncEvents = { + NEW_WORKER_PROCESS: "newWorkerProcess", + + SUITE_BEGIN: "beginSuite", + SUITE_END: "endSuite", + + TEST_BEGIN: "beginTest", + TEST_END: "endTest", + + TEST_PASS: "passTest", + TEST_FAIL: "failTest", + TEST_PENDING: "pendingTest", + + RETRY: "retry", +} as const; + +export type RunnerSyncEvents = typeof RunnerSyncEvents; + +export type RunnerSyncEvent = ValueOf; + +export const CommonSyncEvents = { + CLI: "cli", + + BEGIN: "begin", + END: "end", + + BEFORE_FILE_READ: "beforeFileRead", + AFTER_FILE_READ: "afterFileRead", + + AFTER_TESTS_READ: "afterTestsRead", + + INFO: "info", + WARNING: "warning", + ERROR: "err", +} as const; + +export type CommonSyncEvents = typeof CommonSyncEvents; + +export type CommonSyncEvent = ValueOf; + +export const MasterSyncEvents = { + ...RunnerSyncEvents, + ...CommonSyncEvents, +}; + +export type MasterSyncEvents = typeof MasterSyncEvents; + +export type MasterSyncEvent = RunnerSyncEvent | CommonSyncEvent; + +export const MasterEvents = { + ...MasterAsyncEvents, + ...MasterSyncEvents, +} as const; + +export type MasterEvents = typeof MasterEvents; + +export type MasterEvent = MasterAsyncEvent | MasterSyncEvent; + +export const WorkerEvents = { + INIT: MasterEvents.INIT, + + BEFORE_FILE_READ: MasterEvents.BEFORE_FILE_READ, + AFTER_FILE_READ: MasterEvents.AFTER_FILE_READ, + + AFTER_TESTS_READ: MasterEvents.AFTER_TESTS_READ, + + NEW_BROWSER: "newBrowser", + + UPDATE_REFERENCE: "updateReference", +} as const; + +export type WorkerEvents = typeof WorkerEvents; + +export type WorkerEvent = ValueOf; + +export type InterceptedEvent = ValueOf< + Pick< + typeof RunnerSyncEvents, + "SUITE_BEGIN" | "SUITE_END" | "TEST_BEGIN" | "TEST_END" | "TEST_PASS" | "TEST_FAIL" | "TEST_PENDING" | "RETRY" + > +>; + +export const Events = { + ...MasterEvents, + ...WorkerEvents, +} as const; + +export type Events = typeof Events; diff --git a/src/events/types.ts b/src/events/types.ts new file mode 100644 index 000000000..df841c19c --- /dev/null +++ b/src/events/types.ts @@ -0,0 +1,14 @@ +import { InterceptedEvent } from "."; +import { Test } from "../test-reader/test-object/test"; + +export interface InterceptData { + event?: InterceptedEvent; + data?: Test; +} + +export type InterceptHandler = (arg: InterceptData) => InterceptData | void; + +export interface Interceptor { + event: InterceptedEvent; + handler: InterceptHandler; +} diff --git a/src/hermione.ts b/src/hermione.ts index e21934276..d5318e43c 100644 --- a/src/hermione.ts +++ b/src/hermione.ts @@ -1,75 +1,116 @@ -"use strict"; - -const _ = require("lodash"); - -const RunnerStats = require("./stats"); -const BaseHermione = require("./base-hermione"); -const Runner = require("./runner"); -const RuntimeConfig = require("./config/runtime-config"); -const RunnerEvents = require("./constants/runner-events"); -const eventsUtils = require("./events/utils"); -const signalHandler = require("./signal-handler"); -const TestReader = require("./test-reader"); -const TestCollection = require("./test-collection").default; -const validateUnknownBrowsers = require("./validators").validateUnknownBrowsers; -const { initReporters } = require("./reporters"); -const logger = require("./utils/logger"); - -module.exports = class Hermione extends BaseHermione { - constructor(config) { +import { CommanderStatic } from "@gemini-testing/commander"; +import * as _ from "lodash"; +import { Stats as RunnerStats } from "./stats"; +import { BaseHermione } from "./base-hermione"; +import { MainRunner } from "./runner"; +import RuntimeConfig from "./config/runtime-config"; +import { MasterAsyncEvents, MasterEvents, MasterSyncEvents } from "./events"; +import eventsUtils from "./events/utils"; +import signalHandler from "./signal-handler"; +import TestReader from "./test-reader"; +import { TestCollection } from "./test-collection"; +import { validateUnknownBrowsers } from "./validators"; +import { initReporters } from "./reporters"; +import logger from "./utils/logger"; +import { ConfigInput } from "./config/types"; +import { MasterEventHandler, Test } from "./types"; + +interface RunOpts { + browsers?: string[]; + sets?: string[]; + grep?: string; + updateRefs?: boolean; + requireModules?: string[]; + inspectMode?: { + inspect: boolean; + inspectBrk: boolean; + }; + reporters?: string[]; +} + +interface ReadTestsOpts { + browsers: string[]; + sets: string[]; + grep: string | RegExp; + silent: boolean; + ignore: string | string[]; +} + +export interface Hermione { + on: MasterEventHandler; + once: MasterEventHandler; + prependListener: MasterEventHandler; +} + +export class Hermione extends BaseHermione { + protected failed: boolean; + protected runner: MainRunner | null; + + constructor(config?: string | ConfigInput) { super(config); - this._failed = false; + this.failed = false; + this.runner = null; } - extendCli(parser) { - this.emit(RunnerEvents.CLI, parser); + extendCli(parser: CommanderStatic): void { + this.emit(MasterEvents.CLI, parser); } - async run(testPaths, { browsers, sets, grep, updateRefs, requireModules, inspectMode, reporters = [] } = {}) { + async run( + testPaths: TestCollection | string[], + { browsers, sets, grep, updateRefs, requireModules, inspectMode, reporters = [] }: Partial = {}, + ): Promise { validateUnknownBrowsers(browsers, _.keys(this._config.browsers)); RuntimeConfig.getInstance().extend({ updateRefs, requireModules, inspectMode }); - this._runner = Runner.create(this._config, this._interceptors); + const runner = MainRunner.create(this._config, this._interceptors); + this.runner = runner; - this.on(RunnerEvents.TEST_FAIL, () => this._fail()).on(RunnerEvents.ERROR, err => this.halt(err)); + this.on(MasterEvents.TEST_FAIL, () => this._fail()).on(MasterEvents.ERROR, (err: Error) => this.halt(err)); await initReporters(reporters, this); - eventsUtils.passthroughEvent(this._runner, this, _.values(RunnerEvents.getSync())); - eventsUtils.passthroughEventAsync(this._runner, this, _.values(RunnerEvents.getAsync())); - eventsUtils.passthroughEventAsync(signalHandler, this, RunnerEvents.EXIT); + eventsUtils.passthroughEvent(this.runner, this, _.values(MasterSyncEvents)); + eventsUtils.passthroughEventAsync(this.runner, this, _.values(MasterAsyncEvents)); + eventsUtils.passthroughEventAsync(signalHandler, this, MasterEvents.EXIT); await this._init(); - this._runner.init(); - await this._runner.run(await this._readTests(testPaths, { browsers, sets, grep }), RunnerStats.create(this)); + runner.init(); + await runner.run(await this._readTests(testPaths, { browsers, sets, grep }), RunnerStats.create(this)); return !this.isFailed(); } - async _readTests(testPaths, opts) { + protected async _readTests( + testPaths: string[] | TestCollection, + opts: Partial, + ): Promise { return testPaths instanceof TestCollection ? testPaths : await this.readTests(testPaths, opts); } - addTestToRun(test, browserId) { - return this._runner ? this._runner.addTestToRun(test, browserId) : false; + addTestToRun(test: Test, browserId: string): boolean { + return this.runner ? this.runner.addTestToRun(test, browserId) : false; } - async readTests(testPaths, { browsers, sets, grep, silent, ignore } = {}) { + async readTests( + testPaths: string[], + { browsers, sets, grep, silent, ignore }: Partial = {}, + ): Promise { const testReader = TestReader.create(this._config); if (!silent) { await this._init(); eventsUtils.passthroughEvent(testReader, this, [ - RunnerEvents.BEFORE_FILE_READ, - RunnerEvents.AFTER_FILE_READ, + MasterEvents.BEFORE_FILE_READ, + MasterEvents.AFTER_FILE_READ, ]); } const specs = await testReader.read({ paths: testPaths, browsers, ignore, sets, grep }); - const collection = TestCollection.create(specs, this._config); + const collection = TestCollection.create(specs); collection.getBrowsers().forEach(bro => { if (this._config.forBrowser(bro).strictTestsOrder) { @@ -78,25 +119,25 @@ module.exports = class Hermione extends BaseHermione { }); if (!silent) { - this.emit(RunnerEvents.AFTER_TESTS_READ, collection); + this.emit(MasterEvents.AFTER_TESTS_READ, collection); } return collection; } - isFailed() { - return this._failed; + isFailed(): boolean { + return this.failed; } - _fail() { - this._failed = true; + protected _fail(): void { + this.failed = true; } - isWorker() { + isWorker(): boolean { return false; } - halt(err, timeout = 60000) { + halt(err: Error, timeout = 60000): void { logger.error("Terminating on critical error:", err); this._fail(); @@ -108,8 +149,8 @@ module.exports = class Hermione extends BaseHermione { }, timeout).unref(); } - if (this._runner) { - this._runner.cancel(); + if (this.runner) { + this.runner.cancel(); } } -}; +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 000000000..06c6bb55b --- /dev/null +++ b/src/index.ts @@ -0,0 +1,17 @@ +// Declares global hooks +import "../typings/global"; +// Augments browser and element methods +import "./browser/types"; +// Declares global expect function +import "expect-webdriverio"; + +import { GlobalHelper } from "./types"; +export { Hermione as default } from "./hermione"; + +export type { WdioBrowser } from "./types"; +export type { Config } from "./config"; +export type { ConfigInput } from "./config/types"; + +declare global { + const hermione: GlobalHelper; +} diff --git a/src/reporters/base.js b/src/reporters/base.js index 921642d2b..04c2e2124 100644 --- a/src/reporters/base.js +++ b/src/reporters/base.js @@ -1,7 +1,7 @@ "use strict"; const chalk = require("chalk"); -const RunnerEvents = require("../constants/runner-events"); +const { MasterEvents } = require("../events"); const icons = require("./utils/icons"); const helpers = require("./utils/helpers"); const { initInformer } = require("./informers"); @@ -18,15 +18,15 @@ module.exports = class BaseReporter { } attachRunner(runner) { - runner.on(RunnerEvents.TEST_PASS, test => this._onTestPass(test)); - runner.on(RunnerEvents.TEST_FAIL, test => this._onTestFail(test)); - runner.on(RunnerEvents.RETRY, test => this._onRetry(test)); - runner.on(RunnerEvents.TEST_PENDING, test => this._onTestPending(test)); - runner.on(RunnerEvents.RUNNER_END, stats => this._onRunnerEnd(stats)); - - runner.on(RunnerEvents.WARNING, info => this._onWarning(info)); - runner.on(RunnerEvents.ERROR, error => this._onError(error)); - runner.on(RunnerEvents.INFO, info => this._onInfo(info)); + runner.on(MasterEvents.TEST_PASS, test => this._onTestPass(test)); + runner.on(MasterEvents.TEST_FAIL, test => this._onTestFail(test)); + runner.on(MasterEvents.RETRY, test => this._onRetry(test)); + runner.on(MasterEvents.TEST_PENDING, test => this._onTestPending(test)); + runner.on(MasterEvents.RUNNER_END, stats => this._onRunnerEnd(stats)); + + runner.on(MasterEvents.WARNING, info => this._onWarning(info)); + runner.on(MasterEvents.ERROR, error => this._onError(error)); + runner.on(MasterEvents.INFO, info => this._onInfo(info)); } _onTestPass(test) { diff --git a/src/runner/browser-runner.ts b/src/runner/browser-runner.ts index 40bc57cfb..583a3fc3e 100644 --- a/src/runner/browser-runner.ts +++ b/src/runner/browser-runner.ts @@ -1,84 +1,96 @@ -"use strict"; - -const _ = require("lodash"); - -const Runner = require("./runner"); -const TestRunner = require("./test-runner"); -const Events = require("../constants/runner-events"); -const SuiteMonitor = require("./suite-monitor"); -const BrowserAgent = require("./browser-agent"); -const PromiseGroup = require("./promise-group"); - -module.exports = class BrowserRunner extends Runner { - constructor(browserId, config, browserPool, workers) { +import { EventEmitter } from "events"; +import _ from "lodash"; +import { Runner } from "./runner"; +import * as TestRunner from "./test-runner"; +import { InterceptedEvent, MasterEvents } from "../events"; +import SuiteMonitor from "./suite-monitor"; +import BrowserAgent from "./browser-agent"; +import PromiseGroup from "./promise-group"; +import { Config } from "../config"; +import { BrowserPool } from "../browser-pool"; +import { Workers } from "./index"; +import type { Test } from "../types"; +import { TestCollection } from "../test-collection"; + +export interface BrowserRunner { + on(event: InterceptedEvent, handler: (test: Test) => void): this; +} + +export class BrowserRunner extends Runner { + private _browserId: string; + private config: Config; + private browserPool: BrowserPool; + private suiteMonitor: SuiteMonitor; + private activeTestRunners: Set; + private workers: Workers; + private running: PromiseGroup; + + constructor(browserId: string, config: Config, browserPool: BrowserPool, workers: Workers) { super(); - this._browserId = browserId; - this._config = config; - this._browserPool = browserPool; - - this._suiteMonitor = SuiteMonitor.create(); - this._passthroughEvents(this._suiteMonitor, [Events.SUITE_BEGIN, Events.SUITE_END]); - - this._activeTestRunners = new Set(); - this._workers = workers; - this._running = new PromiseGroup(); + this.config = config; + this.browserPool = browserPool; + this.suiteMonitor = SuiteMonitor.create(); + this.passthroughEvents(this.suiteMonitor, [MasterEvents.SUITE_BEGIN, MasterEvents.SUITE_END]); + this.activeTestRunners = new Set(); + this.workers = workers; + this.running = new PromiseGroup(); } - get browserId() { + get browserId(): string { return this._browserId; } - async run(testCollection) { - testCollection.eachTestByVersions(this._browserId, test => { - this._running.add(this._runTest(test)); + async run(testCollection: TestCollection): Promise { + testCollection.eachTestByVersions(this._browserId, (test: Test) => { + this.running.add(this._runTest(test)); }); - await this._running.done(); + await this.running.done(); } - addTestToRun(test) { - if (this._running.isFulfilled()) { + addTestToRun(test: Test): boolean { + if (this.running.isFulfilled()) { return false; } - this._running.add(this._runTest(test)); + this.running.add(this._runTest(test)); return true; } - async _runTest(test) { - const browserAgent = BrowserAgent.create(this._browserId, test.browserVersion, this._browserPool); - const runner = TestRunner.create(test, this._config, browserAgent); + private async _runTest(test: Test): Promise { + const browserAgent = BrowserAgent.create(this._browserId, test.browserVersion, this.browserPool); + const runner = TestRunner.create(test, this.config, browserAgent); - runner.on(Events.TEST_BEGIN, test => this._suiteMonitor.testBegin(test)); + runner.on(MasterEvents.TEST_BEGIN, (test: Test) => this.suiteMonitor.testBegin(test)); - this._passthroughEvents(runner, [ - Events.TEST_BEGIN, - Events.TEST_END, - Events.TEST_PASS, - Events.TEST_FAIL, - Events.TEST_PENDING, - Events.RETRY, + this.passthroughEvents(runner, [ + MasterEvents.TEST_BEGIN, + MasterEvents.TEST_END, + MasterEvents.TEST_PASS, + MasterEvents.TEST_FAIL, + MasterEvents.TEST_PENDING, + MasterEvents.RETRY, ]); - runner.on(Events.TEST_END, test => this._suiteMonitor.testEnd(test)); - runner.on(Events.RETRY, test => this._suiteMonitor.testRetry(test)); + runner.on(MasterEvents.TEST_END, (test: Test) => this.suiteMonitor.testEnd(test)); + runner.on(MasterEvents.RETRY, (test: Test) => this.suiteMonitor.testRetry(test)); - this._activeTestRunners.add(runner); + this.activeTestRunners.add(runner); - await runner.run(this._workers); + await runner.run(this.workers); - this._activeTestRunners.delete(runner); + this.activeTestRunners.delete(runner); } - cancel() { - this._activeTestRunners.forEach(runner => runner.cancel()); + cancel(): void { + this.activeTestRunners.forEach(runner => runner.cancel()); } - _passthroughEvents(runner, events) { + private passthroughEvents(runner: EventEmitter, events: InterceptedEvent[]): void { events.forEach(event => { - runner.on(event, data => this.emit(event, _.extend(data, { browserId: this._browserId }))); + runner.on(event, (data: unknown) => this.emit(event, _.extend(data, { browserId: this._browserId }))); }); } -}; +} diff --git a/src/runner/index.ts b/src/runner/index.ts index 6045a2714..94e36e6a8 100644 --- a/src/runner/index.ts +++ b/src/runner/index.ts @@ -1,147 +1,181 @@ -"use strict"; - -const _ = require("lodash"); -const eventsUtils = require("../events/utils"); -const temp = require("../temp"); - -const pool = require("../browser-pool"); -const BrowserRunner = require("./browser-runner"); -const Events = require("../constants/runner-events"); -const Runner = require("./runner"); -const RuntimeConfig = require("../config/runtime-config"); -const WorkersRegistry = require("../utils/workers-registry"); -const PromiseGroup = require("./promise-group"); -const TestCollection = require("../test-collection").default; -const logger = require("../utils/logger"); - -module.exports = class MainRunner extends Runner { - constructor(config, interceptors) { +import _ from "lodash"; +import * as eventsUtils from "../events/utils"; +import * as temp from "../temp"; +import * as pool from "../browser-pool"; +import { BrowserRunner } from "./browser-runner"; +import { + RunnerSyncEvents, + MasterEvents, + InterceptedEvent, + RunnerSyncEvent, + Interceptor, + InterceptData, +} from "../events"; +import { Runner } from "./runner"; +import RuntimeConfig from "../config/runtime-config"; +import WorkersRegistry from "../utils/workers-registry"; +import PromiseGroup from "./promise-group"; +import { TestCollection } from "../test-collection"; +import * as logger from "../utils/logger"; +import { Config } from "../config"; +import type { runTest } from "../worker"; +import type { Stats as RunnerStats } from "../stats"; +import EventEmitter from "events"; +import { Test } from "../types"; + +interface WorkerMethods { + runTest: typeof runTest; +} + +export interface Workers extends EventEmitter, WorkerMethods {} + +type MapOfMethods> = { + [K in T[number]]: (...args: Array) => Promise | unknown; +}; + +type RegisterWorkers> = EventEmitter & MapOfMethods; + +export class MainRunner extends Runner { + protected config: Config; + protected interceptors: Interceptor[]; + protected browserPool: pool.BrowserPool | null; + protected activeBrowserRunners: Map; + protected running: PromiseGroup; + protected runned: boolean; + protected cancelled: boolean; + protected workersRegistry: WorkersRegistry; + protected workers: Workers | null; + + constructor(config: Config, interceptors: Interceptor[]) { super(); - this._config = config; - this._interceptors = interceptors; - this._browserPool = null; + this.config = config; + this.interceptors = interceptors; + this.browserPool = null; - this._activeBrowserRunners = new Map(); + this.activeBrowserRunners = new Map(); - this._running = new PromiseGroup(); - this._runned = false; - this._cancelled = false; + this.running = new PromiseGroup(); + this.runned = false; + this.cancelled = false; - this._workersRegistry = WorkersRegistry.create(this._config); - this._workers = null; - eventsUtils.passthroughEvent(this._workersRegistry, this, [Events.NEW_WORKER_PROCESS, Events.ERROR]); + this.workersRegistry = WorkersRegistry.create(this.config); + this.workers = null; + eventsUtils.passthroughEvent(this.workersRegistry, this, [MasterEvents.NEW_WORKER_PROCESS, MasterEvents.ERROR]); - temp.init(this._config.system.tempDir); + temp.init(this.config.system.tempDir); RuntimeConfig.getInstance().extend({ tempOpts: temp.serialize() }); } - init() { - if (this._workers) { + init(): void { + if (this.workers) { return; } - this._workersRegistry.init(); - this._workers = this._workersRegistry.register(require.resolve("../worker"), ["runTest"]); - this._browserPool = pool.create(this._config, this); + this.workersRegistry.init(); + this.workers = this.workersRegistry.register(require.resolve("../worker"), ["runTest"]) as Workers; + this.browserPool = pool.create(this.config, this); } - _isRunning() { - return this._runned && !this._workersRegistry.isEnded() && !this._cancelled; + _isRunning(): boolean { + return this.runned && !this.workersRegistry.isEnded() && !this.cancelled; } - async run(testCollection, stats) { - this._runned = true; + async run(testCollection: TestCollection, stats: RunnerStats): Promise { + this.runned = true; try { - await this.emitAndWait(Events.RUNNER_START, this); - this.emit(Events.BEGIN); - !this._cancelled && (await this._runTests(testCollection)); + await this.emitAndWait(MasterEvents.RUNNER_START, this); + this.emit(MasterEvents.BEGIN); + !this.cancelled && (await this._runTests(testCollection)); } finally { - this.emit(Events.END); - await this.emitAndWait(Events.RUNNER_END, stats.getResult()).catch(logger.warn); - await this._workersRegistry.end(); + this.emit(MasterEvents.END); + await this.emitAndWait(MasterEvents.RUNNER_END, stats.getResult()).catch(logger.warn); + await this.workersRegistry.end(); } } - addTestToRun(test, browserId) { - if (!this._isRunning() || this._running.isFulfilled()) { + addTestToRun(test: Test, browserId: string): boolean { + if (!this._isRunning() || this.running.isFulfilled()) { return false; } - const runner = this._activeBrowserRunners.get(browserId); + const runner = this.activeBrowserRunners.get(browserId); if (runner && runner.addTestToRun(test)) { return true; } - const collection = TestCollection.create({ [browserId]: [test] }, this._config); - this._running.add(this._runTestsInBrowser(collection, browserId)); + const collection = TestCollection.create({ [browserId]: [test] }); + this.running.add(this._runTestsInBrowser(collection, browserId)); return true; } - _runTests(testCollection) { - testCollection.getBrowsers().forEach(browserId => { - this._running.add(this._runTestsInBrowser(testCollection, browserId)); + protected async _runTests(testCollection: TestCollection): Promise { + testCollection.getBrowsers().forEach((browserId: string) => { + this.running.add(this._runTestsInBrowser(testCollection, browserId)); }); - return this._running.done(); + return this.running.done(); } - async _runTestsInBrowser(testCollection, browserId) { - const runner = BrowserRunner.create(browserId, this._config, this._browserPool, this._workers); + protected async _runTestsInBrowser(testCollection: TestCollection, browserId: string): Promise { + const runner = BrowserRunner.create(browserId, this.config, this.browserPool, this.workers); - eventsUtils.passthroughEvent(runner, this, this._getEventsToPassthrough()); - this._interceptEvents(runner, this._getEventsToIntercept()); + eventsUtils.passthroughEvent(runner, this, this.getEventsToPassthrough()); + this.interceptEvents(runner, this.getEventsToIntercept()); - this._activeBrowserRunners.set(browserId, runner); + this.activeBrowserRunners.set(browserId, runner); await runner.run(testCollection); - this._activeBrowserRunners.delete(browserId); + this.activeBrowserRunners.delete(browserId); } - _getEventsToPassthrough() { - return _(Events.getRunnerSync()).values().difference(this._getEventsToIntercept()).value(); + protected getEventsToPassthrough(): RunnerSyncEvent[] { + return _(RunnerSyncEvents).values().difference(this.getEventsToIntercept()).value(); } - _getEventsToIntercept() { - return _(this._interceptors).map("event").uniq().value(); + protected getEventsToIntercept(): InterceptedEvent[] { + return _(this.interceptors).map("event").uniq().value(); } - _interceptEvents(runner, events) { - events.forEach(event => { + protected interceptEvents(runner: BrowserRunner, events: InterceptedEvent[]): void { + events.forEach((event: InterceptedEvent) => { runner.on(event, data => { try { - const toEmit = this._applyInterceptors({ event, data }, this._interceptors); + const toEmit = this.applyInterceptors({ event, data }, this.interceptors); toEmit && toEmit.event && this.emit(toEmit.event, toEmit.data); } catch (e) { - this.emit(Events.ERROR, e); + this.emit(MasterEvents.ERROR, e); } }); }); } - _applyInterceptors({ event, data } = {}, interceptors) { + protected applyInterceptors( + { event, data }: Partial = {}, + interceptors: Interceptor[], + ): Partial { const interceptor = _.find(interceptors, { event }); if (!interceptor) { return { event, data }; } - return this._applyInterceptors( + return this.applyInterceptors( interceptor.handler({ event, data }) || { event, data }, _.without(interceptors, interceptor), ); } - cancel() { - this._cancelled = true; - this._browserPool.cancel(); + cancel(): void { + this.cancelled = true; + this.browserPool?.cancel(); - this._activeBrowserRunners.forEach(runner => runner.cancel()); + this.activeBrowserRunners.forEach(runner => runner.cancel()); } - registerWorkers(workerFilepath, exportedMethods) { - return this._workersRegistry.register(workerFilepath, exportedMethods); + registerWorkers>(workerFilepath: string, exportedMethods: T): RegisterWorkers { + return this.workersRegistry.register(workerFilepath, exportedMethods) as RegisterWorkers; } -}; +} diff --git a/src/runner/runner.ts b/src/runner/runner.ts index d93702167..764e18c65 100644 --- a/src/runner/runner.ts +++ b/src/runner/runner.ts @@ -1,16 +1,12 @@ -"use strict"; +import { Constructor } from "type-fest"; +import { AsyncEmitter } from "../events"; -const AsyncEmitter = require("../events/async-emitter"); - -module.exports = class Runner extends AsyncEmitter { - static create(...args) { +export abstract class Runner extends AsyncEmitter { + static create(this: Constructor, ...args: unknown[]): T { return new this(...args); } - run() { - throw new Error("Not implemented"); - } + abstract run(...args: unknown[]): Promise; - // eslint-disable-next-line @typescript-eslint/no-empty-function - cancel() {} -}; + abstract cancel(): void; +} diff --git a/src/runner/suite-monitor.js b/src/runner/suite-monitor.js index 5ae624b7c..0b41bcc69 100644 --- a/src/runner/suite-monitor.js +++ b/src/runner/suite-monitor.js @@ -1,7 +1,7 @@ "use strict"; const { EventEmitter } = require("events"); -const Events = require("../constants/runner-events"); +const { MasterEvents } = require("../events"); module.exports = class SuiteMonitor extends EventEmitter { static create() { @@ -26,7 +26,7 @@ module.exports = class SuiteMonitor extends EventEmitter { this._addTest(suite.parent); if (!this._suites.has(suite)) { - this.emit(Events.SUITE_BEGIN, suite); + this.emit(MasterEvents.SUITE_BEGIN, suite); this._suites.set(suite, { runningTests: 1, retries: 0 }); } else { const suiteInfo = this._suites.get(suite); @@ -49,7 +49,7 @@ module.exports = class SuiteMonitor extends EventEmitter { const suiteInfo = this._suites.get(suite); if (--suiteInfo.runningTests === 0 && suiteInfo.retries === 0) { this._suites.delete(suite); - this.emit(Events.SUITE_END, suite); + this.emit(MasterEvents.SUITE_END, suite); } this._rmTest(suite.parent); diff --git a/src/runner/test-runner/index.ts b/src/runner/test-runner/index.ts index 904e23606..3b4a30220 100644 --- a/src/runner/test-runner/index.ts +++ b/src/runner/test-runner/index.ts @@ -1,9 +1,12 @@ -"use strict"; +import SkippedTestRunner from "./skipped-test-runner"; +import InsistantTestRunner from "./insistant-test-runner"; +import { Config } from "../../config"; +import type { Test } from "../../types"; +import type BrowserAgent from "../browser-agent"; -const SkippedTestRunner = require("./skipped-test-runner"); -const InsistantTestRunner = require("./insistant-test-runner"); +export type TestRunner = SkippedTestRunner | InsistantTestRunner; -exports.create = function (test, config, browserAgent) { +export const create = function (test: Test, config: Config, browserAgent: BrowserAgent): TestRunner { return test.pending || test.disabled ? SkippedTestRunner.create(test) : InsistantTestRunner.create(test, config, browserAgent); diff --git a/src/runner/test-runner/insistant-test-runner.js b/src/runner/test-runner/insistant-test-runner.js index b2784b47c..bffa5025a 100644 --- a/src/runner/test-runner/insistant-test-runner.js +++ b/src/runner/test-runner/insistant-test-runner.js @@ -2,12 +2,12 @@ const _ = require("lodash"); -const Runner = require("../runner"); +const { Runner } = require("../runner"); const RegularTestRunner = require("./regular-test-runner"); const HighPriorityBrowserAgent = require("./high-priority-browser-agent"); -const Events = require("../../constants/runner-events"); +const { MasterEvents } = require("../../events"); const { passthroughEvent } = require("../../events/utils"); -const NoRefImageError = require("../../browser/commands/assert-view/errors/no-ref-image-error"); +const { NoRefImageError } = require("../../browser/commands/assert-view/errors/no-ref-image-error"); module.exports = class InsistantTestRunner extends Runner { constructor(test, config, browserAgent) { @@ -28,16 +28,16 @@ module.exports = class InsistantTestRunner extends Runner { const browserAgent = this._retriesPerformed > 0 ? HighPriorityBrowserAgent.create(this._browserAgent) : this._browserAgent; - const runner = RegularTestRunner.create(this._test, browserAgent).on(Events.TEST_FAIL, data => { + const runner = RegularTestRunner.create(this._test, browserAgent).on(MasterEvents.TEST_FAIL, data => { if (this._shouldRetry(data)) { - this.emit(Events.RETRY, _.extend(data, { retriesLeft: this._retriesLeft })); + this.emit(MasterEvents.RETRY, _.extend(data, { retriesLeft: this._retriesLeft })); retry = true; } else { - this.emit(Events.TEST_FAIL, data); + this.emit(MasterEvents.TEST_FAIL, data); } }); - passthroughEvent(runner, this, [Events.TEST_BEGIN, Events.TEST_PASS, Events.TEST_END]); + passthroughEvent(runner, this, [MasterEvents.TEST_BEGIN, MasterEvents.TEST_PASS, MasterEvents.TEST_END]); await runner.run(workers); diff --git a/src/runner/test-runner/regular-test-runner.js b/src/runner/test-runner/regular-test-runner.js index c737bf4bd..449bd9c9a 100644 --- a/src/runner/test-runner/regular-test-runner.js +++ b/src/runner/test-runner/regular-test-runner.js @@ -1,9 +1,9 @@ "use strict"; const _ = require("lodash"); -const Runner = require("../runner"); +const { Runner } = require("../runner"); const logger = require("../../utils/logger"); -const Events = require("../../constants/runner-events"); +const { MasterEvents } = require("../../events"); const AssertViewResults = require("../../browser/commands/assert-view/assert-view-results"); module.exports = class RegularTestRunner extends Runner { @@ -27,23 +27,23 @@ module.exports = class RegularTestRunner extends Runner { }); } - this._emit(Events.TEST_BEGIN); + this._emit(MasterEvents.TEST_BEGIN); this._test.startTime = Date.now(); const results = await this._runTest(workers); this._applyTestResults(results); - this._emit(Events.TEST_PASS); + this._emit(MasterEvents.TEST_PASS); } catch (error) { this._test.err = error; this._applyTestResults(error); - this._emit(Events.TEST_FAIL); + this._emit(MasterEvents.TEST_FAIL); } - this._emit(Events.TEST_END); + this._emit(MasterEvents.TEST_END); await (freeBrowserPromise || this._freeBrowser()); } diff --git a/src/runner/test-runner/skipped-test-runner.js b/src/runner/test-runner/skipped-test-runner.js index 9e64a2d0b..e5efe4e9d 100644 --- a/src/runner/test-runner/skipped-test-runner.js +++ b/src/runner/test-runner/skipped-test-runner.js @@ -1,7 +1,7 @@ "use strict"; -const Runner = require("../runner"); -const Events = require("../../constants/runner-events"); +const { Runner } = require("../runner"); +const { MasterEvents } = require("../../events"); module.exports = class SkippedTestRunner extends Runner { constructor(test) { @@ -15,9 +15,9 @@ module.exports = class SkippedTestRunner extends Runner { return; } - this.emit(Events.TEST_BEGIN, this._test); - this.emit(Events.TEST_PENDING, this._test); - this.emit(Events.TEST_END, this._test); + this.emit(MasterEvents.TEST_BEGIN, this._test); + this.emit(MasterEvents.TEST_PENDING, this._test); + this.emit(MasterEvents.TEST_END, this._test); } _isSilentlySkipped({ silentSkip, parent }) { diff --git a/src/signal-handler.js b/src/signal-handler.js index fc18615d4..462eff9a0 100644 --- a/src/signal-handler.js +++ b/src/signal-handler.js @@ -1,6 +1,6 @@ "use strict"; -const AsyncEmitter = require("./events/async-emitter"); +const { AsyncEmitter } = require("./events/async-emitter"); const { log } = require("./utils/logger"); const signalHandler = new AsyncEmitter(); diff --git a/src/stats.ts b/src/stats.ts index e9809d028..561da7db6 100644 --- a/src/stats.ts +++ b/src/stats.ts @@ -1,32 +1,52 @@ -"use strict"; +import _ from "lodash"; +import { MasterEvents } from "./events"; +import { Hermione } from "./hermione"; -const _ = require("lodash"); -const RunnerEvents = require("./constants/runner-events"); +import type { Test } from "./types"; -module.exports = class Stats { - static create(...args) { - return new this(...args); - } +export interface StatsResult { + total: number; + passed: number; + failed: number; + retries: number; + skipped: number; + perBrowser: Record>; +} + +type GroupName = Exclude; + +type StatEvent = { + group: GroupName; + id: string; + browserId: string; +}; - constructor(runner) { - this._events = []; +export class Stats { + private events: StatEvent[] = []; + static create(this: new (runner?: Hermione) => T, runner?: Hermione): T { + return new this(runner); + } + + constructor(runner?: Hermione) { const pushEvent_ = - group => - ({ id, browserId }) => - this._events.push({ group, id, browserId }); + (group: GroupName) => + ({ id, browserId }: Test): void => { + this.events.push({ group, id, browserId }); + }; - runner && + if (runner) { runner - .on(RunnerEvents.TEST_PASS, pushEvent_("passed")) - .on(RunnerEvents.TEST_FAIL, pushEvent_("failed")) - .on(RunnerEvents.RETRY, pushEvent_("retries")) - .on(RunnerEvents.TEST_PENDING, pushEvent_("skipped")); + .on(MasterEvents.TEST_PASS, pushEvent_("passed")) + .on(MasterEvents.TEST_FAIL, pushEvent_("failed")) + .on(MasterEvents.RETRY, pushEvent_("retries")) + .on(MasterEvents.TEST_PENDING, pushEvent_("skipped")); + } } - getResult() { - const emptyStat = { passed: 0, failed: 0, retries: 0, skipped: 0, total: 0 }; - const statsByBrowser = _(this._events) + getResult(): StatsResult { + const emptyStat: Partial = { passed: 0, failed: 0, retries: 0, skipped: 0, total: 0 }; + const statsByBrowser = _(this.events) .groupBy("browserId") .mapValues(events => { const stats = _(events).groupBy("group").mapValues("length").value(); @@ -39,8 +59,8 @@ module.exports = class Stats { }) .value(); - const overall = _.mergeWith(emptyStat, ...Object.values(statsByBrowser), (a, b) => a + b); + const overall = _.mergeWith(emptyStat, ...Object.values(statsByBrowser), (a: number, b: number) => a + b); return { ...overall, perBrowser: statsByBrowser }; } -}; +} diff --git a/src/test-collection.ts b/src/test-collection.ts index 0c7c96fbe..40622eaca 100644 --- a/src/test-collection.ts +++ b/src/test-collection.ts @@ -1,18 +1,19 @@ import _ from "lodash"; -import type { Suite } from "./test-reader/test-object/suite"; -import type { Test } from "./test-reader/test-object/test"; +import type { Suite, RootSuite, Test } from "./types"; -type RootSuite = Suite & { root: true }; type TestDisabled = Test & { disabled: true }; type TestsCallback = (test: Test, browserId: string) => T; type SortTestsCallback = (test1: Test, test2: Test) => number; -export default class TestCollection { +export class TestCollection { readonly #specs: Record; readonly #originalSpecs: Record; - static create(specs: Record) { + static create( + this: new (specs: Record) => T, + specs: Record, + ): T { return new this(specs); } diff --git a/src/test-reader/controllers/browser-version-controller.ts b/src/test-reader/controllers/browser-version-controller.ts index 9c9cb3f05..4928939d3 100644 --- a/src/test-reader/controllers/browser-version-controller.ts +++ b/src/test-reader/controllers/browser-version-controller.ts @@ -1,20 +1,29 @@ -const ReadEvents = require("../read-events"); +import { TestReaderEvents as ReadEvents } from "../../events"; +import { EventEmitter } from "events"; -class BrowserVersionController { - #browserId; - #eventBus; +type TreeBuilder = { + addTrap: (trap: (obj: { browserId: string; browserVersion?: string }) => void) => void; +}; + +export class BrowserVersionController { + #browserId: string; + #eventBus: EventEmitter; - static create(...args) { - return new this(...args); + static create( + this: new (browserId: string, eventBug: EventEmitter) => T, + browserId: string, + eventBug: EventEmitter, + ): T { + return new this(browserId, eventBug); } - constructor(browserId, eventBus) { + constructor(browserId: string, eventBus: EventEmitter) { this.#browserId = browserId; this.#eventBus = eventBus; } - version(browserVersion) { - this.#eventBus.emit(ReadEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }) => { + version(browserVersion: string): this { + this.#eventBus.emit(ReadEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }: { treeBuilder: TreeBuilder }) => { treeBuilder.addTrap(obj => { if (obj.browserId === this.#browserId) { obj.browserVersion = browserVersion; @@ -26,7 +35,10 @@ class BrowserVersionController { } } -function mkProvider(knownBrowsers, eventBus) { +export function mkProvider( + knownBrowsers: string[], + eventBus: EventEmitter, +): (browserId: string) => BrowserVersionController { return browserId => { if (!knownBrowsers.includes(browserId)) { throw new Error(`browser "${browserId}" was not found in config file`); @@ -35,8 +47,3 @@ function mkProvider(knownBrowsers, eventBus) { return BrowserVersionController.create(browserId, eventBus); }; } - -module.exports = { - mkProvider, - BrowserVersionController, -}; diff --git a/src/test-reader/controllers/config-controller.ts b/src/test-reader/controllers/config-controller.ts index 9906c22fd..f66a7dd4f 100644 --- a/src/test-reader/controllers/config-controller.ts +++ b/src/test-reader/controllers/config-controller.ts @@ -1,25 +1,26 @@ -const ReadEvents = require("../read-events"); +import { TestReaderEvents as ReadEvents } from "../../events"; +import { EventEmitter } from "events"; -class ConfigController { - #eventBus; +type TreeBuilder = { + addTrap: (trap: (obj: { timeout: number }) => void) => void; +}; + +export class ConfigController { + #eventBus: EventEmitter; - static create(...args) { - return new this(...args); + static create(this: new (eventBus: EventEmitter) => T, eventBus: EventEmitter): T { + return new this(eventBus); } - constructor(eventBus) { + constructor(eventBus: EventEmitter) { this.#eventBus = eventBus; } - testTimeout(timeout) { - this.#eventBus.emit(ReadEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }) => { + testTimeout(timeout: number): this { + this.#eventBus.emit(ReadEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }: { treeBuilder: TreeBuilder }) => { treeBuilder.addTrap(obj => (obj.timeout = timeout)); }); return this; } } - -module.exports = { - ConfigController, -}; diff --git a/src/test-reader/controllers/only-controller.ts b/src/test-reader/controllers/only-controller.ts index ab9d3c2e8..bc440be0f 100644 --- a/src/test-reader/controllers/only-controller.ts +++ b/src/test-reader/controllers/only-controller.ts @@ -1,30 +1,35 @@ -const ReadEvents = require("../read-events"); +import { TestReaderEvents as ReadEvents } from "../../events"; +import { EventEmitter } from "events"; -class OnlyController { - #eventBus; +type TreeBuilder = { + addTrap: (trap: (obj: { browserId: string; disable: () => void }) => void) => void; +}; + +export class OnlyController { + #eventBus: EventEmitter; - static create(...args) { - return new this(...args); + static create(this: new (eventBus: EventEmitter) => T, eventBus: EventEmitter): T { + return new this(eventBus); } - constructor(eventBus) { + constructor(eventBus: EventEmitter) { this.#eventBus = eventBus; } - in(matchers) { + in(matchers: string | RegExp | Array): this { this.#addTrap(browserId => this.#match(browserId, matchers)); return this; } - notIn(matchers) { + notIn(matchers: string | RegExp | Array): this { this.#addTrap(browserId => !this.#match(browserId, matchers)); return this; } - #addTrap(match) { - this.#eventBus.emit(ReadEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }) => { + #addTrap(match: (browserId: string) => boolean): void { + this.#eventBus.emit(ReadEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }: { treeBuilder: TreeBuilder }) => { treeBuilder.addTrap(obj => { if (!match(obj.browserId)) { obj.disable(); @@ -33,13 +38,9 @@ class OnlyController { }); } - #match(browserId, matchers) { - return [].concat(matchers).some(m => { + #match(browserId: string, matchers: string | RegExp | Array): boolean { + return ([] as Array).concat(matchers).some(m => { return m instanceof RegExp ? m.test(browserId) : m === browserId; }); } } - -module.exports = { - OnlyController, -}; diff --git a/src/test-reader/controllers/skip-controller.ts b/src/test-reader/controllers/skip-controller.ts index 4636d65bf..899bf42e3 100644 --- a/src/test-reader/controllers/skip-controller.ts +++ b/src/test-reader/controllers/skip-controller.ts @@ -1,30 +1,41 @@ -const ReadEvents = require("../read-events"); +import { Test } from "../../types"; +import { TestReaderEvents as ReadEvents } from "../../events"; +import { EventEmitter } from "events"; -class SkipController { - #eventBus; +interface TreeBuilder { + addTrap: (trap: (test: Test) => void) => void; +} + +interface SkipOpts { + negate?: boolean; + silent?: boolean; +} - static create(...args) { - return new this(...args); +export class SkipController { + #eventBus: EventEmitter; + + static create(this: new (eventBus: EventEmitter) => T, eventBus: EventEmitter): T { + return new this(eventBus); } - constructor(eventBus) { + constructor(eventBus: EventEmitter) { this.#eventBus = eventBus; } - in(matchers, reason, { silent } = {}) { + in(matchers: string | RegExp | Array, reason: string, { silent }: SkipOpts = {}): this { this.#addTrap(browserId => this.#match(matchers, browserId), reason, { silent }); return this; } - notIn(matchers, reason, { silent } = {}) { + notIn(matchers: string | RegExp | Array, reason: string, { silent }: SkipOpts = {}): this { this.#addTrap(browserId => !this.#match(matchers, browserId), reason, { silent }); return this; } - #addTrap(match, reason, { silent } = {}) { - this.#eventBus.emit(ReadEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }) => { + #addTrap(match: (browserId: string) => boolean, reason: string, { silent }: SkipOpts = {}): void { + this.#eventBus.emit(ReadEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }: { treeBuilder: TreeBuilder }) => { treeBuilder.addTrap(obj => { if (!match(obj.browserId)) { return; @@ -39,13 +50,9 @@ class SkipController { }); } - #match(matchers, browserId) { - return [].concat(matchers).some(m => { + #match(matchers: string | RegExp | Array, browserId: string): boolean { + return ([] as Array).concat(matchers).some(m => { return m instanceof RegExp ? m.test(browserId) : m === browserId; }); } } - -module.exports = { - SkipController, -}; diff --git a/src/test-reader/index.js b/src/test-reader/index.js index bb789c60a..96d59b4e0 100644 --- a/src/test-reader/index.js +++ b/src/test-reader/index.js @@ -3,7 +3,7 @@ const { EventEmitter } = require("events"); const { passthroughEvent } = require("../events/utils"); const SetsBuilder = require("./sets-builder"); const { TestParser } = require("./test-parser"); -const Events = require("../constants/runner-events"); +const { MasterEvents } = require("../events"); const env = require("../utils/env"); module.exports = class TestReader extends EventEmitter { @@ -30,7 +30,7 @@ module.exports = class TestReader extends EventEmitter { .build(process.cwd(), { ignore }, fileExtensions); const parser = new TestParser(); - passthroughEvent(parser, this, [Events.BEFORE_FILE_READ, Events.AFTER_FILE_READ]); + passthroughEvent(parser, this, [MasterEvents.BEFORE_FILE_READ, MasterEvents.AFTER_FILE_READ]); await parser.loadFiles(setCollection.getAllFiles(), this.#config); diff --git a/src/test-reader/mocha-reader/index.js b/src/test-reader/mocha-reader/index.js index a3dd1557c..7ff5ea5ee 100644 --- a/src/test-reader/mocha-reader/index.js +++ b/src/test-reader/mocha-reader/index.js @@ -2,8 +2,8 @@ const { MochaEventBus } = require("./mocha-event-bus"); const { TreeBuilderDecorator } = require("./tree-builder-decorator"); -const ReadEvents = require("../read-events"); -const RunnerEvents = require("../../constants/runner-events"); +const { TestReaderEvents } = require("../../events"); +const { MasterEvents } = require("../../events"); const Mocha = require("mocha"); async function readFiles(files, { esmDecorator, config, eventBus }) { @@ -20,7 +20,7 @@ async function readFiles(files, { esmDecorator, config, eventBus }) { } function initBuildContext(outBus) { - outBus.emit(ReadEvents.NEW_BUILD_INSTRUCTION, ctx => { + outBus.emit(TestReaderEvents.NEW_BUILD_INSTRUCTION, ctx => { ctx.treeBuilder = TreeBuilderDecorator.create(ctx.treeBuilder); }); } @@ -49,8 +49,8 @@ function forbidSuiteHooks(bus) { function passthroughFileEvents(inBus, outBus) { [ - [MochaEventBus.events.EVENT_FILE_PRE_REQUIRE, RunnerEvents.BEFORE_FILE_READ], - [MochaEventBus.events.EVENT_FILE_POST_REQUIRE, RunnerEvents.AFTER_FILE_READ], + [MochaEventBus.events.EVENT_FILE_PRE_REQUIRE, MasterEvents.BEFORE_FILE_READ], + [MochaEventBus.events.EVENT_FILE_POST_REQUIRE, MasterEvents.AFTER_FILE_READ], ].forEach(([mochaEvent, ourEvent]) => { inBus.on(mochaEvent, (ctx, file) => outBus.emit(ourEvent, { file })); }); @@ -70,7 +70,9 @@ function registerTestObjects(inBus, outBus) { ], ].forEach(([event, instruction]) => { inBus.on(event, testObject => { - outBus.emit(ReadEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }) => instruction(treeBuilder, testObject)); + outBus.emit(TestReaderEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }) => + instruction(treeBuilder, testObject), + ); }); }); } @@ -88,7 +90,7 @@ function applyOnly(rootSuite, eventBus) { rootSuite.filterOnly(); rootSuite.eachTest(mochaTest => titlesToRun.push(mochaTest.fullTitle())); - eventBus.emit(ReadEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }) => { + eventBus.emit(TestReaderEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }) => { treeBuilder.addTestFilter(test => titlesToRun.includes(test.fullTitle())); }); } diff --git a/src/test-reader/read-events.js b/src/test-reader/read-events.js deleted file mode 100644 index a0b74647b..000000000 --- a/src/test-reader/read-events.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - NEW_BUILD_INSTRUCTION: "newBuildInstruction", -}; diff --git a/src/test-reader/test-parser-api.ts b/src/test-reader/test-parser-api.ts index e299011ba..e4eb3a8d3 100644 --- a/src/test-reader/test-parser-api.ts +++ b/src/test-reader/test-parser-api.ts @@ -1,29 +1,43 @@ -const ReadEvents = require("./read-events"); +import { TestReaderEvents } from "../events"; +import { EventEmitter } from "events"; +import { GlobalHelper } from "../types"; +import type { TreeBuilder } from "./tree-builder"; -module.exports = class TestParserAPI { - #ctx; - #eventBus; +type Context = GlobalHelper & Record>; +type Methods = Record unknown>; - static create(...args) { - return new this(...args); +interface NewBuildEventOpts { + treeBuilder: TreeBuilder; +} + +export class TestParserAPI { + #ctx: Context; + #eventBus: EventEmitter; + + static create( + this: new (ctx: Context, eventBus: EventEmitter) => T, + ctx: Context, + eventBus: EventEmitter, + ): T { + return new this(ctx, eventBus); } - constructor(ctx, eventBus) { + constructor(ctx: Context, eventBus: EventEmitter) { this.#ctx = ctx; this.#eventBus = eventBus; } - setController(namespace, methods) { + setController(namespace: string, methods: Methods): void { this.#ctx[namespace] = {}; Object.entries(methods).forEach(([cbName, cb]) => { - this.#ctx[namespace][cbName] = (...args) => { - this.#eventBus.emit(ReadEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }) => { - treeBuilder.addTrap(obj => cb.call(obj, ...args)); + this.#ctx[namespace][cbName] = (...args: unknown[]): Record => { + this.#eventBus.emit(TestReaderEvents.NEW_BUILD_INSTRUCTION, ({ treeBuilder }: NewBuildEventOpts) => { + treeBuilder.addTrap((obj: unknown) => cb.call(obj, ...args)); }); return this.#ctx[namespace]; }; }); } -}; +} diff --git a/src/test-reader/test-parser.js b/src/test-reader/test-parser.js index e2871b620..1cad5ef3f 100644 --- a/src/test-reader/test-parser.js +++ b/src/test-reader/test-parser.js @@ -6,9 +6,9 @@ const { ConfigController } = require("./controllers/config-controller"); const browserVersionController = require("./controllers/browser-version-controller"); const { TreeBuilder } = require("./tree-builder"); const { readFiles } = require("./mocha-reader"); -const ReadEvents = require("./read-events"); -const TestParserAPI = require("./test-parser-api"); -const RunnerEvents = require("../constants/runner-events"); +const { TestReaderEvents } = require("../events"); +const { TestParserAPI } = require("./test-parser-api"); +const { MasterEvents } = require("../events"); const _ = require("lodash"); const clearRequire = require("clear-require"); const path = require("path"); @@ -56,9 +56,9 @@ class TestParser extends EventEmitter { let currentFile; eventBus - .on(RunnerEvents.BEFORE_FILE_READ, ({ file }) => (currentFile = file)) - .on(RunnerEvents.AFTER_FILE_READ, () => (currentFile = undefined)) - .on(ReadEvents.NEW_BUILD_INSTRUCTION, instruction => + .on(MasterEvents.BEFORE_FILE_READ, ({ file }) => (currentFile = file)) + .on(MasterEvents.AFTER_FILE_READ, () => (currentFile = undefined)) + .on(TestReaderEvents.NEW_BUILD_INSTRUCTION, instruction => this.#buildInstructions.push(instruction, currentFile), ); } @@ -74,8 +74,8 @@ class TestParser extends EventEmitter { ); }; - passthroughEvent_(RunnerEvents.BEFORE_FILE_READ, { testParser: TestParserAPI.create(hermione, eventBus) }); - passthroughEvent_(RunnerEvents.AFTER_FILE_READ); + passthroughEvent_(MasterEvents.BEFORE_FILE_READ, { testParser: TestParserAPI.create(hermione, eventBus) }); + passthroughEvent_(MasterEvents.AFTER_FILE_READ); } #clearRequireCach(files) { diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 000000000..7f0203af0 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,151 @@ +import type { Browser } from "webdriverio"; +import { Events } from "../events"; +import { MainRunner } from "../runner"; +import { TestCollection } from "../test-collection"; +import type { Test } from "../test-reader/test-object/test"; +import type { Suite } from "../test-reader/test-object/suite"; +import type { TestParserAPI } from "../test-reader/test-parser-api"; +import { StatsResult } from "../stats"; +import { ConfigController } from "../test-reader/controllers/config-controller"; +import { OnlyController } from "../test-reader/controllers/only-controller"; +import { SkipController } from "../test-reader/controllers/skip-controller"; +import { BrowserVersionController } from "../test-reader/controllers/browser-version-controller"; +import { WorkerProcess } from "../utils/worker-process"; +import { BaseHermione } from "../base-hermione"; + +export { Suite as RunnerSuite, Test as RunnerTest } from "mocha"; + +export type { Browser as WdioBrowser } from "webdriverio"; + +export type { Test } from "../test-reader/test-object/test"; +export type { Suite } from "../test-reader/test-object/suite"; + +export interface RootSuite extends Suite { + root: true; +} + +export interface BrowserInfo { + browserId: string; + sessionId: string; +} + +export type AsyncSessionEventCallback = (browser: Browser, browserInfo: BrowserInfo) => Promise | void; + +export interface TestError extends Error { + screenshot?: { + base64: string; + }; +} + +export interface ImageSize { + width: number; + height: number; +} + +export interface ImageInfo { + path: string; + size: ImageSize; +} + +export interface AssertViewResultsSuccess { + stateName: string; + refImg: ImageInfo; +} + +export interface CommandHistory { + /** Name: command name */ + n: string; + /** Arguments: array of passed arguments */ + a: unknown[]; + /** Time start */ + ts: number; + /** Time end */ + te: number; + /** Duration */ + d: number; + /** Scope: scope of execution (browser or element) */ + s: "b" | "e"; + /** Children: array of children commands */ + c: CommandHistory[]; +} + +export interface TestResult extends Test { + startTime: number; + duration: number; + assertViewResults: Array; + meta: { [name: string]: unknown }; + hermioneCtx: { + assertViewResults: Array; + }; + history: CommandHistory; + err?: TestError; +} + +export interface TestResultWithRetries extends TestResult { + retriesLeft: number; +} + +export interface GlobalHelper { + ctx: Record; + skip: SkipController; + only: OnlyController; + browser: (browserName: string) => BrowserVersionController; + config: ConfigController; +} + +export interface AfterFileReadData { + hermione: GlobalHelper; + browser: string; + file: string; +} + +export interface BeforeFileReadData extends AfterFileReadData { + testParser: TestParserAPI; +} + +export type SyncSessionEventCallback = ( + browser: Browser, + browserInfo: { browserId: string; browserVersion: string }, +) => void; + +export type MasterEventHandler = { + (event: Events["INIT"], callback: () => Promise | void): T; + (event: Events["RUNNER_START"], callback: (runner: MainRunner) => Promise | void): T; + (event: Events["RUNNER_END"], callback: (result: StatsResult) => Promise | void): T; + (event: Events["SESSION_START"], callback: AsyncSessionEventCallback): T; + (event: Events["SESSION_END"], callback: AsyncSessionEventCallback): T; + (event: Events["EXIT"], callback: () => Promise | void): T; + + (event: Events["NEW_WORKER_PROCESS"], callback: (suite: WorkerProcess) => void): T; + (event: Events["SUITE_BEGIN"], callback: (suite: Suite) => void): T; + (event: Events["SUITE_END"], callback: (suite: Suite) => void): T; + (event: Events["TEST_BEGIN"], callback: (test: Test) => void): T; + (event: Events["TEST_END"], callback: (test: TestResult) => void): T; + (event: Events["TEST_PASS"], callback: (test: TestResult) => void): T; + (event: Events["TEST_FAIL"], callback: (test: TestResult) => void): T; + (event: Events["TEST_PENDING"], callback: (test: Test) => void): T; + (event: Events["RETRY"], callback: (test: TestResultWithRetries) => void): T; + + (event: Events["CLI"], callback: (commander: commander.CommanderStatic) => void): T; + (event: Events["BEGIN"], callback: () => void): T; + (event: Events["END"], callback: () => void): T; + (event: Events["BEFORE_FILE_READ"], callback: (data: BeforeFileReadData) => void): T; + (event: Events["AFTER_FILE_READ"], callback: (data: AfterFileReadData) => void): T; + (event: Events["AFTER_TESTS_READ"], callback: (collection: TestCollection) => void): T; + (event: Events["INFO"], callback: () => void): T; + (event: Events["WARNING"], callback: () => void): T; + (event: Events["ERROR"], callback: (err: Error) => void): T; + + (event: Events["UPDATE_REFERENCE"], callback: (data: { state: string; refImg: ImageInfo }) => void): T; + (event: Events["NEW_BROWSER"], callback: SyncSessionEventCallback): T; +}; + +export type WorkerEventHandler = { + (event: Events["INIT"], callback: () => Promise | void): T; + (event: Events["BEFORE_FILE_READ"], callback: (data: BeforeFileReadData) => void): T; + (event: Events["AFTER_FILE_READ"], callback: (data: AfterFileReadData) => void): T; + (event: Events["AFTER_TESTS_READ"], callback: (collection: TestCollection) => void): T; + + (event: Events["UPDATE_REFERENCE"], callback: (data: { state: string; refImg: ImageInfo }) => void): T; + (event: Events["NEW_BROWSER"], callback: SyncSessionEventCallback): T; +}; diff --git a/src/utils/worker-process.ts b/src/utils/worker-process.ts index 7e9d604c9..eda7b2264 100644 --- a/src/utils/worker-process.ts +++ b/src/utils/worker-process.ts @@ -1,21 +1,23 @@ -"use strict"; +import { ChildProcess } from "child_process"; -module.exports = class WorkerProcess { - static create(...args) { - return new WorkerProcess(...args); +export class WorkerProcess { + protected process: ChildProcess; + + static create(this: new (process: ChildProcess) => T, process: ChildProcess): T { + return new this(process); } - constructor(process) { - this._process = process; + constructor(process: ChildProcess) { + this.process = process; } - send(...args) { - if (!this._process.connected) { + send(message: unknown): boolean { + if (!this.process.connected) { return false; } - this._process.send(...args); + this.process.send(message); return true; } -}; +} diff --git a/src/utils/workers-registry.js b/src/utils/workers-registry.js index 0965caab0..631472412 100644 --- a/src/utils/workers-registry.js +++ b/src/utils/workers-registry.js @@ -4,9 +4,9 @@ const { EventEmitter } = require("events"); const workerFarm = require("worker-farm"); const Promise = require("bluebird"); const _ = require("lodash"); -const Events = require("../constants/runner-events"); +const { MasterEvents } = require("../events"); const RuntimeConfig = require("../config/runtime-config"); -const WorkerProcess = require("./worker-process"); +const { WorkerProcess } = require("./worker-process"); const logger = require("../utils/logger"); const { MASTER_INIT, @@ -126,7 +126,7 @@ module.exports = class WorkersRegistry extends EventEmitter { break; case WORKER_UNHANDLED_REJECTION: if (data.error) { - this.emit(Events.ERROR, data.error); + this.emit(MasterEvents.ERROR, data.error); } break; default: @@ -137,6 +137,6 @@ module.exports = class WorkersRegistry extends EventEmitter { } }); - this.emit(Events.NEW_WORKER_PROCESS, WorkerProcess.create(child)); + this.emit(MasterEvents.NEW_WORKER_PROCESS, WorkerProcess.create(child)); } }; diff --git a/src/worker/constants/runner-events.js b/src/worker/constants/runner-events.js deleted file mode 100644 index 3f2ca73dd..000000000 --- a/src/worker/constants/runner-events.js +++ /dev/null @@ -1,19 +0,0 @@ -"use strict"; - -const MainProcessRunnerEvents = require("../../constants/runner-events"); - -module.exports = { - INIT: MainProcessRunnerEvents.INIT, - - BEFORE_FILE_READ: MainProcessRunnerEvents.BEFORE_FILE_READ, - AFTER_FILE_READ: MainProcessRunnerEvents.AFTER_FILE_READ, - - AFTER_TESTS_READ: MainProcessRunnerEvents.AFTER_TESTS_READ, - - TEST_FAIL: MainProcessRunnerEvents.TEST_FAIL, - ERROR: MainProcessRunnerEvents.ERROR, - - NEW_BROWSER: "newBrowser", - - UPDATE_REFERENCE: "updateReference", -}; diff --git a/src/worker/hermione-facade.js b/src/worker/hermione-facade.js index f68cef004..de0fdef39 100644 --- a/src/worker/hermione-facade.js +++ b/src/worker/hermione-facade.js @@ -1,6 +1,6 @@ "use strict"; -const Hermione = require("./hermione"); +const { Hermione } = require("./hermione"); const RuntimeConfig = require("../config/runtime-config"); const Promise = require("bluebird"); const debug = require("debug")(`hermione:worker:${process.pid}`); diff --git a/src/worker/hermione.ts b/src/worker/hermione.ts index 7101275ef..3f017654e 100644 --- a/src/worker/hermione.ts +++ b/src/worker/hermione.ts @@ -1,42 +1,69 @@ -"use strict"; +import { passthroughEvent } from "../events/utils"; +import { WorkerEvents } from "../events"; +import Runner from "./runner"; +import { BaseHermione } from "../base-hermione"; +import { ImageInfo, WdioBrowser, WorkerEventHandler } from "../types"; -const eventsUtils = require("../events/utils"); +export interface WorkerRunTestOpts { + browserId: string; + browserVersion: string; + file: string; + sessionId: string; + sessionCaps: WdioBrowser["capabilities"]; + sessionOpts: WdioBrowser["options"]; +} -const RunnerEvents = require("./constants/runner-events"); -const Runner = require("./runner"); -const BaseHermione = require("../base-hermione"); +export interface AssertViewResultsSuccess { + stateName: string; + refImg: ImageInfo; +} -module.exports = class Hermione extends BaseHermione { - constructor(configPath) { - super(configPath); +export interface WorkerRunTestHermioneCtx { + assertViewResults: Array; +} + +export interface WorkerRunTestResult { + meta: Record; + hermioneCtx: WorkerRunTestHermioneCtx; +} - this._runner = Runner.create(this._config); +export interface Hermione { + on: WorkerEventHandler; + once: WorkerEventHandler; +} - eventsUtils.passthroughEvent(this._runner, this, [ - RunnerEvents.BEFORE_FILE_READ, - RunnerEvents.AFTER_FILE_READ, +export class Hermione extends BaseHermione { + protected runner: Runner; + + constructor(configPath: string) { + super(configPath); - RunnerEvents.AFTER_TESTS_READ, + this.runner = Runner.create(this._config); - RunnerEvents.NEW_BROWSER, - RunnerEvents.UPDATE_REFERENCE, + passthroughEvent(this.runner, this, [ + WorkerEvents.BEFORE_FILE_READ, + WorkerEvents.AFTER_FILE_READ, + WorkerEvents.AFTER_TESTS_READ, + WorkerEvents.NEW_BROWSER, + WorkerEvents.UPDATE_REFERENCE, ]); } - async init() { + async init(): Promise { await this._init(); if (!global.expect) { + // eslint-disable-next-line @typescript-eslint/no-var-requires const { setOptions } = require("expect-webdriverio"); setOptions(this._config.system.expectOpts); } } - runTest(fullTitle, options) { - return this._runner.runTest(fullTitle, options); + runTest(fullTitle: string, options: WorkerRunTestOpts): Promise { + return this.runner.runTest(fullTitle, options); } - isWorker() { + isWorker(): boolean { return true; } -}; +} diff --git a/src/worker/runner/browser-pool.js b/src/worker/runner/browser-pool.js index 1a8162263..35d48cba5 100644 --- a/src/worker/runner/browser-pool.js +++ b/src/worker/runner/browser-pool.js @@ -3,7 +3,7 @@ const _ = require("lodash"); const Browser = require("../../browser/existing-browser"); const Calibrator = require("../../browser/calibrator"); -const RunnerEvents = require("../constants/runner-events"); +const { WorkerEvents } = require("../../events"); const ipc = require("../../utils/ipc"); const { DEVTOOLS_PROTOCOL } = require("../../constants/config"); @@ -42,7 +42,7 @@ module.exports = class BrowserPool { this._browsers[browserId].push(browser); } - this._emitter.emit(RunnerEvents.NEW_BROWSER, browser.publicAPI, { browserId: browser.id, browserVersion }); + this._emitter.emit(WorkerEvents.NEW_BROWSER, browser.publicAPI, { browserId: browser.id, browserVersion }); return browser; } catch (error) { diff --git a/src/worker/runner/caching-test-parser.js b/src/worker/runner/caching-test-parser.js index b38ed8489..a8a631bfc 100644 --- a/src/worker/runner/caching-test-parser.js +++ b/src/worker/runner/caching-test-parser.js @@ -3,8 +3,8 @@ const { EventEmitter } = require("events"); const { passthroughEvent } = require("../../events/utils"); const SequenceTestParser = require("./sequence-test-parser"); -const TestCollection = require("../../test-collection").default; -const RunnerEvents = require("../constants/runner-events"); +const { TestCollection } = require("../../test-collection"); +const { WorkerEvents } = require("../../events"); module.exports = class CachingTestParser extends EventEmitter { static create(...args) { @@ -18,7 +18,7 @@ module.exports = class CachingTestParser extends EventEmitter { this._cache = {}; this._sequenceTestParser = SequenceTestParser.create(config); - passthroughEvent(this._sequenceTestParser, this, [RunnerEvents.BEFORE_FILE_READ, RunnerEvents.AFTER_FILE_READ]); + passthroughEvent(this._sequenceTestParser, this, [WorkerEvents.BEFORE_FILE_READ, WorkerEvents.AFTER_FILE_READ]); } async parse({ file, browserId }) { @@ -32,7 +32,7 @@ module.exports = class CachingTestParser extends EventEmitter { const tests = await testsPromise; - this.emit(RunnerEvents.AFTER_TESTS_READ, TestCollection.create({ [browserId]: tests }, this._config)); + this.emit(WorkerEvents.AFTER_TESTS_READ, TestCollection.create({ [browserId]: tests }, this._config)); return tests; } diff --git a/src/worker/runner/index.js b/src/worker/runner/index.js index 13f7d4fe6..4fcf3bbe8 100644 --- a/src/worker/runner/index.js +++ b/src/worker/runner/index.js @@ -1,8 +1,8 @@ "use strict"; -const AsyncEmitter = require("../../events/async-emitter"); +const { AsyncEmitter } = require("../../events/async-emitter"); const { passthroughEvent } = require("../../events/utils"); -const RunnerEvents = require("../constants/runner-events"); +const { WorkerEvents } = require("../../events"); const BrowserPool = require("./browser-pool"); const BrowserAgent = require("./browser-agent"); const TestRunner = require("./test-runner"); @@ -21,9 +21,9 @@ module.exports = class Runner extends AsyncEmitter { this._testParser = CachingTestParser.create(config); passthroughEvent(this._testParser, this, [ - RunnerEvents.BEFORE_FILE_READ, - RunnerEvents.AFTER_FILE_READ, - RunnerEvents.AFTER_TESTS_READ, + WorkerEvents.BEFORE_FILE_READ, + WorkerEvents.AFTER_FILE_READ, + WorkerEvents.AFTER_TESTS_READ, ]); } diff --git a/src/worker/runner/sequence-test-parser.js b/src/worker/runner/sequence-test-parser.js index 66ffc942f..462fe13e4 100644 --- a/src/worker/runner/sequence-test-parser.js +++ b/src/worker/runner/sequence-test-parser.js @@ -3,7 +3,7 @@ const { EventEmitter } = require("events"); const { passthroughEvent } = require("../../events/utils"); const SimpleTestParser = require("./simple-test-parser"); -const RunnerEvents = require("../constants/runner-events"); +const { WorkerEvents } = require("../../events"); const fastq = require("fastq"); module.exports = class SequenceTestParser extends EventEmitter { @@ -15,7 +15,7 @@ module.exports = class SequenceTestParser extends EventEmitter { super(); this._parser = SimpleTestParser.create(config); - passthroughEvent(this._parser, this, [RunnerEvents.BEFORE_FILE_READ, RunnerEvents.AFTER_FILE_READ]); + passthroughEvent(this._parser, this, [WorkerEvents.BEFORE_FILE_READ, WorkerEvents.AFTER_FILE_READ]); this._queue = fastq.promise(fn => fn(), 1); } diff --git a/src/worker/runner/simple-test-parser.js b/src/worker/runner/simple-test-parser.js index a66b3bfb7..69207db2f 100644 --- a/src/worker/runner/simple-test-parser.js +++ b/src/worker/runner/simple-test-parser.js @@ -3,7 +3,7 @@ const { EventEmitter } = require("events"); const { passthroughEvent } = require("../../events/utils"); const { TestParser } = require("../../test-reader/test-parser"); -const RunnerEvents = require("../constants/runner-events"); +const { WorkerEvents } = require("../../events"); module.exports = class SimpleTestParser extends EventEmitter { static create(...args) { @@ -19,7 +19,7 @@ module.exports = class SimpleTestParser extends EventEmitter { async parse({ file, browserId }) { const parser = new TestParser(); - passthroughEvent(parser, this, [RunnerEvents.BEFORE_FILE_READ, RunnerEvents.AFTER_FILE_READ]); + passthroughEvent(parser, this, [WorkerEvents.BEFORE_FILE_READ, WorkerEvents.AFTER_FILE_READ]); await parser.loadFiles([file], this._config); diff --git a/src/worker/runner/test-runner/index.js b/src/worker/runner/test-runner/index.js index 9f46d9e9f..28a5f77c4 100644 --- a/src/worker/runner/test-runner/index.js +++ b/src/worker/runner/test-runner/index.js @@ -5,7 +5,7 @@ const _ = require("lodash"); const HookRunner = require("./hook-runner"); const ExecutionThread = require("./execution-thread"); const OneTimeScreenshooter = require("./one-time-screenshooter"); -const AssertViewError = require("../../../browser/commands/assert-view/errors/assert-view-error"); +const { AssertViewError } = require("../../../browser/commands/assert-view/errors/assert-view-error"); const history = require("../../../browser/history"); const { SAVE_HISTORY_MODE } = require("../../../constants/config"); diff --git a/test/src/browser-pool/basic-pool.js b/test/src/browser-pool/basic-pool.js index c501d55d2..5a46fbd48 100644 --- a/test/src/browser-pool/basic-pool.js +++ b/test/src/browser-pool/basic-pool.js @@ -1,10 +1,10 @@ "use strict"; -const AsyncEmitter = require("src/events/async-emitter"); +const { AsyncEmitter } = require("src/events/async-emitter"); const BasicPool = require("src/browser-pool/basic-pool"); const Browser = require("src/browser/new-browser"); -const CancelledError = require("src/browser-pool/cancelled-error"); -const Events = require("src/constants/runner-events"); +const { CancelledError } = require("src/browser-pool/cancelled-error"); +const { MasterEvents: Events } = require("src/events"); const { stubBrowser } = require("./util"); const _ = require("lodash"); const Promise = require("bluebird"); diff --git a/test/src/browser-pool/limited-pool.js b/test/src/browser-pool/limited-pool.js index bc0092f99..51c9338f6 100644 --- a/test/src/browser-pool/limited-pool.js +++ b/test/src/browser-pool/limited-pool.js @@ -2,7 +2,7 @@ const Promise = require("bluebird"); const LimitedPool = require("src/browser-pool/limited-pool"); -const CancelledError = require("src/browser-pool/cancelled-error"); +const { CancelledError } = require("src/browser-pool/cancelled-error"); const stubBrowser = require("./util").stubBrowser; describe("browser-pool/limited-pool", () => { diff --git a/test/src/browser/calibrator.js b/test/src/browser/calibrator.js index 9289c18d7..0ca8f00c1 100644 --- a/test/src/browser/calibrator.js +++ b/test/src/browser/calibrator.js @@ -5,7 +5,7 @@ const fs = require("fs"); const Promise = require("bluebird"); const Image = require("src/image"); const Calibrator = require("src/browser/calibrator"); -const CoreError = require("src/browser/core-error"); +const { CoreError } = require("src/browser/core-error"); describe("calibrator", () => { let browser, calibrator; diff --git a/test/src/browser/client-bridge/client-bridge.js b/test/src/browser/client-bridge/client-bridge.js index d8be22859..be7a1d7b7 100644 --- a/test/src/browser/client-bridge/client-bridge.js +++ b/test/src/browser/client-bridge/client-bridge.js @@ -1,7 +1,7 @@ "use strict"; const Promise = require("bluebird"); const ClientBridge = require("src/browser/client-bridge/client-bridge"); -const ClientBridgeError = require("src/browser/client-bridge/error"); +const { ClientBridgeError } = require("src/browser/client-bridge/error"); const CALL = '__geminiCore.example(1, "two")'; diff --git a/test/src/browser/commands/assert-view/assert-view-results.js b/test/src/browser/commands/assert-view/assert-view-results.js index 988bb435c..497410c89 100644 --- a/test/src/browser/commands/assert-view/assert-view-results.js +++ b/test/src/browser/commands/assert-view/assert-view-results.js @@ -1,13 +1,17 @@ "use strict"; const AssertViewResults = require("src/browser/commands/assert-view/assert-view-results"); -const ImageDiffError = require("src/browser/commands/assert-view/errors/image-diff-error"); -const NoRefImageError = require("src/browser/commands/assert-view/errors/no-ref-image-error"); +const { ImageDiffError } = require("src/browser/commands/assert-view/errors/image-diff-error"); +const { NoRefImageError } = require("src/browser/commands/assert-view/errors/no-ref-image-error"); describe("AssertViewResults", () => { describe("fromRawObject", () => { it("should create an instance form a raw object", () => { - const obj = [{ name: ImageDiffError.name }, { name: NoRefImageError.name }, { foo: "bar" }]; + const obj = [ + { name: ImageDiffError.name }, + { name: NoRefImageError.name, currImg: {}, refImg: {} }, + { foo: "bar" }, + ]; const results = AssertViewResults.fromRawObject(obj).get(); diff --git a/test/src/browser/commands/assert-view/capture-processors/assert-refs.js b/test/src/browser/commands/assert-view/capture-processors/assert-refs.js index 6d74db460..c55fa45e3 100644 --- a/test/src/browser/commands/assert-view/capture-processors/assert-refs.js +++ b/test/src/browser/commands/assert-view/capture-processors/assert-refs.js @@ -2,7 +2,7 @@ const _ = require("lodash"); const { handleImageDiff } = require("src/browser/commands/assert-view/capture-processors/assert-refs"); -const ImageDiffError = require("src/browser/commands/assert-view/errors/image-diff-error"); +const { ImageDiffError } = require("src/browser/commands/assert-view/errors/image-diff-error"); describe("browser/commands/assert-view/capture-processors/assert-refs", () => { const sandbox = sinon.createSandbox(); diff --git a/test/src/browser/commands/assert-view/capture-processors/update-refs.js b/test/src/browser/commands/assert-view/capture-processors/update-refs.js index e2ebea11e..ddc9773b6 100644 --- a/test/src/browser/commands/assert-view/capture-processors/update-refs.js +++ b/test/src/browser/commands/assert-view/capture-processors/update-refs.js @@ -4,7 +4,9 @@ const { EventEmitter } = require("events"); const _ = require("lodash"); const Promise = require("bluebird"); const fs = require("fs-extra"); -const { UPDATE_REFERENCE } = require("src/worker/constants/runner-events"); +const { + WorkerEvents: { UPDATE_REFERENCE }, +} = require("src/events"); const { handleNoRefImage, handleImageDiff, diff --git a/test/src/browser/commands/assert-view/errors/assert-view-error.js b/test/src/browser/commands/assert-view/errors/assert-view-error.js index 8a62da2fd..ebb75b6bd 100644 --- a/test/src/browser/commands/assert-view/errors/assert-view-error.js +++ b/test/src/browser/commands/assert-view/errors/assert-view-error.js @@ -1,6 +1,6 @@ "use strict"; -const AssertViewError = require("src/browser/commands/assert-view/errors/assert-view-error"); +const { AssertViewError } = require("src/browser/commands/assert-view/errors/assert-view-error"); describe("AssertViewError", () => { it("should be an instance of Error", () => { diff --git a/test/src/browser/commands/assert-view/errors/image-diff-error.js b/test/src/browser/commands/assert-view/errors/image-diff-error.js index 5bd186755..abb19ec8e 100644 --- a/test/src/browser/commands/assert-view/errors/image-diff-error.js +++ b/test/src/browser/commands/assert-view/errors/image-diff-error.js @@ -1,8 +1,8 @@ "use strict"; const _ = require("lodash"); -const BaseStateError = require("src/browser/commands/assert-view/errors/base-state-error"); -const ImageDiffError = require("src/browser/commands/assert-view/errors/image-diff-error"); +const { BaseStateError } = require("src/browser/commands/assert-view/errors/base-state-error"); +const { ImageDiffError } = require("src/browser/commands/assert-view/errors/image-diff-error"); const Image = require("src/image"); const mkImageDiffError = (opts = {}) => { diff --git a/test/src/browser/commands/assert-view/errors/no-ref-image-error.js b/test/src/browser/commands/assert-view/errors/no-ref-image-error.js index 62b14b963..c8fa142cb 100644 --- a/test/src/browser/commands/assert-view/errors/no-ref-image-error.js +++ b/test/src/browser/commands/assert-view/errors/no-ref-image-error.js @@ -1,8 +1,8 @@ "use strict"; const _ = require("lodash"); -const BaseStateError = require("src/browser/commands/assert-view/errors/base-state-error"); -const NoRefImageError = require("src/browser/commands/assert-view/errors/no-ref-image-error"); +const { BaseStateError } = require("src/browser/commands/assert-view/errors/base-state-error"); +const { NoRefImageError } = require("src/browser/commands/assert-view/errors/no-ref-image-error"); const mkNoRefImageError = (opts = {}) => { const { stateName, currImg, refImg } = _.defaults(opts, { diff --git a/test/src/browser/commands/assert-view/index.js b/test/src/browser/commands/assert-view/index.js index 79de6eefb..e367e176a 100644 --- a/test/src/browser/commands/assert-view/index.js +++ b/test/src/browser/commands/assert-view/index.js @@ -9,9 +9,9 @@ const Image = require("src/image"); const ScreenShooter = require("src/browser/screen-shooter"); const temp = require("src/temp"); const validator = require("png-validator"); -const AssertViewError = require("src/browser/commands/assert-view/errors/assert-view-error"); -const ImageDiffError = require("src/browser/commands/assert-view/errors/image-diff-error"); -const NoRefImageError = require("src/browser/commands/assert-view/errors/no-ref-image-error"); +const { AssertViewError } = require("src/browser/commands/assert-view/errors/assert-view-error"); +const { ImageDiffError } = require("src/browser/commands/assert-view/errors/image-diff-error"); +const { NoRefImageError } = require("src/browser/commands/assert-view/errors/no-ref-image-error"); const InvalidPngError = require("src/browser/commands/assert-view/errors/invalid-png-error"); const RuntimeConfig = require("src/config/runtime-config"); const updateRefs = require("src/browser/commands/assert-view/capture-processors/update-refs"); diff --git a/test/src/browser/screen-shooter/viewport/coord-validator.js b/test/src/browser/screen-shooter/viewport/coord-validator.js index 5bc58daa9..95fe41907 100644 --- a/test/src/browser/screen-shooter/viewport/coord-validator.js +++ b/test/src/browser/screen-shooter/viewport/coord-validator.js @@ -3,8 +3,12 @@ const _ = require("lodash"); const CoordValidator = require("src/browser/screen-shooter/viewport/coord-validator"); -const HeightViewportError = require("src/browser/screen-shooter/viewport/coord-validator/errors/height-viewport-error"); -const OffsetViewportError = require("src/browser/screen-shooter/viewport/coord-validator/errors/offset-viewport-error"); +const { + HeightViewportError, +} = require("src/browser/screen-shooter/viewport/coord-validator/errors/height-viewport-error"); +const { + OffsetViewportError, +} = require("src/browser/screen-shooter/viewport/coord-validator/errors/offset-viewport-error"); describe("CoordValidator", () => { let coordValidator; diff --git a/test/src/cli/index.js b/test/src/cli/index.js index 75e65fe6f..3fe9c6cc1 100644 --- a/test/src/cli/index.js +++ b/test/src/cli/index.js @@ -5,7 +5,7 @@ const proxyquire = require("proxyquire").noCallThru(); const hermioneCli = require("src/cli"); const info = require("src/cli/info"); const defaults = require("src/config/defaults"); -const Hermione = require("src/hermione"); +const { Hermione } = require("src/hermione"); const logger = require("src/utils/logger"); const any = sinon.match.any; diff --git a/test/src/config/browser-config.js b/test/src/config/browser-config.js index e2043d727..8dd35b59c 100644 --- a/test/src/config/browser-config.js +++ b/test/src/config/browser-config.js @@ -1,6 +1,6 @@ "use strict"; -const BrowserConfig = require("src/config/browser-config"); +const { BrowserConfig } = require("src/config/browser-config"); describe("BrowserConfig", () => { const sandbox = sinon.sandbox.create(); diff --git a/test/src/config/browser-options.js b/test/src/config/browser-options.js index fc4b41465..db80d9bf4 100644 --- a/test/src/config/browser-options.js +++ b/test/src/config/browser-options.js @@ -2,7 +2,7 @@ const _ = require("lodash"); -const Config = require("src/config"); +const { Config } = require("src/config"); const defaults = require("src/config/defaults"); const { WEBDRIVER_PROTOCOL, DEVTOOLS_PROTOCOL, SAVE_HISTORY_MODE } = require("src/constants/config"); diff --git a/test/src/config/index.js b/test/src/config/index.js index 44c2ce59a..b23d45b01 100644 --- a/test/src/config/index.js +++ b/test/src/config/index.js @@ -3,7 +3,7 @@ const path = require("path"); const proxyquire = require("proxyquire").noCallThru(); const defaults = require("src/config/defaults"); -const BrowserConfig = require("src/config/browser-config"); +const { BrowserConfig } = require("src/config/browser-config"); describe("config", () => { const sandbox = sinon.sandbox.create(); @@ -23,7 +23,7 @@ describe("config", () => { const resolvedConfigPath = path.resolve(process.cwd(), config); stubs[resolvedConfigPath] = opts.requireConfigReturns || {}; } - const Config = proxyquire("../../../src/config", stubs); + const Config = proxyquire("../../../src/config", stubs).Config; return Config.create(config, opts.allowOverrides); }; diff --git a/test/src/config/options.js b/test/src/config/options.js index 9a233ec9f..a6f894387 100644 --- a/test/src/config/options.js +++ b/test/src/config/options.js @@ -1,7 +1,7 @@ "use strict"; const _ = require("lodash"); -const Config = require("src/config"); +const { Config } = require("src/config"); const defaults = require("src/config/defaults"); const parser = require("src/config/options"); const { MissingOptionError } = require("gemini-configparser"); diff --git a/test/src/events/async-emitter/index.js b/test/src/events/async-emitter/index.js index 4ac2329d5..2e33767db 100644 --- a/test/src/events/async-emitter/index.js +++ b/test/src/events/async-emitter/index.js @@ -1,6 +1,6 @@ "use strict"; -const AsyncEmitter = require("src/events/async-emitter"); +const { AsyncEmitter } = require("src/events/async-emitter"); const Promise = require("bluebird"); describe("events/async-emitter", () => { diff --git a/test/src/events/utils.js b/test/src/events/utils.js index c85a92515..523973e07 100644 --- a/test/src/events/utils.js +++ b/test/src/events/utils.js @@ -2,7 +2,7 @@ const Promise = require("bluebird"); -const AsyncEmitter = require("src/events/async-emitter"); +const { AsyncEmitter } = require("src/events/async-emitter"); const utils = require("src/events/utils"); describe("events/utils", () => { diff --git a/test/src/hermione.js b/test/src/hermione.js index 4ae61aa96..447f11ddf 100644 --- a/test/src/hermione.js +++ b/test/src/hermione.js @@ -6,17 +6,17 @@ const pluginsLoader = require("plugins-loader"); const Promise = require("bluebird"); const proxyquire = require("proxyquire").noCallThru(); -const Config = require("src/config"); +const { Config } = require("src/config"); const RuntimeConfig = require("src/config/runtime-config"); -const AsyncEmitter = require("src/events/async-emitter"); +const { AsyncEmitter } = require("src/events/async-emitter"); const eventsUtils = require("src/events/utils"); -const Errors = require("src/errors"); -const RunnerStats = require("src/stats"); +const { default: Errors } = require("src/errors"); +const { Stats: RunnerStats } = require("src/stats"); const TestReader = require("src/test-reader"); -const TestCollection = require("src/test-collection").default; -const RunnerEvents = require("src/constants/runner-events"); +const { TestCollection } = require("src/test-collection"); +const { MasterEvents: RunnerEvents, CommonSyncEvents, MasterAsyncEvents, MasterSyncEvents } = require("src/events"); const signalHandler = require("src/signal-handler"); -const Runner = require("src/runner"); +const { MainRunner: Runner } = require("src/runner"); const logger = require("src/utils/logger"); const { makeConfigStub } = require("../utils"); @@ -52,7 +52,7 @@ describe("hermione", () => { Hermione = proxyquire("src/hermione", { "./reporters": { initReporters }, - }); + }).Hermione; }); afterEach(() => sandbox.restore()); @@ -343,7 +343,7 @@ describe("hermione", () => { const hermione = mkHermione_(); return hermione.run().then(() => { - _.forEach(RunnerEvents.getSync(), (event, name) => { + _.forEach(CommonSyncEvents, (event, name) => { const spy = sinon.spy().named(`${name} handler`); hermione.on(event, spy); @@ -364,7 +364,7 @@ describe("hermione", () => { eventsUtils.passthroughEvent, runner, sinon.match.instanceOf(Hermione), - _.values(RunnerEvents.getSync()), + _.values(MasterSyncEvents), ); assert.callOrder(eventsUtils.passthroughEvent, runner.run); }); @@ -375,7 +375,7 @@ describe("hermione", () => { const hermione = mkHermione_(); return hermione.run().then(() => { - _.forEach(RunnerEvents.getAsync(), (event, name) => { + _.forEach(MasterAsyncEvents, (event, name) => { const spy = sinon.spy().named(`${name} handler`); hermione.on(event, spy); @@ -396,7 +396,7 @@ describe("hermione", () => { eventsUtils.passthroughEventAsync, runner, sinon.match.instanceOf(Hermione), - _.values(RunnerEvents.getAsync()), + _.values(MasterAsyncEvents), ); assert.callOrder(eventsUtils.passthroughEventAsync, runner.run); }); diff --git a/test/src/reporters/flat.js b/test/src/reporters/flat.js index 54bfab2b6..6d7ca04d4 100644 --- a/test/src/reporters/flat.js +++ b/test/src/reporters/flat.js @@ -4,7 +4,7 @@ const chalk = require("chalk"); const path = require("path"); const { EventEmitter } = require("events"); const proxyquire = require("proxyquire"); -const RunnerEvents = require("src/constants/runner-events"); +const { MasterEvents: RunnerEvents } = require("src/events"); const { mkTestStub_, getDeserializedResult } = require("./utils"); describe("Flat reporter", () => { diff --git a/test/src/reporters/jsonl.js b/test/src/reporters/jsonl.js index 8bca638d3..02f1b8343 100644 --- a/test/src/reporters/jsonl.js +++ b/test/src/reporters/jsonl.js @@ -2,7 +2,7 @@ const path = require("path"); const { EventEmitter } = require("events"); const proxyquire = require("proxyquire"); const _ = require("lodash"); -const RunnerEvents = require("src/constants/runner-events"); +const { MasterEvents: RunnerEvents } = require("src/events"); const { SUCCESS, FAIL, RETRY, SKIPPED } = require("src/constants/test-statuses"); const testStates = { diff --git a/test/src/reporters/plain.js b/test/src/reporters/plain.js index 622d7800d..4cc019059 100644 --- a/test/src/reporters/plain.js +++ b/test/src/reporters/plain.js @@ -1,7 +1,7 @@ "use strict"; const EventEmitter = require("events").EventEmitter; -const RunnerEvents = require("src/constants/runner-events"); +const { MasterEvents: RunnerEvents } = require("src/events"); const proxyquire = require("proxyquire"); const mkTestStub_ = require("./utils").mkTestStub_; const getDeserializedResult = require("./utils").getDeserializedResult; diff --git a/test/src/runner/browser-runner.js b/test/src/runner/browser-runner.js index 8657e87c0..56296361f 100644 --- a/test/src/runner/browser-runner.js +++ b/test/src/runner/browser-runner.js @@ -1,20 +1,23 @@ "use strict"; const Promise = require("bluebird"); -const BrowserRunner = require("src/runner/browser-runner"); +// const { BrowserRunner } = require("src/runner/browser-runner"); const BrowserAgent = require("src/runner/browser-agent"); const BrowserPool = require("src/browser-pool"); -const TestRunnerFabric = require("src/runner/test-runner"); +const { create } = require("src/runner/test-runner"); const TestRunner = require("src/runner/test-runner/insistant-test-runner"); -const TestCollection = require("src/test-collection").default; +const { TestCollection } = require("src/test-collection"); const { Test } = require("src/test-reader/test-object"); const SuiteMonitor = require("src/runner/suite-monitor"); -const Events = require("src/constants/runner-events"); +const { MasterEvents: Events } = require("src/events"); const { makeConfigStub } = require("../../utils"); +const proxyquire = require("proxyquire"); describe("runner/browser-runner", () => { const sandbox = sinon.sandbox.create(); + let BrowserRunner; + let TestRunnerFabric = { create }; const mkWorkers_ = () => { return { @@ -67,6 +70,10 @@ describe("runner/browser-runner", () => { sandbox.stub(BrowserAgent, "create").returns(browserAgent); stubTestCollection_([Test.create({ title: "defaultTitle" })]); + + BrowserRunner = proxyquire("src/runner/browser-runner", { + "./test-runner": TestRunnerFabric, + }).BrowserRunner; }); afterEach(() => sandbox.restore()); diff --git a/test/src/runner/index.js b/test/src/runner/index.js index 00c7d76dd..d0c94cdda 100644 --- a/test/src/runner/index.js +++ b/test/src/runner/index.js @@ -4,20 +4,21 @@ const _ = require("lodash"); const Promise = require("bluebird"); const temp = require("src/temp"); -const BrowserPool = require("src/browser-pool"); const RuntimeConfig = require("src/config/runtime-config"); -const RunnerStats = require("src/stats"); -const RunnerEvents = require("src/constants/runner-events"); +const { Stats: RunnerStats } = require("src/stats"); +const { MasterEvents: RunnerEvents, RunnerSyncEvents } = require("src/events"); const logger = require("src/utils/logger"); const WorkersRegistry = require("src/utils/workers-registry"); -const Runner = require("src/runner"); -const BrowserRunner = require("src/runner/browser-runner"); -const TestCollection = require("src/test-collection").default; +const { BrowserRunner } = require("src/runner/browser-runner"); +const { TestCollection } = require("src/test-collection"); const { makeConfigStub } = require("../../utils"); +const proxyquire = require("proxyquire"); describe("Runner", () => { const sandbox = sinon.sandbox.create(); + let BrowserPool; + let Runner; const mkWorkers_ = () => { return { @@ -42,22 +43,28 @@ describe("Runner", () => { }; beforeEach(() => { - sandbox.stub(WorkersRegistry.prototype); - sandbox.stub(WorkersRegistry, "create").returns(Object.create(WorkersRegistry.prototype)); + BrowserPool = { + create: sinon.stub().returns({ cancel: sandbox.spy() }), + }; - sandbox.stub(BrowserPool, "create").returns({ cancel: sandbox.spy() }); + sandbox.stub(WorkersRegistry.prototype); + sandbox.stub(WorkersRegistry, "create").returns(Object.create(WorkersRegistry.prototype)); sandbox.stub(temp, "init"); - sandbox.stub(temp, "serialize"); + sandbox.stub(temp, "serialize"); sandbox.stub(logger, "warn"); - sandbox.stub(RuntimeConfig, "getInstance").returns({ extend: () => {} }); + sandbox.stub(RuntimeConfig, "getInstance").returns({ extend: () => {} }); sandbox.stub(TestCollection.prototype); sandbox.spy(BrowserRunner, "create"); sandbox.stub(BrowserRunner.prototype, "run").resolves(); sandbox.stub(BrowserRunner.prototype, "addTestToRun").resolves(); + + Runner = proxyquire("src/runner", { + "../browser-pool": BrowserPool, + }).MainRunner; }); afterEach(() => sandbox.restore()); @@ -296,7 +303,7 @@ describe("Runner", () => { assert.calledOnce(secondResolveMarker); }); - _.forEach(RunnerEvents.getRunnerSync(), (event, name) => { + _.forEach(RunnerSyncEvents, (event, name) => { it(`should passthrough ${name} event from browser runner`, async () => { onRun(browserRunner => browserRunner.emit(event, { foo: "bar" })); @@ -310,7 +317,7 @@ describe("Runner", () => { }); describe("interceptors", () => { - _.forEach(RunnerEvents.getRunnerSync(), (event, name) => { + _.forEach(RunnerSyncEvents, (event, name) => { it(`should call interceptor for ${name} with event name and event data`, async () => { onRun(browserRunner => browserRunner.emit(event, { foo: "bar" })); diff --git a/test/src/runner/suite-monitor.js b/test/src/runner/suite-monitor.js index 1acea0a45..37ef98d47 100644 --- a/test/src/runner/suite-monitor.js +++ b/test/src/runner/suite-monitor.js @@ -1,7 +1,7 @@ "use strict"; const SuiteMonitor = require("src/runner/suite-monitor"); -const Events = require("src/constants/runner-events"); +const { MasterEvents: Events } = require("src/events"); const { makeSuite, makeTest } = require("../../utils"); describe("suite-monitor", () => { diff --git a/test/src/runner/test-runner/insistant-test-runner.js b/test/src/runner/test-runner/insistant-test-runner.js index c4259a26b..b6ec3584c 100644 --- a/test/src/runner/test-runner/insistant-test-runner.js +++ b/test/src/runner/test-runner/insistant-test-runner.js @@ -3,10 +3,10 @@ const RegularTestRunner = require("src/runner/test-runner/regular-test-runner"); const InsistantTestRunner = require("src/runner/test-runner/insistant-test-runner"); const HighPriorityBrowserAgent = require("src/runner/test-runner/high-priority-browser-agent"); -const Events = require("src/constants/runner-events"); +const { MasterEvents: Events } = require("src/events"); const BrowserAgent = require("src/runner/browser-agent"); -const AssertViewError = require("src/browser/commands/assert-view/errors/assert-view-error"); -const NoRefImageError = require("src/browser/commands/assert-view/errors/no-ref-image-error"); +const { AssertViewError } = require("src/browser/commands/assert-view/errors/assert-view-error"); +const { NoRefImageError } = require("src/browser/commands/assert-view/errors/no-ref-image-error"); const { Test } = require("src/test-reader/test-object"); const { makeConfigStub } = require("../../../utils"); @@ -229,7 +229,7 @@ describe("runner/test-runner/insistant-test-runner", () => { onEachTestRun_(innerRunner => { const test = new Test({}); test.err = new AssertViewError(); - test.assertViewResults = [new NoRefImageError()]; + test.assertViewResults = [new NoRefImageError("some-state", {}, {})]; innerRunner.emit(Events.TEST_FAIL, test); }); @@ -243,7 +243,7 @@ describe("runner/test-runner/insistant-test-runner", () => { onFirstTestRun_(innerRunner => { const test = new Test({}); test.err = new Error(); - test.assertViewResults = [new NoRefImageError()]; + test.assertViewResults = [new NoRefImageError("some-state", {}, {})]; innerRunner.emit(Events.TEST_FAIL, test); }); diff --git a/test/src/runner/test-runner/regular-test-runner.js b/test/src/runner/test-runner/regular-test-runner.js index 6a0cb0e94..0e8a2e6d4 100644 --- a/test/src/runner/test-runner/regular-test-runner.js +++ b/test/src/runner/test-runner/regular-test-runner.js @@ -6,7 +6,7 @@ const BrowserAgent = require("src/runner/browser-agent"); const RegularTestRunner = require("src/runner/test-runner/regular-test-runner"); const WorkersRegistry = require("src/utils/workers-registry"); const logger = require("src/utils/logger"); -const Events = require("src/constants/runner-events"); +const { MasterEvents: Events } = require("src/events"); const AssertViewResults = require("src/browser/commands/assert-view/assert-view-results"); const { Test } = require("src/test-reader/test-object"); const Promise = require("bluebird"); diff --git a/test/src/runner/test-runner/skipped-test-runner.js b/test/src/runner/test-runner/skipped-test-runner.js index 121b1b3d3..19add5dd5 100644 --- a/test/src/runner/test-runner/skipped-test-runner.js +++ b/test/src/runner/test-runner/skipped-test-runner.js @@ -1,7 +1,7 @@ "use strict"; const SkippedTestRunner = require("src/runner/test-runner/skipped-test-runner"); -const Events = require("src/constants/runner-events"); +const { MasterEvents: Events } = require("src/events"); const { Test, Suite } = require("src/test-reader/test-object"); describe("runner/test-runner/skipped-test-runner", () => { diff --git a/test/src/stats.js b/test/src/stats.js index 1d77ecbbf..49e3ca36f 100644 --- a/test/src/stats.js +++ b/test/src/stats.js @@ -1,8 +1,8 @@ "use strict"; const { EventEmitter } = require("events"); -const RunnerEvents = require("src/constants/runner-events"); -const Stats = require("src/stats"); +const { MasterEvents: RunnerEvents } = require("src/events"); +const { Stats } = require("src/stats"); const { makeTest } = require("../utils"); describe("Stats", () => { diff --git a/test/src/test-collection.ts b/test/src/test-collection.ts index 7fa54d608..28bbcf803 100644 --- a/test/src/test-collection.ts +++ b/test/src/test-collection.ts @@ -1,5 +1,5 @@ import _ from "lodash"; -import TestCollection from "src/test-collection"; +import { TestCollection } from "src/test-collection"; import { Test } from "src/test-reader/test-object"; import type { Suite } from "src/test-reader/test-object/suite"; diff --git a/test/src/test-reader/controllers/browser-version-controller.js b/test/src/test-reader/controllers/browser-version-controller.js index f2fc3ae75..55f79b94d 100644 --- a/test/src/test-reader/controllers/browser-version-controller.js +++ b/test/src/test-reader/controllers/browser-version-controller.js @@ -2,7 +2,7 @@ const { mkProvider, BrowserVersionController } = require("src/test-reader/controllers/browser-version-controller"); const { TreeBuilder } = require("src/test-reader/tree-builder"); -const ReadEvents = require("src/test-reader/read-events"); +const { TestReaderEvents: ReadEvents } = require("src/events"); const { EventEmitter } = require("events"); describe("test-reader/controllers/browser-version-controller", () => { diff --git a/test/src/test-reader/controllers/config-controller.js b/test/src/test-reader/controllers/config-controller.js index 0c2ef0d0c..a4d48aae7 100644 --- a/test/src/test-reader/controllers/config-controller.js +++ b/test/src/test-reader/controllers/config-controller.js @@ -2,7 +2,7 @@ const { ConfigController } = require("src/test-reader/controllers/config-controller"); const { TreeBuilder } = require("src/test-reader/tree-builder"); -const ReadEvents = require("src/test-reader/read-events"); +const { TestReaderEvents: ReadEvents } = require("src/events"); const { EventEmitter } = require("events"); describe("test-reader/controllers/config-controller", () => { diff --git a/test/src/test-reader/controllers/only-controller.js b/test/src/test-reader/controllers/only-controller.js index e46d4a847..3a8628c8a 100644 --- a/test/src/test-reader/controllers/only-controller.js +++ b/test/src/test-reader/controllers/only-controller.js @@ -3,7 +3,7 @@ const { OnlyController } = require("src/test-reader/controllers/only-controller"); const { TreeBuilder } = require("src/test-reader/tree-builder"); const { ConfigurableTestObject } = require("src/test-reader/test-object/configurable-test-object"); -const ReadEvents = require("src/test-reader/read-events"); +const { TestReaderEvents: ReadEvents } = require("src/events"); const { EventEmitter } = require("events"); describe("test-reader/controllers/only-controller", () => { diff --git a/test/src/test-reader/controllers/skip-controller.js b/test/src/test-reader/controllers/skip-controller.js index d59e3290c..0121cb040 100644 --- a/test/src/test-reader/controllers/skip-controller.js +++ b/test/src/test-reader/controllers/skip-controller.js @@ -3,7 +3,7 @@ const { SkipController } = require("src/test-reader/controllers/skip-controller"); const { TreeBuilder } = require("src/test-reader/tree-builder"); const { ConfigurableTestObject } = require("src/test-reader/test-object/configurable-test-object"); -const ReadEvents = require("src/test-reader/read-events"); +const { TestReaderEvents: ReadEvents } = require("src/events"); const { EventEmitter } = require("events"); describe("test-reader/controllers/skip-controller", () => { diff --git a/test/src/test-reader/index.js b/test/src/test-reader/index.js index e07e07439..aae170a06 100644 --- a/test/src/test-reader/index.js +++ b/test/src/test-reader/index.js @@ -2,7 +2,7 @@ const TestReader = require("src/test-reader"); const { TestParser } = require("src/test-reader/test-parser"); -const Events = require("src/constants/runner-events"); +const { MasterEvents: Events } = require("src/events"); const SetsBuilder = require("src/test-reader/sets-builder"); const SetCollection = require("src/test-reader/sets-builder/set-collection"); const { makeConfigStub } = require("../../utils"); diff --git a/test/src/test-reader/mocha-reader/index.js b/test/src/test-reader/mocha-reader/index.js index ae305997d..0ad1c183e 100644 --- a/test/src/test-reader/mocha-reader/index.js +++ b/test/src/test-reader/mocha-reader/index.js @@ -4,8 +4,8 @@ const { MochaEventBus } = require("src/test-reader/mocha-reader/mocha-event-bus" const { TreeBuilderDecorator } = require("src/test-reader/mocha-reader/tree-builder-decorator"); const { TreeBuilder } = require("src/test-reader/tree-builder"); const { Test } = require("src/test-reader/test-object"); -const ReadEvents = require("src/test-reader/read-events"); -const RunnerEvents = require("src/constants/runner-events"); +const { TestReaderEvents: ReadEvents } = require("src/events"); +const { MasterEvents: RunnerEvents } = require("src/events"); const Mocha = require("mocha"); const proxyquire = require("proxyquire").noCallThru(); const { EventEmitter } = require("events"); diff --git a/test/src/test-reader/test-parser-api.js b/test/src/test-reader/test-parser-api.js index 8714276cf..19bbfabad 100644 --- a/test/src/test-reader/test-parser-api.js +++ b/test/src/test-reader/test-parser-api.js @@ -1,8 +1,8 @@ "use strict"; -const TestParserAPI = require("src/test-reader/test-parser-api"); +const { TestParserAPI } = require("src/test-reader/test-parser-api"); const { TreeBuilder } = require("src/test-reader/tree-builder"); -const ReadEvents = require("src/test-reader/read-events"); +const { TestReaderEvents: ReadEvents } = require("src/events"); const { EventEmitter } = require("events"); describe("test-reader/test-parser-api", () => { diff --git a/test/src/test-reader/test-parser.js b/test/src/test-reader/test-parser.js index 7859fa8a2..2d655f752 100644 --- a/test/src/test-reader/test-parser.js +++ b/test/src/test-reader/test-parser.js @@ -5,23 +5,26 @@ const { InstructionsList, Instructions } = require("src/test-reader/build-instru const { SkipController } = require("src/test-reader/controllers/skip-controller"); const { OnlyController } = require("src/test-reader/controllers/only-controller"); const { ConfigController } = require("src/test-reader/controllers/config-controller"); -const browserVersionController = require("src/test-reader/controllers/browser-version-controller"); -const TestParserAPI = require("src/test-reader/test-parser-api"); -const { NEW_BUILD_INSTRUCTION } = require("src/test-reader/read-events"); +const { TestParserAPI } = require("src/test-reader/test-parser-api"); const { Test, Suite } = require("src/test-reader/test-object"); -const RunnerEvents = require("src/constants/runner-events"); +const { MasterEvents: RunnerEvents, TestReaderEvents } = require("src/events"); const { makeConfigStub } = require("../../utils"); const proxyquire = require("proxyquire").noCallThru(); const path = require("path"); const { EventEmitter } = require("events"); const _ = require("lodash"); +const { NEW_BUILD_INSTRUCTION } = TestReaderEvents; + describe("test-reader/test-parser", () => { const sandbox = sinon.sandbox.create(); let TestParser; let clearRequire; let readFiles; + let browserVersionController = { + mkProvider: sinon.stub().returns(() => {}), + }; beforeEach(() => { clearRequire = sandbox.stub().named("clear-require"); @@ -30,6 +33,7 @@ describe("test-reader/test-parser", () => { TestParser = proxyquire("src/test-reader/test-parser", { "clear-require": clearRequire, "./mocha-reader": { readFiles }, + "./controllers/browser-version-controller": browserVersionController, }).TestParser; sandbox.stub(InstructionsList.prototype, "push").returnsThis(); @@ -85,10 +89,11 @@ describe("test-reader/test-parser", () => { assert.deepEqual(ctx, { foo: "bar" }); }); }); + ``; describe("hermione.browser", () => { beforeEach(() => { - sandbox.stub(browserVersionController, "mkProvider").returns(() => {}); + // sandbox.stub(browserVersionController, "mkProvider").get(() => {}); }); it("should set controller provider", async () => { diff --git a/test/src/utils/worker-process.js b/test/src/utils/worker-process.js index 9d998f486..b02caa710 100644 --- a/test/src/utils/worker-process.js +++ b/test/src/utils/worker-process.js @@ -1,6 +1,6 @@ "use strict"; -const WorkerProcess = require("src/utils/worker-process"); +const { WorkerProcess } = require("src/utils/worker-process"); describe("WorkerProcess", () => { describe("send", () => { diff --git a/test/src/utils/workers-registry.js b/test/src/utils/workers-registry.js index 2f21e94ac..3976c0d93 100644 --- a/test/src/utils/workers-registry.js +++ b/test/src/utils/workers-registry.js @@ -4,8 +4,8 @@ const proxyquire = require("proxyquire"); const { EventEmitter } = require("events"); const _ = require("lodash"); const RuntimeConfig = require("src/config/runtime-config"); -const Events = require("src/constants/runner-events"); -const WorkerProcess = require("src/utils/worker-process"); +const { MasterEvents: Events } = require("src/events"); +const { WorkerProcess } = require("src/utils/worker-process"); const logger = require("src/utils/logger"); const { MASTER_INIT, diff --git a/test/src/worker/hermione-facade.js b/test/src/worker/hermione-facade.js index 8788de7da..7ab14954c 100644 --- a/test/src/worker/hermione-facade.js +++ b/test/src/worker/hermione-facade.js @@ -1,7 +1,7 @@ "use strict"; -const AsyncEmitter = require("src/events/async-emitter"); -const Hermione = require("src/worker/hermione"); +const { AsyncEmitter } = require("src/events/async-emitter"); +const { Hermione } = require("src/worker/hermione"); const { makeConfigStub } = require("../../utils"); const ipc = require("src/utils/ipc"); const HermioneFacade = require("src/worker/hermione-facade"); diff --git a/test/src/worker/hermione.js b/test/src/worker/hermione.js index 4e0103d65..f4207394e 100644 --- a/test/src/worker/hermione.js +++ b/test/src/worker/hermione.js @@ -3,11 +3,11 @@ const _ = require("lodash"); const proxyquire = require("proxyquire").noCallThru(); const pluginsLoader = require("plugins-loader"); -const Config = require("src/config"); -const RunnerEvents = require("src/constants/runner-events"); -const Errors = require("src/errors"); -const TestCollection = require("src/test-collection").default; -const WorkerRunnerEvents = require("src/worker/constants/runner-events"); +const { Config } = require("src/config"); +const { MasterEvents: RunnerEvents } = require("src/events"); +const Errors = require("src/errors").default; +const { TestCollection } = require("src/test-collection"); +const { WorkerEvents: WorkerRunnerEvents } = require("src/events"); const Runner = require("src/worker/runner"); const { makeConfigStub, makeSuite } = require("../../utils"); @@ -24,7 +24,7 @@ describe("worker/hermione", () => { Hermione = proxyquire("src/worker/hermione", { "expect-webdriverio": ExpectWebdriverio, - }); + }).Hermione; sandbox.stub(Config, "create").returns(makeConfigStub()); diff --git a/test/src/worker/runner/browser-pool.js b/test/src/worker/runner/browser-pool.js index c2b08d32e..86b28ebff 100644 --- a/test/src/worker/runner/browser-pool.js +++ b/test/src/worker/runner/browser-pool.js @@ -5,7 +5,7 @@ const _ = require("lodash"); const Browser = require("src/browser/existing-browser"); const BrowserPool = require("src/worker/runner/browser-pool"); const Calibrator = require("src/browser/calibrator"); -const RunnerEvents = require("src/worker/constants/runner-events"); +const { WorkerEvents: RunnerEvents } = require("src/events"); const logger = require("src/utils/logger"); const ipc = require("src/utils/ipc"); const { WEBDRIVER_PROTOCOL, DEVTOOLS_PROTOCOL } = require("src/constants/config"); diff --git a/test/src/worker/runner/caching-test-parser.js b/test/src/worker/runner/caching-test-parser.js index c03e67091..583a75d70 100644 --- a/test/src/worker/runner/caching-test-parser.js +++ b/test/src/worker/runner/caching-test-parser.js @@ -2,8 +2,8 @@ const CachingTestParser = require("src/worker/runner/caching-test-parser"); const SequenceTestParser = require("src/worker/runner/sequence-test-parser"); -const RunnerEvents = require("src/worker/constants/runner-events"); -const TestCollection = require("src/test-collection").default; +const { WorkerEvents: RunnerEvents } = require("src/events"); +const { TestCollection } = require("src/test-collection"); const { makeConfigStub, makeTest } = require("../../../utils"); describe("worker/runner/caching-test-parser", () => { diff --git a/test/src/worker/runner/index.js b/test/src/worker/runner/index.js index 5f007ba58..43d022258 100644 --- a/test/src/worker/runner/index.js +++ b/test/src/worker/runner/index.js @@ -4,7 +4,7 @@ const Runner = require("src/worker/runner"); const BrowserPool = require("src/worker/runner/browser-pool"); const CachingTestParser = require("src/worker/runner/caching-test-parser"); const BrowserAgent = require("src/worker/runner/browser-agent"); -const RunnerEvents = require("src/worker/constants/runner-events"); +const { WorkerEvents: RunnerEvents } = require("src/events"); const TestRunner = require("src/worker/runner/test-runner"); const { makeConfigStub, makeTest } = require("../../../utils"); diff --git a/test/src/worker/runner/sequence-test-parser.js b/test/src/worker/runner/sequence-test-parser.js index 05846d6ac..48149c0c8 100644 --- a/test/src/worker/runner/sequence-test-parser.js +++ b/test/src/worker/runner/sequence-test-parser.js @@ -2,7 +2,7 @@ const SequenceTestParser = require("src/worker/runner/sequence-test-parser"); const SimpleTestParser = require("src/worker/runner/simple-test-parser"); -const RunnerEvents = require("src/worker/constants/runner-events"); +const { WorkerEvents: RunnerEvents } = require("src/events"); const { makeConfigStub, makeTest } = require("../../../utils"); const Promise = require("bluebird"); diff --git a/test/src/worker/runner/simple-test-parser.js b/test/src/worker/runner/simple-test-parser.js index 6e9e3b6e1..76d2fdcf2 100644 --- a/test/src/worker/runner/simple-test-parser.js +++ b/test/src/worker/runner/simple-test-parser.js @@ -1,7 +1,7 @@ "use strict"; const SimpleTestParser = require("src/worker/runner/sequence-test-parser"); -const RunnerEvents = require("src/worker/constants/runner-events"); +const { WorkerEvents: RunnerEvents } = require("src/events"); const { TestParser } = require("src/test-reader/test-parser"); const { makeConfigStub, makeTest } = require("../../../utils"); diff --git a/test/src/worker/runner/test-runner/index.js b/test/src/worker/runner/test-runner/index.js index 24bfe6980..0db8f3e47 100644 --- a/test/src/worker/runner/test-runner/index.js +++ b/test/src/worker/runner/test-runner/index.js @@ -7,7 +7,7 @@ const HookRunner = require("src/worker/runner/test-runner/hook-runner"); const ExecutionThread = require("src/worker/runner/test-runner/execution-thread"); const OneTimeScreenshooter = require("src/worker/runner/test-runner/one-time-screenshooter"); const BrowserAgent = require("src/worker/runner/browser-agent"); -const AssertViewError = require("src/browser/commands/assert-view/errors/assert-view-error"); +const { AssertViewError } = require("src/browser/commands/assert-view/errors/assert-view-error"); const AssertViewResults = require("src/browser/commands/assert-view/assert-view-results"); const { Suite, Test } = require("src/test-reader/test-object"); const history = require("src/browser/history"); diff --git a/tsconfig.common.json b/tsconfig.common.json index 7e81ed387..f4295fda7 100644 --- a/tsconfig.common.json +++ b/tsconfig.common.json @@ -1,6 +1,7 @@ { "include": ["src"], "compilerOptions": { + "lib": ["DOM", "es2021"], "allowJs": true, "declaration": true, "declarationMap": false, @@ -14,6 +15,7 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, - "target": "es2017" + "target": "es2021", + "resolveJsonModule": true } } diff --git a/typings/api.d.ts b/typings/api.d.ts new file mode 100644 index 000000000..6b6825de0 --- /dev/null +++ b/typings/api.d.ts @@ -0,0 +1,4 @@ +declare const it: TestDefinition; +declare const describe: SuiteDefinition; +declare const beforeEach: TestHookDefinition; +declare const afterEach: TestHookDefinition; diff --git a/typings/global.d.ts b/typings/global.d.ts index f2438584e..05161856b 100644 --- a/typings/global.d.ts +++ b/typings/global.d.ts @@ -1,4 +1,30 @@ -declare const hermione: Hermione.GlobalHelper; -declare const it: Hermione.TestDefinition; -declare const beforeEach: Hermione.TestHookDefinition; -declare const afterEach: Hermione.TestHookDefinition; +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// +/// +/// + +interface TestFunctionCtx { + browser: WebdriverIO.Browser; + currentTest: Mocha.Test; +} + +type TestFunction = (this: TestFunctionCtx, ctx: T) => void | Promise; + +interface TestHookDefinition { + = TestFunctionCtx>(fn?: TestFunction): void; +} + +interface TestDefinition { + = TestFunctionCtx>(title: string, fn?: TestFunction): Mocha.Test; + + only: (title: string, fn?: TestFunction) => Mocha.Test; + + skip: (title: string, fn?: TestFunction) => Mocha.Test; +} + +interface SuiteDefinition { + (title: string, fn: (this: Mocha.Suite) => void): Mocha.Suite; + + only: (title: string, fn: (this: Mocha.Suite) => void) => Mocha.Suite; + skip: (title: string, fn: (this: Mocha.Suite) => void) => Mocha.Suite; +} diff --git a/typings/index.d.ts b/typings/index.d.ts deleted file mode 100644 index 88bd8df59..000000000 --- a/typings/index.d.ts +++ /dev/null @@ -1,817 +0,0 @@ -/* eslint-disable @typescript-eslint/triple-slash-reference */ -/// -/// -/// -/// -/// -/* eslint-enable @typescript-eslint/triple-slash-reference */ - -/* eslint-disable no-use-before-define */ -class Hermione extends Hermione.AsyncEmitter implements Hermione.Process { - static create(config: string | Hermione.CommonConfig): Hermione; - constructor(config: string | Hermione.CommonConfig); - - config: Hermione.Config; - events: Hermione.EVENTS; - errors: Hermione.Errors; - - isWorker(): boolean; - intercept(event: Hermione.InterceptedEvent, handler: Hermione.InterceptHandler): this; - extendCli(parser: commander.CommanderStatic): void; - run(testPaths: Hermione.TestCollection | Array, opts: Hermione.RunOpts): Promise; - addTestToRun(test: Hermione.Test, browserId: string): boolean; - readTests(testPaths: Array, opts: Hermione.ReadTestsOpts): Promise; - isFailed(): boolean; - halt(err: T, timeout: number): void; - - on(event: Hermione.INIT_EVENT, callback: () => Promise | void): this; - on(event: Hermione.RUNNER_START_EVENT, callback: (runner: Hermione.MainRunner) => Promise | void): this; - on(event: Hermione.RUNNER_END_EVENT, callback: (result: Hermione.StatsResult) => Promise | void): this; - on(event: Hermione.SESSION_START_EVENT, callback: Hermione.AsyncSessionEventCallback): this; - on(event: Hermione.SESSION_END_EVENT, callback: Hermione.AsyncSessionEventCallback): this; - on(event: Hermione.EXIT_EVENT, callback: () => Promise | void): this; - - on(event: Hermione.NEW_WORKER_PROCESS_EVENT, callback: (suite: Hermione.NewWorkerProcess) => void): this; - on(event: Hermione.SUITE_BEGIN_EVENT, callback: (suite: Hermione.Suite) => void): this; - on(event: Hermione.SUITE_END_EVENT, callback: (suite: Hermione.Suite) => void): this; - on(event: Hermione.TEST_BEGIN_EVENT, callback: Hermione.TestEventCallback): this; - on(event: Hermione.TEST_END_EVENT, callback: (test: Hermione.TestResult) => void): this; - on(event: Hermione.TEST_PASS_EVENT, callback: (test: Hermione.TestResult) => void): this; - on(event: Hermione.TEST_FAIL_EVENT, callback: (test: Hermione.TestResult) => void): this; - on(event: Hermione.TEST_PENDING_EVENT, callback: Hermione.TestEventCallback): this; - on(event: Hermione.RETRY_EVENT, callback: (test: Hermione.TestResultWithRetries) => void): this; - - on(event: Hermione.CLI_EVENT, callback: (commander: commander.CommanderStatic) => void): this; - on(event: Hermione.BEGIN_EVENT, callback: () => void): this; - on(event: Hermione.END_EVENT, callback: () => void): this; - on(event: Hermione.BEFORE_FILE_READ_EVENT, callback: (data: Hermione.BeforeFileReadData) => void): this; - on(event: Hermione.AFTER_FILE_READ_EVENT, callback: (data: Hermione.AfterFileReadData) => void): this; - on(event: Hermione.AFTER_TESTS_READ_EVENT, callback: (collection: Hermione.TestCollection) => void): this; - on(event: Hermione.INFO_EVENT, callback: () => void): this; - on(event: Hermione.WARNING_EVENT, callback: () => void): this; - on(event: Hermione.ERROR_EVENT, callback: (err: Error) => void): this; - - on( - event: Hermione.UPDATE_REFERENCE_EVENT, - callback: (data: { state: string; refImg: Hermione.ImageInfo }) => void, - ): this; - on(event: Hermione.NEW_BROWSER_EVENT, callback: Hermione.SyncSessionEventCallback): this; - - once(event: Hermione.INIT_EVENT, callback: () => Promise | void): this; - once(event: Hermione.RUNNER_START_EVENT, callback: (runner: Hermione.MainRunner) => Promise | void): this; - once(event: Hermione.RUNNER_END_EVENT, callback: (result: Hermione.StatsResult) => Promise | void): this; - once(event: Hermione.SESSION_START_EVENT, callback: Hermione.AsyncSessionEventCallback): this; - once(event: Hermione.SESSION_END_EVENT, callback: Hermione.AsyncSessionEventCallback): this; - once(event: Hermione.EXIT_EVENT, callback: () => Promise | void): this; - - once(event: Hermione.NEW_WORKER_PROCESS_EVENT, callback: (suite: Hermione.NewWorkerProcess) => void): this; - once(event: Hermione.SUITE_BEGIN_EVENT, callback: (suite: Hermione.Suite) => void): this; - once(event: Hermione.SUITE_END_EVENT, callback: (suite: Hermione.Suite) => void): this; - once(event: Hermione.TEST_BEGIN_EVENT, callback: Hermione.TestEventCallback): this; - once(event: Hermione.TEST_END_EVENT, callback: (test: Hermione.TestResult) => void): this; - once(event: Hermione.TEST_PASS_EVENT, callback: (test: Hermione.TestResult) => void): this; - once(event: Hermione.TEST_FAIL_EVENT, callback: (test: Hermione.TestResult) => void): this; - once(event: Hermione.TEST_PENDING_EVENT, callback: Hermione.TestEventCallback): this; - once(event: Hermione.RETRY_EVENT, callback: (test: Hermione.TestResultWithRetries) => void): this; - - once(event: Hermione.CLI_EVENT, callback: (commander: commander.CommanderStatic) => void): this; - once(event: Hermione.BEGIN_EVENT, callback: () => void): this; - once(event: Hermione.END_EVENT, callback: () => void): this; - once(event: Hermione.BEFORE_FILE_READ_EVENT, callback: (data: Hermione.BeforeFileReadData) => void): this; - once(event: Hermione.AFTER_FILE_READ_EVENT, callback: (data: Hermione.AfterFileReadData) => void): this; - once(event: Hermione.AFTER_TESTS_READ_EVENT, callback: (collection: Hermione.TestCollection) => void): this; - once(event: Hermione.INFO_EVENT, callback: () => void): this; - once(event: Hermione.WARNING_EVENT, callback: () => void): this; - once(event: Hermione.ERROR_EVENT, callback: (err: Error) => void): this; - - once( - event: Hermione.UPDATE_REFERENCE_EVENT, - callback: (data: { state: string; refImg: Hermione.ImageInfo }) => void, - ): this; - once(event: Hermione.NEW_BROWSER_EVENT, callback: Hermione.SyncSessionEventCallback): this; - - prependListener(event: Hermione.INIT_EVENT, callback: () => Promise | void): this; - prependListener( - event: Hermione.RUNNER_START_EVENT, - callback: (runner: Hermione.MainRunner) => Promise | void, - ): this; - prependListener( - event: Hermione.RUNNER_END_EVENT, - callback: (result: Hermione.StatsResult) => Promise | void, - ): this; - prependListener(event: Hermione.SESSION_START_EVENT, callback: Hermione.AsyncSessionEventCallback): this; - prependListener(event: Hermione.SESSION_END_EVENT, callback: Hermione.AsyncSessionEventCallback): this; - prependListener(event: Hermione.EXIT_EVENT, callback: () => Promise | void): this; - - prependListener( - event: Hermione.NEW_WORKER_PROCESS_EVENT, - callback: (suite: Hermione.NewWorkerProcess) => void, - ): this; - prependListener(event: Hermione.SUITE_BEGIN_EVENT, callback: (suite: Hermione.Suite) => void): this; - prependListener(event: Hermione.SUITE_END_EVENT, callback: (suite: Hermione.Suite) => void): this; - prependListener(event: Hermione.TEST_BEGIN_EVENT, callback: Hermione.TestEventCallback): this; - prependListener(event: Hermione.TEST_END_EVENT, callback: (test: Hermione.TestResult) => void): this; - prependListener(event: Hermione.TEST_PASS_EVENT, callback: (test: Hermione.TestResult) => void): this; - prependListener(event: Hermione.TEST_FAIL_EVENT, callback: (test: Hermione.TestResult) => void): this; - prependListener(event: Hermione.TEST_PENDING_EVENT, callback: Hermione.TestEventCallback): this; - prependListener(event: Hermione.RETRY_EVENT, callback: (test: Hermione.TestResultWithRetries) => void): this; - - prependListener(event: Hermione.CLI_EVENT, callback: (commander: commander.CommanderStatic) => void): this; - prependListener(event: Hermione.BEGIN_EVENT, callback: () => void): this; - prependListener(event: Hermione.END_EVENT, callback: () => void): this; - prependListener( - event: Hermione.BEFORE_FILE_READ_EVENT, - callback: (data: Hermione.BeforeFileReadData) => void, - ): this; - prependListener(event: Hermione.AFTER_FILE_READ_EVENT, callback: (data: Hermione.AfterFileReadData) => void): this; - prependListener( - event: Hermione.AFTER_TESTS_READ_EVENT, - callback: (collection: Hermione.TestCollection) => void, - ): this; - prependListener(event: Hermione.INFO_EVENT, callback: () => void): this; - prependListener(event: Hermione.WARNING_EVENT, callback: () => void): this; - prependListener(event: Hermione.ERROR_EVENT, callback: (err: Error) => void): this; - - prependListener( - event: Hermione.UPDATE_REFERENCE_EVENT, - callback: (data: { state: string; refImg: Hermione.ImageInfo }) => void, - ): this; - prependListener(event: Hermione.NEW_BROWSER_EVENT, callback: Hermione.SyncSessionEventCallback): this; -} - -declare namespace Hermione { - export class AsyncEmitter extends NodeJS.EventEmitter { - emitAndWait(event: string, ...args: Array): Promise>; - } - - export interface Process extends AsyncEmitter { - config: Config; - events: EVENTS; - errors: Errors; - - isWorker(): boolean; - intercept(event: InterceptedEvent, handler: InterceptHandler): this; - - on(event: INIT_EVENT, callback: () => Promise | void): this; - on(event: BEFORE_FILE_READ_EVENT, callback: (data: BeforeFileReadData) => void): this; - on(event: AFTER_FILE_READ_EVENT, callback: (data: AfterFileReadData) => void): this; - on(event: AFTER_TESTS_READ_EVENT, callback: (collection: TestCollection) => void): this; - - once(event: INIT_EVENT, callback: () => Promise | void): this; - once(event: BEFORE_FILE_READ_EVENT, callback: (data: BeforeFileReadData) => void): this; - once(event: AFTER_FILE_READ_EVENT, callback: (data: AfterFileReadData) => void): this; - once(event: AFTER_TESTS_READ_EVENT, callback: (collection: TestCollection) => void): this; - } - - export interface Worker extends Process { - init(): Promise>; - runTest(fullTitle: string, opts: WorkerRunTestOpts): Promise; - isWorker(): true; - - on(event: UPDATE_REFERENCE_EVENT, callback: (data: { state: string; refImg: ImageInfo }) => void): this; - on(event: NEW_BROWSER_EVENT, callback: SyncSessionEventCallback): this; - - once(event: UPDATE_REFERENCE_EVENT, callback: (data: { state: string; refImg: ImageInfo }) => void): this; - once(event: NEW_BROWSER_EVENT, callback: SyncSessionEventCallback): this; - } - - export interface WorkerRunTestResult { - meta: { [name: string]: unknown }; - hermioneCtx: WorkerRunTestHermioneCtx; - } - - export interface WorkerRunTestHermioneCtx { - assertViewResults: Array; - } - - export interface AssertViewResultsSuccess { - stateName: string; - refImg: ImageInfo; - } - - export interface ImageInfo { - path: string; - size: ImageSize; - } - - export interface ImageSize { - width: number; - height: number; - } - - export interface WorkerRunTestOpts { - browserId: string; - file: string; - sessionId: string; - } - - type InterceptHandler = (arg: InterceptHandlerArg) => InterceptHandlerArg | void; - - export interface InterceptHandlerArg { - event?: InterceptedEvent; - data?: unknown; - } - - export interface RunOpts { - browsers?: Array; - sets?: Array; - grep?: string | RegExp; - updateRefs?: boolean; - reporters?: Array; - inspectMode?: InspectMode; - } - - export interface ReadTestsOpts { - browsers?: Array; - sets?: Array; - grep?: string | RegExp; - silent?: boolean; - ignore?: string | Array; - } - - type InspectMode = { - inpect: boolean | string; - inspectBrk: boolean | string; - }; - - export interface Context { - currentTest: Test; - } - - export interface Suite extends Hermione.MochaSuite { - id(): string; - browserId: string; - history: History; - } - - /** - * History of commands were called durung the test execution - * - * @param n - command name - * @param a - list of passed arguments - * @param s - scope of execution (browser or element) - * @param ts - time start - * @param te - time end - * @param d - duration - * @param c - list of children commands - */ - export interface History { - n: string; - a: any[]; - ts: number; - te: number; - d: number; - s: "b" | "e"; - c: History[]; - } - - export interface RootSuite extends Suite { - root: true; - } - - export interface Hook extends Hermione.MochaRunnable { - type: "hook"; - } - - export interface Test extends Hermione.MochaTest { - id(): string; - browserId: string; - sessionId: string; - } - - export interface TestError extends Error { - screenshot?: { - base64: string; - }; - } - - export interface TestResult extends Test { - startTime: number; - duration: number; - assertViewResults: Array; - meta: { [name: string]: unknown }; - hermioneCtx: { - assertViewResults: Array; - }; - history: History; - err?: TestError; - } - - export interface TestResultWithRetries extends TestResult { - retriesLeft: number; - } - - export interface TestHookDefinition { - (callback: Hermione.TestDefinitionCallback): void; - } - - export interface TestDefinition { - (expectation: string, callback?: TestDefinitionCallback): Test; - } - - type TestDefinitionCallback = (this: TestDefinitionCallbackCtx, ctx: TestDefinitionCallbackCtx) => any; - - export interface TestDefinitionCallbackCtx { - browser: WebdriverIO.Browser; - currentTest: Test; - } - - export interface AfterFileReadData { - hermione: GlobalHelper; - browser: string; - file: string; - } - - export interface BeforeFileReadData extends AfterFileReadData { - testParser: TestParserAPI; - } - - export interface TestParserAPI { - events: TEST_PARSER_API_EVENTS; - - setController(name: string, methods: { [method: string]: (...args: Array) => void }): void; - - on(event: TEST_EVENT, callback: (test: Test) => void): this; - on(event: SUITE_EVENT, callback: (suite: Suite) => void): this; - on(event: HOOK_EVENT, callback: (hook: Hook) => void): this; - - once(event: TEST_EVENT, callback: (test: Test) => void): this; - once(event: SUITE_EVENT, callback: (suite: Suite) => void): this; - once(event: HOOK_EVENT, callback: (hook: Hook) => void): this; - } - - export interface MainRunner extends GeminiCore.AsyncEmitter { - init(): void; - run(testCollection: TestCollection, stats: Stats): Promise; - addTestToRun(test: Test, browserId: string): boolean; - cancel(): void; - registerWorkers: RegisterWorkers; - } - - export type RegisterWorkers = ( - workerFilepath: string, - exportedMethods: ReadonlyArray, - ) => { - [K in (typeof exportedMethods)[number]]: (...args: Array) => Promise | any; - }; - - export interface Stats { - addPassed(test: Test): void; - addFailed(test: Test): void; - addSkipped(test: Test): void; - addRetries(test: Test): void; - - getResult(): StatsResult; - } - - export interface StatsResult { - total: number; - updated: number; - passed: number; - failed: number; - retries: number; - skipped: number; - perBrowser: Omit; - } - - export interface NewWorkerProcess { - send(...args: Array): boolean; - } - - export interface GlobalHelper { - ctx: { [name: string]: any }; - skip: SkipBuilder; - only: OnlyBuilder; - browser: (browserName: string) => BrowserConfigurator; - config: ConfigController; - } - - export interface SkipBuilder { - in(browserMatcher: string | RegExp | Array, comment?: string, opts?: SkipOpts): SkipBuilder; - notIn(browserMatcher: string | RegExp | Array, comment?: string, opts?: SkipOpts): SkipBuilder; - } - - export interface OnlyBuilder { - in(browserMatcher: string | RegExp | Array): OnlyBuilder; - notIn(browserMatcher: string | RegExp | Array): OnlyBuilder; - } - - export interface BrowserConfigurator { - version(browserVersion: string): BrowserConfigurator; - } - - export interface SkipOpts { - negate?: boolean; - silent?: boolean; - } - - export interface ConfigController { - testTimeout(timeout: number): void; - } - - export interface CommonConfig { - configPath?: string; - automationProtocol: "webdriver" | "devtools"; - desiredCapabilities: WebDriver.DesiredCapabilities | null; - gridUrl: string; - baseUrl: string; - sessionsPerBrowser: number; - testsPerSession: number; - retry: number; - shouldRetry(testInfo: { ctx: Test; retriesLeft: number }): boolean | null; - httpTimeout: number; - urlHttpTimeout: number | null; - pageLoadTimeout: number | null; - sessionRequestTimeout: number | null; - sessionQuitTimeout: number | null; - testTimeout: number | null; - waitTimeout: number; - saveHistoryMode: "all" | "none" | "onlyFailed"; - takeScreenshotOnFails: { - testFail: boolean; - assertViewFail: boolean; - }; - takeScreenshotOnFailsTimeout: number | null; - takeScreenshotOnFailsMode: "fullpage" | "viewport"; - prepareBrowser(browser: WebdriverIO.Browser): void | null; - screenshotPath: string | null; - screenshotsDir(test: Test): string; - calibrate: boolean; - compositeImage: boolean; - strictTestsOrder: boolean; - screenshotMode: "fullpage" | "viewport" | "auto"; - screenshotDelay: number; - tolerance: number; - antialiasingTolerance: number; - compareOpts: CompareOptsConfig; - buildDiffOpts: BuildDiffOptsConfig; - assertViewOpts: AssertViewOptsConfig; - expectOpts: ExpectOptsConfig; - meta: { [name: string]: unknown }; - windowSize: string | { width: number; height: number } | null; - orientation: "landscape" | "portrait" | null; - resetCursor: boolean; - headers: Record | null; - - system: SystemConfig; - headless: boolean | null; - } - - export interface CompareOptsConfig { - shouldCluster: boolean; - clustersSize: number; - stopOnFirstFail: boolean; - } - - export interface BuildDiffOptsConfig { - ignoreAntialiasing: boolean; - ignoreCaret: boolean; - } - - export interface AssertViewOptsConfig { - /** - * DOM-node selectors which will be ignored (painted with a black rectangle) when comparing images. - * - * @defaultValue `[]` - */ - ignoreElements: string | Array; - /** - * Ability to set capture element from the top area or from current position. - * - * @remarks - * In the first case viewport will be scrolled to the top of the element. - * - * @defaultValue `true` - */ - captureElementFromTop: boolean; - /** - * Disables check that element is outside of the viewport left, top, right or bottom bounds. - * - * @remarks - * By default Hermione throws an error if element is outside the viewport bounds. - * This option disables check that element is outside of the viewport left, top, right or bottom bounds. - * And in this case if browser option {@link https://github.com/gemini-testing/hermione#compositeimage compositeImage} set to `false`, then only visible part of the element will be captured. - * But if {@link https://github.com/gemini-testing/hermione#compositeimage compositeImage} set to `true` (default), then in the resulting screenshot will appear the whole element with not visible parts outside of the bottom bounds of viewport. - * - * @defaultValue `false` - */ - allowViewportOverflow: boolean; - } - - export interface ExpectOptsConfig { - wait: number; - interval: number; - } - - export interface AssertViewOpts extends Partial { - /** - * Maximum allowed difference between colors. - * Overrides config {@link https://github.com/gemini-testing/hermione#browsers browsers}.{@link https://github.com/gemini-testing/hermione#tolerance tolerance} value. - * - * @remarks - * Indicates maximum allowed CIEDE2000 difference between colors. Used only in non-strict mode. - * Increasing global default is not recommended, prefer changing tolerance for particular suites or states instead. - * By default it's 2.3 which should be enough for the most cases. - * - * @defaultValue `2.3` - */ - tolerance?: number; - /** - * Minimum difference in brightness (zero by default) between the darkest/lightest pixel (which is adjacent to the antialiasing pixel) and theirs adjacent pixels. - * Overrides config {@link https://github.com/gemini-testing/hermione#browsers browsers}.{@link https://github.com/gemini-testing/hermione#antialiasingTolerance antialiasingTolerance} value. - * - * @remarks - * Read more about this option in {@link https://github.com/gemini-testing/looks-same#comparing-images-with-ignoring-antialiasing looks-same} - * - * @defaultValue `4` - */ - antialiasingTolerance?: number; - /** - * Allows testing of regions which bottom bounds are outside of a viewport height. - * Overrides config {@link https://github.com/gemini-testing/hermione#browsers browsers}.{@link https://github.com/gemini-testing/hermione#compositeImage compositeImage} value. - * - * @remarks - * In the resulting screenshot the area which fits the viewport bounds will be joined with the area which is outside of the viewport height. - * - * @defaultValue `true` - */ - compositeImage?: boolean; - /** - * Allows to specify a delay (in milliseconds) before making any screenshot. - * Overrides config {@link https://github.com/gemini-testing/hermione#browsers browsers}.{@link https://github.com/gemini-testing/hermione#screenshotDelay screenshotDelay} value. - * - * @remarks - * This is useful when the page has elements which are animated or if you do not want to screen a scrollbar. - * - * @defaultValue `0` - */ - screenshotDelay?: number; - /** - * Ability to set DOM-node selector which should be scroll when the captured element does not completely fit on the screen. - * - * @remarks - * Useful when you capture the modal (popup). In this case a duplicate of the modal appears on the screenshot. - * That happens because we scroll the page using `window` selector, which scroll only the background of the modal, and the modal itself remains in place. - * Default value is `undefined` (it means scroll relative to `window`). Works only when `compositeImage` is `true` (default). - * - * @defaultValue `undefined` - */ - selectorToScroll?: string; - } - - export interface SystemConfig { - debug: boolean; - mochaOpts: MochaOpts; - ctx: { [name: string]: unknown }; - patternsOnReject: Array; - workers: number; - testsPerWorker: number; - diffColor: string; - tempDir: string; - parallelLimit: number; - fileExtensions: Array; - } - - export interface MochaOpts { - /** milliseconds to wait before considering a test slow. */ - slow?: number; - - /** timeout in milliseconds or time string like '1s'. */ - timeout?: number; - - /** string or regexp to filter tests with. */ - grep?: string | RegExp; - } - - export interface Config extends CommonConfig { - browsers: { [name: string]: BrowserConfig }; - plugins: { [name: string]: unknown }; - sets: Record; - - prepareEnvironment(): void | null; - forBrowser(id: string): BrowserConfig; - getBrowserIds(): Array; - serialize(): Omit; - mergeWith(config: Config): void; - } - - export interface SetsConfig { - files: string | Array; - ignoreFiles?: Array; - browsers?: Array; - } - - export interface BrowserConfig extends CommonConfig { - id: string; - getScreenshotPath(test: Test, stateName: string): string; - serialize(): Omit; - } - - // async events - export type INIT_EVENT = "init"; - export type RUNNER_START_EVENT = "startRunner"; - export type RUNNER_END_EVENT = "endRunner"; - export type SESSION_START_EVENT = "startSession"; - export type SESSION_END_EVENT = "endSession"; - export type EXIT_EVENT = "exit"; - - // sync events - export type CLI_EVENT = "cli"; - export type BEGIN_EVENT = "begin"; - export type END_EVENT = "end"; - export type BEFORE_FILE_READ_EVENT = "beforeFileRead"; - export type AFTER_FILE_READ_EVENT = "afterFileRead"; - export type AFTER_TESTS_READ_EVENT = "afterTestsRead"; - export type INFO_EVENT = "info"; - export type WARNING_EVENT = "warning"; - export type ERROR_EVENT = "err"; - - // runner sync events - export type NEW_WORKER_PROCESS_EVENT = "newWorkerProcess"; - export type SUITE_BEGIN_EVENT = "beginSuite"; - export type SUITE_END_EVENT = "endSuite"; - export type TEST_BEGIN_EVENT = "beginTest"; - export type TEST_END_EVENT = "endTest"; - export type TEST_PASS_EVENT = "passTest"; - export type TEST_FAIL_EVENT = "failTest"; - export type TEST_PENDING_EVENT = "pendingTest"; - export type RETRY_EVENT = "retry"; - export type NEW_BROWSER_EVENT = "newBrowser"; - export type UPDATE_REFERENCE_EVENT = "updateReference"; - - // test parser api events - export type TEST_EVENT = "test"; - export type SUITE_EVENT = "suite"; - export type HOOK_EVENT = "hook"; - - export interface EVENTS { - INIT: INIT_EVENT; - RUNNER_START: RUNNER_START_EVENT; - RUNNER_END: RUNNER_END_EVENT; - SESSION_START: SESSION_START_EVENT; - SESSION_END: SESSION_END_EVENT; - EXIT: EXIT_EVENT; - NEW_WORKER_PROCESS: NEW_WORKER_PROCESS_EVENT; - SUITE_BEGIN: SUITE_BEGIN_EVENT; - SUITE_END: SUITE_END_EVENT; - TEST_BEGIN: TEST_BEGIN_EVENT; - TEST_END: TEST_END_EVENT; - TEST_PASS: TEST_PASS_EVENT; - TEST_FAIL: TEST_FAIL_EVENT; - TEST_PENDING: TEST_PENDING_EVENT; - RETRY: RETRY_EVENT; - CLI: CLI_EVENT; - BEGIN: BEGIN_EVENT; - END: END_EVENT; - BEFORE_FILE_READ: BEFORE_FILE_READ_EVENT; - AFTER_FILE_READ: AFTER_FILE_READ_EVENT; - AFTER_TESTS_READ: AFTER_TESTS_READ_EVENT; - INFO: INFO_EVENT; - WARNING: WARNING_EVENT; - ERROR: ERROR_EVENT; - UPDATE_REFERENCE: UPDATE_REFERENCE_EVENT; - NEW_BROWSER: NEW_BROWSER_EVENT; - } - - export interface TEST_PARSER_API_EVENTS { - TEST: TEST_EVENT; - SUITE: SUITE_EVENT; - HOOK: HOOK_EVENT; - } - - export type MasterEvent = - | INIT_EVENT - | RUNNER_START_EVENT - | RUNNER_END_EVENT - | SESSION_START_EVENT - | SESSION_END_EVENT - | EXIT_EVENT - | NEW_WORKER_PROCESS_EVENT - | SUITE_BEGIN_EVENT - | SUITE_END_EVENT - | TEST_BEGIN_EVENT - | TEST_END_EVENT - | TEST_PASS_EVENT - | TEST_FAIL_EVENT - | TEST_PENDING_EVENT - | RETRY_EVENT - | CLI_EVENT - | BEGIN_EVENT - | END_EVENT - | BEFORE_FILE_READ_EVENT - | AFTER_FILE_READ_EVENT - | AFTER_TESTS_READ_EVENT - | INFO_EVENT - | WARNING_EVENT - | ERROR_EVENT; - - export type WorkerEvent = - | INIT_EVENT - | BEFORE_FILE_READ_EVENT - | AFTER_FILE_READ_EVENT - | AFTER_TESTS_READ_EVENT - | NEW_BROWSER_EVENT - | UPDATE_REFERENCE_EVENT; - - export type InterceptedEvent = - | SUITE_BEGIN_EVENT - | SUITE_END_EVENT - | TEST_END_EVENT - | TEST_END_EVENT - | TEST_PASS_EVENT - | TEST_FAIL_EVENT - | RETRY_EVENT; - - export type TestParserAPIEvent = TEST_EVENT | SUITE_EVENT | HOOK_EVENT; - - export interface Errors { - AssertViewError: AssertViewError; - ImageDiffError: ImageDiffError; - NoRefImageError: NoRefImageError; - } - - export class AssertViewError extends Error { - constructor(message: string); - } - - export class ImageDiffError { - static create( - stateName: string, - currImg: ImageInfo, - refImg: ImageInfo, - diffOpts: unknown, - diffAreas: unknown, - ): ImageDiffError; // TODO: export types from looks-same - static fromObject(data: { - stateName: string; - currImg: ImageInfo; - refImg: ImageInfo; - diffOpts: unknown; - diffAreas: unknown; - }): ImageDiffError; - constructor(stateName: string, currImg: ImageInfo, refImg: ImageInfo, diffOpts: unknown, diffAreas: unknown); - - saveDiffTo(diffPath: string): void; - } - - export class NoRefImageError { - static create(stateName: string, currImg: ImageInfo, refImg: ImageInfo): NoRefImageError; - static fromObject(data: { stateName: string; currImg: ImageInfo; refImg: ImageInfo }): NoRefImageError; - constructor(stateName: string, currImg: ImageInfo, refImg: ImageInfo); - } - - export interface TestCollection { - getRootSuite(browserId: string): RootSuite; - eachRootSuite(callback: (root: RootSuite, browserId: string) => void): void; - - getBrowsers(): string[]; - - mapTests(callback: TestsCallback): Array; - mapTests(browserId: string, callback: TestsCallback): Array; - - sortTests(callback: SortTestsCallback): TestCollection; - sortTests(browserId: string, callback: SortTestsCallback): TestCollection; - - eachTest(callback: TestsCallback): void; - eachTest(browserId: string, callback: TestsCallback): void; - - eachTestByVersions( - browserId: string, - callback: (test: Test, browserId: string, browserVersion: string) => void, - ): void; - - disableAll(browserId: string): TestCollection; - disableTest(fullTitle: string, browserId?: string): TestCollection; - - enableAll(browserId?: string): TestCollection; - enableTest(fullTitle: string, browserId?: string): TestCollection; - } - - export type TestsCallback = (test: Test, browserId: string) => T; - export type SortTestsCallback = (test1: Test, test2: Test) => boolean; - - export interface BrowserMeta { - pid: number; - browserVersion: string; - [name: string]: unknown; - } - - export interface BrowserInfo { - browserId: string; - sessionId: string; - } - - export type AsyncSessionEventCallback = ( - browser: WebdriverIO.Browser, - browserInfo: BrowserInfo, - ) => Promise | void; - export type SyncSessionEventCallback = ( - browser: WebdriverIO.Browser, - browserInfo: { browserId: string; browserVersion: string }, - ) => void; - export type TestEventCallback = (test: Test) => void; -} - -declare module "hermione" { - export = Hermione; -} diff --git a/typings/mocha/index.d.ts b/typings/mocha/index.d.ts deleted file mode 100644 index 18c01b1ab..000000000 --- a/typings/mocha/index.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/// - -declare namespace Hermione { - /* eslint-disable @typescript-eslint/no-empty-interface */ - export interface MochaSuite extends Mocha.Suite {} - export interface MochaTest extends Mocha.Test {} - export interface MochaRunnable extends Mocha.Runnable {} - /* eslint-enable @typescript-eslint/no-empty-interface */ -} diff --git a/typings/webdriverio/index.d.ts b/typings/webdriverio/index.d.ts deleted file mode 100644 index c48a3be60..000000000 --- a/typings/webdriverio/index.d.ts +++ /dev/null @@ -1,103 +0,0 @@ -/// -/// - -declare namespace WebdriverIO { - interface Browser { - getMeta(): Promise; - getMeta(key: string): Promise; - - setMeta(key: string, value: unknown): Promise; - - extendOptions(opts: { [name: string]: unknown }): Promise; - - /** - * Takes a screenshot of the passed selector and compares the received screenshot with the reference. - * - * @remarks - * For more details, see {@link https://github.com/gemini-testing/hermione#assertview documentation}. - * - * @example - * ```ts - * - * it('some test', async function() { - * await this.browser.url('some/url'); - * await this.browser.assertView('plain', '.button', { - * ignoreElements: ['.link'], - * tolerance: 2.3, - * antialiasingTolerance: 4, - * allowViewportOverflow: true, - * captureElementFromTop: true, - * compositeImage: true, - * screenshotDelay: 600, - * selectorToScroll: '.modal' - * }); - *}); - * ``` - * - * @param state state name, should be unique within one test - * @param selectors DOM-node selector that you need to capture - * @param opts additional options, currently available: - * "ignoreElements", "tolerance", "antialiasingTolerance", "allowViewportOverflow", "captureElementFromTop", - * "compositeImage", "screenshotDelay", "selectorToScroll" - */ - assertView(state: string, selectors: string | Array, opts?: Hermione.AssertViewOpts): Promise; - - /** - * Command that allows to add human-readable description for commands in history - * - * @remarks - * For more details, see {@link https://github.com/gemini-testing/hermione#runstep documentation} - * - * @example - * ```ts - * it('some test', async function () { - * await this.browser.runStep('step name', async () => { - * await this.browser.url('some/url'); - * ... - * }); - * }) - * ``` - * - * @param stepName step name - * @param stepCb step callback - * @returns {Promise} value, returned by `stepCb` - */ - runStep(stepName: string, stepCb: () => Promise | any): Promise; - } - - interface Element { - /** - * Takes a screenshot of the element and compares the received screenshot with the reference. - * - * @remarks - * For more details, see {@link https://github.com/gemini-testing/hermione#assertview documentation}. - * - * @example - * ```ts - * - * it('some test', async function() { - * await this.browser.url('some/url'); - * const button = await this.browser.$('.button'); - * await button.assertView('plain', { - * ignoreElements: ['.link'], - * tolerance: 2.3, - * antialiasingTolerance: 4, - * allowViewportOverflow: true, - * captureElementFromTop: true, - * compositeImage: true, - * screenshotDelay: 600, - * selectorToScroll: '.modal' - * }); - *}); - * ``` - * - * @param state state name, should be unique within one test - * @param opts additional options, currently available: - * "ignoreElements", "tolerance", "antialiasingTolerance", "allowViewportOverflow", "captureElementFromTop", - * "compositeImage", "screenshotDelay", "selectorToScroll" - */ - assertView(state: string, opts?: Hermione.AssertViewOpts): Promise; - } -} - -declare const expect: ExpectWebdriverIO.Expect;