velocious 1.0.642 → 1.0.644
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 +25 -2
- package/build/authorization/base-resource.js +18 -5
- package/build/configuration-types.js +34 -2
- package/build/database/record/index.js +42 -11
- package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +17 -3
- package/build/frontend-model-controller.js +10 -5
- package/build/frontend-model-resource/base-resource.js +95 -41
- package/build/frontend-models/base.js +1 -1
- package/build/src/authorization/base-resource.d.ts +19 -6
- package/build/src/authorization/base-resource.d.ts.map +1 -1
- package/build/src/authorization/base-resource.js +16 -6
- package/build/src/configuration-types.d.ts +72 -5
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +31 -3
- package/build/src/database/record/index.d.ts +44 -46
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +39 -12
- 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 +19 -4
- package/build/src/frontend-model-controller.d.ts +1 -1
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +9 -6
- package/build/src/frontend-model-resource/base-resource.d.ts +66 -32
- package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/base-resource.js +89 -42
- package/build/src/frontend-models/base.d.ts +1 -0
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +2 -2
- package/build/src/sync/assets/cache.d.ts +288 -0
- package/build/src/sync/assets/cache.d.ts.map +1 -0
- package/build/src/sync/assets/cache.js +883 -0
- package/build/src/sync/assets/types.d.ts +199 -0
- package/build/src/sync/assets/types.d.ts.map +1 -0
- package/build/src/sync/assets/types.js +56 -0
- package/build/src/sync/sync-api-controller.d.ts.map +1 -1
- package/build/src/sync/sync-api-controller.js +6 -5
- package/build/src/sync/sync-envelope-replay-service.d.ts +1 -1
- package/build/src/sync/sync-envelope-replay-service.d.ts.map +1 -1
- package/build/src/sync/sync-envelope-replay-service.js +3 -2
- package/build/src/sync/sync-resource-base.d.ts +2 -0
- package/build/src/sync/sync-resource-base.d.ts.map +1 -1
- package/build/src/sync/sync-resource-base.js +3 -1
- package/build/src/testing/test-runner.d.ts +8 -0
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +49 -548
- package/build/src/testing/velocious-attempt-executor.d.ts +69 -0
- package/build/src/testing/velocious-attempt-executor.d.ts.map +1 -0
- package/build/src/testing/velocious-attempt-executor.js +402 -0
- package/build/src/testing/velocious-runner-reporter.d.ts +72 -0
- package/build/src/testing/velocious-runner-reporter.d.ts.map +1 -0
- package/build/src/testing/velocious-runner-reporter.js +152 -0
- package/build/src/testing/velocious-suite-hook-executor.d.ts +37 -0
- package/build/src/testing/velocious-suite-hook-executor.d.ts.map +1 -0
- package/build/src/testing/velocious-suite-hook-executor.js +66 -0
- package/build/src/testing/velocious-test-arguments.d.ts +34 -0
- package/build/src/testing/velocious-test-arguments.d.ts.map +1 -0
- package/build/src/testing/velocious-test-arguments.js +47 -0
- package/build/src/utils/sha256-bytes-hex.d.ts +7 -0
- package/build/src/utils/sha256-bytes-hex.d.ts.map +1 -0
- package/build/src/utils/sha256-bytes-hex.js +117 -0
- package/build/src/utils/sha256-hex.d.ts +2 -4
- package/build/src/utils/sha256-hex.d.ts.map +1 -1
- package/build/src/utils/sha256-hex.js +6 -120
- package/build/sync/assets/cache.js +998 -0
- package/build/sync/assets/types.js +62 -0
- package/build/sync/sync-api-controller.js +5 -5
- package/build/sync/sync-envelope-replay-service.js +2 -1
- package/build/sync/sync-resource-base.js +3 -0
- package/build/testing/test-runner.js +46 -584
- package/build/testing/velocious-attempt-executor.js +431 -0
- package/build/testing/velocious-runner-reporter.js +166 -0
- package/build/testing/velocious-suite-hook-executor.js +71 -0
- package/build/testing/velocious-test-arguments.js +55 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/utils/sha256-bytes-hex.js +132 -0
- package/build/utils/sha256-hex.js +7 -135
- package/package.json +1 -1
- package/src/authorization/base-resource.js +18 -5
- package/src/configuration-types.js +34 -2
- package/src/database/record/index.js +42 -11
- package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +17 -3
- package/src/frontend-model-controller.js +10 -5
- package/src/frontend-model-resource/base-resource.js +95 -41
- package/src/frontend-models/base.js +1 -1
- package/src/sync/assets/cache.js +998 -0
- package/src/sync/assets/types.js +62 -0
- package/src/sync/sync-api-controller.js +5 -5
- package/src/sync/sync-envelope-replay-service.js +2 -1
- package/src/sync/sync-resource-base.js +3 -0
- package/src/testing/test-runner.js +46 -584
- package/src/testing/velocious-attempt-executor.js +431 -0
- package/src/testing/velocious-runner-reporter.js +166 -0
- package/src/testing/velocious-suite-hook-executor.js +71 -0
- package/src/testing/velocious-test-arguments.js +55 -0
- package/src/utils/sha256-bytes-hex.js +132 -0
- package/src/utils/sha256-hex.js +7 -135
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import {addTrackedStackToError} from "../utils/with-tracked-stack.js"
|
|
4
3
|
import fs from "node:fs/promises"
|
|
5
4
|
import path from "path"
|
|
6
5
|
import {format} from "node:util"
|
|
7
6
|
import {AsyncLocalStorage} from "node:async_hooks"
|
|
8
7
|
import Application from "../../src/application.js"
|
|
9
|
-
import BacktraceCleaner from "../utils/backtrace-cleaner-node.js"
|
|
10
|
-
import {TestDatabaseAccessRevokedError} from "../environment-handlers/base.js"
|
|
11
8
|
import RequestClient from "./request-client.js"
|
|
12
9
|
import picocolors from "picocolors"
|
|
13
10
|
import restArgsError from "../utils/rest-args-error.js"
|
|
14
|
-
import {testConfig,
|
|
11
|
+
import {testConfig, tests} from "./test.js"
|
|
15
12
|
import {pathToFileURL} from "url"
|
|
16
|
-
import {clearDeliveries} from "../mailer.js"
|
|
17
13
|
import SharedTransactionBroker from "./shared-transaction-broker.js"
|
|
18
14
|
import { SHARED_TRANSACTION_BROKER_ENV } from "./shared-transaction-proxy-driver.js"
|
|
19
15
|
import {synchronizeTestingPackageTests} from "./testing-package-adapter.js"
|
|
16
|
+
import VelociousAttemptExecutor from "./velocious-attempt-executor.js"
|
|
17
|
+
import VelociousRunnerReporter from "./velocious-runner-reporter.js"
|
|
18
|
+
import VelociousSuiteHookExecutor from "./velocious-suite-hook-executor.js"
|
|
19
|
+
import VelociousTestArguments from "./velocious-test-arguments.js"
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* ConsoleMethodName type.
|
|
@@ -120,9 +120,8 @@ import {synchronizeTestingPackageTests} from "./testing-package-adapter.js"
|
|
|
120
120
|
* @property {Record<string, TestsArgument>} subs - Optional child nodes. Each item is another `Node`, allowing recursion.
|
|
121
121
|
*/
|
|
122
122
|
/**
|
|
123
|
-
* Marks the error thrown by
|
|
124
|
-
* lifecycle
|
|
125
|
-
* ordinary test failure (the promise already settled).
|
|
123
|
+
* Marks the error thrown by the attempt timeout so the runner can distinguish
|
|
124
|
+
* detached lifecycle cleanup from an ordinary test failure.
|
|
126
125
|
* @typedef {Error & {velociousTestTimeout?: true}} TestTimeoutError
|
|
127
126
|
*/
|
|
128
127
|
/**
|
|
@@ -145,94 +144,6 @@ import {synchronizeTestingPackageTests} from "./testing-package-adapter.js"
|
|
|
145
144
|
* @property {import("../database/pool/base.js").TestSharedConnectionRegistration | undefined} sharedRegistration - Physical-key shared registration once published.
|
|
146
145
|
*/
|
|
147
146
|
|
|
148
|
-
/**
|
|
149
|
-
* Runs run with timeout.
|
|
150
|
-
*
|
|
151
|
-
* On timeout the wrapped `promise` is NOT cancelled — it keeps running detached.
|
|
152
|
-
* The rejected error is tagged with `velociousTestTimeout` so the runner knows
|
|
153
|
-
* the lifecycle (and its afterEach database cleanup) is still in flight and can
|
|
154
|
-
* wait for it to settle before the next test reuses the shared connection.
|
|
155
|
-
* @param {Promise<ReturnType<typeof JSON.parse>> | ReturnType<typeof JSON.parse>} promise - Promise or value.
|
|
156
|
-
* @param {number} timeoutMs - Timeout in milliseconds.
|
|
157
|
-
* @param {string} testDescription - Test description.
|
|
158
|
-
* @returns {Promise<ReturnType<typeof JSON.parse>>} - Resolves or rejects based on timeout or promise result.
|
|
159
|
-
*/
|
|
160
|
-
function runWithTimeout(promise, timeoutMs, testDescription) {
|
|
161
|
-
const timeoutSeconds = (timeoutMs / 1000).toFixed(3).replace(/\.?0+$/, "")
|
|
162
|
-
/** @type {TestTimeoutError} */
|
|
163
|
-
const timeoutError = new Error(`Timed out after ${timeoutSeconds}s: ${testDescription}`)
|
|
164
|
-
timeoutError.velociousTestTimeout = true
|
|
165
|
-
|
|
166
|
-
return new Promise((resolve, reject) => {
|
|
167
|
-
const timeout = setTimeout(() => reject(timeoutError), timeoutMs)
|
|
168
|
-
|
|
169
|
-
Promise.resolve(promise).then((result) => {
|
|
170
|
-
clearTimeout(timeout)
|
|
171
|
-
resolve(result)
|
|
172
|
-
}).catch((error) => {
|
|
173
|
-
clearTimeout(timeout)
|
|
174
|
-
reject(error)
|
|
175
|
-
})
|
|
176
|
-
})
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Waits for an abandoned (timed-out) test lifecycle to settle, bounded by a
|
|
181
|
-
* grace period, so its afterEach database cleanup runs on the shared connection
|
|
182
|
-
* before the next test reuses it. Returns the fulfillment/rejection outcome if
|
|
183
|
-
* the lifecycle settles, or a pending outcome once the grace elapses.
|
|
184
|
-
*
|
|
185
|
-
* The grace timer is kept ref'd so it cannot let Node exit with an unsettled
|
|
186
|
-
* top-level await when the timed-out lifecycle has no ref'd handles of its own
|
|
187
|
-
* (for example a stalled mocked async API). Once the caller continues past this
|
|
188
|
-
* await, the timer has already resolved and no longer anchors the event loop.
|
|
189
|
-
* @param {Promise<ReturnType<typeof JSON.parse>>} lifecycle - The abandoned per-test lifecycle promise.
|
|
190
|
-
* @param {number} graceMs - Maximum time to wait for the lifecycle to settle.
|
|
191
|
-
* @returns {Promise<{settled: false} | {settled: true, status: "fulfilled"} | {settled: true, status: "rejected", reason: unknown}>} - Settlement outcome.
|
|
192
|
-
*/
|
|
193
|
-
function awaitSettledOrGrace(lifecycle, graceMs) {
|
|
194
|
-
return new Promise((resolve) => {
|
|
195
|
-
let settled = false
|
|
196
|
-
const graceTimer = setTimeout(() => {
|
|
197
|
-
if (settled) return
|
|
198
|
-
|
|
199
|
-
settled = true
|
|
200
|
-
resolve({settled: false})
|
|
201
|
-
}, graceMs)
|
|
202
|
-
|
|
203
|
-
Promise.resolve(lifecycle).then(
|
|
204
|
-
() => {
|
|
205
|
-
if (settled) return
|
|
206
|
-
|
|
207
|
-
settled = true
|
|
208
|
-
clearTimeout(graceTimer)
|
|
209
|
-
resolve({settled: true, status: "fulfilled"})
|
|
210
|
-
},
|
|
211
|
-
(reason) => {
|
|
212
|
-
if (settled) return
|
|
213
|
-
|
|
214
|
-
settled = true
|
|
215
|
-
clearTimeout(graceTimer)
|
|
216
|
-
resolve({settled: true, status: "rejected", reason})
|
|
217
|
-
}
|
|
218
|
-
)
|
|
219
|
-
})
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Checks whether a late lifecycle stopped only because its test access was revoked.
|
|
224
|
-
* @param {unknown} error - Lifecycle rejection.
|
|
225
|
-
* @returns {boolean} - Whether every contained error is expected revocation.
|
|
226
|
-
*/
|
|
227
|
-
function isTestDatabaseAccessRevocation(error) {
|
|
228
|
-
if (error instanceof TestDatabaseAccessRevokedError) return true
|
|
229
|
-
if (error instanceof AggregateError) {
|
|
230
|
-
return error.errors.length > 0 && error.errors.every((nestedError) => isTestDatabaseAccessRevocation(nestedError))
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return false
|
|
234
|
-
}
|
|
235
|
-
|
|
236
147
|
/**
|
|
237
148
|
* Captured console methods.
|
|
238
149
|
* @type {ConsoleMethodName[]} */
|
|
@@ -300,6 +211,10 @@ export default class TestRunner {
|
|
|
300
211
|
this._lastTestContext = null
|
|
301
212
|
/** @type {Array<{fullDescription: string, filePath: string, line: number, durationMs: number}>} */
|
|
302
213
|
this._testDurations = []
|
|
214
|
+
this._attemptExecutor = new VelociousAttemptExecutor({testRunner: this})
|
|
215
|
+
this._runnerReporter = new VelociousRunnerReporter({testRunner: this})
|
|
216
|
+
this._suiteHookExecutor = new VelociousSuiteHookExecutor({testRunner: this})
|
|
217
|
+
this._testArguments = new VelociousTestArguments({testRunner: this})
|
|
303
218
|
}
|
|
304
219
|
|
|
305
220
|
/**
|
|
@@ -1530,28 +1445,7 @@ export default class TestRunner {
|
|
|
1530
1445
|
* @returns {Promise<void>} - Resolves after every hook runs.
|
|
1531
1446
|
*/
|
|
1532
1447
|
async runAfterEaches({afterEaches, testArgs, testData}) {
|
|
1533
|
-
|
|
1534
|
-
const afterEachErrors = []
|
|
1535
|
-
|
|
1536
|
-
for (const afterEachData of afterEaches) {
|
|
1537
|
-
try {
|
|
1538
|
-
await this.runProfileSpan({
|
|
1539
|
-
phase: "afterEach",
|
|
1540
|
-
declarationIndex: afterEachData.declarationIndex,
|
|
1541
|
-
declarationScopeId: afterEachData.declarationScopeId,
|
|
1542
|
-
filePath: afterEachData.ownerFilePath
|
|
1543
|
-
}, async () => {
|
|
1544
|
-
await afterEachData.callback({configuration: this.getConfiguration(), testArgs, testData})
|
|
1545
|
-
})
|
|
1546
|
-
} catch (error) {
|
|
1547
|
-
afterEachErrors.push(error)
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
if (afterEachErrors.length == 1) throw afterEachErrors[0]
|
|
1552
|
-
if (afterEachErrors.length > 1) {
|
|
1553
|
-
throw new AggregateError(afterEachErrors, "Multiple afterEach hooks failed", {cause: afterEachErrors[0]})
|
|
1554
|
-
}
|
|
1448
|
+
await this._attemptExecutor.runAfterEaches({afterEaches, testArgs, testData})
|
|
1555
1449
|
}
|
|
1556
1450
|
|
|
1557
1451
|
/**
|
|
@@ -1597,32 +1491,17 @@ export default class TestRunner {
|
|
|
1597
1491
|
try {
|
|
1598
1492
|
const beforeAlls = this.profileHookEntries(tests.beforeAlls || [], profileScopeId, scopeOwnerFilePath)
|
|
1599
1493
|
|
|
1600
|
-
|
|
1601
|
-
await this.runProfileSpan({
|
|
1602
|
-
phase: "beforeAll",
|
|
1603
|
-
declarationIndex: beforeAllData.declarationIndex,
|
|
1604
|
-
declarationScopeId: beforeAllData.declarationScopeId,
|
|
1605
|
-
filePath: beforeAllData.ownerFilePath
|
|
1606
|
-
}, async () => {
|
|
1607
|
-
await beforeAllData.callback({configuration: this.getConfiguration()})
|
|
1608
|
-
})
|
|
1609
|
-
}
|
|
1494
|
+
await this._suiteHookExecutor.runBeforeAlls({hooks: beforeAlls})
|
|
1610
1495
|
|
|
1611
1496
|
for (const testDescription in tests.tests) {
|
|
1612
1497
|
const testData = tests.tests[testDescription]
|
|
1613
|
-
const testArgs =
|
|
1498
|
+
const testArgs = this._testArguments.copy(testData)
|
|
1614
1499
|
const includeByLine = scopeLineMatch || this.matchesLineFilter(testData)
|
|
1615
1500
|
|
|
1616
1501
|
if (this._onlyFocussed && !testArgs.focus) continue
|
|
1617
1502
|
if (this.shouldSkipTest(testArgs, testData, testDescription, descriptions, includeByLine)) continue
|
|
1618
1503
|
|
|
1619
|
-
|
|
1620
|
-
testArgs.application = await this.application()
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
|
-
if (testArgs.type == "request") {
|
|
1624
|
-
testArgs.client = await this.requestClient()
|
|
1625
|
-
}
|
|
1504
|
+
await this._testArguments.inject(testArgs)
|
|
1626
1505
|
|
|
1627
1506
|
const retryCount = typeof testArgs.retry === "number" && Number.isFinite(testArgs.retry)
|
|
1628
1507
|
? Math.max(0, Math.floor(testArgs.retry))
|
|
@@ -1643,435 +1522,42 @@ export default class TestRunner {
|
|
|
1643
1522
|
const testStartMs = Date.now()
|
|
1644
1523
|
|
|
1645
1524
|
while (true) {
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
* Defines caughtError.
|
|
1649
|
-
* @type {ReturnType<typeof JSON.parse>} */
|
|
1650
|
-
let caughtError
|
|
1651
|
-
/**
|
|
1652
|
-
* Defines failedError.
|
|
1653
|
-
* @type {ReturnType<typeof JSON.parse>} */
|
|
1654
|
-
let failedError
|
|
1655
|
-
/**
|
|
1656
|
-
* Defines lastError.
|
|
1657
|
-
* @type {ReturnType<typeof JSON.parse>} */
|
|
1658
|
-
let lastError
|
|
1659
|
-
let willRetry = false
|
|
1660
|
-
/**
|
|
1661
|
-
* The per-test lifecycle promise, hoisted so the timeout branch can
|
|
1662
|
-
* still wait for it to settle after runWithTimeout has abandoned it.
|
|
1663
|
-
* @type {Promise<ReturnType<typeof JSON.parse>> | undefined} */
|
|
1664
|
-
let testLifecycle
|
|
1665
|
-
/** @type {{pool: import("../database/pool/base.js").default, registration: import("../database/pool/base.js").TestSharedConnectionRegistration}[]} */
|
|
1666
|
-
let testSharedConnectionRegistrations = []
|
|
1667
|
-
let testSharedConnectionsActive = false
|
|
1668
|
-
/** @type {SharedTransactionBrokerRegistration | undefined} */
|
|
1669
|
-
let sharedTransactionBrokerRegistration
|
|
1670
|
-
/** @type {SharedTransactionBrokerRegistration | undefined} */
|
|
1671
|
-
let sharedTransactionBrokerPreparation
|
|
1672
|
-
/** @type {TransactionalTenantRegistration[]} */
|
|
1673
|
-
const transactionalTenantRegistrations = []
|
|
1674
|
-
/** @type {BrowserDummyConnectionRegistration[]} */
|
|
1675
|
-
const browserDummyConnectionRegistrations = []
|
|
1676
|
-
const testDatabaseAccessScope = {revoked: false}
|
|
1677
|
-
/** @type {Set<Error>} */
|
|
1678
|
-
const recordedTimeoutCleanupErrors = new Set()
|
|
1679
|
-
testArgs.registerTransactionalTenant = async (args) => {
|
|
1680
|
-
await this.registerTransactionalTenant(args, transactionalTenantRegistrations)
|
|
1681
|
-
}
|
|
1682
|
-
const stopConsoleCapture = this.startConsoleCapture({
|
|
1683
|
-
passthrough: testConfig.consoleOutput === "live"
|
|
1684
|
-
})
|
|
1685
|
-
const profiler = this._profiler
|
|
1686
|
-
const profileAttempt = profiler?.startAttempt({
|
|
1687
|
-
descriptions,
|
|
1525
|
+
const attemptResult = await this._attemptExecutor.execute({
|
|
1526
|
+
afterEaches: newAfterEaches,
|
|
1688
1527
|
attemptNumber,
|
|
1528
|
+
beforeEaches: newBeforeEaches,
|
|
1529
|
+
descriptions,
|
|
1530
|
+
testArgs,
|
|
1689
1531
|
testData,
|
|
1690
|
-
testDescription
|
|
1532
|
+
testDescription,
|
|
1533
|
+
timeoutMs
|
|
1691
1534
|
})
|
|
1692
|
-
let attemptTimedOut = false
|
|
1693
|
-
|
|
1694
|
-
try {
|
|
1695
|
-
// Run the whole per-test lifecycle (dummy/server startup, connection
|
|
1696
|
-
// acquisition, beforeEach hooks, the test body and afterEach hooks) as
|
|
1697
|
-
// one promise so the timeout below can cover all of it.
|
|
1698
|
-
const runLifecycleCallback = async () => await this.runWithDummyIfNeeded(testArgs, async () => {
|
|
1699
|
-
const useTransaction = testArgs.databaseCleaning?.transaction === true
|
|
1700
|
-
const shouldTruncate = testArgs.databaseCleaning?.truncate ?? !useTransaction
|
|
1701
|
-
const useSharedTestConnections = useTransaction || testArgs.type == "request"
|
|
1702
|
-
const useTestConnections = useSharedTestConnections || shouldTruncate
|
|
1703
|
-
const runTestAttempt = async () => {
|
|
1704
|
-
// Register dynamic candidates before hooks so transaction state changes
|
|
1705
|
-
// made during a hook are immediately visible to any in-process work.
|
|
1706
|
-
// Prepare transaction sharing before hooks so long-lived services cannot
|
|
1707
|
-
// use the shared connection while its coordinator is still missing.
|
|
1708
|
-
if (useSharedTestConnections) {
|
|
1709
|
-
testSharedConnectionRegistrations = this.activateTestSharedConnections()
|
|
1710
|
-
testSharedConnectionsActive = true
|
|
1711
|
-
}
|
|
1712
|
-
/** @type {unknown[]} */
|
|
1713
|
-
const lifecycleErrors = []
|
|
1714
|
-
let runCleanupHooks = false
|
|
1715
|
-
|
|
1716
|
-
try {
|
|
1717
|
-
if (useSharedTestConnections) {
|
|
1718
|
-
sharedTransactionBrokerPreparation = await this.prepareSharedTransactionBroker()
|
|
1719
|
-
}
|
|
1720
|
-
runCleanupHooks = true
|
|
1721
|
-
|
|
1722
|
-
clearDeliveries()
|
|
1723
|
-
for (const beforeEachData of newBeforeEaches) {
|
|
1724
|
-
await this.runProfileSpan({
|
|
1725
|
-
phase: "beforeEach",
|
|
1726
|
-
declarationIndex: beforeEachData.declarationIndex,
|
|
1727
|
-
declarationScopeId: beforeEachData.declarationScopeId,
|
|
1728
|
-
filePath: beforeEachData.ownerFilePath
|
|
1729
|
-
}, async () => {
|
|
1730
|
-
await beforeEachData.callback({configuration: this.getConfiguration(), testArgs, testData})
|
|
1731
|
-
})
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
|
-
if (useSharedTestConnections) {
|
|
1735
|
-
const activeSharedTransactionConnections = this.sharedTransactionConnections({transactionsOnly: true})
|
|
1736
|
-
if (sharedTransactionBrokerPreparation && !this.sharedTransactionBrokerMatchesConnections(sharedTransactionBrokerPreparation, activeSharedTransactionConnections)) {
|
|
1737
|
-
this.clearTestSharedConnections(testSharedConnectionRegistrations)
|
|
1738
|
-
testSharedConnectionRegistrations = []
|
|
1739
|
-
testSharedConnectionsActive = false
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
sharedTransactionBrokerRegistration = await this.startSharedTransactionBroker(sharedTransactionBrokerPreparation, activeSharedTransactionConnections)
|
|
1743
|
-
sharedTransactionBrokerPreparation = undefined
|
|
1744
|
-
if (sharedTransactionBrokerRegistration && !testSharedConnectionsActive) {
|
|
1745
|
-
testSharedConnectionRegistrations = this.activateTestSharedConnections()
|
|
1746
|
-
testSharedConnectionsActive = true
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
// Record which test is running so an async crash (an unhandled
|
|
1751
|
-
// rejection detached from any await) that fires during or shortly
|
|
1752
|
-
// after this test can be attributed to it in run()'s handler.
|
|
1753
|
-
this._lastTestContext = {
|
|
1754
|
-
fullDescription: this.buildFullDescription(descriptions, testDescription),
|
|
1755
|
-
filePath: testData.filePath ?? "<unknown>",
|
|
1756
|
-
line: testData.line ?? 0
|
|
1757
|
-
}
|
|
1758
|
-
await this.runProfileSpan({phase: "test body", filePath: testData.ownerFilePath ?? testData.filePath}, async () => {
|
|
1759
|
-
await testData.function(testArgs)
|
|
1760
|
-
})
|
|
1761
|
-
} catch (error) {
|
|
1762
|
-
lifecycleErrors.push(error)
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
if (runCleanupHooks) {
|
|
1766
|
-
try {
|
|
1767
|
-
// Framework-owned post-commit broadcasts are intentionally
|
|
1768
|
-
// detached; drain them before test cleanup so their DB
|
|
1769
|
-
// checkouts cannot leak into the next test's lifecycle.
|
|
1770
|
-
await this.getConfiguration().awaitPendingBroadcasts()
|
|
1771
|
-
} catch (error) {
|
|
1772
|
-
lifecycleErrors.push(error)
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
|
-
try {
|
|
1776
|
-
if (testSharedConnectionsActive) {
|
|
1777
|
-
this.clearTestSharedConnections(testSharedConnectionRegistrations)
|
|
1778
|
-
testSharedConnectionRegistrations = []
|
|
1779
|
-
testSharedConnectionsActive = false
|
|
1780
|
-
}
|
|
1781
|
-
} catch (error) {
|
|
1782
|
-
lifecycleErrors.push(error)
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
try {
|
|
1786
|
-
await this.stopSharedTransactionBroker(sharedTransactionBrokerRegistration || sharedTransactionBrokerPreparation)
|
|
1787
|
-
sharedTransactionBrokerRegistration = undefined
|
|
1788
|
-
sharedTransactionBrokerPreparation = undefined
|
|
1789
|
-
} catch (error) {
|
|
1790
|
-
lifecycleErrors.push(error)
|
|
1791
|
-
}
|
|
1792
|
-
|
|
1793
|
-
try {
|
|
1794
|
-
await this.runAfterEaches({afterEaches: newAfterEaches, testArgs, testData})
|
|
1795
|
-
} catch (error) {
|
|
1796
|
-
lifecycleErrors.push(error)
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
|
-
try {
|
|
1800
|
-
await this.cleanupTransactionalTenants(transactionalTenantRegistrations)
|
|
1801
|
-
} catch (error) {
|
|
1802
|
-
lifecycleErrors.push(error)
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
if (testSharedConnectionsActive) {
|
|
1807
|
-
try {
|
|
1808
|
-
this.clearTestSharedConnections(testSharedConnectionRegistrations)
|
|
1809
|
-
} catch (error) {
|
|
1810
|
-
lifecycleErrors.push(error)
|
|
1811
|
-
}
|
|
1812
|
-
testSharedConnectionsActive = false
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
|
-
if (lifecycleErrors.length == 1) throw lifecycleErrors[0]
|
|
1816
|
-
if (lifecycleErrors.length > 1) {
|
|
1817
|
-
throw new AggregateError(lifecycleErrors, "Test lifecycle and cleanup failed", {cause: lifecycleErrors[0]})
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
if (useTestConnections) {
|
|
1822
|
-
// Database cleaning requires one connection for beforeEach, the test
|
|
1823
|
-
// body and afterEach; only transactions and requests share it dynamically.
|
|
1824
|
-
await this.getConfiguration().ensureConnections({name: `Test: ${testDescription}`}, runTestAttempt)
|
|
1825
|
-
} else {
|
|
1826
|
-
await runTestAttempt()
|
|
1827
|
-
}
|
|
1828
|
-
}, browserDummyConnectionRegistrations)
|
|
1829
|
-
const lifecycleCallback = async () => await this.getConfiguration().runWithTestDatabaseAccessScope(testDatabaseAccessScope, runLifecycleCallback)
|
|
1830
|
-
testLifecycle = profileAttempt && profiler
|
|
1831
|
-
? profiler.runAttempt(profileAttempt, lifecycleCallback)
|
|
1832
|
-
: lifecycleCallback()
|
|
1833
|
-
|
|
1834
|
-
// Time out the ENTIRE lifecycle, not just the test body. A hang in any
|
|
1835
|
-
// phase — a connection checkout that never resolves, a beforeEach/afterEach
|
|
1836
|
-
// waiting on a lock, or dummy server startup — would otherwise stall the
|
|
1837
|
-
// whole run indefinitely (until CI kills the build) instead of failing the
|
|
1838
|
-
// single offending test.
|
|
1839
|
-
if (useTimeout && timeoutMs !== undefined) {
|
|
1840
|
-
await runWithTimeout(testLifecycle, timeoutMs, testDescription)
|
|
1841
|
-
} else {
|
|
1842
|
-
await testLifecycle
|
|
1843
|
-
}
|
|
1844
|
-
|
|
1845
|
-
// A test is successful only after its complete lifecycle settles.
|
|
1846
|
-
// Cleanup failures and timed-out detached work must not overlap the
|
|
1847
|
-
// final successful and failed counters used for executed-test totals.
|
|
1848
|
-
this._successfulTests++
|
|
1849
|
-
} catch (error) {
|
|
1850
|
-
caughtError = error
|
|
1851
|
-
lastError = error
|
|
1852
|
-
|
|
1853
|
-
// A timeout REJECTS while the lifecycle keeps running detached on the
|
|
1854
|
-
// shared per-suite connection — including its afterEach database
|
|
1855
|
-
// cleanup (e.g. transaction rollback). If the next test starts before
|
|
1856
|
-
// that rollback runs, its own startTransaction() implicitly COMMITS
|
|
1857
|
-
// the timed-out test's rows on the shared connection, poisoning every
|
|
1858
|
-
// later test in the shard (duplicate-key / foreign-key cascades from
|
|
1859
|
-
// leaked fixtures). Wait — bounded — for the abandoned lifecycle to
|
|
1860
|
-
// settle so its cleanup lands first. If it remains active after the
|
|
1861
|
-
// bounded grace, quarantine its browser connections and stop running
|
|
1862
|
-
// tests rather than sharing unsafe state.
|
|
1863
|
-
const timedOut = Boolean(/** @type {TestTimeoutError} */ (error)?.velociousTestTimeout)
|
|
1864
|
-
attemptTimedOut = timedOut
|
|
1865
|
-
|
|
1866
|
-
if (timedOut && testLifecycle) {
|
|
1867
|
-
const emergencyCleanupErrors = []
|
|
1868
|
-
|
|
1869
|
-
if (profileAttempt && profiler) profiler.finishAttempt(profileAttempt, "timed-out")
|
|
1870
|
-
const lifecycleOutcome = await awaitSettledOrGrace(testLifecycle, timeoutMs ?? 60000)
|
|
1871
|
-
|
|
1872
|
-
if (lifecycleOutcome.settled && lifecycleOutcome.status === "rejected") {
|
|
1873
|
-
emergencyCleanupErrors.push(lifecycleOutcome.reason)
|
|
1874
|
-
}
|
|
1875
|
-
|
|
1876
|
-
// If the abandoned lifecycle never settled within the grace, its
|
|
1877
|
-
// cleanup has not completed. Quarantine browser-owned connections
|
|
1878
|
-
// before any scope cleanup can race the abandoned callback.
|
|
1879
|
-
if (!lifecycleOutcome.settled) {
|
|
1880
|
-
testDatabaseAccessScope.revoked = true
|
|
1881
|
-
void testLifecycle.catch((cleanupError) => {
|
|
1882
|
-
if (isTestDatabaseAccessRevocation(cleanupError)) return
|
|
1883
|
-
this.recordTimeoutCleanupFailure(cleanupError, "test lifecycle", recordedTimeoutCleanupErrors)
|
|
1884
|
-
})
|
|
1885
|
-
const quarantine = this.quarantineBrowserDummyConnections(browserDummyConnectionRegistrations)
|
|
1886
|
-
const quarantineOutcome = await awaitSettledOrGrace(quarantine, timeoutMs ?? 60000)
|
|
1887
|
-
const usesBrowserTransactions = testArgs.databaseCleaning?.transaction === true
|
|
1888
|
-
const usesBrowserTruncation = testArgs.databaseCleaning?.truncate ?? !usesBrowserTransactions
|
|
1889
|
-
|
|
1890
|
-
this._abortRemainingTests = this.isBrowserTestMode()
|
|
1891
|
-
&& this.hasTag(testArgs, "dummy")
|
|
1892
|
-
&& (usesBrowserTransactions || usesBrowserTruncation)
|
|
1893
|
-
|
|
1894
|
-
if (quarantineOutcome.settled && quarantineOutcome.status === "rejected") {
|
|
1895
|
-
emergencyCleanupErrors.push(quarantineOutcome.reason)
|
|
1896
|
-
} else if (!quarantineOutcome.settled) {
|
|
1897
|
-
void quarantine.catch((cleanupError) => {
|
|
1898
|
-
this.recordTimeoutCleanupFailure(cleanupError, "browser dummy connection quarantine", recordedTimeoutCleanupErrors)
|
|
1899
|
-
})
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
|
-
try {
|
|
1904
|
-
if (testSharedConnectionsActive) {
|
|
1905
|
-
this.clearTestSharedConnections(testSharedConnectionRegistrations)
|
|
1906
|
-
testSharedConnectionRegistrations = []
|
|
1907
|
-
testSharedConnectionsActive = false
|
|
1908
|
-
}
|
|
1909
|
-
} catch (cleanupError) {
|
|
1910
|
-
emergencyCleanupErrors.push(cleanupError)
|
|
1911
|
-
}
|
|
1912
|
-
|
|
1913
|
-
const brokerCleanup = this.stopSharedTransactionBroker(sharedTransactionBrokerRegistration || sharedTransactionBrokerPreparation)
|
|
1914
|
-
const brokerCleanupOutcome = await awaitSettledOrGrace(brokerCleanup, timeoutMs ?? 60000)
|
|
1915
|
-
|
|
1916
|
-
if (brokerCleanupOutcome.settled && brokerCleanupOutcome.status === "rejected") {
|
|
1917
|
-
emergencyCleanupErrors.push(brokerCleanupOutcome.reason)
|
|
1918
|
-
} else if (!brokerCleanupOutcome.settled) {
|
|
1919
|
-
void brokerCleanup.catch((cleanupError) => {
|
|
1920
|
-
this.recordTimeoutCleanupFailure(cleanupError, "shared transaction broker", recordedTimeoutCleanupErrors)
|
|
1921
|
-
})
|
|
1922
|
-
}
|
|
1923
|
-
sharedTransactionBrokerRegistration = undefined
|
|
1924
|
-
sharedTransactionBrokerPreparation = undefined
|
|
1925
|
-
const emergencyCleanup = this.cleanupTransactionalTenants(transactionalTenantRegistrations, {discard: true})
|
|
1926
|
-
const emergencyCleanupOutcome = await awaitSettledOrGrace(emergencyCleanup, timeoutMs ?? 60000)
|
|
1927
|
-
|
|
1928
|
-
if (emergencyCleanupOutcome.settled && emergencyCleanupOutcome.status === "rejected") {
|
|
1929
|
-
emergencyCleanupErrors.push(emergencyCleanupOutcome.reason)
|
|
1930
|
-
} else if (!emergencyCleanupOutcome.settled) {
|
|
1931
|
-
// The timed-out attempt must not block the runner indefinitely, but a
|
|
1932
|
-
// later rollback/discard failure still becomes a visible test failure.
|
|
1933
|
-
void emergencyCleanup.catch((cleanupError) => {
|
|
1934
|
-
this.recordTimeoutCleanupFailure(cleanupError, "transactional tenant", recordedTimeoutCleanupErrors)
|
|
1935
|
-
})
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
if (emergencyCleanupErrors.length > 0) {
|
|
1939
|
-
caughtError = new AggregateError(
|
|
1940
|
-
[caughtError, ...emergencyCleanupErrors],
|
|
1941
|
-
"Test timeout and emergency cleanup failed",
|
|
1942
|
-
{cause: caughtError}
|
|
1943
|
-
)
|
|
1944
|
-
lastError = caughtError
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
|
-
|
|
1948
|
-
if (browserDummyConnectionRegistrations.some((registration) => registration.quarantined)) {
|
|
1949
|
-
testDatabaseAccessScope.revoked = true
|
|
1950
|
-
this._abortRemainingTests = true
|
|
1951
|
-
}
|
|
1952
|
-
|
|
1953
|
-
willRetry = !this._abortRemainingTests && retriesUsed < retryCount
|
|
1954
|
-
|
|
1955
|
-
if (willRetry) {
|
|
1956
|
-
retriesUsed++
|
|
1957
|
-
}
|
|
1958
|
-
|
|
1959
|
-
if (willRetry) {
|
|
1960
|
-
shouldRetry = true
|
|
1961
|
-
} else {
|
|
1962
|
-
failedError = caughtError
|
|
1963
|
-
}
|
|
1964
|
-
} finally {
|
|
1965
|
-
testDatabaseAccessScope.revoked = true
|
|
1966
|
-
const consoleOutput = stopConsoleCapture()
|
|
1967
|
-
|
|
1968
|
-
if (profileAttempt && profiler) {
|
|
1969
|
-
profiler.finishAttempt(profileAttempt, caughtError === undefined
|
|
1970
|
-
? "passed"
|
|
1971
|
-
: (attemptTimedOut ? "timed-out" : "failed"))
|
|
1972
|
-
}
|
|
1973
|
-
|
|
1974
|
-
if (consoleOutput) {
|
|
1975
|
-
attemptConsoleOutputs.push({attemptNumber, output: consoleOutput})
|
|
1976
|
-
}
|
|
1977
|
-
}
|
|
1978
1535
|
|
|
1979
|
-
if (
|
|
1980
|
-
|
|
1981
|
-
configuration: this.getConfiguration(),
|
|
1982
|
-
descriptions,
|
|
1983
|
-
error: caughtError,
|
|
1984
|
-
attemptNumber,
|
|
1985
|
-
nextAttempt: willRetry ? attemptNumber + 1 : undefined,
|
|
1986
|
-
retriesUsed,
|
|
1987
|
-
retryCount,
|
|
1988
|
-
testArgs,
|
|
1989
|
-
testData,
|
|
1990
|
-
testDescription,
|
|
1991
|
-
testRunner: this,
|
|
1992
|
-
willRetry
|
|
1993
|
-
})
|
|
1536
|
+
if (attemptResult.consoleOutput) {
|
|
1537
|
+
attemptConsoleOutputs.push({attemptNumber, output: attemptResult.consoleOutput})
|
|
1994
1538
|
}
|
|
1539
|
+
if (attemptResult.abortRemainingTests) this._abortRemainingTests = true
|
|
1995
1540
|
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
await this.emitEvent("testRetrying", {
|
|
1999
|
-
configuration: this.getConfiguration(),
|
|
2000
|
-
descriptions,
|
|
2001
|
-
error: lastError,
|
|
2002
|
-
nextAttempt: attemptNumber + 1,
|
|
2003
|
-
retriesUsed,
|
|
2004
|
-
retryCount,
|
|
2005
|
-
testArgs,
|
|
2006
|
-
testData,
|
|
2007
|
-
testDescription,
|
|
2008
|
-
testRunner: this
|
|
2009
|
-
})
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
|
-
if (attemptNumber > 1) {
|
|
2013
|
-
await this.emitEvent("testRetried", {
|
|
2014
|
-
configuration: this.getConfiguration(),
|
|
2015
|
-
descriptions,
|
|
2016
|
-
error: lastError,
|
|
2017
|
-
attemptNumber,
|
|
2018
|
-
retriesUsed,
|
|
2019
|
-
retryCount,
|
|
2020
|
-
testArgs,
|
|
2021
|
-
testData,
|
|
2022
|
-
testDescription,
|
|
2023
|
-
testRunner: this
|
|
2024
|
-
})
|
|
2025
|
-
}
|
|
1541
|
+
const willRetry = attemptResult.failed && !this._abortRemainingTests && retriesUsed < retryCount
|
|
1542
|
+
if (willRetry) retriesUsed++
|
|
2026
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
|
+
})
|
|
2027
1558
|
attemptNumber++
|
|
2028
1559
|
|
|
2029
|
-
if (
|
|
2030
|
-
|
|
2031
|
-
if (failedError) {
|
|
2032
|
-
const consoleOutput = this.buildConsoleOutput(attemptConsoleOutputs)
|
|
2033
|
-
|
|
2034
|
-
if (failedError instanceof Error) {
|
|
2035
|
-
console.error(picocolors.red(`${leftPadding} Test failed: ${failedError.message}`))
|
|
2036
|
-
addTrackedStackToError(failedError)
|
|
2037
|
-
|
|
2038
|
-
const backtraceCleaner = new BacktraceCleaner(failedError)
|
|
2039
|
-
const cleanedStack = backtraceCleaner.getCleanedStack()
|
|
2040
|
-
const stackLines = cleanedStack?.split("\n")
|
|
2041
|
-
|
|
2042
|
-
if (stackLines) {
|
|
2043
|
-
for (const stackLine of stackLines) {
|
|
2044
|
-
console.error(picocolors.red(`${leftPadding} ${stackLine}`))
|
|
2045
|
-
}
|
|
2046
|
-
}
|
|
2047
|
-
} else {
|
|
2048
|
-
console.error(picocolors.red(`${leftPadding} Test failed with a ${typeof failedError}: ${String(failedError)}`))
|
|
2049
|
-
}
|
|
2050
|
-
|
|
2051
|
-
this.printFailedConsoleOutput({consoleOutput, leftPadding})
|
|
2052
|
-
this._failedTests++
|
|
2053
|
-
this._failedTestDetails.push({
|
|
2054
|
-
fullDescription: this.buildFullDescription(descriptions, testDescription),
|
|
2055
|
-
filePath: testData.filePath,
|
|
2056
|
-
line: testData.line,
|
|
2057
|
-
error: failedError,
|
|
2058
|
-
consoleOutput: consoleOutput || undefined
|
|
2059
|
-
})
|
|
2060
|
-
|
|
2061
|
-
await this.emitEvent("testFailed", {
|
|
2062
|
-
configuration: this.getConfiguration(),
|
|
2063
|
-
descriptions,
|
|
2064
|
-
error: failedError,
|
|
2065
|
-
testArgs,
|
|
2066
|
-
testData,
|
|
2067
|
-
testDescription,
|
|
2068
|
-
testRunner: this
|
|
2069
|
-
})
|
|
2070
|
-
|
|
2071
|
-
this.printRerunCommand({descriptions, testDescription, testData, leftPadding})
|
|
2072
|
-
}
|
|
2073
|
-
|
|
2074
|
-
break
|
|
1560
|
+
if (!willRetry) break
|
|
2075
1561
|
}
|
|
2076
1562
|
|
|
2077
1563
|
this._testDurations.push({
|
|
@@ -2142,33 +1628,13 @@ export default class TestRunner {
|
|
|
2142
1628
|
scopeEntry.afterAllsRun = true
|
|
2143
1629
|
|
|
2144
1630
|
const scopeOwnerFilePath = scopeEntry.tests.ownerFilePath ?? scopeEntry.tests.filePath
|
|
2145
|
-
const afterAlls =
|
|
1631
|
+
const afterAlls = this.profileHookEntries(
|
|
2146
1632
|
scopeEntry.tests.afterAlls || [],
|
|
2147
1633
|
scopeEntry.profileScopeId,
|
|
2148
1634
|
scopeOwnerFilePath
|
|
2149
|
-
)
|
|
2150
|
-
/** @type {unknown[]} */
|
|
2151
|
-
const afterAllErrors = []
|
|
2152
|
-
|
|
2153
|
-
for (const afterAllData of afterAlls) {
|
|
2154
|
-
try {
|
|
2155
|
-
await this.runProfileSpan({
|
|
2156
|
-
phase: "afterAll",
|
|
2157
|
-
declarationIndex: afterAllData.declarationIndex,
|
|
2158
|
-
declarationScopeId: afterAllData.declarationScopeId,
|
|
2159
|
-
filePath: afterAllData.ownerFilePath
|
|
2160
|
-
}, async () => {
|
|
2161
|
-
await afterAllData.callback({configuration: this.getConfiguration()})
|
|
2162
|
-
})
|
|
2163
|
-
} catch (error) {
|
|
2164
|
-
afterAllErrors.push(error)
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
1635
|
+
)
|
|
2167
1636
|
|
|
2168
|
-
|
|
2169
|
-
if (afterAllErrors.length > 1) {
|
|
2170
|
-
throw new AggregateError(afterAllErrors, "Multiple afterAll hooks failed", {cause: afterAllErrors[0]})
|
|
2171
|
-
}
|
|
1637
|
+
await this._suiteHookExecutor.runAfterAlls({hooks: afterAlls})
|
|
2172
1638
|
}
|
|
2173
1639
|
|
|
2174
1640
|
/**
|
|
@@ -2178,11 +1644,7 @@ export default class TestRunner {
|
|
|
2178
1644
|
* @returns {Promise<void>} - Resolves when all listeners complete.
|
|
2179
1645
|
*/
|
|
2180
1646
|
async emitEvent(eventName, payload) {
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
for (const listener of listeners) {
|
|
2184
|
-
await listener(payload)
|
|
2185
|
-
}
|
|
1647
|
+
await this._runnerReporter.emitEvent(eventName, payload)
|
|
2186
1648
|
}
|
|
2187
1649
|
|
|
2188
1650
|
/**
|