velocious 1.0.494 → 1.0.495
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/build/frontend-models/resource-definition.js +3 -1
- package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
- package/build/src/frontend-models/resource-definition.js +4 -2
- package/build/src/testing/test-runner.d.ts +21 -0
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +78 -9
- package/build/testing/test-runner.js +82 -8
- package/package.json +1 -1
- package/src/frontend-models/resource-definition.js +3 -1
- package/src/testing/test-runner.js +82 -8
|
@@ -187,6 +187,8 @@ export default class TestRunner {
|
|
|
187
187
|
this._testsCount = 0
|
|
188
188
|
this._activeAfterAllScopes = []
|
|
189
189
|
this._failedTestDetails = []
|
|
190
|
+
/** @type {{fullDescription: string, filePath: string, line: number} | null} */
|
|
191
|
+
this._lastTestContext = null
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
/**
|
|
@@ -638,16 +640,80 @@ export default class TestRunner {
|
|
|
638
640
|
* Runs run.
|
|
639
641
|
* @returns {Promise<void>} - Resolves when complete.
|
|
640
642
|
*/
|
|
643
|
+
/**
|
|
644
|
+
* Records an asynchronous crash (an unhandled promise rejection detached from
|
|
645
|
+
* any await, e.g. a `void connection.afterCommit(async () => broadcast(...))`
|
|
646
|
+
* frontend-model publish) as a real, visible, attributed test failure.
|
|
647
|
+
*
|
|
648
|
+
* Without this, such a rejection has no handler, so on modern Node the process
|
|
649
|
+
* is TERMINATED — the run ends with no reported failures and CI just sees a
|
|
650
|
+
* crashed/retried shard with an empty result (the recurring "silent
|
|
651
|
+
* test-runner death": invisible and impossible to diagnose). Turning it into a
|
|
652
|
+
* failure makes the run go red with something debuggable instead of vanishing.
|
|
653
|
+
* @param {"unhandledRejection"} kind - Async-crash kind.
|
|
654
|
+
* @param {unknown} reason - Rejection reason.
|
|
655
|
+
* @returns {void}
|
|
656
|
+
*/
|
|
657
|
+
recordAsyncCrash(kind, reason) {
|
|
658
|
+
const error = reason instanceof Error ? reason : new Error(`${kind}: ${String(reason)}`)
|
|
659
|
+
const near = this._lastTestContext
|
|
660
|
+
const attribution = near ? `, near test: ${near.fullDescription} (${near.filePath}:${near.line})` : ""
|
|
661
|
+
|
|
662
|
+
this._failedTests = (this._failedTests || 0) + 1
|
|
663
|
+
this._failedTestDetails.push({
|
|
664
|
+
fullDescription: `<${kind} during test run${attribution}>`,
|
|
665
|
+
filePath: near ? near.filePath : "<test runner>",
|
|
666
|
+
line: near ? near.line : 0,
|
|
667
|
+
error,
|
|
668
|
+
consoleOutput: undefined
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
console.error(picocolors.red(`\n[test-runner] ${kind} during the test run — this would otherwise terminate the process silently and surface only as a crashed/retried shard with zero reported failures.${attribution}`))
|
|
672
|
+
console.error(error)
|
|
673
|
+
}
|
|
674
|
+
|
|
641
675
|
async run() {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
676
|
+
/**
|
|
677
|
+
* Handles a process-level unhandled rejection during the run.
|
|
678
|
+
* @param {unknown} reason - Rejection reason.
|
|
679
|
+
* @returns {void}
|
|
680
|
+
*/
|
|
681
|
+
const onUnhandledRejection = (reason) => {
|
|
682
|
+
// If a test attached its OWN unhandledRejection listener, it is
|
|
683
|
+
// intentionally observing/triggering the rejection (e.g. beacon
|
|
684
|
+
// error-reporting-spec.js) — Node dispatches to EVERY listener, so also
|
|
685
|
+
// failing the suite here would break those tests. Defer to the test's
|
|
686
|
+
// handler; only treat a rejection as a silent-death crash when ours is the
|
|
687
|
+
// sole listener (no persistent framework listener exists to mask this).
|
|
688
|
+
if (process.listenerCount("unhandledRejection") > 1) return
|
|
689
|
+
|
|
690
|
+
this.recordAsyncCrash("unhandledRejection", reason)
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
process.on("unhandledRejection", onUnhandledRejection)
|
|
694
|
+
|
|
695
|
+
try {
|
|
696
|
+
await this.getConfiguration().ensureConnections({name: "Test runner suite"}, async () => {
|
|
697
|
+
await this.runTests({
|
|
698
|
+
afterEaches: [],
|
|
699
|
+
beforeEaches: [],
|
|
700
|
+
tests,
|
|
701
|
+
descriptions: [],
|
|
702
|
+
indentLevel: 0
|
|
703
|
+
})
|
|
704
|
+
|
|
705
|
+
// A rejection scheduled by the final test (a detached rejected promise,
|
|
706
|
+
// or an afterCommit callback rejecting as the suite drains) is reported
|
|
707
|
+
// by Node on a LATER turn. Drain a few turns while the handler is still
|
|
708
|
+
// attached — and connections still open — so those late rejections are
|
|
709
|
+
// recorded instead of escaping to the default crash path after cleanup.
|
|
710
|
+
for (let drainTurn = 0; drainTurn < 3; drainTurn++) {
|
|
711
|
+
await new Promise((resolve) => setImmediate(resolve))
|
|
712
|
+
}
|
|
649
713
|
})
|
|
650
|
-
}
|
|
714
|
+
} finally {
|
|
715
|
+
process.off("unhandledRejection", onUnhandledRejection)
|
|
716
|
+
}
|
|
651
717
|
}
|
|
652
718
|
|
|
653
719
|
/**
|
|
@@ -798,6 +864,14 @@ export default class TestRunner {
|
|
|
798
864
|
await beforeEachData.callback({configuration: this.getConfiguration(), testArgs, testData})
|
|
799
865
|
}
|
|
800
866
|
|
|
867
|
+
// Record which test is running so an async crash (an unhandled
|
|
868
|
+
// rejection detached from any await) that fires during or shortly
|
|
869
|
+
// after this test can be attributed to it in run()'s handler.
|
|
870
|
+
this._lastTestContext = {
|
|
871
|
+
fullDescription: this.buildFullDescription(descriptions, testDescription),
|
|
872
|
+
filePath: testData.filePath ?? "<unknown>",
|
|
873
|
+
line: testData.line ?? 0
|
|
874
|
+
}
|
|
801
875
|
await testData.function(testArgs)
|
|
802
876
|
this._successfulTests++
|
|
803
877
|
} finally {
|
package/package.json
CHANGED
|
@@ -40,11 +40,13 @@ const RESOURCE_STATIC_CONFIG_KEYS = new Set([
|
|
|
40
40
|
"modelName",
|
|
41
41
|
"ModelClass",
|
|
42
42
|
"primaryKey",
|
|
43
|
+
"quickSearchColumns",
|
|
43
44
|
"relationships",
|
|
44
45
|
"server",
|
|
45
46
|
"SharedResource",
|
|
46
47
|
"sync",
|
|
47
|
-
"translatedAttributes"
|
|
48
|
+
"translatedAttributes",
|
|
49
|
+
"writableAttributes"
|
|
48
50
|
])
|
|
49
51
|
|
|
50
52
|
/**
|
|
@@ -187,6 +187,8 @@ export default class TestRunner {
|
|
|
187
187
|
this._testsCount = 0
|
|
188
188
|
this._activeAfterAllScopes = []
|
|
189
189
|
this._failedTestDetails = []
|
|
190
|
+
/** @type {{fullDescription: string, filePath: string, line: number} | null} */
|
|
191
|
+
this._lastTestContext = null
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
/**
|
|
@@ -638,16 +640,80 @@ export default class TestRunner {
|
|
|
638
640
|
* Runs run.
|
|
639
641
|
* @returns {Promise<void>} - Resolves when complete.
|
|
640
642
|
*/
|
|
643
|
+
/**
|
|
644
|
+
* Records an asynchronous crash (an unhandled promise rejection detached from
|
|
645
|
+
* any await, e.g. a `void connection.afterCommit(async () => broadcast(...))`
|
|
646
|
+
* frontend-model publish) as a real, visible, attributed test failure.
|
|
647
|
+
*
|
|
648
|
+
* Without this, such a rejection has no handler, so on modern Node the process
|
|
649
|
+
* is TERMINATED — the run ends with no reported failures and CI just sees a
|
|
650
|
+
* crashed/retried shard with an empty result (the recurring "silent
|
|
651
|
+
* test-runner death": invisible and impossible to diagnose). Turning it into a
|
|
652
|
+
* failure makes the run go red with something debuggable instead of vanishing.
|
|
653
|
+
* @param {"unhandledRejection"} kind - Async-crash kind.
|
|
654
|
+
* @param {unknown} reason - Rejection reason.
|
|
655
|
+
* @returns {void}
|
|
656
|
+
*/
|
|
657
|
+
recordAsyncCrash(kind, reason) {
|
|
658
|
+
const error = reason instanceof Error ? reason : new Error(`${kind}: ${String(reason)}`)
|
|
659
|
+
const near = this._lastTestContext
|
|
660
|
+
const attribution = near ? `, near test: ${near.fullDescription} (${near.filePath}:${near.line})` : ""
|
|
661
|
+
|
|
662
|
+
this._failedTests = (this._failedTests || 0) + 1
|
|
663
|
+
this._failedTestDetails.push({
|
|
664
|
+
fullDescription: `<${kind} during test run${attribution}>`,
|
|
665
|
+
filePath: near ? near.filePath : "<test runner>",
|
|
666
|
+
line: near ? near.line : 0,
|
|
667
|
+
error,
|
|
668
|
+
consoleOutput: undefined
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
console.error(picocolors.red(`\n[test-runner] ${kind} during the test run — this would otherwise terminate the process silently and surface only as a crashed/retried shard with zero reported failures.${attribution}`))
|
|
672
|
+
console.error(error)
|
|
673
|
+
}
|
|
674
|
+
|
|
641
675
|
async run() {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
676
|
+
/**
|
|
677
|
+
* Handles a process-level unhandled rejection during the run.
|
|
678
|
+
* @param {unknown} reason - Rejection reason.
|
|
679
|
+
* @returns {void}
|
|
680
|
+
*/
|
|
681
|
+
const onUnhandledRejection = (reason) => {
|
|
682
|
+
// If a test attached its OWN unhandledRejection listener, it is
|
|
683
|
+
// intentionally observing/triggering the rejection (e.g. beacon
|
|
684
|
+
// error-reporting-spec.js) — Node dispatches to EVERY listener, so also
|
|
685
|
+
// failing the suite here would break those tests. Defer to the test's
|
|
686
|
+
// handler; only treat a rejection as a silent-death crash when ours is the
|
|
687
|
+
// sole listener (no persistent framework listener exists to mask this).
|
|
688
|
+
if (process.listenerCount("unhandledRejection") > 1) return
|
|
689
|
+
|
|
690
|
+
this.recordAsyncCrash("unhandledRejection", reason)
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
process.on("unhandledRejection", onUnhandledRejection)
|
|
694
|
+
|
|
695
|
+
try {
|
|
696
|
+
await this.getConfiguration().ensureConnections({name: "Test runner suite"}, async () => {
|
|
697
|
+
await this.runTests({
|
|
698
|
+
afterEaches: [],
|
|
699
|
+
beforeEaches: [],
|
|
700
|
+
tests,
|
|
701
|
+
descriptions: [],
|
|
702
|
+
indentLevel: 0
|
|
703
|
+
})
|
|
704
|
+
|
|
705
|
+
// A rejection scheduled by the final test (a detached rejected promise,
|
|
706
|
+
// or an afterCommit callback rejecting as the suite drains) is reported
|
|
707
|
+
// by Node on a LATER turn. Drain a few turns while the handler is still
|
|
708
|
+
// attached — and connections still open — so those late rejections are
|
|
709
|
+
// recorded instead of escaping to the default crash path after cleanup.
|
|
710
|
+
for (let drainTurn = 0; drainTurn < 3; drainTurn++) {
|
|
711
|
+
await new Promise((resolve) => setImmediate(resolve))
|
|
712
|
+
}
|
|
649
713
|
})
|
|
650
|
-
}
|
|
714
|
+
} finally {
|
|
715
|
+
process.off("unhandledRejection", onUnhandledRejection)
|
|
716
|
+
}
|
|
651
717
|
}
|
|
652
718
|
|
|
653
719
|
/**
|
|
@@ -798,6 +864,14 @@ export default class TestRunner {
|
|
|
798
864
|
await beforeEachData.callback({configuration: this.getConfiguration(), testArgs, testData})
|
|
799
865
|
}
|
|
800
866
|
|
|
867
|
+
// Record which test is running so an async crash (an unhandled
|
|
868
|
+
// rejection detached from any await) that fires during or shortly
|
|
869
|
+
// after this test can be attributed to it in run()'s handler.
|
|
870
|
+
this._lastTestContext = {
|
|
871
|
+
fullDescription: this.buildFullDescription(descriptions, testDescription),
|
|
872
|
+
filePath: testData.filePath ?? "<unknown>",
|
|
873
|
+
line: testData.line ?? 0
|
|
874
|
+
}
|
|
801
875
|
await testData.function(testArgs)
|
|
802
876
|
this._successfulTests++
|
|
803
877
|
} finally {
|