cabloy 5.1.112 → 5.1.113
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/.cabloy-version +1 -1
- package/.claude/skills/cabloy-backend-scaffold/SKILL.md +2 -0
- package/.claude/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +9 -0
- package/.github/workflows/playwright-e2e.yml +34 -2
- package/.github/workflows/vona-test-pg.yml +8 -0
- package/CHANGELOG.md +13 -0
- package/CLAUDE.md +2 -0
- package/cabloy-docs/backend/migration-and-changes.md +13 -9
- package/cabloy-docs/backend/unit-testing.md +25 -0
- package/e2e/scripts/startE2eVona.ts +51 -13
- package/e2e/specs/a-commerce/commerce.spec.ts +1 -0
- package/e2e/specs/cabloy-basic/basic.spec.ts +12 -7
- package/package.json +1 -1
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.test.ts +6 -9
- package/vona/packages-cli/cli-set-api/toolsIsolate/test.ts +50 -21
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +67 -67
- package/vona/src/backend/config/config/config.test.ts +1 -1
- package/vona/src/suite/a-commerce/modules/commerce-catalog/test/catalog.test.ts +234 -0
- package/vona/src/suite/a-commerce/modules/commerce-seed/src/bean/meta.version.ts +3 -3
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/bean/meta.version.ts +4 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/config/locale/en-us.ts +4 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/config/locale/zh-cn.ts +4 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/entity/stockAudit.tsx +39 -7
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/stockBalance.ts +38 -1
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/stockBalance.test.ts +268 -65
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/stockReservation.test.ts +276 -81
- package/vona/src/suite-vendor/a-vona/modules/a-startup/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-startup/src/service/startup.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-version/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-version/src/service/version.ts +12 -12
- package/vona/src/suite-vendor/a-vona/modules/a-version/src/types/version.ts +4 -4
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/pnpm-lock.yaml +14 -14
- package/zova/src/suite/a-commerce/modules/commerce-catalog/src/page/product/controller.tsx +0 -1
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/cart.ts +1 -0
- package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/controller.tsx +17 -7
- package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/render.header.tsx +1 -1
- package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/controller.tsx +17 -7
package/.cabloy-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.1.
|
|
1
|
+
5.1.113
|
|
@@ -172,6 +172,8 @@ For the canonical distinction, read [Backend Foundation](../../../cabloy-docs/ba
|
|
|
172
172
|
|
|
173
173
|
### Verification
|
|
174
174
|
|
|
175
|
+
For persisted test data, classify each record as a durable module seed or a test-local fixture. Durable seed data belongs in the owning module's `meta.version.ts` `seed()` hook, runs from a newly recreated managed database, and is read-only to tests; test-local resources must be tracked and deleted in `finally` in reverse dependency order.
|
|
176
|
+
|
|
175
177
|
Check whether the feature needs:
|
|
176
178
|
|
|
177
179
|
- unit tests
|
|
@@ -35,6 +35,15 @@ After generating or extending a backend thread, check which follow-up layers app
|
|
|
35
35
|
- add a warm-query → mutate-source → repeat-query regression test for each new dependency path; read [Vona Cross-Model Query-Cache Dependencies](../../../../.docs-internal/architecture/vona-cross-model-query-cache-dependencies.md) for the source-backed decision rules
|
|
36
36
|
- transaction behavior
|
|
37
37
|
|
|
38
|
+
## Test resource lifecycle follow-up
|
|
39
|
+
|
|
40
|
+
- classify persisted test data as either an owning-module durable seed or a test-local fixture
|
|
41
|
+
- create durable test or local-development baseline data in the owning module's `meta.version.ts` `seed()` hook; this managed path starts from a newly recreated database
|
|
42
|
+
- treat durable seed data as read-only in tests; create an independent fixture when a scenario needs mutation
|
|
43
|
+
- retain exact IDs or entities for every test-local persisted resource and delete them from `finally`
|
|
44
|
+
- delete joins, children, derived records, and other dependents before their owners, in the same active tenant/instance scope
|
|
45
|
+
- do not use unscoped, timestamp-, prefix-, or broad business-condition deletion when an exact test-owned identity is available
|
|
46
|
+
|
|
38
47
|
## Module composition and dependency intent
|
|
39
48
|
|
|
40
49
|
- target module is already composed into the application when code uses cross-module scope lookup
|
|
@@ -4,7 +4,7 @@ on:
|
|
|
4
4
|
branches:
|
|
5
5
|
- main
|
|
6
6
|
jobs:
|
|
7
|
-
|
|
7
|
+
basic-e2e:
|
|
8
8
|
runs-on: ubuntu-latest
|
|
9
9
|
env:
|
|
10
10
|
DATABASE_DEFAULT_CLIENT: sqlite3
|
|
@@ -28,6 +28,38 @@ jobs:
|
|
|
28
28
|
run: npx playwright install --with-deps chromium
|
|
29
29
|
- name: run Basic E2E baseline
|
|
30
30
|
run: npm run test:e2e:basic:clean
|
|
31
|
+
- name: upload Playwright artifacts
|
|
32
|
+
if: failure()
|
|
33
|
+
uses: actions/upload-artifact@v4
|
|
34
|
+
with:
|
|
35
|
+
name: playwright-artifacts-basic
|
|
36
|
+
path: |
|
|
37
|
+
playwright-report/
|
|
38
|
+
test-results/
|
|
39
|
+
if-no-files-found: ignore
|
|
40
|
+
|
|
41
|
+
commerce-e2e:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
env:
|
|
44
|
+
DATABASE_DEFAULT_CLIENT: sqlite3
|
|
45
|
+
PNPM_CONFIG_MINIMUM_RELEASE_AGE: 0
|
|
46
|
+
services:
|
|
47
|
+
redis:
|
|
48
|
+
image: redis:latest
|
|
49
|
+
ports:
|
|
50
|
+
- 6379:6379
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v6
|
|
53
|
+
- uses: actions/setup-node@v6
|
|
54
|
+
with:
|
|
55
|
+
node-version: 24
|
|
56
|
+
- uses: pnpm/action-setup@v5
|
|
57
|
+
with:
|
|
58
|
+
version: 11.5.2
|
|
59
|
+
- name: init
|
|
60
|
+
run: npm run init
|
|
61
|
+
- name: install Chromium
|
|
62
|
+
run: npx playwright install --with-deps chromium
|
|
31
63
|
- name: build Commerce Zova artifacts
|
|
32
64
|
run: npm run build:zova:commerce
|
|
33
65
|
- name: synchronize Vona dependencies
|
|
@@ -38,7 +70,7 @@ jobs:
|
|
|
38
70
|
if: failure()
|
|
39
71
|
uses: actions/upload-artifact@v4
|
|
40
72
|
with:
|
|
41
|
-
name: playwright-artifacts
|
|
73
|
+
name: playwright-artifacts-commerce
|
|
42
74
|
path: |
|
|
43
75
|
playwright-report/
|
|
44
76
|
test-results/
|
|
@@ -34,6 +34,14 @@ jobs:
|
|
|
34
34
|
version: 11.5.2
|
|
35
35
|
- name: init
|
|
36
36
|
run: npm run init
|
|
37
|
+
- name: run Commerce stock contention gate
|
|
38
|
+
run: >-
|
|
39
|
+
TEST_CONCURRENCY=false DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test --
|
|
40
|
+
commerce-trade/test/stockBalance.test.ts
|
|
41
|
+
commerce-trade/test/stockReservation.test.ts
|
|
42
|
+
commerce-trade/test/stockAudit.test.ts
|
|
43
|
+
--flavor=normal
|
|
44
|
+
working-directory: vona
|
|
37
45
|
- run: DATABASE_DEFAULT_CLIENT=pg npm run test
|
|
38
46
|
working-directory: vona
|
|
39
47
|
- run: DATABASE_DEFAULT_CLIENT=pg npm run build -- --flavor=ci
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.113
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Update application capabilities and workflows.
|
|
8
|
+
|
|
9
|
+
### Improvements
|
|
10
|
+
|
|
11
|
+
- Rename the version test lifecycle to the seed lifecycle.
|
|
12
|
+
- Run Commerce end-to-end tests independently in CI.
|
|
13
|
+
- Strengthen Commerce catalog, stock balance, and stock reservation test coverage.
|
|
14
|
+
- Record Commerce stock verification and Phase 20 CI evidence.
|
|
15
|
+
|
|
3
16
|
## 5.1.112
|
|
4
17
|
|
|
5
18
|
### Features
|
package/CLAUDE.md
CHANGED
|
@@ -72,6 +72,8 @@ Before inventing a custom implementation path:
|
|
|
72
72
|
- In Vona, a tenant corresponds to an instance. Ordinary resource-model CRUD is automatically scoped to the active instance; treat records absent from that scope as absent, and do not use raw cross-instance probes merely to choose between `403` and not-found behavior. Model future multi-merchant boundaries explicitly inside an instance.
|
|
73
73
|
- Model cross-Model query-cache dependencies as one directed, acyclic `modelsClear` / `modelsClearedBy` graph, and verify source mutations refresh warmed dependent queries; read `.docs-internal/architecture/vona-cross-model-query-cache-dependencies.md` before designing a nontrivial graph.
|
|
74
74
|
- For `@Api.field(...)` and related schemaLike composition, framework guards now preserve previously attached OpenAPI metadata across schema rebuilds, but structure-shaping schemaLike is still order-sensitive. Treat `v.object(...)`, `v.array(...)`, `v.optional()`, `v.nullable()`, `v.default(...)`, and preprocess/transform wrappers as structure-shaping; keep the final structure-defining schemaLike last and verify emitted schema/OpenAPI output after such edits.
|
|
75
|
+
- Unit tests must delete every test-owned persisted resource in `finally`, using precise owned identities and reverse dependency order.
|
|
76
|
+
- Shared durable test or local-development fixtures must be created through the owning module's `meta.version.ts` `seed()` hook and treated as read-only by tests; the managed seed path starts from a newly recreated database rather than repeating against one database.
|
|
75
77
|
|
|
76
78
|
## Verification expectations
|
|
77
79
|
|
|
@@ -73,19 +73,19 @@ Three migration scenarios are defined:
|
|
|
73
73
|
| -------- | ------------------------------------------------ |
|
|
74
74
|
| `update` | schema evolution such as tables and fields |
|
|
75
75
|
| `init` | instance- or tenant-specific initialization data |
|
|
76
|
-
| `
|
|
76
|
+
| `seed` | shared baseline data for the test environment |
|
|
77
77
|
|
|
78
78
|
This split is one of the most important Vona migration ideas because it separates:
|
|
79
79
|
|
|
80
80
|
- structural change
|
|
81
81
|
- initialization logic
|
|
82
|
-
-
|
|
82
|
+
- shared baseline data setup
|
|
83
83
|
|
|
84
84
|
A practical generated-thread interpretation is:
|
|
85
85
|
|
|
86
86
|
- `update` follows schema and entity/model evolution
|
|
87
87
|
- `init` follows instance-aware initialization needs introduced by the backend feature
|
|
88
|
-
- `
|
|
88
|
+
- `seed` provides shared baseline data while the application starts in test mode
|
|
89
89
|
|
|
90
90
|
## Update: schema migration
|
|
91
91
|
|
|
@@ -128,23 +128,27 @@ export class MetaVersion extends BeanBase implements IMetaVersionInit {
|
|
|
128
128
|
|
|
129
129
|
The important point is that initialization can run per instance or tenant, which keeps tenant data isolated.
|
|
130
130
|
|
|
131
|
-
##
|
|
131
|
+
## Seed: test-environment baseline data
|
|
132
132
|
|
|
133
133
|
Representative pattern:
|
|
134
134
|
|
|
135
135
|
```typescript
|
|
136
136
|
@Meta()
|
|
137
|
-
export class MetaVersion extends BeanBase implements
|
|
138
|
-
async
|
|
137
|
+
export class MetaVersion extends BeanBase implements IMetaVersionSeed {
|
|
138
|
+
async seed() {
|
|
139
139
|
await this.scope.model.student.insert({
|
|
140
140
|
name: 'Jimmy',
|
|
141
|
-
description: '
|
|
141
|
+
description: 'Shared test-environment baseline data',
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
This is valuable because
|
|
147
|
+
This is valuable because shared baseline data becomes part of the structured version lifecycle instead of being scattered across unrelated setup code.
|
|
148
|
+
|
|
149
|
+
Vona invokes `seed()` while the application starts in test mode. Use it for durable baseline fixtures shared by multiple tests, E2E tests, or the managed local-development test-data workflow. The seed path starts from a newly recreated managed database; it is not a repeatable, same-database data-import mechanism.
|
|
150
|
+
|
|
151
|
+
Treat shared `seed()` records as read-only in ordinary tests. A scenario that needs different state must create independent, test-owned data and clean it up in `finally`; it must not mutate or delete the shared baseline. `seed()` remains distinct from `init()`: use `init()` for instance-aware initialization data and reserve `seed()` for shared test-environment baseline data.
|
|
148
152
|
|
|
149
153
|
## Version changes across the generated backend thread
|
|
150
154
|
|
|
@@ -185,7 +189,7 @@ When changing backend schema or module initialization behavior, do not only edit
|
|
|
185
189
|
Also ask:
|
|
186
190
|
|
|
187
191
|
1. does this change require a `fileVersion` increment?
|
|
188
|
-
2. does `meta.version` need an `update`, `init`, or `
|
|
192
|
+
2. does `meta.version` need an `update`, `init`, or `seed` branch?
|
|
189
193
|
3. should the local verification path include `test` or `db:reset`?
|
|
190
194
|
4. does the change affect the CRUD-generated thread or frontend-facing API contract as well?
|
|
191
195
|
|
|
@@ -86,6 +86,31 @@ await app.bean.executor.mockCtx(async () => {
|
|
|
86
86
|
|
|
87
87
|
Locale-sensitive variants and additional request-context helpers are also available.
|
|
88
88
|
|
|
89
|
+
## Persisted fixture lifecycle
|
|
90
|
+
|
|
91
|
+
Classify persisted test data before creating it:
|
|
92
|
+
|
|
93
|
+
- use a **test-local fixture** for a single test or scenario;
|
|
94
|
+
- use an owning module's `meta.version.ts` `seed()` hook only for stable baseline data shared across tests or intentionally used by the local-development test-data workflow.
|
|
95
|
+
|
|
96
|
+
A test owns every persisted resource it creates. Keep the returned entity or exact ID and delete owned records from `finally`, including when an assertion or action fails. Delete dependents before their owners and use the same active tenant/instance context that created them. Do not discover cleanup targets through broad table queries, time-based prefixes, or business conditions when the test already knows the exact identity.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
let parentId: number | undefined;
|
|
100
|
+
let childId: number | undefined;
|
|
101
|
+
try {
|
|
102
|
+
parentId = await createParent();
|
|
103
|
+
childId = await createChild(parentId);
|
|
104
|
+
|
|
105
|
+
// exercise and assert the behavior under test
|
|
106
|
+
} finally {
|
|
107
|
+
if (childId) await deleteChild(childId);
|
|
108
|
+
if (parentId) await deleteParent(parentId);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Treat shared `seed()` records as read-only. If a scenario needs to change a record, create a separate test-local fixture instead and clean it up. See [Migration and Changes](/backend/migration-and-changes#seed-test-environment-baseline-data) for the durable seed lifecycle.
|
|
113
|
+
|
|
89
114
|
## Working with module scope in tests
|
|
90
115
|
|
|
91
116
|
Representative pattern:
|
|
@@ -8,31 +8,69 @@ const child = spawn('npm', ['run', 'dev:one'], {
|
|
|
8
8
|
stdio: 'inherit',
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
+
const gracefulShutdownTimeout = 7000;
|
|
12
|
+
|
|
11
13
|
let stopping = false;
|
|
14
|
+
let forced = false;
|
|
15
|
+
let shutdownTimer: NodeJS.Timeout | undefined;
|
|
12
16
|
|
|
13
|
-
function
|
|
14
|
-
if (
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
function finish(code: number) {
|
|
18
|
+
if (shutdownTimer) {
|
|
19
|
+
clearTimeout(shutdownTimer);
|
|
20
|
+
shutdownTimer = undefined;
|
|
21
|
+
}
|
|
22
|
+
process.exit(code);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function signalChild(signal: NodeJS.Signals) {
|
|
26
|
+
if (!child.pid) return;
|
|
27
|
+
try {
|
|
28
|
+
if (process.platform === 'win32') {
|
|
29
|
+
child.kill(signal);
|
|
30
|
+
} else {
|
|
31
|
+
process.kill(-child.pid, signal);
|
|
32
|
+
}
|
|
33
|
+
} catch (error: any) {
|
|
34
|
+
if (error.code !== 'ESRCH') {
|
|
35
|
+
// eslint-disable-next-line
|
|
36
|
+
console.error(error);
|
|
23
37
|
}
|
|
24
38
|
}
|
|
25
39
|
}
|
|
26
40
|
|
|
41
|
+
function forceStop() {
|
|
42
|
+
if (forced) return;
|
|
43
|
+
forced = true;
|
|
44
|
+
// eslint-disable-next-line
|
|
45
|
+
console.error(
|
|
46
|
+
`E2E Vona process group did not stop within ${gracefulShutdownTimeout}ms; forcing termination: ${child.pid}`,
|
|
47
|
+
);
|
|
48
|
+
signalChild('SIGKILL');
|
|
49
|
+
finish(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function stop() {
|
|
53
|
+
if (stopping) {
|
|
54
|
+
forceStop();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
stopping = true;
|
|
58
|
+
// Vona handles SIGINT, not SIGTERM. Keep this deadline below Playwright's 10-second timeout.
|
|
59
|
+
signalChild('SIGINT');
|
|
60
|
+
shutdownTimer = setTimeout(forceStop, gracefulShutdownTimeout);
|
|
61
|
+
shutdownTimer.unref();
|
|
62
|
+
}
|
|
63
|
+
|
|
27
64
|
process.on('SIGINT', stop);
|
|
28
65
|
process.on('SIGTERM', stop);
|
|
29
66
|
|
|
30
67
|
child.on('error', error => {
|
|
31
68
|
// eslint-disable-next-line
|
|
32
69
|
console.error(error);
|
|
33
|
-
|
|
70
|
+
finish(1);
|
|
34
71
|
});
|
|
35
72
|
|
|
36
|
-
child.on('exit', code => {
|
|
37
|
-
|
|
73
|
+
child.on('exit', (code, signal) => {
|
|
74
|
+
// Playwright waits for this wrapper to exit, not only for Vona's workers to stop.
|
|
75
|
+
finish(code ?? (signal ? 1 : 0));
|
|
38
76
|
});
|
|
@@ -111,6 +111,7 @@ test(
|
|
|
111
111
|
const documentResponse = await page.goto(productPath, { waitUntil: 'load' });
|
|
112
112
|
expect(documentResponse?.ok()).toBeTruthy();
|
|
113
113
|
await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', 'commerce');
|
|
114
|
+
await expect(page).toHaveURL(productPath);
|
|
114
115
|
await expect(page.getByRole('heading', { name: 'Wireless Headphones' })).toBeVisible();
|
|
115
116
|
|
|
116
117
|
const cartLink = page.locator('a[href="/commerce/cart"]');
|
|
@@ -98,6 +98,14 @@ function assertVisualOrder(geometry: IFieldGeometry[]) {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
async function getDocumentHorizontalOverflow(page: Page) {
|
|
102
|
+
return page.evaluate(() => {
|
|
103
|
+
return (
|
|
104
|
+
Math.max(document.documentElement.scrollWidth, document.body.scrollWidth) - window.innerWidth
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
101
109
|
test(
|
|
102
110
|
'ATP-BASIC-SSR-01: anonymous Web HTML hydrates through the default site',
|
|
103
111
|
{ tag: ['@web', '@smoke'] },
|
|
@@ -217,6 +225,8 @@ test(
|
|
|
217
225
|
}
|
|
218
226
|
|
|
219
227
|
await page.setViewportSize({ width: 700, height: 900 });
|
|
228
|
+
const drawer = page.locator('.drawer').first();
|
|
229
|
+
await expect(drawer).not.toHaveClass(/\bdrawer-open\b/);
|
|
220
230
|
await expect
|
|
221
231
|
.poll(
|
|
222
232
|
async () =>
|
|
@@ -238,15 +248,10 @@ test(
|
|
|
238
248
|
const createdAtEndBox = await createdAtEnd.boundingBox();
|
|
239
249
|
expect(createdAtEndBox).not.toBeNull();
|
|
240
250
|
expect(createdAtEndBox!.x + createdAtEndBox!.width).toBeLessThanOrEqual(viewport.width + 1);
|
|
241
|
-
await expect
|
|
242
|
-
.poll(() =>
|
|
243
|
-
page.evaluate(
|
|
244
|
-
() => document.documentElement.scrollWidth - document.documentElement.clientWidth,
|
|
245
|
-
),
|
|
246
|
-
)
|
|
247
|
-
.toBeLessThanOrEqual(1);
|
|
251
|
+
await expect.poll(() => getDocumentHorizontalOverflow(page)).toBeLessThanOrEqual(1);
|
|
248
252
|
|
|
249
253
|
await page.setViewportSize({ width: 1440, height: 900 });
|
|
254
|
+
await expect(drawer).toHaveClass(/\bdrawer-open\b/);
|
|
250
255
|
await name.fill('Flow E2E');
|
|
251
256
|
await level.selectOption('2');
|
|
252
257
|
await createdAtStart.fill('2026-01-10');
|
package/package.json
CHANGED
|
@@ -77,15 +77,12 @@ export class CliBinTest extends BeanCliBase {
|
|
|
77
77
|
patterns.join(','),
|
|
78
78
|
]);
|
|
79
79
|
// args = args.concat(['--experimental-transform-types', getImportEsm(), testFile, projectPath, (!!argv.coverage).toString(), patterns.join(',')]);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
cwd: projectPath,
|
|
87
|
-
},
|
|
88
|
-
});
|
|
80
|
+
await this.helper.spawnExe({
|
|
81
|
+
cmd: 'node',
|
|
82
|
+
args,
|
|
83
|
+
options: {
|
|
84
|
+
cwd: projectPath,
|
|
85
|
+
},
|
|
89
86
|
});
|
|
90
87
|
}
|
|
91
88
|
|
|
@@ -63,9 +63,27 @@ async function testRun(projectPath: string, coverage: boolean, patterns: string[
|
|
|
63
63
|
];
|
|
64
64
|
// app
|
|
65
65
|
const app: VonaApplication = await createGeneralApp(projectPath);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
let testError: unknown;
|
|
67
|
+
let closeError: unknown;
|
|
68
|
+
let closePromise: Promise<void> | undefined;
|
|
69
|
+
const closeApplication = async () => {
|
|
70
|
+
const [_, error] = await catchError(() => app.close());
|
|
71
|
+
closeError = error;
|
|
72
|
+
// handles
|
|
73
|
+
if (process.env.TEST_WHYISNODERUNNING === 'true') {
|
|
74
|
+
await sleep(2000);
|
|
75
|
+
const handles = (process as any)._getActiveHandles();
|
|
76
|
+
if (handles.length > 3) {
|
|
77
|
+
whyIsNodeRunning();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const closeApplicationOnce = () => {
|
|
82
|
+
return (closePromise ??= closeApplication());
|
|
83
|
+
};
|
|
84
|
+
try {
|
|
85
|
+
// concurrency
|
|
86
|
+
const concurrency = await prepareConcurrency(app);
|
|
69
87
|
const testStream = run({
|
|
70
88
|
isolation: 'none',
|
|
71
89
|
concurrency,
|
|
@@ -80,27 +98,12 @@ async function testRun(projectPath: string, coverage: boolean, patterns: string[
|
|
|
80
98
|
.on('test:coverage', data => {
|
|
81
99
|
outputCoverageReport(data.summary.totals);
|
|
82
100
|
})
|
|
83
|
-
.on('test:
|
|
84
|
-
resolve(undefined);
|
|
85
|
-
})
|
|
86
|
-
.on('test:pass', async t => {
|
|
101
|
+
.on('test:pass', t => {
|
|
87
102
|
if (t.name === '---done---') {
|
|
88
|
-
|
|
89
|
-
return app.close();
|
|
90
|
-
});
|
|
91
|
-
if (err) {
|
|
92
|
-
console.error(err);
|
|
93
|
-
}
|
|
94
|
-
// handles
|
|
95
|
-
if (process.env.TEST_WHYISNODERUNNING === 'true') {
|
|
96
|
-
await sleep(2000);
|
|
97
|
-
const handles = (process as any)._getActiveHandles();
|
|
98
|
-
if (handles.length > 3) {
|
|
99
|
-
whyIsNodeRunning();
|
|
100
|
-
}
|
|
101
|
-
}
|
|
103
|
+
void closeApplicationOnce();
|
|
102
104
|
}
|
|
103
105
|
});
|
|
106
|
+
const summaryPromise = waitForTestSummary(testStream);
|
|
104
107
|
if (coverage) {
|
|
105
108
|
const reporterDir = path.join(projectPath, 'coverage');
|
|
106
109
|
fse.ensureDirSync(reporterDir);
|
|
@@ -109,6 +112,32 @@ async function testRun(projectPath: string, coverage: boolean, patterns: string[
|
|
|
109
112
|
} else {
|
|
110
113
|
testStream.compose(spec).pipe(process.stdout);
|
|
111
114
|
}
|
|
115
|
+
|
|
116
|
+
const summarySuccess = await summaryPromise;
|
|
117
|
+
if (!summarySuccess) {
|
|
118
|
+
throw new Error('node:test reported failed tests');
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
testError = error;
|
|
122
|
+
} finally {
|
|
123
|
+
await closeApplicationOnce();
|
|
124
|
+
}
|
|
125
|
+
if (testError) {
|
|
126
|
+
if (closeError) console.error(closeError);
|
|
127
|
+
throw toError(testError);
|
|
128
|
+
}
|
|
129
|
+
if (closeError) {
|
|
130
|
+
throw toError(closeError);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function toError(error: unknown) {
|
|
135
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function waitForTestSummary(testStream: ReturnType<typeof run>) {
|
|
139
|
+
return new Promise<boolean>((resolve, reject) => {
|
|
140
|
+
testStream.once('test:summary', summary => resolve(summary.success)).once('error', reject);
|
|
112
141
|
});
|
|
113
142
|
}
|
|
114
143
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.78",
|
|
4
4
|
"gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
|
|
5
5
|
"description": "Vona is an intuitive, elegant and powerful Node.js framework for rapidly developing enterprise applications of any size",
|
|
6
6
|
"keywords": [
|