@testspectra/cli 1.1.8-rc.2 → 1.1.8-rc.20
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 +47 -0
- package/bin/spectra.js +0 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/commands/__tests__/run-e2e.test.js +49 -49
- package/dist/commands/add.js +2 -1
- package/dist/commands/devices.js +1 -0
- package/dist/commands/doctor.js +1 -0
- package/dist/commands/init.js +150 -149
- package/dist/commands/install.js +2 -0
- package/dist/commands/license.js +1 -0
- package/dist/commands/test.js +5 -3
- package/dist/generator/__tests__/tsconfig-isolation.test.js +2 -1
- package/dist/generator/__tests__/type-generator.test.js +38 -0
- package/dist/generator/tsconfig-generator.js +1 -1
- package/dist/generator/type-generator.d.ts +5 -2
- package/dist/generator/type-generator.js +14 -17
- package/dist/index.js +11 -11
- package/dist/linter/__tests__/component-test-imports.test.js +51 -51
- package/dist/linter/__tests__/shared-lib-boundary.test.js +15 -15
- package/dist/linter/__tests__/spec-schema.test.js +167 -167
- package/dist/linter/discovery.d.ts +3 -3
- package/dist/linter/discovery.js +5 -5
- package/dist/linter/schemas/spec.schema.d.ts +2 -2
- package/dist/linter/schemas/suite.schema.d.ts +4 -4
- package/dist/runner/bridge.js +53 -4
- package/package.json +34 -29
- package/templates/default/CLAUDE.md +16 -16
- package/templates/default/global-hooks/after/mobile.hook.ts +3 -3
- package/templates/default/global-hooks/after/web.hook.ts +3 -3
- package/templates/default/global-hooks/before/mobile.hook.ts +3 -3
- package/templates/default/global-hooks/before/web.hook.ts +3 -3
- package/templates/default/package.json +2 -2
- package/templates/default/page-objects/AuthPage/mobile.ts +5 -5
- package/templates/default/page-objects/AuthPage/web.ts +10 -10
- package/templates/default/page-objects/MatchersPage/mobile.ts +70 -70
- package/templates/default/page-objects/MatchersPage/web.ts +43 -43
- package/templates/default/page-objects/NavigationPage/mobile.ts +14 -14
- package/templates/default/page-objects/NavigationPage/web.ts +8 -8
- package/templates/default/page-objects/PlaygroundPage/mobile.ts +42 -42
- package/templates/default/page-objects/PlaygroundPage/web.ts +31 -31
- package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +53 -53
- package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/spec.md +19 -19
- package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +31 -31
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/android.test.ts +3 -3
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/common.test.ts +3 -3
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +27 -27
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/spec.md +19 -19
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +26 -26
- package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/common.test.ts +30 -30
- package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/spec.md +22 -22
- package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/common.test.ts +27 -27
- package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/spec.md +20 -20
- package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/spec.md +24 -24
- package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/web.test.ts +26 -26
- package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/spec.md +37 -37
- package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/web.test.ts +21 -21
- package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/spec.md +29 -29
- package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/web.test.ts +16 -16
- package/templates/default/specs/ApiInterception/suite.md +10 -10
- package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/common.test.ts +30 -30
- package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/spec.md +23 -23
- package/templates/default/specs/ApiSeeding/hooks/after/common.hook.ts +16 -16
- package/templates/default/specs/ApiSeeding/hooks/before/common.hook.ts +28 -28
- package/templates/default/specs/ApiSeeding/suite.md +9 -9
- package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/mobile.test.ts +7 -7
- package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/spec.md +19 -19
- package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/web.test.ts +9 -9
- package/templates/default/specs/Authentication/suite.md +9 -9
- package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +9 -9
- package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/spec.md +17 -17
- package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +19 -19
- package/templates/default/specs/BrowserContext/TC-0022-history-navigation/mobile.test.ts +17 -17
- package/templates/default/specs/BrowserContext/TC-0022-history-navigation/spec.md +28 -28
- package/templates/default/specs/BrowserContext/TC-0022-history-navigation/web.test.ts +26 -26
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/common.test.ts +26 -26
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/spec.md +19 -19
- package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +11 -11
- package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/spec.md +17 -17
- package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +11 -11
- package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/common.test.ts +14 -14
- package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/spec.md +21 -21
- package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/mobile.test.ts +28 -28
- package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/spec.md +29 -29
- package/templates/default/specs/DeepLink/suite.md +9 -9
- package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +11 -11
- package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/spec.md +17 -17
- package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +11 -11
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +29 -29
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/spec.md +19 -19
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +29 -29
- package/templates/default/specs/ElementMatchers/suite.md +10 -10
- package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -5
- package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -16
- package/templates/default/specs/EnvironmentConfig/suite.md +9 -9
- package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/mobile.test.ts +26 -26
- package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/spec.md +22 -22
- package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/mobile.test.ts +13 -13
- package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/spec.md +20 -20
- package/templates/default/specs/PermissionRationale/suite.md +9 -9
- package/templates/default/specs/Playground/TC-0001-form-controls/mobile.test.ts +6 -6
- package/templates/default/specs/Playground/TC-0001-form-controls/spec.md +18 -18
- package/templates/default/specs/Playground/TC-0001-form-controls/web.test.ts +6 -6
- package/templates/default/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +6 -6
- package/templates/default/specs/Playground/TC-0002-toggle-elements/spec.md +18 -18
- package/templates/default/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -6
- package/templates/default/spectra.config.ts +52 -52
- package/templates/default/support/actions/fillCredentials/mobile.action.ts +4 -4
- package/templates/default/support/actions/fillCredentials/web.action.ts +4 -4
- package/templates/default/support/actions/seedSession/mobile.action.ts +9 -9
- package/templates/default/support/actions/seedSession/web.action.ts +13 -13
- package/templates/default/support/steps/togglePlaygroundStates/mobile.step.ts +5 -5
- package/templates/default/support/steps/togglePlaygroundStates/web.step.ts +5 -5
- package/templates/default/support/steps/verifyPlaygroundState/mobile.step.ts +6 -6
- package/templates/default/support/steps/verifyPlaygroundState/web.step.ts +6 -6
- package/templates/nx/.nx/workspace-data/{59842386-c7c4-44ca-b2c2-20e1700bd13d.db-shm → 70094CFD-E89B-57A1-9619-2FC5F4963C54.db-shm} +0 -0
- package/templates/nx/.nx/workspace-data/70094CFD-E89B-57A1-9619-2FC5F4963C54.db-wal +0 -0
- package/templates/nx/.nx/workspace-data/d/daemon.log +6269 -1577
- package/templates/nx/.nx/workspace-data/file-map.json +129 -129
- package/templates/nx/.nx/workspace-data/lockfile.hash +1 -1
- package/templates/nx/.nx/workspace-data/nx_files.nxt +0 -0
- package/templates/nx/.nx/workspace-data/parsed-lock-file.json +535 -535
- package/templates/nx/.nx/workspace-data/project-graph.json +589 -590
- package/templates/nx/CLAUDE.md +10 -10
- package/templates/nx/package.json +2 -2
- package/templates/nx/packages/matchers/e2e/project.json +2 -2
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +27 -27
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +27 -27
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +24 -24
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +24 -24
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +3 -3
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +19 -19
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +3 -3
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +17 -17
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +3 -3
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +11 -11
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +3 -3
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +11 -11
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +3 -3
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +29 -29
- package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -5
- package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -16
- package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/suite.md +9 -9
- package/templates/nx/packages/playground/e2e/project.json +2 -2
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/mobile.test.ts +3 -3
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/web.test.ts +6 -6
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +3 -3
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -6
- package/templates/nx/pnpm-lock.yaml +6663 -6663
- package/templates/nx/pnpm-workspace.yaml +9 -9
- package/templates/nx/shared/testing/page-objects/MatchersPage/mobile.ts +33 -33
- package/templates/nx/shared/testing/page-objects/MatchersPage/web.ts +91 -91
- package/templates/nx/shared/testing/page-objects/NavigationPage/mobile.ts +5 -5
- package/templates/nx/shared/testing/page-objects/NavigationPage/web.ts +8 -8
- package/templates/nx/shared/testing/page-objects/PlaygroundPage/mobile.ts +26 -26
- package/templates/nx/shared/testing/page-objects/PlaygroundPage/web.ts +31 -31
- package/templates/nx/shared/testing/project.json +18 -19
- package/templates/nx/shared/testing/support/actions/fillCredentials/mobile.action.ts +4 -4
- package/templates/nx/shared/testing/support/actions/fillCredentials/web.action.ts +4 -4
- package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/mobile.step.ts +3 -3
- package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/web.step.ts +5 -5
- package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/mobile.step.ts +4 -4
- package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/web.step.ts +6 -6
- package/templates/nx/spectra.config.ts +52 -52
- package/templates/react-component/.cursorrules +17 -17
- package/templates/react-component/.vscode/extensions.json +5 -5
- package/templates/react-component/CLAUDE.md +14 -14
- package/templates/react-component/README.md +69 -69
- package/templates/react-component/fixtures/component-mock.json +12 -12
- package/templates/react-component/global-hooks/after/web.hook.ts +3 -3
- package/templates/react-component/global-hooks/before/web.hook.ts +3 -3
- package/templates/react-component/package.json +27 -27
- package/templates/react-component/page-objects/BadgeComponent/web.ts +23 -23
- package/templates/react-component/page-objects/ButtonComponent/web.ts +23 -23
- package/templates/react-component/page-objects/InputComponent/web.ts +13 -13
- package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/spec.md +18 -18
- package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/web.test.tsx +11 -11
- package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/spec.md +18 -18
- package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/web.test.tsx +10 -10
- package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/spec.md +18 -18
- package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/web.test.tsx +16 -16
- package/templates/react-component/specs/BadgeComponent/suite.md +12 -12
- package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/spec.md +19 -19
- package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/web.test.tsx +28 -28
- package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/spec.md +19 -19
- package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/web.test.tsx +22 -22
- package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/spec.md +18 -18
- package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/web.test.tsx +8 -8
- package/templates/react-component/specs/ButtonComponent/suite.md +12 -12
- package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/spec.md +20 -20
- package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/web.test.tsx +28 -28
- package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/spec.md +18 -18
- package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/web.test.tsx +9 -9
- package/templates/react-component/specs/InputComponent/suite.md +12 -12
- package/templates/react-component/spectra.config.ts +27 -27
- package/templates/react-component/src/components/Badge/Badge.tsx +60 -60
- package/templates/react-component/src/components/Button/Button.tsx +57 -57
- package/templates/react-component/src/components/Input/Input.tsx +41 -41
- package/templates/react-component/src/test-utils.tsx +23 -23
- package/templates/react-component/support/actions/fillInputField/web.action.ts +9 -9
- package/templates/react-component/support/steps/verifyButtonState/web.step.ts +12 -12
- package/bin/.testspectra-runner.hash +0 -1
- package/bin/testspectra-runner.exe +0 -0
- package/templates/nx/.nx/workspace-data/59842386-c7c4-44ca-b2c2-20e1700bd13d.db-wal +0 -0
- package/templates/nx/.nx/workspace-data/d/server-process.json +0 -3
- /package/templates/nx/.nx/workspace-data/{59842386-c7c4-44ca-b2c2-20e1700bd13d.db → 70094CFD-E89B-57A1-9619-2FC5F4963C54.db} +0 -0
|
@@ -146,10 +146,10 @@ describe('End-to-End (E2E) Test Plan: Native Orchestrator Execution (docs/v2/cor
|
|
|
146
146
|
// Inject a failing step / invalid selector in a new test case
|
|
147
147
|
const failingDir = path.join(tmpDir, 'specs', 'Playground', 'TC-0099-failing');
|
|
148
148
|
fs.mkdirSync(failingDir, { recursive: true });
|
|
149
|
-
fs.writeFileSync(path.join(failingDir, 'web.test.ts'), `it("should fail when element does not exist", async () => {
|
|
150
|
-
await Step.verifyPlaygroundState();
|
|
151
|
-
await $("#non-existent-button-999").shouldBeVisible();
|
|
152
|
-
});
|
|
149
|
+
fs.writeFileSync(path.join(failingDir, 'web.test.ts'), `it("should fail when element does not exist", async () => {
|
|
150
|
+
await Step.verifyPlaygroundState();
|
|
151
|
+
await $("#non-existent-button-999").shouldBeVisible();
|
|
152
|
+
});
|
|
153
153
|
`, 'utf-8');
|
|
154
154
|
// Mock exit to prevent process termination during unit test assertion
|
|
155
155
|
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { }));
|
|
@@ -166,13 +166,13 @@ describe('End-to-End (E2E) Test Plan: Native Orchestrator Execution (docs/v2/cor
|
|
|
166
166
|
// 1. Test a valid spec using pure Spectra static commands
|
|
167
167
|
const spectraStaticDir = path.join(tmpDir, 'specs', 'Playground', 'TC-0100-spectra-static');
|
|
168
168
|
fs.mkdirSync(spectraStaticDir, { recursive: true });
|
|
169
|
-
fs.writeFileSync(path.join(spectraStaticDir, 'web.test.ts'), `it("should execute via unified Spectra static object", async () => {
|
|
170
|
-
await Spectra.navigate("/#playground");
|
|
171
|
-
await Spectra.type("#demo-email-input-pg", "automated@spectra.dev");
|
|
172
|
-
await Spectra.get("#demo-email-input-pg").shouldHaveValue("automated@spectra.dev");
|
|
173
|
-
await Spectra.wait(50);
|
|
174
|
-
await Spectra.getAll(".demo-product-item").shouldHaveLengthGreaterThan(0);
|
|
175
|
-
});
|
|
169
|
+
fs.writeFileSync(path.join(spectraStaticDir, 'web.test.ts'), `it("should execute via unified Spectra static object", async () => {
|
|
170
|
+
await Spectra.navigate("/#playground");
|
|
171
|
+
await Spectra.type("#demo-email-input-pg", "automated@spectra.dev");
|
|
172
|
+
await Spectra.get("#demo-email-input-pg").shouldHaveValue("automated@spectra.dev");
|
|
173
|
+
await Spectra.wait(50);
|
|
174
|
+
await Spectra.getAll(".demo-product-item").shouldHaveLengthGreaterThan(0);
|
|
175
|
+
});
|
|
176
176
|
`, 'utf-8');
|
|
177
177
|
await runCommand(['specs/Playground/TC-0100-spectra-static/web.test.ts'], { target: 'web', demo: true });
|
|
178
178
|
const resultStatic = JSON.parse(fs.readFileSync(path.join(tmpDir, '.testspectra', 'reports', 'result.json'), 'utf-8'));
|
|
@@ -181,11 +181,11 @@ describe('End-to-End (E2E) Test Plan: Native Orchestrator Execution (docs/v2/cor
|
|
|
181
181
|
// 2. Test that assertion mismatch on existing element strictly FAILS (no false positive)
|
|
182
182
|
const mismatchDir = path.join(tmpDir, 'specs', 'Playground', 'TC-0101-mismatch');
|
|
183
183
|
fs.mkdirSync(mismatchDir, { recursive: true });
|
|
184
|
-
fs.writeFileSync(path.join(mismatchDir, 'web.test.ts'), `it("should strictly fail when assertion text does not match", async () => {
|
|
185
|
-
await Spectra.navigate("/#playground");
|
|
186
|
-
// Element exists, but text is wrong -> MUST throw and fail
|
|
187
|
-
await Spectra.get("#demo-email-input-pg").shouldHaveValue("completely_wrong_value_xyz");
|
|
188
|
-
});
|
|
184
|
+
fs.writeFileSync(path.join(mismatchDir, 'web.test.ts'), `it("should strictly fail when assertion text does not match", async () => {
|
|
185
|
+
await Spectra.navigate("/#playground");
|
|
186
|
+
// Element exists, but text is wrong -> MUST throw and fail
|
|
187
|
+
await Spectra.get("#demo-email-input-pg").shouldHaveValue("completely_wrong_value_xyz");
|
|
188
|
+
});
|
|
189
189
|
`, 'utf-8');
|
|
190
190
|
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { }));
|
|
191
191
|
await runCommand(['specs/Playground/TC-0101-mismatch/web.test.ts'], { target: 'web', demo: true });
|
|
@@ -375,39 +375,39 @@ describe('End-to-End (E2E) Test Plan: Native Orchestrator Execution (docs/v2/cor
|
|
|
375
375
|
it('E2E-10: Spectra.intercept FIFO respondOnce sequential polling and abort simulation in real Chromium', async () => {
|
|
376
376
|
const fifoAbortDir = path.join(tmpDir, 'specs', 'ApiInterception', 'TC-0010-fifo-abort');
|
|
377
377
|
fs.mkdirSync(fifoAbortDir, { recursive: true });
|
|
378
|
-
fs.writeFileSync(path.join(fifoAbortDir, 'web.test.ts'), `it("should handle FIFO respondOnce queue and abort simulation", async () => {
|
|
379
|
-
await MatchersPage.open("api-fetch");
|
|
380
|
-
|
|
381
|
-
// Setup intercept with default fallback and FIFO queue
|
|
382
|
-
const mock = await Spectra.intercept("api/posts", "GET", [
|
|
383
|
-
{ id: 999, title: "Default Fallback Post" }
|
|
384
|
-
]);
|
|
385
|
-
|
|
386
|
-
await mock.respondOnce([
|
|
387
|
-
{ id: 101, title: "FIFO Queued Post #1" }
|
|
388
|
-
]);
|
|
389
|
-
await mock.respondOnce([
|
|
390
|
-
{ id: 102, title: "FIFO Queued Post #2" }
|
|
391
|
-
]);
|
|
392
|
-
|
|
393
|
-
// 1. First fetch triggers FIFO item #1
|
|
394
|
-
await MatchersPage.fetchApiButton.click();
|
|
395
|
-
await MatchersPage.fetchedItems.first().shouldContainText("FIFO Queued Post #1");
|
|
396
|
-
|
|
397
|
-
// 2. Second fetch triggers FIFO item #2
|
|
398
|
-
await MatchersPage.fetchApiButton.click();
|
|
399
|
-
await MatchersPage.fetchedItems.first().shouldContainText("FIFO Queued Post #2");
|
|
400
|
-
|
|
401
|
-
// 3. Third fetch falls back to default response
|
|
402
|
-
await MatchersPage.fetchApiButton.click();
|
|
403
|
-
await MatchersPage.fetchedItems.first().shouldContainText("Default Fallback Post");
|
|
404
|
-
|
|
405
|
-
// 4. Test abort simulation
|
|
406
|
-
await mock.abort("Failed");
|
|
407
|
-
await MatchersPage.fetchApiButton.click();
|
|
408
|
-
// On network failure, demo app shows fallback 200 OK from catch block
|
|
409
|
-
await MatchersPage.fetchStatusBadge.shouldHaveText("200 OK");
|
|
410
|
-
});
|
|
378
|
+
fs.writeFileSync(path.join(fifoAbortDir, 'web.test.ts'), `it("should handle FIFO respondOnce queue and abort simulation", async () => {
|
|
379
|
+
await MatchersPage.open("api-fetch");
|
|
380
|
+
|
|
381
|
+
// Setup intercept with default fallback and FIFO queue
|
|
382
|
+
const mock = await Spectra.intercept("api/posts", "GET", [
|
|
383
|
+
{ id: 999, title: "Default Fallback Post" }
|
|
384
|
+
]);
|
|
385
|
+
|
|
386
|
+
await mock.respondOnce([
|
|
387
|
+
{ id: 101, title: "FIFO Queued Post #1" }
|
|
388
|
+
]);
|
|
389
|
+
await mock.respondOnce([
|
|
390
|
+
{ id: 102, title: "FIFO Queued Post #2" }
|
|
391
|
+
]);
|
|
392
|
+
|
|
393
|
+
// 1. First fetch triggers FIFO item #1
|
|
394
|
+
await MatchersPage.fetchApiButton.click();
|
|
395
|
+
await MatchersPage.fetchedItems.first().shouldContainText("FIFO Queued Post #1");
|
|
396
|
+
|
|
397
|
+
// 2. Second fetch triggers FIFO item #2
|
|
398
|
+
await MatchersPage.fetchApiButton.click();
|
|
399
|
+
await MatchersPage.fetchedItems.first().shouldContainText("FIFO Queued Post #2");
|
|
400
|
+
|
|
401
|
+
// 3. Third fetch falls back to default response
|
|
402
|
+
await MatchersPage.fetchApiButton.click();
|
|
403
|
+
await MatchersPage.fetchedItems.first().shouldContainText("Default Fallback Post");
|
|
404
|
+
|
|
405
|
+
// 4. Test abort simulation
|
|
406
|
+
await mock.abort("Failed");
|
|
407
|
+
await MatchersPage.fetchApiButton.click();
|
|
408
|
+
// On network failure, demo app shows fallback 200 OK from catch block
|
|
409
|
+
await MatchersPage.fetchStatusBadge.shouldHaveText("200 OK");
|
|
410
|
+
});
|
|
411
411
|
`, 'utf-8');
|
|
412
412
|
await runCommand(['specs/ApiInterception/TC-0010-fifo-abort/web.test.ts'], { target: 'web' });
|
|
413
413
|
const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
|
package/dist/commands/add.js
CHANGED
|
@@ -289,6 +289,7 @@ export async function addCommand(moduleNameArg, options = {}) {
|
|
|
289
289
|
copyRecursive(sampleE2eSrc, targetE2eDir, {
|
|
290
290
|
'playground-e2e': e2eProjectName,
|
|
291
291
|
'packages/playground/e2e': path.relative(rootDir, targetE2eDir).replace(/\\/g, '/'),
|
|
292
|
+
'testspectra:scope:playground': `testspectra:scope:${featureName}`,
|
|
292
293
|
'scope:playground': `scope:${featureName}`,
|
|
293
294
|
});
|
|
294
295
|
// Adjust project.json
|
|
@@ -300,7 +301,7 @@ export async function addCommand(moduleNameArg, options = {}) {
|
|
|
300
301
|
pObj.sourceRoot = path.relative(rootDir, targetE2eDir).replace(/\\/g, '/');
|
|
301
302
|
pObj.targets.e2e.options.cwd = path.relative(rootDir, targetE2eDir).replace(/\\/g, '/');
|
|
302
303
|
pObj.targets['type-check'].options.cwd = '.';
|
|
303
|
-
pObj.tags = [`scope:${featureName}
|
|
304
|
+
pObj.tags = ['testspectra:e2e', `testspectra:scope:${featureName}`];
|
|
304
305
|
fs.writeFileSync(projJsonPath, JSON.stringify(pObj, null, 2), 'utf-8');
|
|
305
306
|
}
|
|
306
307
|
catch { }
|
package/dist/commands/devices.js
CHANGED
package/dist/commands/doctor.js
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -508,6 +508,7 @@ export async function initCommand(options = {}) {
|
|
|
508
508
|
copyRecursive(sampleE2eSrc, targetE2eDir, {
|
|
509
509
|
'playground-e2e': e2eProjectName,
|
|
510
510
|
'packages/playground/e2e': path.relative(cwd, targetE2eDir).replace(/\\/g, '/'),
|
|
511
|
+
'testspectra:scope:playground': `testspectra:scope:${featureName}`,
|
|
511
512
|
'scope:playground': `scope:${featureName}`,
|
|
512
513
|
});
|
|
513
514
|
// Adjust project.json
|
|
@@ -519,7 +520,7 @@ export async function initCommand(options = {}) {
|
|
|
519
520
|
pObj.sourceRoot = path.relative(cwd, targetE2eDir).replace(/\\/g, '/');
|
|
520
521
|
pObj.targets.e2e.options.cwd = path.relative(cwd, targetE2eDir).replace(/\\/g, '/');
|
|
521
522
|
pObj.targets['type-check'].options.cwd = '.';
|
|
522
|
-
pObj.tags = [`scope:${featureName}
|
|
523
|
+
pObj.tags = ['testspectra:e2e', `testspectra:scope:${featureName}`];
|
|
523
524
|
fs.writeFileSync(projJsonPath, JSON.stringify(pObj, null, 2), 'utf-8');
|
|
524
525
|
}
|
|
525
526
|
catch { }
|
|
@@ -557,135 +558,135 @@ export async function initCommand(options = {}) {
|
|
|
557
558
|
// 7. Generate Ambient Types in ROOT only
|
|
558
559
|
TypeGenerator.writeDeclarationFiles(cwd);
|
|
559
560
|
ensureVsCodeExtensions(cwd);
|
|
560
|
-
const archDocContent = `# TestSpectra Enterprise Monorepo Architecture
|
|
561
|
-
|
|
562
|
-
This workspace uses TestSpectra's **"Centralized Configuration, Distributed Implementation"** model for automated cross-platform testing.
|
|
563
|
-
|
|
564
|
-
---
|
|
565
|
-
|
|
566
|
-
## 1. Directory & File Overview
|
|
567
|
-
|
|
568
|
-
### Centralized Root Elements
|
|
569
|
-
- \`./spectra.config.ts\`: Single source of truth for runtime configurations (Base URL, Appium devices, browser targets, timeouts).
|
|
570
|
-
- \`./.testspectra/\`: Local test project cache, runtime metadata, auto-generated master TSConfig (\`.testspectra/tsconfig.json\`), platform sub-configs (\`.testspectra/tsconfig/\`), and universal ambient types (\`.testspectra/types/\`). Global binaries and drivers are managed at \`~/.testspectra/drivers/\`.
|
|
571
|
-
- \`./nx.json\`: Nx target defaults with execution caching and inputs.
|
|
572
|
-
- \`./pnpm-workspace.yaml\`: Monorepo package glob definitions.
|
|
573
|
-
- \`./tsconfig.json\`: Solution-style TypeScript orchestrator referencing \`./.testspectra/tsconfig.json\`.
|
|
574
|
-
|
|
575
|
-
### Centralized Shared Testing Library (\`./${sharedTestingRelPath}\`)
|
|
576
|
-
Provides cross-feature reusable test entities with **100% zero-import global resolution**:
|
|
577
|
-
- \`page-objects/\`: Shared Page Object models (e.g. NavigationBar, AppHeader).
|
|
578
|
-
- \`steps/\`: Cross-feature business step flows (e.g. \`Step.loginAsAdmin()\`).
|
|
579
|
-
- \`actions/\`: Custom atomic actions (e.g. \`Spectra.dismissBanner()\`).
|
|
580
|
-
- \`fixtures/\`: Common test data and environment configurations (e.g. \`Fixture.appConfig\`).
|
|
581
|
-
|
|
582
|
-
### Distributed Feature E2E Modules (${featureE2eDirs.length} Features)
|
|
561
|
+
const archDocContent = `# TestSpectra Enterprise Monorepo Architecture
|
|
562
|
+
|
|
563
|
+
This workspace uses TestSpectra's **"Centralized Configuration, Distributed Implementation"** model for automated cross-platform testing.
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## 1. Directory & File Overview
|
|
568
|
+
|
|
569
|
+
### Centralized Root Elements
|
|
570
|
+
- \`./spectra.config.ts\`: Single source of truth for runtime configurations (Base URL, Appium devices, browser targets, timeouts).
|
|
571
|
+
- \`./.testspectra/\`: Local test project cache, runtime metadata, auto-generated master TSConfig (\`.testspectra/tsconfig.json\`), platform sub-configs (\`.testspectra/tsconfig/\`), and universal ambient types (\`.testspectra/types/\`). Global binaries and drivers are managed at \`~/.testspectra/drivers/\`.
|
|
572
|
+
- \`./nx.json\`: Nx target defaults with execution caching and inputs.
|
|
573
|
+
- \`./pnpm-workspace.yaml\`: Monorepo package glob definitions.
|
|
574
|
+
- \`./tsconfig.json\`: Solution-style TypeScript orchestrator referencing \`./.testspectra/tsconfig.json\`.
|
|
575
|
+
|
|
576
|
+
### Centralized Shared Testing Library (\`./${sharedTestingRelPath}\`)
|
|
577
|
+
Provides cross-feature reusable test entities with **100% zero-import global resolution**:
|
|
578
|
+
- \`page-objects/\`: Shared Page Object models (e.g. NavigationBar, AppHeader).
|
|
579
|
+
- \`steps/\`: Cross-feature business step flows (e.g. \`Step.loginAsAdmin()\`).
|
|
580
|
+
- \`actions/\`: Custom atomic actions (e.g. \`Spectra.dismissBanner()\`).
|
|
581
|
+
- \`fixtures/\`: Common test data and environment configurations (e.g. \`Fixture.appConfig\`).
|
|
582
|
+
|
|
583
|
+
### Distributed Feature E2E Modules (${featureE2eDirs.length} Features)
|
|
583
584
|
${featureE2eDirs
|
|
584
585
|
.map((d) => {
|
|
585
586
|
const rel = path.relative(cwd, d);
|
|
586
587
|
const name = path.basename(path.dirname(d));
|
|
587
588
|
const e2eName = `${name}-${e2eFolderName}`;
|
|
588
|
-
return `- \`./${rel}\` (**${name}**, Nx Project: \`${e2eName}\`):
|
|
589
|
-
- \`specs/\`: Feature test cases (\`specs/<SuiteName>/<CaseId>/\` with \`web.test.ts\`, \`android.test.ts\`, \`ios.test.ts\`).
|
|
590
|
-
- \`page-objects/\`: Feature-specific Page Objects (e.g. \`LoginPage\`, \`ProductPage\`).
|
|
589
|
+
return `- \`./${rel}\` (**${name}**, Nx Project: \`${e2eName}\`):
|
|
590
|
+
- \`specs/\`: Feature test cases (\`specs/<SuiteName>/<CaseId>/\` with \`web.test.ts\`, \`android.test.ts\`, \`ios.test.ts\`).
|
|
591
|
+
- \`page-objects/\`: Feature-specific Page Objects (e.g. \`LoginPage\`, \`ProductPage\`).
|
|
591
592
|
- \`project.json\`: Nx targets (\`e2e\`, \`type-check\`) supporting configurations (\`--configuration=android\`, \`--configuration=ios\`, \`--configuration=headless\`).`;
|
|
592
593
|
})
|
|
593
|
-
.join('\n')}
|
|
594
|
-
|
|
595
|
-
---
|
|
596
|
-
|
|
597
|
-
## 2. Zero-Import Consumption Flow & Mechanics
|
|
598
|
-
|
|
599
|
-
TestSpectra completely eliminates boilerplate \`import\` statements across all test specs, steps, and page objects.
|
|
600
|
-
|
|
601
|
-
### How It Works:
|
|
602
|
-
1. **Centralized Ambient Aggregation**:
|
|
603
|
-
TestSpectra Language Service Plugin & CLI automatically scan:
|
|
604
|
-
- **Local Feature Entities**: \`<feature>/${e2eFolderName}/page-objects/\`, \`<feature>/${e2eFolderName}/steps/\`, \`<feature>/${e2eFolderName}/actions/\`
|
|
605
|
-
- **Shared Library Entities**: \`./${sharedTestingRelPath}/page-objects/\`, \`./${sharedTestingRelPath}/steps/\`, \`./${sharedTestingRelPath}/actions/\`, \`./${sharedTestingRelPath}/fixtures/\`
|
|
606
|
-
2. **Ambient Typing Generation**:
|
|
607
|
-
These are compiled into \`.testspectra/types/web.d.ts\`, \`android.d.ts\`, \`ios.d.ts\`, \`fixtures.d.ts\`.
|
|
608
|
-
3. **Instant IDE Autocomplete & Zero Clutter**:
|
|
609
|
-
Specs consume both local feature objects and shared library utilities globally.
|
|
610
|
-
|
|
611
|
-
### Code Example:
|
|
612
|
-
\`\`\`typescript
|
|
613
|
-
// packages/features/auth/e2e/specs/Auth/TC-AUTH-01/web.test.ts
|
|
614
|
-
// 🌟 NOTICE: 100% Zero-Import & Flat Execution! (No import statement, no describe wrapper)
|
|
615
|
-
|
|
616
|
-
it("should navigate via shared header and login with local POM", async () => {
|
|
617
|
-
// 1. Consumed from Shared Testing Library (${sharedTestingRelPath}/page-objects/NavigationBar)
|
|
618
|
-
await NavigationBar.goToLogin();
|
|
619
|
-
|
|
620
|
-
// 2. Consumed from Shared Testing Library (${sharedTestingRelPath}/steps/loginAsAdmin)
|
|
621
|
-
await Step.loginAsAdmin();
|
|
622
|
-
|
|
623
|
-
// 3. Consumed from Local Feature POM (page-objects/LoginPage)
|
|
624
|
-
await LoginPage.flashAlert.shouldBeVisible();
|
|
625
|
-
await LoginPage.flashAlert.shouldContainText("Welcome");
|
|
626
|
-
|
|
627
|
-
// 4. Consumed from Shared Testing Library Custom Action (${sharedTestingRelPath}/actions/dismissBanner)
|
|
628
|
-
await Spectra.dismissBanner();
|
|
629
|
-
});
|
|
630
|
-
\`\`\`
|
|
631
|
-
|
|
632
|
-
---
|
|
633
|
-
|
|
634
|
-
## 3. How to Run Tests
|
|
635
|
-
|
|
636
|
-
You can execute tests either from the **Monorepo Root via Nx** or directly inside each **Feature Directory via CLI**:
|
|
637
|
-
|
|
638
|
-
### Option A: From Root Monorepo via Nx (Recommended for CI & Team Workflows)
|
|
639
|
-
|
|
640
|
-
\`\`\`bash
|
|
641
|
-
# 1. Run E2E for a SPECIFIC feature module (e.g. auth)
|
|
642
|
-
pnpm nx run auth-${e2eFolderName}:e2e
|
|
643
|
-
|
|
644
|
-
# 2. Run E2E for a specific feature on ANDROID or IOS
|
|
645
|
-
pnpm nx run auth-${e2eFolderName}:e2e --configuration=android
|
|
646
|
-
pnpm nx run auth-${e2eFolderName}:e2e --configuration=ios
|
|
647
|
-
|
|
648
|
-
# 3. Run E2E for a specific feature in HEADLESS browser mode
|
|
649
|
-
pnpm nx run auth-${e2eFolderName}:e2e --configuration=headless
|
|
650
|
-
|
|
651
|
-
# 4. Run ALL feature E2E suites across the entire monorepo in parallel
|
|
652
|
-
pnpm nx run-many -t e2e
|
|
653
|
-
|
|
654
|
-
# 5. Run only E2E tests for features modified in current Git branch / PR
|
|
655
|
-
pnpm nx affected -t e2e
|
|
656
|
-
\`\`\`
|
|
657
|
-
|
|
658
|
-
### Option B: From Inside the Feature Directory (Direct CLI)
|
|
659
|
-
|
|
660
|
-
\`\`\`bash
|
|
661
|
-
# Navigate to the feature e2e folder
|
|
662
|
-
cd ${featureE2eDirs.length > 0 ? path.relative(cwd, featureE2eDirs[0]).replace(/\\/g, '/') : `modules/auth/${e2eFolderName}`}
|
|
663
|
-
|
|
664
|
-
# Run all specs in this feature
|
|
665
|
-
pnpm spectra run
|
|
666
|
-
|
|
667
|
-
# Run a specific test case file
|
|
668
|
-
pnpm spectra run specs/Auth/TC-AUTH-01/web.test.ts
|
|
669
|
-
|
|
670
|
-
# Target Android device or emulator
|
|
671
|
-
pnpm spectra run --target android
|
|
672
|
-
|
|
673
|
-
# Run in headed / interactive browser mode
|
|
674
|
-
pnpm spectra run --no-headless
|
|
675
|
-
\`\`\`
|
|
676
|
-
|
|
677
|
-
---
|
|
678
|
-
|
|
679
|
-
## 4. Type Checking & Validation
|
|
680
|
-
|
|
681
|
-
\`\`\`bash
|
|
682
|
-
# Type check all E2E projects via Nx
|
|
683
|
-
pnpm nx run-many -t type-check
|
|
684
|
-
|
|
685
|
-
# Or standard TypeScript project references build from root
|
|
686
|
-
pnpm type-check
|
|
687
|
-
# or: npx tsc -b
|
|
688
|
-
\`\`\`
|
|
594
|
+
.join('\n')}
|
|
595
|
+
|
|
596
|
+
---
|
|
597
|
+
|
|
598
|
+
## 2. Zero-Import Consumption Flow & Mechanics
|
|
599
|
+
|
|
600
|
+
TestSpectra completely eliminates boilerplate \`import\` statements across all test specs, steps, and page objects.
|
|
601
|
+
|
|
602
|
+
### How It Works:
|
|
603
|
+
1. **Centralized Ambient Aggregation**:
|
|
604
|
+
TestSpectra Language Service Plugin & CLI automatically scan:
|
|
605
|
+
- **Local Feature Entities**: \`<feature>/${e2eFolderName}/page-objects/\`, \`<feature>/${e2eFolderName}/steps/\`, \`<feature>/${e2eFolderName}/actions/\`
|
|
606
|
+
- **Shared Library Entities**: \`./${sharedTestingRelPath}/page-objects/\`, \`./${sharedTestingRelPath}/steps/\`, \`./${sharedTestingRelPath}/actions/\`, \`./${sharedTestingRelPath}/fixtures/\`
|
|
607
|
+
2. **Ambient Typing Generation**:
|
|
608
|
+
These are compiled into \`.testspectra/types/web.d.ts\`, \`android.d.ts\`, \`ios.d.ts\`, \`fixtures.d.ts\`.
|
|
609
|
+
3. **Instant IDE Autocomplete & Zero Clutter**:
|
|
610
|
+
Specs consume both local feature objects and shared library utilities globally.
|
|
611
|
+
|
|
612
|
+
### Code Example:
|
|
613
|
+
\`\`\`typescript
|
|
614
|
+
// packages/features/auth/e2e/specs/Auth/TC-AUTH-01/web.test.ts
|
|
615
|
+
// 🌟 NOTICE: 100% Zero-Import & Flat Execution! (No import statement, no describe wrapper)
|
|
616
|
+
|
|
617
|
+
it("should navigate via shared header and login with local POM", async () => {
|
|
618
|
+
// 1. Consumed from Shared Testing Library (${sharedTestingRelPath}/page-objects/NavigationBar)
|
|
619
|
+
await NavigationBar.goToLogin();
|
|
620
|
+
|
|
621
|
+
// 2. Consumed from Shared Testing Library (${sharedTestingRelPath}/steps/loginAsAdmin)
|
|
622
|
+
await Step.loginAsAdmin();
|
|
623
|
+
|
|
624
|
+
// 3. Consumed from Local Feature POM (page-objects/LoginPage)
|
|
625
|
+
await LoginPage.flashAlert.shouldBeVisible();
|
|
626
|
+
await LoginPage.flashAlert.shouldContainText("Welcome");
|
|
627
|
+
|
|
628
|
+
// 4. Consumed from Shared Testing Library Custom Action (${sharedTestingRelPath}/actions/dismissBanner)
|
|
629
|
+
await Spectra.dismissBanner();
|
|
630
|
+
});
|
|
631
|
+
\`\`\`
|
|
632
|
+
|
|
633
|
+
---
|
|
634
|
+
|
|
635
|
+
## 3. How to Run Tests
|
|
636
|
+
|
|
637
|
+
You can execute tests either from the **Monorepo Root via Nx** or directly inside each **Feature Directory via CLI**:
|
|
638
|
+
|
|
639
|
+
### Option A: From Root Monorepo via Nx (Recommended for CI & Team Workflows)
|
|
640
|
+
|
|
641
|
+
\`\`\`bash
|
|
642
|
+
# 1. Run E2E for a SPECIFIC feature module (e.g. auth)
|
|
643
|
+
pnpm nx run auth-${e2eFolderName}:e2e
|
|
644
|
+
|
|
645
|
+
# 2. Run E2E for a specific feature on ANDROID or IOS
|
|
646
|
+
pnpm nx run auth-${e2eFolderName}:e2e --configuration=android
|
|
647
|
+
pnpm nx run auth-${e2eFolderName}:e2e --configuration=ios
|
|
648
|
+
|
|
649
|
+
# 3. Run E2E for a specific feature in HEADLESS browser mode
|
|
650
|
+
pnpm nx run auth-${e2eFolderName}:e2e --configuration=headless
|
|
651
|
+
|
|
652
|
+
# 4. Run ALL feature E2E suites across the entire monorepo in parallel
|
|
653
|
+
pnpm nx run-many -t e2e
|
|
654
|
+
|
|
655
|
+
# 5. Run only E2E tests for features modified in current Git branch / PR
|
|
656
|
+
pnpm nx affected -t e2e
|
|
657
|
+
\`\`\`
|
|
658
|
+
|
|
659
|
+
### Option B: From Inside the Feature Directory (Direct CLI)
|
|
660
|
+
|
|
661
|
+
\`\`\`bash
|
|
662
|
+
# Navigate to the feature e2e folder
|
|
663
|
+
cd ${featureE2eDirs.length > 0 ? path.relative(cwd, featureE2eDirs[0]).replace(/\\/g, '/') : `modules/auth/${e2eFolderName}`}
|
|
664
|
+
|
|
665
|
+
# Run all specs in this feature
|
|
666
|
+
pnpm spectra run
|
|
667
|
+
|
|
668
|
+
# Run a specific test case file
|
|
669
|
+
pnpm spectra run specs/Auth/TC-AUTH-01/web.test.ts
|
|
670
|
+
|
|
671
|
+
# Target Android device or emulator
|
|
672
|
+
pnpm spectra run --target android
|
|
673
|
+
|
|
674
|
+
# Run in headed / interactive browser mode
|
|
675
|
+
pnpm spectra run --no-headless
|
|
676
|
+
\`\`\`
|
|
677
|
+
|
|
678
|
+
---
|
|
679
|
+
|
|
680
|
+
## 4. Type Checking & Validation
|
|
681
|
+
|
|
682
|
+
\`\`\`bash
|
|
683
|
+
# Type check all E2E projects via Nx
|
|
684
|
+
pnpm nx run-many -t type-check
|
|
685
|
+
|
|
686
|
+
# Or standard TypeScript project references build from root
|
|
687
|
+
pnpm type-check
|
|
688
|
+
# or: npx tsc -b
|
|
689
|
+
\`\`\`
|
|
689
690
|
`;
|
|
690
691
|
fs.writeFileSync(path.join(cwd, 'ARCHITECTURE.md'), archDocContent, 'utf-8');
|
|
691
692
|
// 8. Ensure root .gitignore ignores .testspectra/ and *.tsbuildinfo
|
|
@@ -741,32 +742,32 @@ pnpm type-check
|
|
|
741
742
|
TypeGenerator.writeDeclarationFiles(cwd);
|
|
742
743
|
ensureVsCodeExtensions(cwd);
|
|
743
744
|
// Standalone Architecture Documentation
|
|
744
|
-
const standaloneArchDoc = `# TestSpectra Project Architecture
|
|
745
|
-
|
|
746
|
-
This project is built with the TestSpectra testing framework.
|
|
747
|
-
|
|
748
|
-
## Structure & File Map
|
|
749
|
-
- \`./spectra.config.ts\`: Typed runner configuration.
|
|
750
|
-
- \`./specs/\`: Layered test cases (\`specs/<Suite>/<CaseId>/\` with \`web.test.ts\`, \`android.test.ts\`, \`ios.test.ts\`).
|
|
751
|
-
- \`./page-objects/\`: Page Object models per platform.
|
|
752
|
-
- \`./steps/\`: Business flows accessible globally via \`Step.*\`.
|
|
753
|
-
- \`./actions/\`: Atomic actions accessible globally via \`Spectra.*\`.
|
|
754
|
-
- \`./fixtures/\`: JSON fixtures accessible globally via \`Fixture.*\`.
|
|
755
|
-
- \`./global-hooks/\`: Global suite lifecycle hooks.
|
|
756
|
-
- \`./.testspectra/\`: Auto-generated ambient typings and local runtime cache.
|
|
757
|
-
|
|
758
|
-
## Execution
|
|
759
|
-
\`\`\`bash
|
|
760
|
-
# Run web test suite
|
|
761
|
-
pnpm test
|
|
762
|
-
|
|
763
|
-
# Run Android / iOS
|
|
764
|
-
pnpm spectra run --target android
|
|
765
|
-
pnpm spectra run --target ios
|
|
766
|
-
|
|
767
|
-
# Type check
|
|
768
|
-
pnpm type-check
|
|
769
|
-
\`\`\`
|
|
745
|
+
const standaloneArchDoc = `# TestSpectra Project Architecture
|
|
746
|
+
|
|
747
|
+
This project is built with the TestSpectra testing framework.
|
|
748
|
+
|
|
749
|
+
## Structure & File Map
|
|
750
|
+
- \`./spectra.config.ts\`: Typed runner configuration.
|
|
751
|
+
- \`./specs/\`: Layered test cases (\`specs/<Suite>/<CaseId>/\` with \`web.test.ts\`, \`android.test.ts\`, \`ios.test.ts\`).
|
|
752
|
+
- \`./page-objects/\`: Page Object models per platform.
|
|
753
|
+
- \`./steps/\`: Business flows accessible globally via \`Step.*\`.
|
|
754
|
+
- \`./actions/\`: Atomic actions accessible globally via \`Spectra.*\`.
|
|
755
|
+
- \`./fixtures/\`: JSON fixtures accessible globally via \`Fixture.*\`.
|
|
756
|
+
- \`./global-hooks/\`: Global suite lifecycle hooks.
|
|
757
|
+
- \`./.testspectra/\`: Auto-generated ambient typings and local runtime cache.
|
|
758
|
+
|
|
759
|
+
## Execution
|
|
760
|
+
\`\`\`bash
|
|
761
|
+
# Run web test suite
|
|
762
|
+
pnpm test
|
|
763
|
+
|
|
764
|
+
# Run Android / iOS
|
|
765
|
+
pnpm spectra run --target android
|
|
766
|
+
pnpm spectra run --target ios
|
|
767
|
+
|
|
768
|
+
# Type check
|
|
769
|
+
pnpm type-check
|
|
770
|
+
\`\`\`
|
|
770
771
|
`;
|
|
771
772
|
fs.writeFileSync(path.join(cwd, 'ARCHITECTURE.md'), standaloneArchDoc, 'utf-8');
|
|
772
773
|
// Safely update root README.md (do not overwrite existing content)
|
package/dist/commands/install.js
CHANGED
|
@@ -34,6 +34,7 @@ export async function installCommand(item, options = {}) {
|
|
|
34
34
|
cwd: workspaceRoot,
|
|
35
35
|
env: runnerEnv,
|
|
36
36
|
stdio: 'inherit',
|
|
37
|
+
windowsHide: true,
|
|
37
38
|
});
|
|
38
39
|
}
|
|
39
40
|
else {
|
|
@@ -41,6 +42,7 @@ export async function installCommand(item, options = {}) {
|
|
|
41
42
|
cwd,
|
|
42
43
|
env: runnerEnv,
|
|
43
44
|
stdio: 'inherit',
|
|
45
|
+
windowsHide: true,
|
|
44
46
|
});
|
|
45
47
|
}
|
|
46
48
|
child.on('close', (code) => {
|
package/dist/commands/license.js
CHANGED
package/dist/commands/test.js
CHANGED
|
@@ -15,7 +15,7 @@ import { runSystemChecks } from './doctor.js';
|
|
|
15
15
|
async function resolveBrowserExecutable(cwd) {
|
|
16
16
|
try {
|
|
17
17
|
const { dependencies } = await runSystemChecks(cwd);
|
|
18
|
-
const chrome = dependencies.find((d) => d.category === 'web' &&
|
|
18
|
+
const chrome = dependencies.find((d) => d.category === 'web' && d.name.includes('Chrome'));
|
|
19
19
|
return chrome?.installed && chrome.location ? chrome.location : null;
|
|
20
20
|
}
|
|
21
21
|
catch {
|
|
@@ -81,9 +81,11 @@ export async function testCommand(options = {}) {
|
|
|
81
81
|
concurrency: concurrency !== undefined && !isNaN(concurrency) ? concurrency : undefined,
|
|
82
82
|
browserExecutablePath,
|
|
83
83
|
onLine: (line) => {
|
|
84
|
-
|
|
84
|
+
// Strip ANSI and whitespace since Vitest might prefix or colorize console logs
|
|
85
|
+
const cleanLine = line.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, '').trim();
|
|
86
|
+
if (!cleanLine.startsWith('[TESTSPECTRA_'))
|
|
85
87
|
return;
|
|
86
|
-
reporter.addLog({ timestamp: new Date().toLocaleTimeString(), level: 'INFO', message:
|
|
88
|
+
reporter.addLog({ timestamp: new Date().toLocaleTimeString(), level: 'INFO', message: cleanLine });
|
|
87
89
|
if (!reporter.isInteractiveMode()) {
|
|
88
90
|
console.log(line);
|
|
89
91
|
}
|
|
@@ -41,7 +41,8 @@ describe('Platform TSConfigs & Type Isolation (docs/v2/cli/tsconfig-platform-iso
|
|
|
41
41
|
expect(rootConfig.references).toEqual([{ path: './.testspectra/tsconfig.json' }]);
|
|
42
42
|
// Verify dynamic master solution references all 5 platform solution tsconfigs in .testspectra/tsconfig/
|
|
43
43
|
const solutionConfig = JSON.parse(fs.readFileSync(path.join(tmpDir, '.testspectra', 'tsconfig.json'), 'utf-8'));
|
|
44
|
-
expect(solutionConfig.compilerOptions.
|
|
44
|
+
expect(solutionConfig.compilerOptions.composite).toBe(true);
|
|
45
|
+
expect(solutionConfig.compilerOptions.noEmit).toBeUndefined();
|
|
45
46
|
expect(solutionConfig.references).toEqual([
|
|
46
47
|
{ path: './tsconfig/web.json' },
|
|
47
48
|
{ path: './tsconfig/mobile.json' },
|
|
@@ -383,4 +383,42 @@ async function verifyZeroImport() {
|
|
|
383
383
|
expect(fs.existsSync(path.join(tmpDir, '.testspectra', 'types', 'web.d.ts'))).toBe(false);
|
|
384
384
|
expect(fs.existsSync(path.join(tmpDir, '.testspectra', 'types', 'common.d.ts'))).toBe(false);
|
|
385
385
|
});
|
|
386
|
+
it('nx monorepo: does NOT treat feature library as shared library and discovers nested e2e subproject', () => {
|
|
387
|
+
// 1. Shared library with testspectra:shared
|
|
388
|
+
const sharedDir = path.join(tmpDir, 'shared', 'testing');
|
|
389
|
+
fs.mkdirSync(path.join(sharedDir, 'page-objects', 'SharedNav'), { recursive: true });
|
|
390
|
+
fs.writeFileSync(path.join(sharedDir, 'project.json'), JSON.stringify({ name: 'shared-testing', projectType: 'library', tags: ['testspectra:shared'] }));
|
|
391
|
+
fs.writeFileSync(path.join(sharedDir, 'page-objects', 'SharedNav', 'common.ts'), 'export default class SharedNav {}');
|
|
392
|
+
// 2. Nx Feature library (projectType: "library", standard Nx tags, NO testspectra tags)
|
|
393
|
+
const featureLibDir = path.join(tmpDir, 'packages', 'features', 'auth');
|
|
394
|
+
fs.mkdirSync(featureLibDir, { recursive: true });
|
|
395
|
+
fs.writeFileSync(path.join(featureLibDir, 'project.json'), JSON.stringify({
|
|
396
|
+
name: '@tagsamurai/feature-auth',
|
|
397
|
+
projectType: 'library',
|
|
398
|
+
sourceRoot: 'packages/features/auth',
|
|
399
|
+
targets: {},
|
|
400
|
+
tags: ['type:feature', 'scope:feature-auth'],
|
|
401
|
+
}));
|
|
402
|
+
// 3. Nested E2E subproject inside feature library
|
|
403
|
+
const featureE2eDir = path.join(featureLibDir, 'e2e');
|
|
404
|
+
fs.mkdirSync(path.join(featureE2eDir, 'specs', 'AuthSuite', 'TC-001'), { recursive: true });
|
|
405
|
+
fs.writeFileSync(path.join(featureE2eDir, 'project.json'), JSON.stringify({
|
|
406
|
+
name: 'feature-auth-e2e',
|
|
407
|
+
projectType: 'application',
|
|
408
|
+
tags: ['testspectra:e2e', 'testspectra:scope:feature-auth'],
|
|
409
|
+
}));
|
|
410
|
+
fs.writeFileSync(path.join(featureE2eDir, 'specs', 'AuthSuite', 'TC-001', 'web.test.ts'), 'it("auth test", async () => {});');
|
|
411
|
+
const scopes = TypeGenerator.getEntityScopes(tmpDir);
|
|
412
|
+
expect(scopes).toHaveLength(2);
|
|
413
|
+
const sharedScope = scopes.find((s) => s.isShared);
|
|
414
|
+
expect(sharedScope).toBeDefined();
|
|
415
|
+
expect(sharedScope?.name).toBe('shared');
|
|
416
|
+
expect(sharedScope?.dir).toBe(path.resolve(sharedDir));
|
|
417
|
+
const featureScope = scopes.find((s) => !s.isShared);
|
|
418
|
+
expect(featureScope).toBeDefined();
|
|
419
|
+
expect(featureScope?.name).toBe('feature-auth');
|
|
420
|
+
expect(featureScope?.dir).toBe(path.resolve(featureE2eDir));
|
|
421
|
+
// Verify packages/features/auth is NOT in scopes
|
|
422
|
+
expect(scopes.some((s) => s.dir === path.resolve(featureLibDir))).toBe(false);
|
|
423
|
+
});
|
|
386
424
|
});
|
|
@@ -9,8 +9,11 @@ export interface EntityScope {
|
|
|
9
9
|
* Discovers all feature directories and shared library directories.
|
|
10
10
|
* Standard discovery hierarchy:
|
|
11
11
|
* 1. Nx / Monorepo Project Descriptors (`project.json`):
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
12
|
+
* - tags: ["testspectra:shared"] => Shared Library Scope (isShared: true)
|
|
13
|
+
* - tags: ["testspectra:e2e"] or ["testspectra:scope:<name>"] => Feature Scope (isShared: false)
|
|
14
|
+
* - Generic Nx libraries/apps without testspectra tags are not TestSpectra scopes and recursion
|
|
15
|
+
* continues into their subdirectories so nested e2e projects (e.g. `packages/features/auth/e2e`)
|
|
16
|
+
* are discovered.
|
|
14
17
|
* 2. Explicit User Configuration (`tsconfig.spectra.shared.json`):
|
|
15
18
|
* - Custom include path for shared testing utilities
|
|
16
19
|
* 3. Dynamic Workspace Filesystem Discovery (Fallback for non-Nx / custom layouts):
|