@testspectra/cli 1.1.8-rc.2 → 1.1.8-rc.21
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/.testspectra-runner.hash +1 -1
- package/bin/spectra.js +0 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/commands/__tests__/cloud-run.test.d.ts +1 -0
- package/dist/commands/__tests__/cloud-run.test.js +93 -0
- package/dist/commands/__tests__/run-e2e.test.js +49 -49
- package/dist/commands/add.js +2 -1
- package/dist/commands/cloud-run.d.ts +13 -0
- package/dist/commands/cloud-run.js +120 -0
- package/dist/commands/devices.js +1 -0
- package/dist/commands/docs.d.ts +4 -0
- package/dist/commands/docs.js +17 -0
- package/dist/commands/doctor.js +1 -0
- package/dist/commands/init.js +7 -160
- 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/architecture-doc-generator.d.ts +14 -0
- package/dist/generator/architecture-doc-generator.js +82 -0
- package/dist/generator/index.d.ts +1 -0
- package/dist/generator/index.js +1 -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 +18 -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/docs/ARCHITECTURE.default.md +26 -0
- package/templates/docs/ARCHITECTURE.nx.md +119 -0
- 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/{59842386-c7c4-44ca-b2c2-20e1700bd13d.db-wal → 70094CFD-E89B-57A1-9619-2FC5F4963C54.db-wal} +0 -0
- package/templates/nx/.nx/workspace-data/d/daemon.log +1090 -1577
- package/templates/nx/.nx/workspace-data/d/server-process.json +1 -1
- package/templates/nx/.nx/workspace-data/file-map.json +56 -56
- 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 +185 -6681
- package/templates/nx/.nx/workspace-data/project-graph.json +190 -7434
- package/templates/nx/CLAUDE.md +10 -10
- package/templates/nx/package.json +3 -3
- 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.exe +0 -0
- /package/templates/nx/.nx/workspace-data/{59842386-c7c4-44ca-b2c2-20e1700bd13d.db → 70094CFD-E89B-57A1-9619-2FC5F4963C54.db} +0 -0
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
it("Mock a Server-Sent Events (EventSource) stream via the existing network-intercept engine", async () => {
|
|
2
|
-
await MatchersPage.open("sse");
|
|
3
|
-
|
|
4
|
-
// EventSource's initial request is a plain HTTP GET under the hood — CDP's Fetch domain
|
|
5
|
-
// intercepts it exactly like any other fetch()/XHR request. Fulfilling it with a
|
|
6
|
-
// text/event-stream body delivers every "data: ...\n\n" block at once; the browser's own
|
|
7
|
-
// EventSource parser still splits that into individual "message" events and dispatches them
|
|
8
|
-
// just as it would for a real stream, which is the part this demo actually cares about.
|
|
9
|
-
//
|
|
10
|
-
// What this can't fake: `Fetch.fulfillRequest` delivers a complete, connection-closing
|
|
11
|
-
// response (there's no way to keep it open indefinitely the way a real SSE endpoint would), so
|
|
12
|
-
// the browser's EventSource treats that closure as a connection error shortly after — the
|
|
13
|
-
// events still arrive and get processed first, but the app's own "CONNECTED" status flips back
|
|
14
|
-
// to disconnected right after, same as it would after a real server unexpectedly closed the
|
|
15
|
-
// connection. So this test verifies the events were received, not that the connection stayed
|
|
16
|
-
// open — genuinely-persistent SSE isn't something a request/response-shaped mock can simulate.
|
|
17
|
-
await Spectra.intercept("/api/sse-feed", "GET", {
|
|
18
|
-
body: 'data: {"text":"hello from mock SSE"}\n\ndata: {"text":"second event"}\n\n',
|
|
19
|
-
headers: { "Content-Type": "text/event-stream" },
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
await Spectra.get("#sse-connect-btn").click();
|
|
23
|
-
await Spectra.getAll(".sse-message-item").shouldHaveLength(2);
|
|
24
|
-
await Spectra.getAll(".sse-message-item").first().shouldContainText("hello from mock SSE");
|
|
25
|
-
await Spectra.getAll(".sse-message-item").last().shouldContainText("second event");
|
|
26
|
-
});
|
|
1
|
+
it("Mock a Server-Sent Events (EventSource) stream via the existing network-intercept engine", async () => {
|
|
2
|
+
await MatchersPage.open("sse");
|
|
3
|
+
|
|
4
|
+
// EventSource's initial request is a plain HTTP GET under the hood — CDP's Fetch domain
|
|
5
|
+
// intercepts it exactly like any other fetch()/XHR request. Fulfilling it with a
|
|
6
|
+
// text/event-stream body delivers every "data: ...\n\n" block at once; the browser's own
|
|
7
|
+
// EventSource parser still splits that into individual "message" events and dispatches them
|
|
8
|
+
// just as it would for a real stream, which is the part this demo actually cares about.
|
|
9
|
+
//
|
|
10
|
+
// What this can't fake: `Fetch.fulfillRequest` delivers a complete, connection-closing
|
|
11
|
+
// response (there's no way to keep it open indefinitely the way a real SSE endpoint would), so
|
|
12
|
+
// the browser's EventSource treats that closure as a connection error shortly after — the
|
|
13
|
+
// events still arrive and get processed first, but the app's own "CONNECTED" status flips back
|
|
14
|
+
// to disconnected right after, same as it would after a real server unexpectedly closed the
|
|
15
|
+
// connection. So this test verifies the events were received, not that the connection stayed
|
|
16
|
+
// open — genuinely-persistent SSE isn't something a request/response-shaped mock can simulate.
|
|
17
|
+
await Spectra.intercept("/api/sse-feed", "GET", {
|
|
18
|
+
body: 'data: {"text":"hello from mock SSE"}\n\ndata: {"text":"second event"}\n\n',
|
|
19
|
+
headers: { "Content-Type": "text/event-stream" },
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await Spectra.get("#sse-connect-btn").click();
|
|
23
|
+
await Spectra.getAll(".sse-message-item").shouldHaveLength(2);
|
|
24
|
+
await Spectra.getAll(".sse-message-item").first().shouldContainText("hello from mock SSE");
|
|
25
|
+
await Spectra.getAll(".sse-message-item").last().shouldContainText("second event");
|
|
26
|
+
});
|
package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/spec.md
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: TC-0016-bypass-websocket-with-app-level-mock
|
|
3
|
-
title: Bypass a real WebSocket connection via an app-level test-only mock flag
|
|
4
|
-
priority: Medium
|
|
5
|
-
caseType: Positive
|
|
6
|
-
status: ready-for-automation
|
|
7
|
-
coverage:
|
|
8
|
-
web: automated
|
|
9
|
-
mobile: unsupported
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# Bypass a real WebSocket connection via an app-level test-only mock flag
|
|
13
|
-
|
|
14
|
-
**Why this isn't network-level interception like `Spectra.intercept()`:** a WebSocket connection
|
|
15
|
-
starts as an HTTP `Upgrade` handshake and then becomes a raw bidirectional frame stream — it's
|
|
16
|
-
never a single request/response CDP's `Fetch` domain can fulfill the way it fulfills a normal
|
|
17
|
-
`fetch()`/`XHR`/`EventSource` request. Chrome DevTools Protocol's WebSocket-related events
|
|
18
|
-
(`Network.webSocketCreated`, `webSocketFrameSent`/`webSocketFrameReceived`, etc.) are
|
|
19
|
-
observation-only — CDP has no command that mocks or rewrites a WS frame in flight. This is a
|
|
20
|
-
genuine protocol-level limitation, not a gap in this driver.
|
|
21
|
-
|
|
22
|
-
The practical workaround, and what this demo shows: the app itself checks for a test-only global
|
|
23
|
-
flag (`window.__mockWebSocketMessages`) and swaps in an in-memory fake socket when present,
|
|
24
|
-
instead of opening a real connection. `Spectra.browser.evaluate()` sets that flag before the app
|
|
25
|
-
connects. This is the same pattern used by Cypress/Playwright test suites for the same reason.
|
|
26
|
-
|
|
27
|
-
Mobile coverage is `unsupported`: React Native's WebSocket implementation is a native module,
|
|
28
|
-
not a page-context global reachable via a browser-style `evaluate()` call — a mobile equivalent
|
|
29
|
-
would need the RN app's own WebSocket client wrapper to support a comparable test-only bypass.
|
|
30
|
-
|
|
31
|
-
## Test Steps
|
|
32
|
-
1. Open the WebSocket matcher section
|
|
33
|
-
2. Set the app-level mock flag via browser.evaluate before the app connects
|
|
34
|
-
3. Click the connect button
|
|
35
|
-
4. Assert the connection status displays CONNECTED and two mocked frames render
|
|
36
|
-
5. Click the disconnect button
|
|
37
|
-
6. Assert the connection status displays DISCONNECTED
|
|
1
|
+
---
|
|
2
|
+
id: TC-0016-bypass-websocket-with-app-level-mock
|
|
3
|
+
title: Bypass a real WebSocket connection via an app-level test-only mock flag
|
|
4
|
+
priority: Medium
|
|
5
|
+
caseType: Positive
|
|
6
|
+
status: ready-for-automation
|
|
7
|
+
coverage:
|
|
8
|
+
web: automated
|
|
9
|
+
mobile: unsupported
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Bypass a real WebSocket connection via an app-level test-only mock flag
|
|
13
|
+
|
|
14
|
+
**Why this isn't network-level interception like `Spectra.intercept()`:** a WebSocket connection
|
|
15
|
+
starts as an HTTP `Upgrade` handshake and then becomes a raw bidirectional frame stream — it's
|
|
16
|
+
never a single request/response CDP's `Fetch` domain can fulfill the way it fulfills a normal
|
|
17
|
+
`fetch()`/`XHR`/`EventSource` request. Chrome DevTools Protocol's WebSocket-related events
|
|
18
|
+
(`Network.webSocketCreated`, `webSocketFrameSent`/`webSocketFrameReceived`, etc.) are
|
|
19
|
+
observation-only — CDP has no command that mocks or rewrites a WS frame in flight. This is a
|
|
20
|
+
genuine protocol-level limitation, not a gap in this driver.
|
|
21
|
+
|
|
22
|
+
The practical workaround, and what this demo shows: the app itself checks for a test-only global
|
|
23
|
+
flag (`window.__mockWebSocketMessages`) and swaps in an in-memory fake socket when present,
|
|
24
|
+
instead of opening a real connection. `Spectra.browser.evaluate()` sets that flag before the app
|
|
25
|
+
connects. This is the same pattern used by Cypress/Playwright test suites for the same reason.
|
|
26
|
+
|
|
27
|
+
Mobile coverage is `unsupported`: React Native's WebSocket implementation is a native module,
|
|
28
|
+
not a page-context global reachable via a browser-style `evaluate()` call — a mobile equivalent
|
|
29
|
+
would need the RN app's own WebSocket client wrapper to support a comparable test-only bypass.
|
|
30
|
+
|
|
31
|
+
## Test Steps
|
|
32
|
+
1. Open the WebSocket matcher section
|
|
33
|
+
2. Set the app-level mock flag via browser.evaluate before the app connects
|
|
34
|
+
3. Click the connect button
|
|
35
|
+
4. Assert the connection status displays CONNECTED and two mocked frames render
|
|
36
|
+
5. Click the disconnect button
|
|
37
|
+
6. Assert the connection status displays DISCONNECTED
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
it("Bypass a real WebSocket connection via an app-level test-only mock flag", async () => {
|
|
2
|
-
await MatchersPage.open("websocket");
|
|
3
|
-
|
|
4
|
-
// Set BEFORE the app opens its connection — the component checks this flag inside its own
|
|
5
|
-
// connect() handler and, if present, uses an in-memory fake socket instead of a real
|
|
6
|
-
// `new WebSocket(...)`. This is necessarily app-level, not network-level: see this TC's
|
|
7
|
-
// spec.md for why CDP has no way to intercept/mock a WebSocket handshake or its frames the
|
|
8
|
-
// way Spectra.intercept() does for fetch()/XHR/EventSource.
|
|
9
|
-
await Spectra.browser.evaluate(
|
|
10
|
-
"window.__mockWebSocketMessages = ['hello from mock WS', 'second frame']",
|
|
11
|
-
);
|
|
12
|
-
|
|
13
|
-
await Spectra.get("#ws-connect-btn").click();
|
|
14
|
-
await Spectra.get("#ws-connection-status").shouldHaveText("CONNECTED");
|
|
15
|
-
await Spectra.getAll(".ws-message-item").shouldHaveLength(2);
|
|
16
|
-
await Spectra.getAll(".ws-message-item").first().shouldContainText("hello from mock WS");
|
|
17
|
-
await Spectra.getAll(".ws-message-item").last().shouldContainText("second frame");
|
|
18
|
-
|
|
19
|
-
await Spectra.get("#ws-disconnect-btn").click();
|
|
20
|
-
await Spectra.get("#ws-connection-status").shouldHaveText("DISCONNECTED");
|
|
21
|
-
});
|
|
1
|
+
it("Bypass a real WebSocket connection via an app-level test-only mock flag", async () => {
|
|
2
|
+
await MatchersPage.open("websocket");
|
|
3
|
+
|
|
4
|
+
// Set BEFORE the app opens its connection — the component checks this flag inside its own
|
|
5
|
+
// connect() handler and, if present, uses an in-memory fake socket instead of a real
|
|
6
|
+
// `new WebSocket(...)`. This is necessarily app-level, not network-level: see this TC's
|
|
7
|
+
// spec.md for why CDP has no way to intercept/mock a WebSocket handshake or its frames the
|
|
8
|
+
// way Spectra.intercept() does for fetch()/XHR/EventSource.
|
|
9
|
+
await Spectra.browser.evaluate(
|
|
10
|
+
"window.__mockWebSocketMessages = ['hello from mock WS', 'second frame']",
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
await Spectra.get("#ws-connect-btn").click();
|
|
14
|
+
await Spectra.get("#ws-connection-status").shouldHaveText("CONNECTED");
|
|
15
|
+
await Spectra.getAll(".ws-message-item").shouldHaveLength(2);
|
|
16
|
+
await Spectra.getAll(".ws-message-item").first().shouldContainText("hello from mock WS");
|
|
17
|
+
await Spectra.getAll(".ws-message-item").last().shouldContainText("second frame");
|
|
18
|
+
|
|
19
|
+
await Spectra.get("#ws-disconnect-btn").click();
|
|
20
|
+
await Spectra.get("#ws-connection-status").shouldHaveText("DISCONNECTED");
|
|
21
|
+
});
|
package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/spec.md
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: TC-0017-bypass-captcha-with-test-mode-flag
|
|
3
|
-
title: Bypass a live reCAPTCHA widget via an app-level test-only flag
|
|
4
|
-
priority: Medium
|
|
5
|
-
caseType: Positive
|
|
6
|
-
status: ready-for-automation
|
|
7
|
-
coverage:
|
|
8
|
-
web: automated
|
|
9
|
-
mobile: unsupported
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# Bypass a live reCAPTCHA widget via an app-level test-only flag
|
|
13
|
-
|
|
14
|
-
Real reCAPTCHA renders inside a cross-origin iframe (`google.com`), which Google explicitly
|
|
15
|
-
discourages driving via browser automation — there's no `Spectra.intercept()`-style network mock
|
|
16
|
-
for a live third-party widget's challenge/response flow. The demo embeds the real widget (site key
|
|
17
|
-
`6LcWK4QtAAAAAAeA8IAN20JceGQ7L-RWE-LfhZnB`) for visual completeness, but the test itself uses the
|
|
18
|
-
same pattern real projects use in CI: an app-level `window.__bypassCaptcha` flag the submit
|
|
19
|
-
handler honors instead of ever attempting to automate the actual challenge.
|
|
20
|
-
|
|
21
|
-
Mobile coverage is `unsupported`: a native captcha SDK (rather than a page-context script tag)
|
|
22
|
-
would need its own test-only bypass hook, which is outside what a generic web-style demo can show.
|
|
23
|
-
|
|
24
|
-
## Test Steps
|
|
25
|
-
1. Open the captcha matcher section
|
|
26
|
-
2. Click submit without the bypass flag and assert the widget refuses
|
|
27
|
-
3. Set the app-level bypass flag via browser.evaluate
|
|
28
|
-
4. Click submit again
|
|
29
|
-
5. Assert the submission message indicates the form was bypassed for automated testing
|
|
1
|
+
---
|
|
2
|
+
id: TC-0017-bypass-captcha-with-test-mode-flag
|
|
3
|
+
title: Bypass a live reCAPTCHA widget via an app-level test-only flag
|
|
4
|
+
priority: Medium
|
|
5
|
+
caseType: Positive
|
|
6
|
+
status: ready-for-automation
|
|
7
|
+
coverage:
|
|
8
|
+
web: automated
|
|
9
|
+
mobile: unsupported
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Bypass a live reCAPTCHA widget via an app-level test-only flag
|
|
13
|
+
|
|
14
|
+
Real reCAPTCHA renders inside a cross-origin iframe (`google.com`), which Google explicitly
|
|
15
|
+
discourages driving via browser automation — there's no `Spectra.intercept()`-style network mock
|
|
16
|
+
for a live third-party widget's challenge/response flow. The demo embeds the real widget (site key
|
|
17
|
+
`6LcWK4QtAAAAAAeA8IAN20JceGQ7L-RWE-LfhZnB`) for visual completeness, but the test itself uses the
|
|
18
|
+
same pattern real projects use in CI: an app-level `window.__bypassCaptcha` flag the submit
|
|
19
|
+
handler honors instead of ever attempting to automate the actual challenge.
|
|
20
|
+
|
|
21
|
+
Mobile coverage is `unsupported`: a native captcha SDK (rather than a page-context script tag)
|
|
22
|
+
would need its own test-only bypass hook, which is outside what a generic web-style demo can show.
|
|
23
|
+
|
|
24
|
+
## Test Steps
|
|
25
|
+
1. Open the captcha matcher section
|
|
26
|
+
2. Click submit without the bypass flag and assert the widget refuses
|
|
27
|
+
3. Set the app-level bypass flag via browser.evaluate
|
|
28
|
+
4. Click submit again
|
|
29
|
+
5. Assert the submission message indicates the form was bypassed for automated testing
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
it("Bypass a live reCAPTCHA widget via an app-level test-only flag", async () => {
|
|
2
|
-
await MatchersPage.open("captcha");
|
|
3
|
-
|
|
4
|
-
// Baseline: without the flag (and no completed challenge), the widget refuses to submit —
|
|
5
|
-
// proving the success below is specifically driven by the bypass flag, not a stale state.
|
|
6
|
-
await Spectra.get("#captcha-submit-btn").click();
|
|
7
|
-
await Spectra.get("#captcha-submit-message").shouldContainText("complete the reCAPTCHA challenge");
|
|
8
|
-
|
|
9
|
-
// Set BEFORE submitting — see this TC's spec.md for why this has to be an app-level bypass
|
|
10
|
-
// rather than a network-level mock (real reCAPTCHA lives in a cross-origin iframe with no
|
|
11
|
-
// request/response Spectra.intercept() could fulfill).
|
|
12
|
-
await Spectra.browser.evaluate("window.__bypassCaptcha = true");
|
|
13
|
-
|
|
14
|
-
await Spectra.get("#captcha-submit-btn").click();
|
|
15
|
-
await Spectra.get("#captcha-submit-message").shouldContainText("bypassed for automated test");
|
|
16
|
-
});
|
|
1
|
+
it("Bypass a live reCAPTCHA widget via an app-level test-only flag", async () => {
|
|
2
|
+
await MatchersPage.open("captcha");
|
|
3
|
+
|
|
4
|
+
// Baseline: without the flag (and no completed challenge), the widget refuses to submit —
|
|
5
|
+
// proving the success below is specifically driven by the bypass flag, not a stale state.
|
|
6
|
+
await Spectra.get("#captcha-submit-btn").click();
|
|
7
|
+
await Spectra.get("#captcha-submit-message").shouldContainText("complete the reCAPTCHA challenge");
|
|
8
|
+
|
|
9
|
+
// Set BEFORE submitting — see this TC's spec.md for why this has to be an app-level bypass
|
|
10
|
+
// rather than a network-level mock (real reCAPTCHA lives in a cross-origin iframe with no
|
|
11
|
+
// request/response Spectra.intercept() could fulfill).
|
|
12
|
+
await Spectra.browser.evaluate("window.__bypassCaptcha = true");
|
|
13
|
+
|
|
14
|
+
await Spectra.get("#captcha-submit-btn").click();
|
|
15
|
+
await Spectra.get("#captcha-submit-message").shouldContainText("bypassed for automated test");
|
|
16
|
+
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: suite-api-interception
|
|
3
|
-
name: ApiInterception
|
|
4
|
-
description: REST API network mocking, GET/POST payload interception, and status assertions
|
|
5
|
-
executionOrder: 1.4
|
|
6
|
-
parallel: true
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Suite: ApiInterception
|
|
10
|
-
|
|
1
|
+
---
|
|
2
|
+
id: suite-api-interception
|
|
3
|
+
name: ApiInterception
|
|
4
|
+
description: REST API network mocking, GET/POST payload interception, and status assertions
|
|
5
|
+
executionOrder: 1.4
|
|
6
|
+
parallel: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Suite: ApiInterception
|
|
10
|
+
|
package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/common.test.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
// The `before` hook (hooks/before/common.hook.ts) already ran by the time this test body
|
|
2
|
-
// executes — it seeded a post via a real POST to the demo server's self-hosted API and stashed
|
|
3
|
-
// the result on globalThis for this test to read. Cleanup (hooks/after/common.hook.ts) runs
|
|
4
|
-
// after this whole suite finishes, so it can't be observed from inside the suite's own tests —
|
|
5
|
-
// a separate monitoring suite/run would be how a real project verifies cleanup actually happened.
|
|
6
|
-
//
|
|
7
|
-
// Suite-scoped hooks only run when a worker's whole batch is unambiguously scoped to this one
|
|
8
|
-
// suite (e.g. `spectra run specs/ApiSeeding` or targeting this TC directly) — hook functions are
|
|
9
|
-
// process-global singleton slots, not per-test-case, so a worker batch spanning multiple suites
|
|
10
|
-
// has no correct way to scope suite A's hooks to only run around suite A's own tests, and skips
|
|
11
|
-
// suite-level hooks entirely rather than risk applying the wrong suite's hooks to the wrong
|
|
12
|
-
// tests. Running the FULL default template (`spectra run -t web`/`android`, every suite at once)
|
|
13
|
-
// will legitimately fail this specific test for that reason — that's an inherent limitation of
|
|
14
|
-
// the current worker-batching model, not a bug in this demo.
|
|
15
|
-
it("Verify test data seeded by a before hook via the demo server's self-hosted API is available to the test", async () => {
|
|
16
|
-
const seeding = (globalThis as any).__apiSeeding;
|
|
17
|
-
|
|
18
|
-
if (!seeding?.seededPost) {
|
|
19
|
-
throw new Error("Expected the before hook to have seeded a post onto globalThis.__apiSeeding, found none");
|
|
20
|
-
}
|
|
21
|
-
if (typeof seeding.seededPost.id !== "number") {
|
|
22
|
-
throw new Error(`Expected seeded post to have a numeric id, got ${JSON.stringify(seeding.seededPost.id)}`);
|
|
23
|
-
}
|
|
24
|
-
if (seeding.seededPost.title !== "TestSpectra seeded post") {
|
|
25
|
-
throw new Error(`Expected seeded post title to match, got "${seeding.seededPost.title}"`);
|
|
26
|
-
}
|
|
27
|
-
if (seeding.cleanedUp !== false) {
|
|
28
|
-
throw new Error("Expected cleanedUp to still be false — the after hook hasn't run yet at this point");
|
|
29
|
-
}
|
|
30
|
-
});
|
|
1
|
+
// The `before` hook (hooks/before/common.hook.ts) already ran by the time this test body
|
|
2
|
+
// executes — it seeded a post via a real POST to the demo server's self-hosted API and stashed
|
|
3
|
+
// the result on globalThis for this test to read. Cleanup (hooks/after/common.hook.ts) runs
|
|
4
|
+
// after this whole suite finishes, so it can't be observed from inside the suite's own tests —
|
|
5
|
+
// a separate monitoring suite/run would be how a real project verifies cleanup actually happened.
|
|
6
|
+
//
|
|
7
|
+
// Suite-scoped hooks only run when a worker's whole batch is unambiguously scoped to this one
|
|
8
|
+
// suite (e.g. `spectra run specs/ApiSeeding` or targeting this TC directly) — hook functions are
|
|
9
|
+
// process-global singleton slots, not per-test-case, so a worker batch spanning multiple suites
|
|
10
|
+
// has no correct way to scope suite A's hooks to only run around suite A's own tests, and skips
|
|
11
|
+
// suite-level hooks entirely rather than risk applying the wrong suite's hooks to the wrong
|
|
12
|
+
// tests. Running the FULL default template (`spectra run -t web`/`android`, every suite at once)
|
|
13
|
+
// will legitimately fail this specific test for that reason — that's an inherent limitation of
|
|
14
|
+
// the current worker-batching model, not a bug in this demo.
|
|
15
|
+
it("Verify test data seeded by a before hook via the demo server's self-hosted API is available to the test", async () => {
|
|
16
|
+
const seeding = (globalThis as any).__apiSeeding;
|
|
17
|
+
|
|
18
|
+
if (!seeding?.seededPost) {
|
|
19
|
+
throw new Error("Expected the before hook to have seeded a post onto globalThis.__apiSeeding, found none");
|
|
20
|
+
}
|
|
21
|
+
if (typeof seeding.seededPost.id !== "number") {
|
|
22
|
+
throw new Error(`Expected seeded post to have a numeric id, got ${JSON.stringify(seeding.seededPost.id)}`);
|
|
23
|
+
}
|
|
24
|
+
if (seeding.seededPost.title !== "TestSpectra seeded post") {
|
|
25
|
+
throw new Error(`Expected seeded post title to match, got "${seeding.seededPost.title}"`);
|
|
26
|
+
}
|
|
27
|
+
if (seeding.cleanedUp !== false) {
|
|
28
|
+
throw new Error("Expected cleanedUp to still be false — the after hook hasn't run yet at this point");
|
|
29
|
+
}
|
|
30
|
+
});
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: TC-0014-seed-and-cleanup-via-hooks
|
|
3
|
-
title: Verify test data seeded by a before hook via the demo server's self-hosted API is available to the test
|
|
4
|
-
priority: Medium
|
|
5
|
-
caseType: Positive
|
|
6
|
-
status: ready-for-automation
|
|
7
|
-
coverage:
|
|
8
|
-
web: automated
|
|
9
|
-
mobile: automated
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# Verify test data seeded by a before hook via the demo server's self-hosted API is available to the test
|
|
13
|
-
|
|
14
|
-
The before hook seeds against the demo server's self-hosted `/api/posts` (POST), and the after
|
|
15
|
-
hook cleans up via DELETE. Because the hooks run on the host worker (not on the device), the demo
|
|
16
|
-
server must be up on the host — run this suite with `--demo` on any platform, e.g.
|
|
17
|
-
`spectra run "specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/common.test.ts" -t android --demo`.
|
|
18
|
-
|
|
19
|
-
## Test Steps
|
|
20
|
-
1. Before hook seeds a post via a real POST to the demo server's self-hosted API and stashes it on globalThis
|
|
21
|
-
2. Verify the seeded post is present on globalThis
|
|
22
|
-
3. Assert the seeded post has a numeric id and the expected title
|
|
23
|
-
4. Assert cleanup has not yet run (the after hook runs after this suite finishes)
|
|
1
|
+
---
|
|
2
|
+
id: TC-0014-seed-and-cleanup-via-hooks
|
|
3
|
+
title: Verify test data seeded by a before hook via the demo server's self-hosted API is available to the test
|
|
4
|
+
priority: Medium
|
|
5
|
+
caseType: Positive
|
|
6
|
+
status: ready-for-automation
|
|
7
|
+
coverage:
|
|
8
|
+
web: automated
|
|
9
|
+
mobile: automated
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Verify test data seeded by a before hook via the demo server's self-hosted API is available to the test
|
|
13
|
+
|
|
14
|
+
The before hook seeds against the demo server's self-hosted `/api/posts` (POST), and the after
|
|
15
|
+
hook cleans up via DELETE. Because the hooks run on the host worker (not on the device), the demo
|
|
16
|
+
server must be up on the host — run this suite with `--demo` on any platform, e.g.
|
|
17
|
+
`spectra run "specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/common.test.ts" -t android --demo`.
|
|
18
|
+
|
|
19
|
+
## Test Steps
|
|
20
|
+
1. Before hook seeds a post via a real POST to the demo server's self-hosted API and stashes it on globalThis
|
|
21
|
+
2. Verify the seeded post is present on globalThis
|
|
22
|
+
3. Assert the seeded post has a numeric id and the expected title
|
|
23
|
+
4. Assert cleanup has not yet run (the after hook runs after this suite finishes)
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
// Suite-level `after` hook — runs once after this suite's tests complete, regardless of whether
|
|
2
|
-
// they passed, so seeded data is always cleaned up rather than leaking into later runs.
|
|
3
|
-
export default async function () {
|
|
4
|
-
const seeding = (globalThis as any).__apiSeeding;
|
|
5
|
-
if (!seeding?.seededPost?.id) return;
|
|
6
|
-
|
|
7
|
-
const res = await fetch(`http://localhost:5200/api/posts/${seeding.seededPost.id}`, {
|
|
8
|
-
method: "DELETE",
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
if (!res.ok) {
|
|
12
|
-
throw new Error(`ApiSeeding after hook: failed to clean up seeded post ${seeding.seededPost.id} (${res.status})`);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
seeding.cleanedUp = true;
|
|
16
|
-
}
|
|
1
|
+
// Suite-level `after` hook — runs once after this suite's tests complete, regardless of whether
|
|
2
|
+
// they passed, so seeded data is always cleaned up rather than leaking into later runs.
|
|
3
|
+
export default async function () {
|
|
4
|
+
const seeding = (globalThis as any).__apiSeeding;
|
|
5
|
+
if (!seeding?.seededPost?.id) return;
|
|
6
|
+
|
|
7
|
+
const res = await fetch(`http://localhost:5200/api/posts/${seeding.seededPost.id}`, {
|
|
8
|
+
method: "DELETE",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
if (!res.ok) {
|
|
12
|
+
throw new Error(`ApiSeeding after hook: failed to clean up seeded post ${seeding.seededPost.id} (${res.status})`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
seeding.cleanedUp = true;
|
|
16
|
+
}
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
// Suite-level `before` hook — runs once before this suite's tests. Hooks run in the same worker
|
|
2
|
-
// process/global scope as the test files, so data seeded here is handed to tests via a plain
|
|
3
|
-
// `globalThis` namespace rather than a return value (hooks don't have a return channel).
|
|
4
|
-
//
|
|
5
|
-
// Seeds against the demo server's own self-hosted API (started with `--demo`) instead of an
|
|
6
|
-
// external public API, so the round trip is deterministic and offline-safe — same seed-in-
|
|
7
|
-
// `before`, clean-up-in-`after` hook mechanics, with a REAL network round trip, just against a
|
|
8
|
-
// server we control. Because the hook runs on the HOST (not the device), this suite needs the
|
|
9
|
-
// demo server up on the host: run it with `--demo` (e.g. `spectra run ... -t android --demo`).
|
|
10
|
-
// Swap the base URL for a real backend's seeding endpoint in an actual project.
|
|
11
|
-
export default async function () {
|
|
12
|
-
const res = await fetch("http://localhost:5200/api/posts", {
|
|
13
|
-
method: "POST",
|
|
14
|
-
headers: { "Content-Type": "application/json; charset=UTF-8" },
|
|
15
|
-
body: JSON.stringify({
|
|
16
|
-
title: "TestSpectra seeded post",
|
|
17
|
-
body: "Created by ApiSeeding's before hook for TC-0014.",
|
|
18
|
-
userId: 1,
|
|
19
|
-
}),
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
if (!res.ok) {
|
|
23
|
-
throw new Error(`ApiSeeding before hook: failed to seed post (${res.status} ${res.statusText})`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const seeded = await res.json();
|
|
27
|
-
(globalThis as any).__apiSeeding = { seededPost: seeded, cleanedUp: false };
|
|
28
|
-
}
|
|
1
|
+
// Suite-level `before` hook — runs once before this suite's tests. Hooks run in the same worker
|
|
2
|
+
// process/global scope as the test files, so data seeded here is handed to tests via a plain
|
|
3
|
+
// `globalThis` namespace rather than a return value (hooks don't have a return channel).
|
|
4
|
+
//
|
|
5
|
+
// Seeds against the demo server's own self-hosted API (started with `--demo`) instead of an
|
|
6
|
+
// external public API, so the round trip is deterministic and offline-safe — same seed-in-
|
|
7
|
+
// `before`, clean-up-in-`after` hook mechanics, with a REAL network round trip, just against a
|
|
8
|
+
// server we control. Because the hook runs on the HOST (not the device), this suite needs the
|
|
9
|
+
// demo server up on the host: run it with `--demo` (e.g. `spectra run ... -t android --demo`).
|
|
10
|
+
// Swap the base URL for a real backend's seeding endpoint in an actual project.
|
|
11
|
+
export default async function () {
|
|
12
|
+
const res = await fetch("http://localhost:5200/api/posts", {
|
|
13
|
+
method: "POST",
|
|
14
|
+
headers: { "Content-Type": "application/json; charset=UTF-8" },
|
|
15
|
+
body: JSON.stringify({
|
|
16
|
+
title: "TestSpectra seeded post",
|
|
17
|
+
body: "Created by ApiSeeding's before hook for TC-0014.",
|
|
18
|
+
userId: 1,
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
throw new Error(`ApiSeeding before hook: failed to seed post (${res.status} ${res.statusText})`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const seeded = await res.json();
|
|
27
|
+
(globalThis as any).__apiSeeding = { seededPost: seeded, cleanedUp: false };
|
|
28
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: suite-api-seeding
|
|
3
|
-
name: ApiSeeding
|
|
4
|
-
description: Seeding and cleaning up test data via a real free public API in before/after hooks
|
|
5
|
-
executionOrder: 2.5
|
|
6
|
-
parallel: true
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Suite: ApiSeeding
|
|
1
|
+
---
|
|
2
|
+
id: suite-api-seeding
|
|
3
|
+
name: ApiSeeding
|
|
4
|
+
description: Seeding and cleaning up test data via a real free public API in before/after hooks
|
|
5
|
+
executionOrder: 2.5
|
|
6
|
+
parallel: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Suite: ApiSeeding
|
package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/mobile.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
it("Seed an authenticated session via Spectra.seedSession() instead of the real login UI", async () => {
|
|
2
|
-
// The deep link asks the app to log itself in against a known test account and persist the
|
|
3
|
-
// result itself — no login form was ever touched.
|
|
4
|
-
await Spectra.seedSession();
|
|
5
|
-
|
|
6
|
-
await AuthPage.statusBadge.shouldHaveText("Authenticated as demo@testspectra.dev");
|
|
7
|
-
});
|
|
1
|
+
it("Seed an authenticated session via Spectra.seedSession() instead of the real login UI", async () => {
|
|
2
|
+
// The deep link asks the app to log itself in against a known test account and persist the
|
|
3
|
+
// result itself — no login form was ever touched.
|
|
4
|
+
await Spectra.seedSession();
|
|
5
|
+
|
|
6
|
+
await AuthPage.statusBadge.shouldHaveText("Authenticated as demo@testspectra.dev");
|
|
7
|
+
});
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: TC-0021-seed-authenticated-session
|
|
3
|
-
title: Seed an authenticated session via Spectra.seedSession() instead of the real login UI
|
|
4
|
-
priority: High
|
|
5
|
-
caseType: Positive
|
|
6
|
-
status: ready-for-automation
|
|
7
|
-
coverage:
|
|
8
|
-
web: automated
|
|
9
|
-
mobile: automated
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# Seed an authenticated session via Spectra.seedSession() instead of the real login UI
|
|
13
|
-
|
|
14
|
-
## Test Steps
|
|
15
|
-
1. Call the `seedSession` support action — logs in via a direct API call instead of walking
|
|
16
|
-
through a login form (web: host-side `fetch()` + `Spectra.browser.setCookies()` applies the
|
|
17
|
-
resulting `HttpOnly` cookie to the browser; mobile: a deep link asks the app to log itself in
|
|
18
|
-
against a known test account and persist the result itself)
|
|
19
|
-
2. Assert the app reflects an authenticated state
|
|
1
|
+
---
|
|
2
|
+
id: TC-0021-seed-authenticated-session
|
|
3
|
+
title: Seed an authenticated session via Spectra.seedSession() instead of the real login UI
|
|
4
|
+
priority: High
|
|
5
|
+
caseType: Positive
|
|
6
|
+
status: ready-for-automation
|
|
7
|
+
coverage:
|
|
8
|
+
web: automated
|
|
9
|
+
mobile: automated
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Seed an authenticated session via Spectra.seedSession() instead of the real login UI
|
|
13
|
+
|
|
14
|
+
## Test Steps
|
|
15
|
+
1. Call the `seedSession` support action — logs in via a direct API call instead of walking
|
|
16
|
+
through a login form (web: host-side `fetch()` + `Spectra.browser.setCookies()` applies the
|
|
17
|
+
resulting `HttpOnly` cookie to the browser; mobile: a deep link asks the app to log itself in
|
|
18
|
+
against a known test account and persist the result itself)
|
|
19
|
+
2. Assert the app reflects an authenticated state
|
package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/web.test.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
it("Seed an authenticated session via Spectra.seedSession() instead of the real login UI", async () => {
|
|
2
|
-
// Logs in via a direct host-side API call and applies the resulting HttpOnly cookie straight
|
|
3
|
-
// to the browser — no login form was ever touched.
|
|
4
|
-
await Spectra.seedSession();
|
|
5
|
-
|
|
6
|
-
await AuthPage.open();
|
|
7
|
-
await AuthPage.checkStatusButton.click();
|
|
8
|
-
await AuthPage.statusBadge.shouldHaveText("Authenticated as demo@testspectra.dev");
|
|
9
|
-
});
|
|
1
|
+
it("Seed an authenticated session via Spectra.seedSession() instead of the real login UI", async () => {
|
|
2
|
+
// Logs in via a direct host-side API call and applies the resulting HttpOnly cookie straight
|
|
3
|
+
// to the browser — no login form was ever touched.
|
|
4
|
+
await Spectra.seedSession();
|
|
5
|
+
|
|
6
|
+
await AuthPage.open();
|
|
7
|
+
await AuthPage.checkStatusButton.click();
|
|
8
|
+
await AuthPage.statusBadge.shouldHaveText("Authenticated as demo@testspectra.dev");
|
|
9
|
+
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: suite-authentication
|
|
3
|
-
name: Authentication
|
|
4
|
-
description: Skipping the real login UI by seeding an authenticated session via a direct API call
|
|
5
|
-
executionOrder: 2.6
|
|
6
|
-
parallel: true
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Suite: Authentication
|
|
1
|
+
---
|
|
2
|
+
id: suite-authentication
|
|
3
|
+
name: Authentication
|
|
4
|
+
description: Skipping the real login UI by seeding an authenticated session via a direct API call
|
|
5
|
+
executionOrder: 2.6
|
|
6
|
+
parallel: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Suite: Authentication
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
it("Verify page title and sidebar navigation links", async () => {
|
|
2
|
-
await MatchersPage.open();
|
|
3
|
-
|
|
4
|
-
// Navigation sidebar collection verification
|
|
5
|
-
await NavigationPage.sidebarLinks.shouldHaveLengthGreaterThan(0);
|
|
6
|
-
|
|
7
|
-
// Close sidebar drawer
|
|
8
|
-
await NavigationPage.closeSidebarButton.click();
|
|
9
|
-
});
|
|
1
|
+
it("Verify page title and sidebar navigation links", async () => {
|
|
2
|
+
await MatchersPage.open();
|
|
3
|
+
|
|
4
|
+
// Navigation sidebar collection verification
|
|
5
|
+
await NavigationPage.sidebarLinks.shouldHaveLengthGreaterThan(0);
|
|
6
|
+
|
|
7
|
+
// Close sidebar drawer
|
|
8
|
+
await NavigationPage.closeSidebarButton.click();
|
|
9
|
+
});
|