velocious 1.0.644 → 1.0.646
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -9
- package/build/authorization/ability.js +36 -13
- package/build/background-jobs/adapter.js +9 -0
- package/build/background-jobs/main.js +35 -10
- package/build/background-jobs/store.js +217 -38
- package/build/background-jobs/types.js +15 -0
- package/build/configuration-types.js +1 -1
- package/build/configuration.js +45 -9
- package/build/database/drivers/base-column.js +8 -0
- package/build/database/drivers/mssql/sql/update.js +1 -3
- package/build/database/drivers/mysql/sql/update.js +1 -3
- package/build/database/drivers/pgsql/column.js +22 -0
- package/build/database/drivers/pgsql/sql/update.js +1 -3
- package/build/database/drivers/sqlite/sql/alter-table.js +11 -1
- package/build/database/drivers/sqlite/sql/update.js +1 -3
- package/build/database/query/index.js +2 -2
- package/build/database/query/model-class-query.js +26 -10
- package/build/database/query/preloader/has-many.js +4 -1
- package/build/database/query/query-data.js +2 -1
- package/build/database/query/update-base.js +14 -0
- package/build/database/query/with-count.js +3 -1
- package/build/database/record/acts-as-list.js +37 -13
- package/build/database/record/attachments/store.js +336 -26
- package/build/database/record/auditing.js +6 -3
- package/build/database/record/index.js +197 -42
- package/build/database/record/instance-relationships/belongs-to.js +2 -1
- package/build/database/record/validators/uniqueness.js +8 -5
- package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -8
- package/build/frontend-model-controller.js +136 -53
- package/build/frontend-model-resource/base-resource.js +87 -37
- package/build/frontend-model-resource/velocious-attachment-resource.js +98 -31
- package/build/frontend-models/base.js +307 -59
- package/build/frontend-models/preloader.js +6 -3
- package/build/frontend-models/query.js +15 -6
- package/build/frontend-models/resource-definition.js +21 -2
- package/build/frontend-models/use-destroyed-event.js +1 -1
- package/build/frontend-models/use-model-class-event.js +2 -2
- package/build/frontend-models/use-updated-event.js +1 -1
- package/build/frontend-models/websocket-channel.js +256 -93
- package/build/frontend-models/websocket-publishers.js +218 -18
- package/build/http-server/client/websocket-session.js +13 -1
- package/build/http-server/websocket-channel.js +15 -2
- package/build/src/authorization/ability.d.ts +9 -3
- package/build/src/authorization/ability.d.ts.map +1 -1
- package/build/src/authorization/ability.js +33 -14
- package/build/src/background-jobs/adapter.d.ts +6 -0
- package/build/src/background-jobs/adapter.d.ts.map +1 -1
- package/build/src/background-jobs/adapter.js +9 -1
- package/build/src/background-jobs/main.d.ts +6 -0
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +34 -11
- package/build/src/background-jobs/store.d.ts +66 -12
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +191 -35
- package/build/src/background-jobs/types.d.ts +55 -0
- package/build/src/background-jobs/types.d.ts.map +1 -1
- package/build/src/background-jobs/types.js +16 -1
- package/build/src/configuration-types.d.ts +3 -3
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +2 -2
- package/build/src/configuration.d.ts +18 -8
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +38 -7
- package/build/src/database/drivers/base-column.d.ts +5 -0
- package/build/src/database/drivers/base-column.d.ts.map +1 -1
- package/build/src/database/drivers/base-column.js +8 -1
- package/build/src/database/drivers/mssql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/sql/update.js +2 -4
- package/build/src/database/drivers/mysql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/sql/update.js +2 -4
- package/build/src/database/drivers/pgsql/column.d.ts +5 -0
- package/build/src/database/drivers/pgsql/column.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/column.js +19 -1
- package/build/src/database/drivers/pgsql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/sql/update.js +2 -4
- package/build/src/database/drivers/sqlite/sql/alter-table.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/sql/alter-table.js +6 -2
- package/build/src/database/drivers/sqlite/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/sql/update.js +2 -4
- package/build/src/database/query/index.d.ts +4 -4
- package/build/src/database/query/index.d.ts.map +1 -1
- package/build/src/database/query/index.js +3 -3
- package/build/src/database/query/model-class-query.d.ts +8 -2
- package/build/src/database/query/model-class-query.d.ts.map +1 -1
- package/build/src/database/query/model-class-query.js +25 -11
- package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-many.js +4 -2
- package/build/src/database/query/query-data.d.ts.map +1 -1
- package/build/src/database/query/query-data.js +3 -2
- package/build/src/database/query/update-base.d.ts +7 -0
- package/build/src/database/query/update-base.d.ts.map +1 -1
- package/build/src/database/query/update-base.js +13 -1
- package/build/src/database/query/with-count.d.ts.map +1 -1
- package/build/src/database/query/with-count.js +3 -2
- package/build/src/database/record/acts-as-list.d.ts.map +1 -1
- package/build/src/database/record/acts-as-list.js +35 -14
- package/build/src/database/record/attachments/store.d.ts +67 -0
- package/build/src/database/record/attachments/store.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.js +301 -25
- package/build/src/database/record/auditing.d.ts.map +1 -1
- package/build/src/database/record/auditing.js +7 -4
- package/build/src/database/record/index.d.ts +78 -26
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +171 -42
- package/build/src/database/record/instance-relationships/belongs-to.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/belongs-to.js +3 -2
- package/build/src/database/record/validators/uniqueness.d.ts.map +1 -1
- package/build/src/database/record/validators/uniqueness.js +8 -6
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +4 -4
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -12
- package/build/src/frontend-model-controller.d.ts +29 -8
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +122 -51
- package/build/src/frontend-model-resource/base-resource.d.ts +51 -24
- package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/base-resource.js +83 -37
- package/build/src/frontend-model-resource/velocious-attachment-resource.d.ts +45 -26
- package/build/src/frontend-model-resource/velocious-attachment-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/velocious-attachment-resource.js +89 -31
- package/build/src/frontend-models/base.d.ts +53 -20
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +275 -59
- package/build/src/frontend-models/preloader.d.ts.map +1 -1
- package/build/src/frontend-models/preloader.js +6 -4
- package/build/src/frontend-models/query.d.ts +2 -2
- package/build/src/frontend-models/query.d.ts.map +1 -1
- package/build/src/frontend-models/query.js +14 -7
- package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
- package/build/src/frontend-models/resource-definition.js +20 -3
- package/build/src/frontend-models/use-destroyed-event.d.ts +1 -1
- package/build/src/frontend-models/use-destroyed-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-destroyed-event.js +2 -2
- package/build/src/frontend-models/use-model-class-event.d.ts +2 -2
- package/build/src/frontend-models/use-model-class-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-model-class-event.js +3 -3
- package/build/src/frontend-models/use-updated-event.d.ts +1 -1
- package/build/src/frontend-models/use-updated-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-updated-event.js +2 -2
- package/build/src/frontend-models/websocket-channel.d.ts +60 -41
- package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-channel.js +225 -85
- package/build/src/frontend-models/websocket-publishers.d.ts +9 -0
- package/build/src/frontend-models/websocket-publishers.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-publishers.js +190 -19
- package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
- package/build/src/http-server/client/websocket-session.js +11 -2
- package/build/src/http-server/websocket-channel.d.ts +20 -8
- package/build/src/http-server/websocket-channel.d.ts.map +1 -1
- package/build/src/http-server/websocket-channel.js +15 -3
- package/build/src/sync/sync-api-client.d.ts.map +1 -1
- package/build/src/sync/sync-api-client.js +4 -3
- package/build/src/sync/sync-client.d.ts.map +1 -1
- package/build/src/sync/sync-client.js +3 -2
- package/build/src/sync/sync-envelope-replay-service.d.ts.map +1 -1
- package/build/src/sync/sync-envelope-replay-service.js +6 -5
- package/build/src/sync/sync-publisher.d.ts.map +1 -1
- package/build/src/sync/sync-publisher.js +4 -3
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts +2 -2
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts.map +1 -1
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.js +3 -3
- package/build/src/testing/test-profiler.d.ts +4 -4
- package/build/src/testing/test-profiler.d.ts.map +1 -1
- package/build/src/testing/test-profiler.js +3 -3
- package/build/src/testing/test-runner.d.ts +271 -117
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +496 -427
- package/build/src/testing/test.d.ts +11 -72
- package/build/src/testing/test.d.ts.map +1 -1
- package/build/src/testing/test.js +131 -284
- package/build/src/testing/velocious-attempt-executor.d.ts +14 -29
- package/build/src/testing/velocious-attempt-executor.d.ts.map +1 -1
- package/build/src/testing/velocious-attempt-executor.js +52 -46
- package/build/src/testing/velocious-runner-reporter.d.ts +29 -34
- package/build/src/testing/velocious-runner-reporter.d.ts.map +1 -1
- package/build/src/testing/velocious-runner-reporter.js +145 -57
- package/build/src/testing/velocious-suite-hook-executor.d.ts +5 -23
- package/build/src/testing/velocious-suite-hook-executor.d.ts.map +1 -1
- package/build/src/testing/velocious-suite-hook-executor.js +22 -47
- package/build/src/testing/velocious-test-arguments.d.ts +11 -6
- package/build/src/testing/velocious-test-arguments.d.ts.map +1 -1
- package/build/src/testing/velocious-test-arguments.js +18 -10
- package/build/src/utils/model-primary-key.d.ts +73 -0
- package/build/src/utils/model-primary-key.d.ts.map +1 -0
- package/build/src/utils/model-primary-key.js +144 -0
- package/build/sync/sync-api-client.js +3 -2
- package/build/sync/sync-client.js +2 -1
- package/build/sync/sync-envelope-replay-service.js +5 -4
- package/build/sync/sync-publisher.js +3 -2
- package/build/testing/browser-frontend-model-event-hook-scenarios.js +2 -2
- package/build/testing/test-profiler.js +2 -2
- package/build/testing/test-runner.js +493 -449
- package/build/testing/test.js +164 -306
- package/build/testing/velocious-attempt-executor.js +55 -45
- package/build/testing/velocious-runner-reporter.js +156 -58
- package/build/testing/velocious-suite-hook-executor.js +22 -50
- package/build/testing/velocious-test-arguments.js +19 -10
- package/build/utils/model-primary-key.js +171 -0
- package/package.json +6 -5
- package/src/authorization/ability.js +36 -13
- package/src/background-jobs/adapter.js +9 -0
- package/src/background-jobs/main.js +35 -10
- package/src/background-jobs/store.js +217 -38
- package/src/background-jobs/types.js +15 -0
- package/src/configuration-types.js +1 -1
- package/src/configuration.js +45 -9
- package/src/database/drivers/base-column.js +8 -0
- package/src/database/drivers/mssql/sql/update.js +1 -3
- package/src/database/drivers/mysql/sql/update.js +1 -3
- package/src/database/drivers/pgsql/column.js +22 -0
- package/src/database/drivers/pgsql/sql/update.js +1 -3
- package/src/database/drivers/sqlite/sql/alter-table.js +11 -1
- package/src/database/drivers/sqlite/sql/update.js +1 -3
- package/src/database/query/index.js +2 -2
- package/src/database/query/model-class-query.js +26 -10
- package/src/database/query/preloader/has-many.js +4 -1
- package/src/database/query/query-data.js +2 -1
- package/src/database/query/update-base.js +14 -0
- package/src/database/query/with-count.js +3 -1
- package/src/database/record/acts-as-list.js +37 -13
- package/src/database/record/attachments/store.js +336 -26
- package/src/database/record/auditing.js +6 -3
- package/src/database/record/index.js +197 -42
- package/src/database/record/instance-relationships/belongs-to.js +2 -1
- package/src/database/record/validators/uniqueness.js +8 -5
- package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -8
- package/src/frontend-model-controller.js +136 -53
- package/src/frontend-model-resource/base-resource.js +87 -37
- package/src/frontend-model-resource/velocious-attachment-resource.js +98 -31
- package/src/frontend-models/base.js +307 -59
- package/src/frontend-models/preloader.js +6 -3
- package/src/frontend-models/query.js +15 -6
- package/src/frontend-models/resource-definition.js +21 -2
- package/src/frontend-models/use-destroyed-event.js +1 -1
- package/src/frontend-models/use-model-class-event.js +2 -2
- package/src/frontend-models/use-updated-event.js +1 -1
- package/src/frontend-models/websocket-channel.js +256 -93
- package/src/frontend-models/websocket-publishers.js +218 -18
- package/src/http-server/client/websocket-session.js +13 -1
- package/src/http-server/websocket-channel.js +15 -2
- package/src/sync/sync-api-client.js +3 -2
- package/src/sync/sync-client.js +2 -1
- package/src/sync/sync-envelope-replay-service.js +5 -4
- package/src/sync/sync-publisher.js +3 -2
- package/src/testing/browser-frontend-model-event-hook-scenarios.js +2 -2
- package/src/testing/test-profiler.js +2 -2
- package/src/testing/test-runner.js +493 -449
- package/src/testing/test.js +164 -306
- package/src/testing/velocious-attempt-executor.js +55 -45
- package/src/testing/velocious-runner-reporter.js +156 -58
- package/src/testing/velocious-suite-hook-executor.js +22 -50
- package/src/testing/velocious-test-arguments.js +19 -10
- package/src/utils/model-primary-key.js +171 -0
- package/build/src/testing/testing-package-adapter.d.ts +0 -9
- package/build/src/testing/testing-package-adapter.d.ts.map +0 -1
- package/build/src/testing/testing-package-adapter.js +0 -69
- package/build/testing/testing-package-adapter.js +0 -76
- package/build/tsconfig.tsbuildinfo +0 -1
- package/src/testing/testing-package-adapter.js +0 -76
|
@@ -2,25 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
import fs from "node:fs/promises"
|
|
4
4
|
import path from "path"
|
|
5
|
-
import {format} from "node:util"
|
|
6
5
|
import {AsyncLocalStorage} from "node:async_hooks"
|
|
6
|
+
import {createTestContext, defaultTestContext} from "@velocious/testing"
|
|
7
|
+
import {TestRunner as PackageTestRunner} from "@velocious/testing/runner"
|
|
7
8
|
import Application from "../../src/application.js"
|
|
8
9
|
import RequestClient from "./request-client.js"
|
|
9
10
|
import picocolors from "picocolors"
|
|
10
11
|
import restArgsError from "../utils/rest-args-error.js"
|
|
11
|
-
import {testConfig
|
|
12
|
-
import {pathToFileURL} from "url"
|
|
12
|
+
import {testConfig} from "./test.js"
|
|
13
|
+
import {fileURLToPath, pathToFileURL} from "url"
|
|
13
14
|
import SharedTransactionBroker from "./shared-transaction-broker.js"
|
|
14
15
|
import { SHARED_TRANSACTION_BROKER_ENV } from "./shared-transaction-proxy-driver.js"
|
|
15
|
-
import {synchronizeTestingPackageTests} from "./testing-package-adapter.js"
|
|
16
16
|
import VelociousAttemptExecutor from "./velocious-attempt-executor.js"
|
|
17
|
-
import VelociousRunnerReporter from "./velocious-runner-reporter.js"
|
|
17
|
+
import VelociousRunnerReporter, {AbortRemainingTestsError} from "./velocious-runner-reporter.js"
|
|
18
18
|
import VelociousSuiteHookExecutor from "./velocious-suite-hook-executor.js"
|
|
19
19
|
import VelociousTestArguments from "./velocious-test-arguments.js"
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
/** @typedef {typeof defaultTestContext} PackageTestContext */
|
|
22
|
+
/** @typedef {(typeof defaultTestContext.registry.suites)[number]} PackageSuiteDeclaration */
|
|
23
|
+
/** @typedef {PackageSuiteDeclaration["tests"][number]} PackageTestDeclaration */
|
|
24
|
+
/** @typedef {PackageSuiteDeclaration["hooks"]["beforeAll"][number]} PackageHookDeclaration */
|
|
25
|
+
/** @typedef {PackageSuiteDeclaration | PackageTestDeclaration | PackageHookDeclaration} PackageRegistration */
|
|
26
|
+
/** @typedef {{hadRetries: boolean, options: PackageTestDeclaration["options"], retries: number | undefined}} PackageRetryOptionRestoration */
|
|
27
|
+
|
|
24
28
|
/**
|
|
25
29
|
* AttemptConsoleOutput type.
|
|
26
30
|
* @typedef {object} AttemptConsoleOutput
|
|
@@ -62,6 +66,7 @@ import VelociousTestArguments from "./velocious-test-arguments.js"
|
|
|
62
66
|
* @property {number} [line] - Source line number.
|
|
63
67
|
* @property {string} [ownerFilePath] - Deterministic importing test file.
|
|
64
68
|
* @property {(arg: TestArgs) => (void|Promise<void>)} function - Test callback to execute.
|
|
69
|
+
* @property {PackageTestDeclaration} [declaration] - Package declaration.
|
|
65
70
|
*/
|
|
66
71
|
/**
|
|
67
72
|
* FailedTestDetail type.
|
|
@@ -73,13 +78,6 @@ import VelociousTestArguments from "./velocious-test-arguments.js"
|
|
|
73
78
|
* @property {string} [consoleOutput] - Captured console output while test ran.
|
|
74
79
|
* @property {string} [consoleLogPath] - Saved console log path.
|
|
75
80
|
*/
|
|
76
|
-
/**
|
|
77
|
-
* ActiveAfterAllScopeEntry type.
|
|
78
|
-
* @typedef {object} ActiveAfterAllScopeEntry
|
|
79
|
-
* @property {TestsArgument} tests - Scope test tree.
|
|
80
|
-
* @property {boolean} afterAllsRun - Whether cleanup hooks have run.
|
|
81
|
-
* @property {string} [profileScopeId] - Opaque profile scope identifier.
|
|
82
|
-
*/
|
|
83
81
|
/**
|
|
84
82
|
* Defines this typedef.
|
|
85
83
|
* @typedef {(args: {configuration: import("../configuration.js").default, testArgs: TestArgs, testData: TestData}) => (void|Promise<void>)} AfterBeforeEachCallbackType
|
|
@@ -144,11 +142,6 @@ import VelociousTestArguments from "./velocious-test-arguments.js"
|
|
|
144
142
|
* @property {import("../database/pool/base.js").TestSharedConnectionRegistration | undefined} sharedRegistration - Physical-key shared registration once published.
|
|
145
143
|
*/
|
|
146
144
|
|
|
147
|
-
/**
|
|
148
|
-
* Captured console methods.
|
|
149
|
-
* @type {ConsoleMethodName[]} */
|
|
150
|
-
const CAPTURED_CONSOLE_METHODS = ["log", "info", "warn", "error", "debug"]
|
|
151
|
-
|
|
152
145
|
/**
|
|
153
146
|
* Runs to file slug.
|
|
154
147
|
* @param {string} value - Value to sanitize.
|
|
@@ -163,10 +156,8 @@ function toFileSlug(value) {
|
|
|
163
156
|
}
|
|
164
157
|
|
|
165
158
|
export default class TestRunner {
|
|
166
|
-
/**
|
|
167
|
-
|
|
168
|
-
* @type {ActiveAfterAllScopeEntry[]} */
|
|
169
|
-
_activeAfterAllScopes
|
|
159
|
+
/** @type {PackageTestContext} */
|
|
160
|
+
_context
|
|
170
161
|
|
|
171
162
|
/**
|
|
172
163
|
* Narrows the runtime value to the documented type.
|
|
@@ -177,6 +168,7 @@ export default class TestRunner {
|
|
|
177
168
|
* Runs constructor.
|
|
178
169
|
* @param {object} args - Options object.
|
|
179
170
|
* @param {import("../configuration.js").default} args.configuration - Configuration instance.
|
|
171
|
+
* @param {PackageTestContext} [args.context] - Declaration context.
|
|
180
172
|
* @param {string[] | string} [args.excludeTags] - Tags to exclude.
|
|
181
173
|
* @param {string[] | string} [args.includeTags] - Tags to include.
|
|
182
174
|
* @param {Array<string>} args.testFiles - Test files.
|
|
@@ -184,18 +176,17 @@ export default class TestRunner {
|
|
|
184
176
|
* @param {RegExp[]} [args.examplePatterns] - Example patterns.
|
|
185
177
|
* @param {import("./test-profiler.js").default} [args.profiler] - Opt-in profiler.
|
|
186
178
|
*/
|
|
187
|
-
constructor({configuration, excludeTags, includeTags, testFiles, lineFilters, examplePatterns, profiler, ...restArgs}) {
|
|
179
|
+
constructor({configuration, context = defaultTestContext, excludeTags, includeTags, testFiles, lineFilters, examplePatterns, profiler, ...restArgs}) {
|
|
188
180
|
restArgsError(restArgs)
|
|
189
181
|
|
|
190
182
|
if (!configuration) throw new Error("configuration is required")
|
|
191
183
|
|
|
192
184
|
this._configuration = configuration
|
|
185
|
+
this._context = context
|
|
193
186
|
this._sharedTransactionCoordinatorOwnerStorage = new AsyncLocalStorage()
|
|
194
187
|
this._testDatabaseAccessScopeStorage = new AsyncLocalStorage()
|
|
195
188
|
this._excludeTags = this.normalizeTags(excludeTags)
|
|
196
|
-
this._excludeTagSet = new Set(this._excludeTags)
|
|
197
189
|
this._includeTags = this.normalizeTags(includeTags)
|
|
198
|
-
this._includeTagSet = new Set(this._includeTags)
|
|
199
190
|
this._testFiles = testFiles
|
|
200
191
|
this._lineFilters = lineFilters || {}
|
|
201
192
|
this._examplePatterns = examplePatterns || []
|
|
@@ -205,18 +196,49 @@ export default class TestRunner {
|
|
|
205
196
|
this._failedTests = 0
|
|
206
197
|
this._successfulTests = 0
|
|
207
198
|
this._testsCount = 0
|
|
208
|
-
this._activeAfterAllScopes = []
|
|
209
199
|
this._failedTestDetails = []
|
|
210
200
|
/** @type {{fullDescription: string, filePath: string, line: number} | null} */
|
|
211
201
|
this._lastTestContext = null
|
|
212
202
|
/** @type {Array<{fullDescription: string, filePath: string, line: number, durationMs: number}>} */
|
|
213
203
|
this._testDurations = []
|
|
204
|
+
/** @type {WeakMap<PackageTestDeclaration, {testArgs: TestArgs, testData: TestData}>} */
|
|
205
|
+
this._testCompatibility = new WeakMap()
|
|
206
|
+
/** @type {WeakSet<PackageTestDeclaration>} */
|
|
207
|
+
this._injectedTests = new WeakSet()
|
|
208
|
+
/** @type {WeakSet<PackageTestDeclaration>} */
|
|
209
|
+
this._completedTests = new WeakSet()
|
|
210
|
+
/** @type {WeakMap<PackageTestDeclaration, {descriptions: string[], testDescription: string, fullDescription: string, ownerFilePath: string | undefined, suites: PackageSuiteDeclaration[]}>} */
|
|
211
|
+
this._testMetadata = new WeakMap()
|
|
212
|
+
/** @type {WeakMap<PackageHookDeclaration, {declarationIndex: number, declarationScopeId: string | undefined, ownerFilePath: string | undefined}>} */
|
|
213
|
+
this._hookMetadata = new WeakMap()
|
|
214
|
+
/** @type {WeakMap<PackageTestDeclaration, Map<number, {abortRemainingTests: boolean, error: ReturnType<typeof JSON.parse>, failed: boolean}>>} */
|
|
215
|
+
this._attemptOutcomes = new WeakMap()
|
|
216
|
+
/** @type {Array<{suite: PackageSuiteDeclaration, phase: "beforeAll" | "afterAll", error: ReturnType<typeof JSON.parse>}>} */
|
|
217
|
+
this._suiteHookFailures = []
|
|
218
|
+
/** @type {Map<string, PackageTestDeclaration[]>} */
|
|
219
|
+
this._testsByFullName = new Map()
|
|
220
|
+
/** @type {WeakMap<PackageRegistration, string>} */
|
|
221
|
+
this._declarationOwners = new WeakMap()
|
|
222
|
+
/** @type {PackageTestRunner | undefined} */
|
|
223
|
+
this._packageRunner = undefined
|
|
224
|
+
/** @type {import("@velocious/testing/runner").TestRunResult | undefined} */
|
|
225
|
+
this._packageResult = undefined
|
|
226
|
+
/** @type {Map<string, TestData> | undefined} */
|
|
227
|
+
this._legacyFixtureDataByFullName = undefined
|
|
228
|
+
/** @type {{filePath?: string, line?: number}} */
|
|
229
|
+
this._legacyFixtureLocation = {}
|
|
214
230
|
this._attemptExecutor = new VelociousAttemptExecutor({testRunner: this})
|
|
215
231
|
this._runnerReporter = new VelociousRunnerReporter({testRunner: this})
|
|
216
232
|
this._suiteHookExecutor = new VelociousSuiteHookExecutor({testRunner: this})
|
|
217
233
|
this._testArguments = new VelociousTestArguments({testRunner: this})
|
|
218
234
|
}
|
|
219
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Gets the package declaration context.
|
|
238
|
+
* @returns {PackageTestContext} - Package declaration context.
|
|
239
|
+
*/
|
|
240
|
+
getTestContext() { return this._context }
|
|
241
|
+
|
|
220
242
|
/**
|
|
221
243
|
* Runs get configuration.
|
|
222
244
|
* @returns {import("../configuration.js").default} - The configuration.
|
|
@@ -258,24 +280,6 @@ export default class TestRunner {
|
|
|
258
280
|
return await this._profiler.runSpan(metadata, callback)
|
|
259
281
|
}
|
|
260
282
|
|
|
261
|
-
/**
|
|
262
|
-
* Adds declaration metadata to hooks only for an active profile.
|
|
263
|
-
* @template {AfterBeforeEachCallbackObjectType | BeforeAfterAllCallbackObjectType} T
|
|
264
|
-
* @param {T[]} hooks - Hooks declared in one scope.
|
|
265
|
-
* @param {string | undefined} declarationScopeId - Profile scope identifier.
|
|
266
|
-
* @param {string | undefined} ownerFilePath - Scope owner file.
|
|
267
|
-
* @returns {T[]} - Profile-aware hook entries.
|
|
268
|
-
*/
|
|
269
|
-
profileHookEntries(hooks, declarationScopeId, ownerFilePath) {
|
|
270
|
-
if (!this._profiler) return hooks
|
|
271
|
-
|
|
272
|
-
return hooks.map((hook, declarationIndex) => Object.assign({}, hook, {
|
|
273
|
-
declarationIndex: hook.declarationIndex ?? declarationIndex,
|
|
274
|
-
declarationScopeId: hook.declarationScopeId ?? declarationScopeId,
|
|
275
|
-
ownerFilePath: hook.ownerFilePath ?? ownerFilePath
|
|
276
|
-
}))
|
|
277
|
-
}
|
|
278
|
-
|
|
279
283
|
/**
|
|
280
284
|
* Runs normalize tags.
|
|
281
285
|
* @param {string[] | string | undefined} tags - Tags.
|
|
@@ -581,107 +585,6 @@ export default class TestRunner {
|
|
|
581
585
|
return new Set([...this._excludeTags, ...configTags])
|
|
582
586
|
}
|
|
583
587
|
|
|
584
|
-
/**
|
|
585
|
-
* Runs has matching tag.
|
|
586
|
-
* @param {string[] | string | undefined} testTags - Test tags.
|
|
587
|
-
* @param {Set<string>} tagSet - Tag set.
|
|
588
|
-
* @returns {boolean} - Whether any tags match.
|
|
589
|
-
*/
|
|
590
|
-
hasMatchingTag(testTags, tagSet) {
|
|
591
|
-
if (!tagSet.size) return false
|
|
592
|
-
|
|
593
|
-
const normalized = this.normalizeTags(testTags)
|
|
594
|
-
|
|
595
|
-
for (const tag of normalized) {
|
|
596
|
-
if (tagSet.has(tag)) return true
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
return false
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* Runs has runnable tests.
|
|
604
|
-
* @param {TestsArgument} tests - Tests.
|
|
605
|
-
* @param {string[]} [descriptions] - Description stack.
|
|
606
|
-
* @param {boolean} [lineMatchedInScope] - Whether line matched in scope.
|
|
607
|
-
* @returns {boolean} - Whether any tests in this scope will run.
|
|
608
|
-
*/
|
|
609
|
-
hasRunnableTests(tests, descriptions = [], lineMatchedInScope = false) {
|
|
610
|
-
for (const testDescription in tests.tests) {
|
|
611
|
-
const testData = tests.tests[testDescription]
|
|
612
|
-
const testArgs = /** @type {TestArgs} */ (Object.assign({}, testData.args))
|
|
613
|
-
const includeByLine = lineMatchedInScope || this.matchesLineFilter(testData)
|
|
614
|
-
|
|
615
|
-
if (this._onlyFocussed && !testArgs.focus) continue
|
|
616
|
-
if (this.shouldSkipTest(testArgs, testData, testDescription, descriptions, includeByLine)) continue
|
|
617
|
-
|
|
618
|
-
return true
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
for (const subDescription in tests.subs) {
|
|
622
|
-
const subTest = tests.subs[subDescription]
|
|
623
|
-
const scopeLineMatch = lineMatchedInScope || this.matchesLineFilter(subTest)
|
|
624
|
-
const nextDescriptions = descriptions.concat([subDescription])
|
|
625
|
-
|
|
626
|
-
if (this._onlyFocussed && !subTest.anyTestsFocussed) continue
|
|
627
|
-
if (this.hasRunnableTests(subTest, nextDescriptions, scopeLineMatch)) return true
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
return false
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
/**
|
|
634
|
-
* Runs should skip test.
|
|
635
|
-
* @param {TestArgs} testArgs - Test args.
|
|
636
|
-
* @param {TestData} testData - Test data.
|
|
637
|
-
* @param {string} testDescription - Test description.
|
|
638
|
-
* @param {string[]} descriptions - Description stack.
|
|
639
|
-
* @param {boolean} lineMatchedInScope - Whether line matched in scope.
|
|
640
|
-
* @returns {boolean} - Whether the test should be skipped.
|
|
641
|
-
*/
|
|
642
|
-
shouldSkipTest(testArgs, testData, testDescription, descriptions, lineMatchedInScope) {
|
|
643
|
-
if (this.hasTag(testArgs, "browser-only") && !this.isBrowserTestMode()) return true
|
|
644
|
-
if (this.hasMatchingTag(testArgs.tags, this.getExcludeTagSet())) return true
|
|
645
|
-
|
|
646
|
-
if (this._includeTagSet.size > 0 && !testArgs.focus) {
|
|
647
|
-
if (!this.hasMatchingTag(testArgs.tags, this._includeTagSet)) return true
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
if (this.getExamplePatterns().length > 0) {
|
|
651
|
-
const fullDescription = this.buildFullDescription(descriptions, testDescription)
|
|
652
|
-
const matches = this.getExamplePatterns().some((pattern) => {
|
|
653
|
-
pattern.lastIndex = 0
|
|
654
|
-
return pattern.test(fullDescription)
|
|
655
|
-
})
|
|
656
|
-
|
|
657
|
-
if (!matches) return true
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
const lineFilters = this.getLineFilters()
|
|
661
|
-
|
|
662
|
-
if (Object.keys(lineFilters).length > 0) {
|
|
663
|
-
if (!lineMatchedInScope && !this.matchesLineFilter(testData)) return true
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
return false
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
/**
|
|
670
|
-
* Runs matches line filter.
|
|
671
|
-
* @param {TestData | TestsArgument} entry - Test entry.
|
|
672
|
-
* @returns {boolean} - Whether line filter matches entry.
|
|
673
|
-
*/
|
|
674
|
-
matchesLineFilter(entry) {
|
|
675
|
-
if (!entry || !entry.filePath || !entry.line) return false
|
|
676
|
-
|
|
677
|
-
const filePath = path.resolve(entry.filePath)
|
|
678
|
-
const lines = this.getLineFilters()[filePath]
|
|
679
|
-
|
|
680
|
-
if (!lines || lines.length === 0) return false
|
|
681
|
-
|
|
682
|
-
return lines.includes(entry.line)
|
|
683
|
-
}
|
|
684
|
-
|
|
685
588
|
/**
|
|
686
589
|
* Runs build full description.
|
|
687
590
|
* @param {string[]} descriptions - Description stack.
|
|
@@ -1043,61 +946,48 @@ export default class TestRunner {
|
|
|
1043
946
|
|
|
1044
947
|
if (!this._profiler) {
|
|
1045
948
|
await environmentHandler.importTestFiles(this.getTestFiles())
|
|
1046
|
-
synchronizeTestingPackageTests(tests)
|
|
1047
949
|
return
|
|
1048
950
|
}
|
|
1049
951
|
|
|
1050
952
|
for (const testFile of this.getTestFiles()) {
|
|
1051
|
-
const existingRegistrations = this.testRegistrationObjects(
|
|
953
|
+
const existingRegistrations = this.testRegistrationObjects()
|
|
1052
954
|
|
|
1053
955
|
await this._profiler.measurePhase("imports", async () => {
|
|
1054
956
|
await environmentHandler.importTestFiles([testFile])
|
|
1055
957
|
}, {filePath: testFile})
|
|
1056
|
-
|
|
1057
|
-
this.assignTestRegistrationOwnership(tests, existingRegistrations, testFile)
|
|
958
|
+
this.assignTestRegistrationOwnership(existingRegistrations, testFile)
|
|
1058
959
|
}
|
|
1059
960
|
}
|
|
1060
961
|
|
|
1061
962
|
/**
|
|
1062
|
-
* Collects
|
|
1063
|
-
* @param {
|
|
1064
|
-
* @
|
|
1065
|
-
* @returns {Set<object>} - Registration identities.
|
|
963
|
+
* Collects package declaration objects by identity.
|
|
964
|
+
* @param {Set<PackageRegistration>} [registrations] - Accumulated identities.
|
|
965
|
+
* @returns {Set<PackageRegistration>} - Registration identities.
|
|
1066
966
|
*/
|
|
1067
|
-
testRegistrationObjects(
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
967
|
+
testRegistrationObjects(registrations = new Set()) {
|
|
968
|
+
const visit = (/** @type {PackageSuiteDeclaration} */ suite) => {
|
|
969
|
+
registrations.add(suite)
|
|
970
|
+
for (const hook of [...suite.hooks.beforeAll, ...suite.hooks.beforeEach, ...suite.hooks.afterEach, ...suite.hooks.afterAll]) {
|
|
971
|
+
registrations.add(hook)
|
|
972
|
+
}
|
|
973
|
+
for (const testDeclaration of suite.tests) registrations.add(testDeclaration)
|
|
974
|
+
for (const childSuite of suite.suites) visit(childSuite)
|
|
1072
975
|
}
|
|
1073
976
|
|
|
1074
|
-
for (const
|
|
1075
|
-
for (const childScope of Object.values(scope.subs)) this.testRegistrationObjects(childScope, registrations)
|
|
977
|
+
for (const suite of this.getTestContext().registry.suites) visit(suite)
|
|
1076
978
|
|
|
1077
979
|
return registrations
|
|
1078
980
|
}
|
|
1079
981
|
|
|
1080
982
|
/**
|
|
1081
|
-
* Assigns deterministic ownership to
|
|
1082
|
-
*
|
|
1083
|
-
* @param {TestsArgument} scope - Test scope.
|
|
1084
|
-
* @param {Set<object>} previousRegistrations - Identities present before import.
|
|
983
|
+
* Assigns deterministic ownership to package declarations added by one entry file.
|
|
984
|
+
* @param {Set<PackageRegistration>} previousRegistrations - Identities present before import.
|
|
1085
985
|
* @param {string} ownerFilePath - Importing entry file.
|
|
1086
986
|
* @returns {void}
|
|
1087
987
|
*/
|
|
1088
|
-
assignTestRegistrationOwnership(
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
for (const hook of [...scope.beforeAlls, ...scope.beforeEaches, ...scope.afterEaches, ...scope.afterAlls]) {
|
|
1092
|
-
if (!previousRegistrations.has(hook)) hook.ownerFilePath ??= ownerFilePath
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
for (const testData of Object.values(scope.tests)) {
|
|
1096
|
-
if (!previousRegistrations.has(testData)) testData.ownerFilePath ??= ownerFilePath
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
for (const childScope of Object.values(scope.subs)) {
|
|
1100
|
-
this.assignTestRegistrationOwnership(childScope, previousRegistrations, ownerFilePath)
|
|
988
|
+
assignTestRegistrationOwnership(previousRegistrations, ownerFilePath) {
|
|
989
|
+
for (const registration of this.testRegistrationObjects()) {
|
|
990
|
+
if (!previousRegistrations.has(registration)) this._declarationOwners.set(registration, ownerFilePath)
|
|
1101
991
|
}
|
|
1102
992
|
}
|
|
1103
993
|
|
|
@@ -1105,7 +995,7 @@ export default class TestRunner {
|
|
|
1105
995
|
* Runs is failed.
|
|
1106
996
|
* @returns {boolean} - Whether failed.
|
|
1107
997
|
*/
|
|
1108
|
-
isFailed() { return this._failedTests !== undefined && this._failedTests > 0 }
|
|
998
|
+
isFailed() { return this._failedTests !== undefined && (this._failedTests > 0 || this._packageResult?.status === "failed") }
|
|
1109
999
|
|
|
1110
1000
|
/**
|
|
1111
1001
|
* Runs get failed tests.
|
|
@@ -1194,7 +1084,7 @@ export default class TestRunner {
|
|
|
1194
1084
|
* @returns {number} - The executed tests count.
|
|
1195
1085
|
*/
|
|
1196
1086
|
getExecutedTestsCount() {
|
|
1197
|
-
return this._testDurations.length
|
|
1087
|
+
return this._packageResult?.tests.length ?? this._testDurations.length
|
|
1198
1088
|
}
|
|
1199
1089
|
|
|
1200
1090
|
/**
|
|
@@ -1220,17 +1110,79 @@ export default class TestRunner {
|
|
|
1220
1110
|
this._abortRemainingTests = false
|
|
1221
1111
|
this._failedTestDetails = []
|
|
1222
1112
|
this._testDurations = []
|
|
1113
|
+
this._testCompatibility = new WeakMap()
|
|
1114
|
+
this._injectedTests = new WeakSet()
|
|
1115
|
+
this._completedTests = new WeakSet()
|
|
1116
|
+
this._testMetadata = new WeakMap()
|
|
1117
|
+
this._hookMetadata = new WeakMap()
|
|
1118
|
+
this._attemptOutcomes = new WeakMap()
|
|
1119
|
+
this._suiteHookFailures = []
|
|
1120
|
+
this._testsByFullName = new Map()
|
|
1121
|
+
this._packageResult = undefined
|
|
1122
|
+
const context = this.getTestContext()
|
|
1123
|
+
/** @type {string | undefined} */
|
|
1124
|
+
let ownerFilePath
|
|
1125
|
+
|
|
1126
|
+
context.reset({config: true})
|
|
1127
|
+
context.setDeclarationLocator(() => this.captureTestDeclarationLocation(ownerFilePath))
|
|
1223
1128
|
const testingConfigPath = this.getConfiguration().getTesting()
|
|
1224
1129
|
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
await this.
|
|
1228
|
-
|
|
1130
|
+
await context.describe("", {databaseCleaning: {transaction: true}}, async () => {
|
|
1131
|
+
if (testingConfigPath) {
|
|
1132
|
+
await this.runProfileSpan({phase: "testing config/global setup"}, async () => {
|
|
1133
|
+
await this.getConfiguration().getEnvironmentHandler().importTestingConfigPath()
|
|
1134
|
+
})
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
if (!this._profiler) {
|
|
1138
|
+
await this.importTestFiles()
|
|
1139
|
+
} else {
|
|
1140
|
+
for (const testFile of this.getTestFiles()) {
|
|
1141
|
+
ownerFilePath = testFile
|
|
1142
|
+
const existingRegistrations = this.testRegistrationObjects()
|
|
1143
|
+
|
|
1144
|
+
await this._profiler.measurePhase("imports", async () => {
|
|
1145
|
+
await this.getConfiguration().getEnvironmentHandler().importTestFiles([testFile])
|
|
1146
|
+
}, {filePath: testFile})
|
|
1147
|
+
this.assignTestRegistrationOwnership(existingRegistrations, testFile)
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
})
|
|
1151
|
+
ownerFilePath = undefined
|
|
1152
|
+
this.analyzeDeclarations()
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
/**
|
|
1156
|
+
* Captures a test source location without attributing package/facade frames.
|
|
1157
|
+
* @param {string | undefined} ownerFilePath - Importing entry file fallback.
|
|
1158
|
+
* @returns {{filePath?: string, line?: number}} - Declaration location.
|
|
1159
|
+
*/
|
|
1160
|
+
captureTestDeclarationLocation(ownerFilePath) {
|
|
1161
|
+
const stack = new Error().stack?.split("\n") || []
|
|
1162
|
+
|
|
1163
|
+
for (const stackLine of stack) {
|
|
1164
|
+
const match = stackLine.match(/(?:\(|\s)(file:\/\/.*?|\/[^"]*?):(\d+):(\d+)\)?$/u)
|
|
1165
|
+
if (!match) continue
|
|
1166
|
+
|
|
1167
|
+
let filePath = match[1]
|
|
1168
|
+
if (filePath.startsWith("file://")) {
|
|
1169
|
+
try {
|
|
1170
|
+
filePath = fileURLToPath(filePath)
|
|
1171
|
+
} catch {
|
|
1172
|
+
continue
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
const resolvedFilePath = path.resolve(filePath)
|
|
1176
|
+
const portablePath = resolvedFilePath.replaceAll(path.sep, "/")
|
|
1177
|
+
|
|
1178
|
+
if (portablePath.endsWith("/src/testing/test-runner.js")) continue
|
|
1179
|
+
if (portablePath.endsWith("/src/testing/test.js")) continue
|
|
1180
|
+
if (portablePath.includes("/node_modules/@velocious/testing/")) continue
|
|
1181
|
+
|
|
1182
|
+
return {filePath: resolvedFilePath, line: Number(match[2])}
|
|
1229
1183
|
}
|
|
1230
1184
|
|
|
1231
|
-
|
|
1232
|
-
await this.analyzeTests(tests)
|
|
1233
|
-
this._onlyFocussed = this.anyTestsFocussed
|
|
1185
|
+
return ownerFilePath ? {filePath: ownerFilePath} : {}
|
|
1234
1186
|
}
|
|
1235
1187
|
|
|
1236
1188
|
/**
|
|
@@ -1355,13 +1307,7 @@ export default class TestRunner {
|
|
|
1355
1307
|
process.on("uncaughtException", onUncaughtException)
|
|
1356
1308
|
|
|
1357
1309
|
try {
|
|
1358
|
-
await this.
|
|
1359
|
-
afterEaches: [],
|
|
1360
|
-
beforeEaches: [],
|
|
1361
|
-
tests,
|
|
1362
|
-
descriptions: [],
|
|
1363
|
-
indentLevel: 0
|
|
1364
|
-
})
|
|
1310
|
+
await this.runPackageTests()
|
|
1365
1311
|
|
|
1366
1312
|
// A rejection scheduled by the final test (a detached rejected promise,
|
|
1367
1313
|
// or an afterCommit callback rejecting as the suite drains) is reported
|
|
@@ -1382,259 +1328,410 @@ export default class TestRunner {
|
|
|
1382
1328
|
* @returns {Promise<void>} - Resolves when cleanup hooks finish.
|
|
1383
1329
|
*/
|
|
1384
1330
|
async runAfterAllsForActiveScopes() {
|
|
1385
|
-
const
|
|
1386
|
-
/** @type {unknown[]} */
|
|
1387
|
-
const afterAllErrors = []
|
|
1331
|
+
const failureStart = this._suiteHookFailures.length
|
|
1388
1332
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1333
|
+
await this._packageRunner?.cleanupActiveSuites()
|
|
1334
|
+
this.throwAfterAllFailures(this._suiteHookFailures.slice(failureStart))
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/** Builds declaration metadata used only by framework adapters and projections. */
|
|
1338
|
+
analyzeDeclarations() {
|
|
1339
|
+
const visit = (/** @type {PackageSuiteDeclaration} */ suite, /** @type {PackageSuiteDeclaration[]} */ ancestors, /** @type {string | undefined} */ parentProfileScopeId) => {
|
|
1340
|
+
const suites = [...ancestors, suite]
|
|
1341
|
+
const descriptions = suites.map((entry) => entry.name).filter((name) => name !== "")
|
|
1342
|
+
const ownerFilePath = this._declarationOwners.get(suite) ?? suite.location.filePath
|
|
1343
|
+
const profileScopeId = this._profiler?.scopeId(suite, {
|
|
1344
|
+
descriptions,
|
|
1345
|
+
filePath: ownerFilePath,
|
|
1346
|
+
line: suite.location.line,
|
|
1347
|
+
parentId: parentProfileScopeId
|
|
1348
|
+
})
|
|
1349
|
+
|
|
1350
|
+
for (const hooks of Object.values(suite.hooks)) {
|
|
1351
|
+
hooks.forEach((hook, declarationIndex) => {
|
|
1352
|
+
this._hookMetadata.set(hook, {
|
|
1353
|
+
declarationIndex,
|
|
1354
|
+
declarationScopeId: profileScopeId,
|
|
1355
|
+
ownerFilePath: this._declarationOwners.get(hook) ?? hook.location.filePath ?? ownerFilePath
|
|
1356
|
+
})
|
|
1357
|
+
})
|
|
1394
1358
|
}
|
|
1395
|
-
}
|
|
1396
1359
|
|
|
1397
|
-
|
|
1360
|
+
for (const testDeclaration of suite.tests) {
|
|
1361
|
+
const fullDescription = this.buildFullDescription(descriptions, testDeclaration.name)
|
|
1362
|
+
const declarations = this._testsByFullName.get(fullDescription) || []
|
|
1363
|
+
|
|
1364
|
+
declarations.push(testDeclaration)
|
|
1365
|
+
this._testsByFullName.set(fullDescription, declarations)
|
|
1366
|
+
this._testMetadata.set(testDeclaration, {
|
|
1367
|
+
descriptions,
|
|
1368
|
+
testDescription: testDeclaration.name,
|
|
1369
|
+
fullDescription,
|
|
1370
|
+
ownerFilePath: this._declarationOwners.get(testDeclaration) ?? testDeclaration.location.filePath ?? ownerFilePath,
|
|
1371
|
+
suites
|
|
1372
|
+
})
|
|
1373
|
+
const legacyTestData = this._legacyFixtureDataByFullName?.get(fullDescription)
|
|
1374
|
+
if (legacyTestData) {
|
|
1375
|
+
this._testCompatibility.set(testDeclaration, {
|
|
1376
|
+
testArgs: this._testArguments.copy(testDeclaration),
|
|
1377
|
+
testData: legacyTestData
|
|
1378
|
+
})
|
|
1379
|
+
}
|
|
1380
|
+
this._testsCount++
|
|
1381
|
+
if (testDeclaration.state === "run" && (testDeclaration.focus || suites.some((entry) => entry.focus))) {
|
|
1382
|
+
this.anyTestsFocussed = true
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1398
1385
|
|
|
1399
|
-
|
|
1400
|
-
if (afterAllErrors.length > 1) {
|
|
1401
|
-
throw new AggregateError(afterAllErrors, "Multiple active afterAll scopes failed", {cause: afterAllErrors[0]})
|
|
1386
|
+
for (const childSuite of suite.suites) visit(childSuite, suites, profileScopeId)
|
|
1402
1387
|
}
|
|
1388
|
+
|
|
1389
|
+
for (const suite of this.getTestContext().registry.suites) visit(suite, [], undefined)
|
|
1403
1390
|
}
|
|
1404
1391
|
|
|
1405
1392
|
/**
|
|
1406
|
-
*
|
|
1407
|
-
* @param {
|
|
1408
|
-
* @returns {{
|
|
1393
|
+
* Gets package hook compatibility metadata.
|
|
1394
|
+
* @param {PackageHookDeclaration} hook - Package hook declaration.
|
|
1395
|
+
* @returns {{declarationIndex: number, declarationScopeId: string | undefined, ownerFilePath: string | undefined}} - Hook metadata.
|
|
1409
1396
|
*/
|
|
1410
|
-
|
|
1411
|
-
|
|
1397
|
+
hookMetadata(hook) {
|
|
1398
|
+
return this._hookMetadata.get(hook) || {declarationIndex: 0, declarationScopeId: undefined, ownerFilePath: hook.location.filePath}
|
|
1399
|
+
}
|
|
1412
1400
|
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1401
|
+
/**
|
|
1402
|
+
* Gets package test compatibility metadata.
|
|
1403
|
+
* @param {PackageTestDeclaration} test - Package test declaration.
|
|
1404
|
+
* @returns {{descriptions: string[], testDescription: string, fullDescription: string, ownerFilePath: string | undefined, suites: PackageSuiteDeclaration[]}} - Declaration metadata.
|
|
1405
|
+
*/
|
|
1406
|
+
testMetadata(test) {
|
|
1407
|
+
const metadata = this._testMetadata.get(test)
|
|
1408
|
+
if (!metadata) throw new Error(`Missing package test metadata: ${test.name}`)
|
|
1409
|
+
return metadata
|
|
1410
|
+
}
|
|
1416
1411
|
|
|
1417
|
-
|
|
1412
|
+
/**
|
|
1413
|
+
* Gets stable compatibility data for a package declaration.
|
|
1414
|
+
* @param {PackageTestDeclaration} test - Package test declaration.
|
|
1415
|
+
* @returns {{testArgs: TestArgs, testData: TestData}} - Stable compatibility data.
|
|
1416
|
+
*/
|
|
1417
|
+
testData(test) {
|
|
1418
|
+
let compatibility = this._testCompatibility.get(test)
|
|
1418
1419
|
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1420
|
+
if (!compatibility) {
|
|
1421
|
+
const testArgs = this._testArguments.copy(test)
|
|
1422
|
+
const metadata = this.testMetadata(test)
|
|
1423
|
+
const testData = {
|
|
1424
|
+
args: testArgs,
|
|
1425
|
+
declaration: test,
|
|
1426
|
+
filePath: test.location.filePath,
|
|
1427
|
+
function: test.callback,
|
|
1428
|
+
line: test.location.line,
|
|
1429
|
+
ownerFilePath: metadata.ownerFilePath
|
|
1422
1430
|
}
|
|
1431
|
+
compatibility = {testArgs, testData}
|
|
1432
|
+
this._testCompatibility.set(test, compatibility)
|
|
1423
1433
|
}
|
|
1424
1434
|
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
const {anyTestsFocussed} = this.analyzeTests(subTest)
|
|
1435
|
+
return compatibility
|
|
1436
|
+
}
|
|
1428
1437
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1438
|
+
/**
|
|
1439
|
+
* Injects framework collaborators into stable compatibility data once.
|
|
1440
|
+
* @param {PackageTestDeclaration} test - Package test declaration.
|
|
1441
|
+
* @returns {Promise<{testArgs: TestArgs, testData: TestData}>} - Injected compatibility data.
|
|
1442
|
+
*/
|
|
1443
|
+
async testCompatibility(test) {
|
|
1444
|
+
const compatibility = this.testData(test)
|
|
1432
1445
|
|
|
1433
|
-
|
|
1446
|
+
if (!this._injectedTests.has(test)) {
|
|
1447
|
+
await this._testArguments.inject(compatibility.testArgs)
|
|
1448
|
+
this._injectedTests.add(test)
|
|
1434
1449
|
}
|
|
1435
1450
|
|
|
1436
|
-
return
|
|
1451
|
+
return compatibility
|
|
1437
1452
|
}
|
|
1438
1453
|
|
|
1439
1454
|
/**
|
|
1440
|
-
*
|
|
1441
|
-
* @param {
|
|
1442
|
-
* @param {
|
|
1443
|
-
* @param {
|
|
1444
|
-
* @
|
|
1445
|
-
* @returns {Promise<void>} - Resolves after every hook runs.
|
|
1455
|
+
* Records a raw framework attempt outcome.
|
|
1456
|
+
* @param {PackageTestDeclaration} test - Package test declaration.
|
|
1457
|
+
* @param {number} attemptNumber - One-based attempt number.
|
|
1458
|
+
* @param {{abortRemainingTests: boolean, error: ReturnType<typeof JSON.parse>, failed: boolean}} outcome - Raw attempt outcome.
|
|
1459
|
+
* @returns {void}
|
|
1446
1460
|
*/
|
|
1447
|
-
|
|
1448
|
-
|
|
1461
|
+
recordAttemptOutcome(test, attemptNumber, outcome) {
|
|
1462
|
+
const outcomes = this._attemptOutcomes.get(test) || new Map()
|
|
1463
|
+
outcomes.set(attemptNumber, outcome)
|
|
1464
|
+
this._attemptOutcomes.set(test, outcomes)
|
|
1465
|
+
if (outcome.abortRemainingTests) this._abortRemainingTests = true
|
|
1449
1466
|
}
|
|
1450
1467
|
|
|
1451
1468
|
/**
|
|
1452
|
-
*
|
|
1453
|
-
* @param {
|
|
1454
|
-
* @param {
|
|
1455
|
-
* @
|
|
1456
|
-
* @param {TestsArgument} args.tests - Tests.
|
|
1457
|
-
* @param {string[]} args.descriptions - Descriptions.
|
|
1458
|
-
* @param {number} args.indentLevel - Indent level.
|
|
1459
|
-
* @param {boolean} [args.lineMatchedInScope] - Whether line matched in scope.
|
|
1460
|
-
* @param {string} [args.parentProfileScopeId] - Parent profile scope.
|
|
1461
|
-
* @returns {Promise<void>} - Resolves when complete.
|
|
1469
|
+
* Gets a raw framework attempt outcome.
|
|
1470
|
+
* @param {PackageTestDeclaration} test - Package test declaration.
|
|
1471
|
+
* @param {number} attemptNumber - One-based attempt number.
|
|
1472
|
+
* @returns {{abortRemainingTests: boolean, error: ReturnType<typeof JSON.parse>, failed: boolean} | undefined} - Raw attempt outcome.
|
|
1462
1473
|
*/
|
|
1463
|
-
|
|
1464
|
-
const environmentHandler = this.getConfiguration().getEnvironmentHandler()
|
|
1465
|
-
|
|
1466
|
-
environmentHandler.installSharedTransactionCoordinatorOwnerStorage(this._sharedTransactionCoordinatorOwnerStorage)
|
|
1467
|
-
environmentHandler.installTestDatabaseAccessScopeStorage(this._testDatabaseAccessScopeStorage)
|
|
1468
|
-
const leftPadding = " ".repeat(indentLevel * 2)
|
|
1469
|
-
const scopeOwnerFilePath = tests.ownerFilePath ?? tests.filePath
|
|
1470
|
-
const profileScopeId = this._profiler?.scopeId(tests, {
|
|
1471
|
-
descriptions,
|
|
1472
|
-
filePath: scopeOwnerFilePath,
|
|
1473
|
-
line: tests.line,
|
|
1474
|
-
parentId: parentProfileScopeId
|
|
1475
|
-
})
|
|
1476
|
-
const ownAfterEaches = [...this.profileHookEntries(tests.afterEaches, profileScopeId, scopeOwnerFilePath)].reverse()
|
|
1477
|
-
const ownBeforeEaches = this.profileHookEntries(tests.beforeEaches, profileScopeId, scopeOwnerFilePath)
|
|
1478
|
-
const newAfterEaches = [...ownAfterEaches, ...afterEaches]
|
|
1479
|
-
const newBeforeEaches = [...beforeEaches, ...ownBeforeEaches]
|
|
1480
|
-
const scopeLineMatch = lineMatchedInScope || this.matchesLineFilter(tests)
|
|
1481
|
-
const shouldRunAnyTests = this.hasRunnableTests(tests, descriptions, scopeLineMatch)
|
|
1474
|
+
attemptOutcome(test, attemptNumber) { return this._attemptOutcomes.get(test)?.get(attemptNumber) }
|
|
1482
1475
|
|
|
1483
|
-
|
|
1476
|
+
/**
|
|
1477
|
+
* Records a raw suite-hook failure.
|
|
1478
|
+
* @param {object} failure - Suite-hook failure.
|
|
1479
|
+
* @param {PackageSuiteDeclaration} failure.suite - Owning package suite.
|
|
1480
|
+
* @param {"beforeAll" | "afterAll"} failure.phase - Hook phase.
|
|
1481
|
+
* @param {ReturnType<typeof JSON.parse>} failure.error - Raw hook failure.
|
|
1482
|
+
* @returns {void}
|
|
1483
|
+
*/
|
|
1484
|
+
recordSuiteHookFailure(failure) { this._suiteHookFailures.push(failure) }
|
|
1484
1485
|
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1486
|
+
/**
|
|
1487
|
+
* Gets the raw ancestor setup failure for a package test.
|
|
1488
|
+
* @param {PackageTestDeclaration} test - Package test declaration.
|
|
1489
|
+
* @returns {ReturnType<typeof JSON.parse>} - Raw setup failure.
|
|
1490
|
+
*/
|
|
1491
|
+
setupFailureFor(test) {
|
|
1492
|
+
const suites = this.testMetadata(test).suites
|
|
1493
|
+
return this._suiteHookFailures.find((failure) => failure.phase === "beforeAll" && suites.includes(failure.suite))?.error
|
|
1494
|
+
}
|
|
1490
1495
|
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
const includeByLine = scopeLineMatch || this.matchesLineFilter(testData)
|
|
1500
|
-
|
|
1501
|
-
if (this._onlyFocussed && !testArgs.focus) continue
|
|
1502
|
-
if (this.shouldSkipTest(testArgs, testData, testDescription, descriptions, includeByLine)) continue
|
|
1503
|
-
|
|
1504
|
-
await this._testArguments.inject(testArgs)
|
|
1505
|
-
|
|
1506
|
-
const retryCount = typeof testArgs.retry === "number" && Number.isFinite(testArgs.retry)
|
|
1507
|
-
? Math.max(0, Math.floor(testArgs.retry))
|
|
1508
|
-
: 0
|
|
1509
|
-
const configTimeoutSeconds = typeof testConfig.defaultTimeoutSeconds === "number" ? testConfig.defaultTimeoutSeconds : undefined
|
|
1510
|
-
const timeoutSeconds = typeof testArgs.timeoutSeconds === "number" ? testArgs.timeoutSeconds : configTimeoutSeconds
|
|
1511
|
-
const useTimeout = typeof timeoutSeconds === "number" && Number.isFinite(timeoutSeconds) && timeoutSeconds > 0
|
|
1512
|
-
const timeoutMs = useTimeout ? timeoutSeconds * 1000 : undefined
|
|
1513
|
-
let retriesUsed = 0
|
|
1514
|
-
let attemptNumber = 1
|
|
1515
|
-
/**
|
|
1516
|
-
* Attempt console outputs.
|
|
1517
|
-
* @type {AttemptConsoleOutput[]} */
|
|
1518
|
-
const attemptConsoleOutputs = []
|
|
1519
|
-
|
|
1520
|
-
console.log(`${leftPadding}it ${testDescription}`)
|
|
1521
|
-
|
|
1522
|
-
const testStartMs = Date.now()
|
|
1523
|
-
|
|
1524
|
-
while (true) {
|
|
1525
|
-
const attemptResult = await this._attemptExecutor.execute({
|
|
1526
|
-
afterEaches: newAfterEaches,
|
|
1527
|
-
attemptNumber,
|
|
1528
|
-
beforeEaches: newBeforeEaches,
|
|
1529
|
-
descriptions,
|
|
1530
|
-
testArgs,
|
|
1531
|
-
testData,
|
|
1532
|
-
testDescription,
|
|
1533
|
-
timeoutMs
|
|
1534
|
-
})
|
|
1496
|
+
/**
|
|
1497
|
+
* Finds the next incomplete declaration with a package full name.
|
|
1498
|
+
* @param {string} fullName - Package full name.
|
|
1499
|
+
* @returns {PackageTestDeclaration | undefined} - Next matching declaration.
|
|
1500
|
+
*/
|
|
1501
|
+
findTestDeclaration(fullName) {
|
|
1502
|
+
return this._testsByFullName.get(fullName)?.find((test) => !this._completedTests.has(test))
|
|
1503
|
+
}
|
|
1535
1504
|
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
if (willRetry) retriesUsed++
|
|
1543
|
-
|
|
1544
|
-
await this._runnerReporter.reportAttempt({
|
|
1545
|
-
attemptConsoleOutputs,
|
|
1546
|
-
attemptNumber,
|
|
1547
|
-
descriptions,
|
|
1548
|
-
error: attemptResult.error,
|
|
1549
|
-
failed: attemptResult.failed,
|
|
1550
|
-
leftPadding,
|
|
1551
|
-
retriesUsed,
|
|
1552
|
-
retryCount,
|
|
1553
|
-
testArgs,
|
|
1554
|
-
testData,
|
|
1555
|
-
testDescription,
|
|
1556
|
-
willRetry
|
|
1557
|
-
})
|
|
1558
|
-
attemptNumber++
|
|
1505
|
+
/**
|
|
1506
|
+
* Marks a package declaration complete.
|
|
1507
|
+
* @param {PackageTestDeclaration} test - Completed declaration.
|
|
1508
|
+
* @returns {void}
|
|
1509
|
+
*/
|
|
1510
|
+
completeTestDeclaration(test) { this._completedTests.add(test) }
|
|
1559
1511
|
|
|
1560
|
-
|
|
1561
|
-
|
|
1512
|
+
/**
|
|
1513
|
+
* Gets the effective package retry count.
|
|
1514
|
+
* @param {PackageTestDeclaration} test - Package test declaration.
|
|
1515
|
+
* @returns {number} - Effective retry count.
|
|
1516
|
+
*/
|
|
1517
|
+
retryCount(test) {
|
|
1518
|
+
const value = test.options.retries ?? test.options.retry ?? this.getTestContext().config.retries
|
|
1519
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0
|
|
1520
|
+
}
|
|
1562
1521
|
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1522
|
+
/**
|
|
1523
|
+
* Normalizes retry inputs for the package execution boundary while retaining
|
|
1524
|
+
* the declarations' original public options after the run.
|
|
1525
|
+
* @returns {() => void} - Restores original declaration options.
|
|
1526
|
+
*/
|
|
1527
|
+
normalizePackageRetriesForExecution() {
|
|
1528
|
+
/** @type {PackageRetryOptionRestoration[]} */
|
|
1529
|
+
const restorations = []
|
|
1530
|
+
/**
|
|
1531
|
+
* Normalizes declarations in one suite.
|
|
1532
|
+
* @param {PackageSuiteDeclaration} suite - Suite whose tests are normalized.
|
|
1533
|
+
*/
|
|
1534
|
+
const visit = (suite) => {
|
|
1535
|
+
for (const test of suite.tests) {
|
|
1536
|
+
// Capture compatibility arguments before temporarily adapting package
|
|
1537
|
+
// execution options so callbacks retain their declared values/identity.
|
|
1538
|
+
this.testData(test)
|
|
1539
|
+
restorations.push({
|
|
1540
|
+
hadRetries: Object.hasOwn(test.options, "retries"),
|
|
1541
|
+
options: test.options,
|
|
1542
|
+
retries: test.options.retries
|
|
1568
1543
|
})
|
|
1569
|
-
|
|
1570
|
-
if (this._abortRemainingTests) break
|
|
1544
|
+
test.options.retries = this.retryCount(test)
|
|
1571
1545
|
}
|
|
1572
1546
|
|
|
1573
|
-
for (const
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
if (
|
|
1581
|
-
|
|
1582
|
-
await this.runTests({
|
|
1583
|
-
afterEaches: newAfterEaches,
|
|
1584
|
-
beforeEaches: newBeforeEaches,
|
|
1585
|
-
tests: subTest,
|
|
1586
|
-
descriptions: newDecriptions,
|
|
1587
|
-
indentLevel: indentLevel + 1,
|
|
1588
|
-
lineMatchedInScope: childScopeLineMatch,
|
|
1589
|
-
parentProfileScopeId: profileScopeId
|
|
1590
|
-
})
|
|
1591
|
-
}
|
|
1547
|
+
for (const childSuite of suite.suites) visit(childSuite)
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
for (const suite of this.getTestContext().registry.suites) visit(suite)
|
|
1551
|
+
|
|
1552
|
+
return () => {
|
|
1553
|
+
for (const restoration of restorations) {
|
|
1554
|
+
if (restoration.hadRetries) restoration.options.retries = restoration.retries
|
|
1555
|
+
else delete restoration.options.retries
|
|
1592
1556
|
}
|
|
1593
|
-
} catch (error) {
|
|
1594
|
-
scopeErrors.push(error)
|
|
1595
1557
|
}
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
/**
|
|
1561
|
+
* Records one completed test duration.
|
|
1562
|
+
* @param {{durationMs: number, filePath: string, fullDescription: string, line: number}} duration - Completed test duration.
|
|
1563
|
+
* @returns {void}
|
|
1564
|
+
*/
|
|
1565
|
+
recordTestDuration(duration) { this._testDurations.push(duration) }
|
|
1566
|
+
|
|
1567
|
+
/** Records one successful package result. */
|
|
1568
|
+
recordSuccessfulTest() { this._successfulTests++ }
|
|
1569
|
+
|
|
1570
|
+
/**
|
|
1571
|
+
* Records one failed package test in the legacy result projection.
|
|
1572
|
+
* @param {object} args - Failed test metadata.
|
|
1573
|
+
* @param {string[]} args.descriptions - Parent descriptions.
|
|
1574
|
+
* @param {ReturnType<typeof JSON.parse>} args.error - Raw failure.
|
|
1575
|
+
* @param {string} args.consoleOutput - Captured console output.
|
|
1576
|
+
* @param {TestData} args.testData - Compatibility test data.
|
|
1577
|
+
* @param {string} args.testDescription - Test description.
|
|
1578
|
+
* @returns {void}
|
|
1579
|
+
*/
|
|
1580
|
+
recordFailedTest({descriptions, error, consoleOutput, testData, testDescription}) {
|
|
1581
|
+
this._failedTests++
|
|
1582
|
+
this._failedTestDetails.push({
|
|
1583
|
+
fullDescription: this.buildFullDescription(descriptions, testDescription),
|
|
1584
|
+
filePath: testData.filePath,
|
|
1585
|
+
line: testData.line,
|
|
1586
|
+
error,
|
|
1587
|
+
consoleOutput: consoleOutput || undefined
|
|
1588
|
+
})
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
/**
|
|
1592
|
+
* Stores the completed package result.
|
|
1593
|
+
* @param {import("@velocious/testing/runner").TestRunResult} result - Package result.
|
|
1594
|
+
* @returns {void}
|
|
1595
|
+
*/
|
|
1596
|
+
recordPackageResult(result) { this._packageResult = result }
|
|
1597
|
+
|
|
1598
|
+
/**
|
|
1599
|
+
* Runs the package kernel with Velocious framework adapters.
|
|
1600
|
+
* @returns {Promise<void>} - Resolves after execution and teardown.
|
|
1601
|
+
*/
|
|
1602
|
+
async runPackageTests() {
|
|
1603
|
+
const environmentHandler = this.getConfiguration().getEnvironmentHandler()
|
|
1604
|
+
environmentHandler.installSharedTransactionCoordinatorOwnerStorage(this._sharedTransactionCoordinatorOwnerStorage)
|
|
1605
|
+
environmentHandler.installTestDatabaseAccessScopeStorage(this._testDatabaseAccessScopeStorage)
|
|
1606
|
+
this._packageRunner = new PackageTestRunner({
|
|
1607
|
+
context: this.getTestContext(),
|
|
1608
|
+
includeTags: this._includeTags,
|
|
1609
|
+
excludeTags: [...this.getExcludeTagSet(), ...(this.isBrowserTestMode() ? [] : ["browser-only"])],
|
|
1610
|
+
examples: this.getExamplePatterns(),
|
|
1611
|
+
lineFilters: this.getLineFilters(),
|
|
1612
|
+
includeTagMode: "any",
|
|
1613
|
+
focusedTestsBypassIncludeTags: true,
|
|
1614
|
+
omitEmptySuiteNames: true,
|
|
1615
|
+
attemptExecutorOwnsTimeout: true,
|
|
1616
|
+
attemptExecutor: (input) => this._attemptExecutor.execute(input),
|
|
1617
|
+
testArgumentResolver: (input) => this._testArguments.resolve(input),
|
|
1618
|
+
suiteHookExecutor: (input) => this._suiteHookExecutor.execute(input),
|
|
1619
|
+
reporter: this._runnerReporter
|
|
1620
|
+
})
|
|
1621
|
+
const failureStart = this._suiteHookFailures.length
|
|
1622
|
+
const restoreRetryOptions = this.normalizePackageRetriesForExecution()
|
|
1623
|
+
let result
|
|
1596
1624
|
|
|
1597
1625
|
try {
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
const scopeIndex = this._activeAfterAllScopes.indexOf(scopeEntry)
|
|
1626
|
+
try {
|
|
1627
|
+
result = await this._packageRunner.run()
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
if (!(error instanceof AbortRemainingTestsError)) throw error
|
|
1603
1630
|
|
|
1604
|
-
|
|
1605
|
-
|
|
1631
|
+
const afterAll = this.afterAllOutcome(this._suiteHookFailures.slice(failureStart))
|
|
1632
|
+
if (afterAll.failed) this.recordTimeoutCleanupFailure(afterAll.error, "afterAll")
|
|
1633
|
+
return
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
this.recordPackageResult(result)
|
|
1637
|
+
this.throwAfterAllFailures(this._suiteHookFailures.slice(failureStart))
|
|
1638
|
+
} finally {
|
|
1639
|
+
restoreRetryOptions()
|
|
1606
1640
|
}
|
|
1641
|
+
}
|
|
1607
1642
|
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1643
|
+
/**
|
|
1644
|
+
* Aggregates raw after-all failures without using error truthiness.
|
|
1645
|
+
* @param {Array<{phase: "beforeAll" | "afterAll", error: ReturnType<typeof JSON.parse>}>} failures - Hook failures.
|
|
1646
|
+
* @returns {{failed: false} | {failed: true, error: ReturnType<typeof JSON.parse>}} - Explicit afterAll outcome.
|
|
1647
|
+
*/
|
|
1648
|
+
afterAllOutcome(failures) {
|
|
1649
|
+
const afterAllErrors = failures.filter((failure) => failure.phase === "afterAll").map((failure) => failure.error)
|
|
1612
1650
|
|
|
1613
|
-
|
|
1614
|
-
|
|
1651
|
+
if (afterAllErrors.length === 0) return {failed: false}
|
|
1652
|
+
if (afterAllErrors.length === 1) return {failed: true, error: afterAllErrors[0]}
|
|
1653
|
+
return {
|
|
1654
|
+
failed: true,
|
|
1655
|
+
error: new AggregateError(afterAllErrors, "Multiple active afterAll scopes failed", {cause: afterAllErrors[0]})
|
|
1615
1656
|
}
|
|
1616
|
-
if (scopeErrors.length == 1) throw scopeErrors[0]
|
|
1617
|
-
if (scopeErrors.length > 1) throw new AggregateError(scopeErrors, "Test scope and afterAll cleanup failed", {cause: scopeErrors[0]})
|
|
1618
1657
|
}
|
|
1619
1658
|
|
|
1620
1659
|
/**
|
|
1621
|
-
*
|
|
1622
|
-
* @param {
|
|
1623
|
-
* @returns {
|
|
1660
|
+
* Throws one raw or aggregated after-all failure.
|
|
1661
|
+
* @param {Array<{phase: "beforeAll" | "afterAll", error: ReturnType<typeof JSON.parse>}>} failures - Hook failures.
|
|
1662
|
+
* @returns {void}
|
|
1624
1663
|
*/
|
|
1625
|
-
|
|
1626
|
-
|
|
1664
|
+
throwAfterAllFailures(failures) {
|
|
1665
|
+
const afterAll = this.afterAllOutcome(failures)
|
|
1627
1666
|
|
|
1628
|
-
|
|
1667
|
+
if (afterAll.failed) throw afterAll.error
|
|
1668
|
+
}
|
|
1629
1669
|
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1670
|
+
/**
|
|
1671
|
+
* Compatibility helper for focused framework lifecycle specs. It converts an
|
|
1672
|
+
* explicit legacy fixture into isolated package declarations; the package
|
|
1673
|
+
* runner remains the sole execution engine.
|
|
1674
|
+
* @param {object} args - Legacy fixture arguments.
|
|
1675
|
+
* @param {TestsArgument} args.tests - Fixture tree.
|
|
1676
|
+
* @returns {Promise<void>} - Resolves after package execution.
|
|
1677
|
+
*/
|
|
1678
|
+
async runTests({tests}) {
|
|
1679
|
+
const context = createTestContext()
|
|
1680
|
+
const originalContext = this._context
|
|
1681
|
+
context.configureTests({
|
|
1682
|
+
consoleOutput: originalContext.config.consoleOutput,
|
|
1683
|
+
defaultTimeoutMs: originalContext.config.defaultTimeoutMs,
|
|
1684
|
+
excludeTags: originalContext.config.excludeTags,
|
|
1685
|
+
failedConsoleOutputMaxLines: originalContext.config.failedConsoleOutputMaxLines,
|
|
1686
|
+
retries: originalContext.config.retries
|
|
1687
|
+
})
|
|
1688
|
+
this._context = context
|
|
1689
|
+
this._testsCount = 0
|
|
1690
|
+
this._testCompatibility = new WeakMap()
|
|
1691
|
+
this._injectedTests = new WeakSet()
|
|
1692
|
+
this._completedTests = new WeakSet()
|
|
1693
|
+
this._testMetadata = new WeakMap()
|
|
1694
|
+
this._hookMetadata = new WeakMap()
|
|
1695
|
+
this._attemptOutcomes = new WeakMap()
|
|
1696
|
+
this._suiteHookFailures = []
|
|
1697
|
+
this._testsByFullName = new Map()
|
|
1698
|
+
this._legacyFixtureDataByFullName = new Map()
|
|
1699
|
+
context.setDeclarationLocator(() => this._legacyFixtureLocation)
|
|
1700
|
+
this.declareLegacyFixture(context, "", tests, [])
|
|
1701
|
+
this.analyzeDeclarations()
|
|
1636
1702
|
|
|
1637
|
-
|
|
1703
|
+
try {
|
|
1704
|
+
await this.runPackageTests()
|
|
1705
|
+
} finally {
|
|
1706
|
+
this._context = originalContext
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
/**
|
|
1711
|
+
* Declares an isolated legacy-shaped test fixture into a package context.
|
|
1712
|
+
* @param {PackageTestContext} context - Isolated package context.
|
|
1713
|
+
* @param {string} name - Suite name.
|
|
1714
|
+
* @param {TestsArgument} scope - Legacy fixture scope.
|
|
1715
|
+
* @param {string[]} descriptions - Ancestor descriptions.
|
|
1716
|
+
* @returns {void}
|
|
1717
|
+
*/
|
|
1718
|
+
declareLegacyFixture(context, name, scope, descriptions) {
|
|
1719
|
+
this._legacyFixtureLocation = {filePath: scope.filePath, line: scope.line}
|
|
1720
|
+
context.describe(name, scope.args || {}, () => {
|
|
1721
|
+
for (const hook of scope.beforeAlls || []) context.beforeAll(hook.callback)
|
|
1722
|
+
for (const hook of scope.beforeEaches || []) context.beforeEach(hook.callback)
|
|
1723
|
+
for (const hook of scope.afterEaches || []) context.afterEach(hook.callback)
|
|
1724
|
+
for (const hook of scope.afterAlls || []) context.afterAll(hook.callback)
|
|
1725
|
+
const nextDescriptions = name === "" ? descriptions : [...descriptions, name]
|
|
1726
|
+
for (const [testName, testData] of Object.entries(scope.tests || {})) {
|
|
1727
|
+
this._legacyFixtureLocation = {filePath: testData.filePath, line: testData.line}
|
|
1728
|
+
this._legacyFixtureDataByFullName?.set(this.buildFullDescription(nextDescriptions, testName), testData)
|
|
1729
|
+
context.it(testName, testData.args, testData.function)
|
|
1730
|
+
}
|
|
1731
|
+
for (const [suiteName, childScope] of Object.entries(scope.subs || {})) {
|
|
1732
|
+
this.declareLegacyFixture(context, suiteName, childScope, nextDescriptions)
|
|
1733
|
+
}
|
|
1734
|
+
})
|
|
1638
1735
|
}
|
|
1639
1736
|
|
|
1640
1737
|
/**
|
|
@@ -1760,57 +1857,4 @@ export default class TestRunner {
|
|
|
1760
1857
|
}
|
|
1761
1858
|
}
|
|
1762
1859
|
|
|
1763
|
-
/**
|
|
1764
|
-
* Runs start console capture.
|
|
1765
|
-
* @param {object} [args] - Options object.
|
|
1766
|
-
* @param {boolean} [args.passthrough] - Whether to pass through to the original console.
|
|
1767
|
-
* @returns {() => string} - Stops the capture and returns captured text.
|
|
1768
|
-
*/
|
|
1769
|
-
startConsoleCapture({passthrough = false} = {}) {
|
|
1770
|
-
/**
|
|
1771
|
-
* Lines.
|
|
1772
|
-
* @type {string[]} */
|
|
1773
|
-
const lines = []
|
|
1774
|
-
/**
|
|
1775
|
-
* Console object.
|
|
1776
|
-
* @type {Record<ConsoleMethodName, (...args: Array<ReturnType<typeof JSON.parse>>) => void>} */
|
|
1777
|
-
const consoleObject = /** @type {Record<ConsoleMethodName, (...args: Array<ReturnType<typeof JSON.parse>>) => void>} */ (console)
|
|
1778
|
-
/**
|
|
1779
|
-
* Original console methods captured as direct references so stopping restores
|
|
1780
|
-
* the exact method that was installed at capture start.
|
|
1781
|
-
* @type {Record<ConsoleMethodName, (...args: Array<ReturnType<typeof JSON.parse>>) => void>} */
|
|
1782
|
-
const originalConsoleMethods = {
|
|
1783
|
-
debug: consoleObject.debug,
|
|
1784
|
-
error: consoleObject.error,
|
|
1785
|
-
info: consoleObject.info,
|
|
1786
|
-
log: consoleObject.log,
|
|
1787
|
-
warn: consoleObject.warn
|
|
1788
|
-
}
|
|
1789
|
-
let stopped = false
|
|
1790
|
-
let outputText = ""
|
|
1791
|
-
|
|
1792
|
-
for (const methodName of CAPTURED_CONSOLE_METHODS) {
|
|
1793
|
-
consoleObject[methodName] = (...args) => {
|
|
1794
|
-
lines.push(`[${new Date().toISOString()}] [${methodName}] ${format(...args)}`)
|
|
1795
|
-
|
|
1796
|
-
if (passthrough) {
|
|
1797
|
-
originalConsoleMethods[methodName].apply(consoleObject, args)
|
|
1798
|
-
}
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
|
|
1802
|
-
return () => {
|
|
1803
|
-
if (!stopped) {
|
|
1804
|
-
stopped = true
|
|
1805
|
-
|
|
1806
|
-
for (const methodName of CAPTURED_CONSOLE_METHODS) {
|
|
1807
|
-
consoleObject[methodName] = originalConsoleMethods[methodName]
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
outputText = lines.join("\n")
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
|
-
return outputText
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
1816
1860
|
}
|