@testspectra/cli 1.0.70 → 1.1.0-rc.1
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/LICENSE.md +48 -0
- package/README.md +36 -270
- package/bin/.testspectra-runner.hash +1 -0
- package/bin/spectra.js +1 -1
- package/bin/testspectra-runner +0 -0
- package/demo-app/assets/index-BhlW-eXG.css +1 -0
- package/demo-app/assets/index-DOtRypCa.js +154 -0
- package/demo-app/index.html +2 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/commands/__tests__/doctor.test.d.ts +1 -0
- package/dist/commands/__tests__/doctor.test.js +39 -0
- package/dist/commands/__tests__/init.test.d.ts +1 -0
- package/dist/commands/__tests__/init.test.js +148 -0
- package/dist/commands/__tests__/license.test.d.ts +1 -0
- package/dist/commands/__tests__/license.test.js +13 -0
- package/dist/commands/__tests__/refactor.test.d.ts +1 -0
- package/dist/commands/__tests__/refactor.test.js +64 -0
- package/dist/commands/__tests__/run-e2e.test.d.ts +1 -0
- package/dist/commands/__tests__/run-e2e.test.js +422 -0
- package/dist/commands/__tests__/run.test.d.ts +1 -0
- package/dist/commands/__tests__/run.test.js +273 -0
- package/dist/commands/__tests__/watch.test.d.ts +1 -0
- package/dist/commands/__tests__/watch.test.js +155 -0
- package/dist/commands/add.js +86 -85
- package/dist/commands/demo.js +21 -11
- package/dist/commands/devices.d.ts +14 -2
- package/dist/commands/devices.js +44 -34
- package/dist/commands/doctor.d.ts +3 -1
- package/dist/commands/doctor.js +43 -264
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +211 -196
- package/dist/commands/install.d.ts +4 -0
- package/dist/commands/install.js +59 -0
- package/dist/commands/license.d.ts +4 -0
- package/dist/commands/license.js +30 -0
- package/dist/commands/lint.d.ts +5 -0
- package/dist/commands/lint.js +55 -0
- package/dist/commands/refactor.d.ts +9 -0
- package/dist/commands/refactor.js +172 -0
- package/dist/commands/run.d.ts +23 -2
- package/dist/commands/run.js +322 -111
- package/dist/commands/skills.d.ts +9 -0
- package/dist/commands/skills.js +28 -0
- package/dist/commands/sync-types.d.ts +4 -0
- package/dist/commands/sync-types.js +15 -0
- package/dist/commands/test.d.ts +17 -0
- package/dist/commands/test.js +76 -0
- package/dist/commands/update.js +39 -43
- package/dist/commands/watch.d.ts +6 -0
- package/dist/commands/watch.js +55 -45
- package/dist/config/loader.d.ts +1 -1
- package/dist/config/loader.js +40 -25
- package/dist/config/schema.d.ts +104 -145
- package/dist/config/schema.js +36 -45
- package/dist/generator/__tests__/tsconfig-isolation.test.d.ts +1 -0
- package/dist/generator/__tests__/tsconfig-isolation.test.js +282 -0
- package/dist/generator/__tests__/type-generator.test.d.ts +1 -0
- package/dist/generator/__tests__/type-generator.test.js +386 -0
- package/dist/generator/index.d.ts +2 -0
- package/dist/generator/index.js +2 -0
- package/dist/generator/tsconfig-generator.d.ts +27 -0
- package/dist/generator/tsconfig-generator.js +419 -0
- package/dist/generator/type-generator.d.ts +59 -0
- package/dist/generator/type-generator.js +740 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.js +165 -51
- package/dist/lifecycle/__tests__/lifecycle-engine.test.d.ts +1 -0
- package/dist/lifecycle/__tests__/lifecycle-engine.test.js +290 -0
- package/dist/lifecycle/hook-discovery.d.ts +24 -0
- package/dist/lifecycle/hook-discovery.js +60 -0
- package/dist/lifecycle/hook-dispatcher.d.ts +34 -0
- package/dist/lifecycle/hook-dispatcher.js +190 -0
- package/dist/lifecycle/index.d.ts +3 -0
- package/dist/lifecycle/index.js +3 -0
- package/dist/lifecycle/parallel-grouping.d.ts +27 -0
- package/dist/lifecycle/parallel-grouping.js +151 -0
- package/dist/linter/__tests__/anonymous-export.test.d.ts +1 -0
- package/dist/linter/__tests__/anonymous-export.test.js +184 -0
- package/dist/linter/__tests__/component-test-imports.test.d.ts +1 -0
- package/dist/linter/__tests__/component-test-imports.test.js +115 -0
- package/dist/linter/__tests__/shared-lib-boundary.test.d.ts +1 -0
- package/dist/linter/__tests__/shared-lib-boundary.test.js +270 -0
- package/dist/linter/__tests__/spec-schema.test.d.ts +1 -0
- package/dist/linter/__tests__/spec-schema.test.js +369 -0
- package/dist/linter/__tests__/step-parity.test.d.ts +1 -0
- package/dist/linter/__tests__/step-parity.test.js +372 -0
- package/dist/linter/discovery.d.ts +30 -0
- package/dist/linter/discovery.js +249 -0
- package/dist/linter/extractor.d.ts +67 -0
- package/dist/linter/extractor.js +245 -0
- package/dist/linter/index.d.ts +7 -0
- package/dist/linter/index.js +7 -0
- package/dist/linter/parser.d.ts +24 -0
- package/dist/linter/parser.js +219 -0
- package/dist/linter/schemas/spec.schema.d.ts +83 -0
- package/dist/linter/schemas/spec.schema.js +150 -0
- package/dist/linter/schemas/suite.schema.d.ts +59 -0
- package/dist/linter/schemas/suite.schema.js +83 -0
- package/dist/linter/spec-linter.d.ts +23 -0
- package/dist/linter/spec-linter.js +630 -0
- package/dist/linter/types.d.ts +28 -0
- package/dist/linter/types.js +1 -0
- package/dist/mobile/__tests__/android-device-e2e.test.d.ts +1 -0
- package/dist/mobile/__tests__/android-device-e2e.test.js +53 -0
- package/dist/mobile/__tests__/device-pool.test.d.ts +1 -0
- package/dist/mobile/__tests__/device-pool.test.js +97 -0
- package/dist/mobile/device-pool.d.ts +23 -0
- package/dist/mobile/device-pool.js +1 -0
- package/dist/mobile/index.d.ts +1 -0
- package/dist/mobile/index.js +1 -0
- package/dist/refactor/__tests__/refactor-engine.test.d.ts +1 -0
- package/dist/refactor/__tests__/refactor-engine.test.js +873 -0
- package/dist/refactor/index.d.ts +1 -0
- package/dist/refactor/index.js +1 -0
- package/dist/refactor/refactor-engine.d.ts +50 -0
- package/dist/refactor/refactor-engine.js +587 -0
- package/dist/reporter/__tests__/semantic-formatter.test.d.ts +1 -0
- package/dist/reporter/__tests__/semantic-formatter.test.js +73 -0
- package/dist/reporter/semantic-formatter.d.ts +6 -0
- package/dist/reporter/semantic-formatter.js +206 -0
- package/dist/reporter/summary-view.d.ts +12 -0
- package/dist/reporter/summary-view.js +19 -0
- package/dist/reporter/terminal-stream.d.ts +32 -0
- package/dist/reporter/terminal-stream.js +82 -0
- package/dist/reporter/types.d.ts +2 -0
- package/dist/reporter/types.js +1 -0
- package/dist/runner/bridge.d.ts +10 -5
- package/dist/runner/bridge.js +101 -45
- package/dist/runner/reporter.d.ts +32 -2
- package/dist/runner/reporter.js +427 -13
- package/dist/server/demo.js +43 -44
- package/dist/types/generator.d.ts +1 -17
- package/dist/types/generator.js +1 -443
- package/dist/utils/__tests__/api-server.test.d.ts +1 -0
- package/dist/utils/__tests__/api-server.test.js +62 -0
- package/dist/utils/__tests__/demo-server.test.d.ts +1 -0
- package/dist/utils/__tests__/demo-server.test.js +35 -0
- package/dist/utils/api-server.d.ts +19 -0
- package/dist/utils/api-server.js +179 -0
- package/dist/utils/demo-server.d.ts +5 -0
- package/dist/utils/demo-server.js +46 -36
- package/package.json +25 -13
- package/templates/default/CLAUDE.md +12 -5
- package/templates/default/fixtures/apiData.json +24 -0
- package/templates/default/package.json +12 -3
- package/templates/default/page-objects/AuthPage/mobile.ts +5 -0
- package/templates/default/page-objects/AuthPage/web.ts +10 -0
- package/templates/default/page-objects/MatchersPage/mobile.ts +66 -17
- package/templates/default/page-objects/MatchersPage/web.ts +22 -2
- package/templates/default/page-objects/NavigationPage/mobile.ts +11 -2
- package/templates/default/page-objects/NavigationPage/web.ts +1 -1
- package/templates/default/page-objects/PlaygroundPage/mobile.ts +34 -18
- package/templates/default/page-objects/PlaygroundPage/web.ts +4 -2
- package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +53 -0
- package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/spec.md +19 -0
- package/templates/default/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +31 -0
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/android.test.ts +3 -0
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/common.test.ts +3 -0
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +27 -0
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/spec.md +19 -0
- package/templates/default/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +26 -0
- package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/common.test.ts +30 -0
- package/templates/default/specs/ApiInterception/TC-0012-direct-and-late-response/spec.md +22 -0
- package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/common.test.ts +27 -0
- package/templates/default/specs/ApiInterception/TC-0013-real-network-call-recorded/spec.md +20 -0
- package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/spec.md +24 -0
- package/templates/default/specs/ApiInterception/TC-0015-mock-server-sent-events/web.test.ts +26 -0
- package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/spec.md +37 -0
- package/templates/default/specs/ApiInterception/TC-0016-bypass-websocket-with-app-level-mock/web.test.ts +21 -0
- package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/spec.md +29 -0
- package/templates/default/specs/ApiInterception/TC-0017-bypass-captcha-with-test-mode-flag/web.test.ts +16 -0
- package/templates/default/specs/ApiInterception/suite.md +10 -0
- package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/common.test.ts +30 -0
- package/templates/default/specs/ApiSeeding/TC-0014-seed-and-cleanup-via-hooks/spec.md +23 -0
- package/templates/default/specs/ApiSeeding/hooks/after/common.hook.ts +16 -0
- package/templates/default/specs/ApiSeeding/hooks/before/common.hook.ts +28 -0
- package/templates/default/specs/ApiSeeding/suite.md +9 -0
- package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/mobile.test.ts +7 -0
- package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/spec.md +19 -0
- package/templates/default/specs/Authentication/TC-0021-seed-authenticated-session/web.test.ts +9 -0
- package/templates/default/specs/Authentication/suite.md +9 -0
- package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +7 -1
- package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/spec.md +5 -0
- package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +17 -3
- package/templates/default/specs/BrowserContext/TC-0022-history-navigation/mobile.test.ts +17 -0
- package/templates/default/specs/BrowserContext/TC-0022-history-navigation/spec.md +28 -0
- package/templates/default/specs/BrowserContext/TC-0022-history-navigation/web.test.ts +26 -0
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/common.test.ts +26 -0
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/spec.md +7 -0
- package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +9 -1
- package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/spec.md +5 -0
- package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +9 -4
- package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/common.test.ts +14 -0
- package/templates/default/specs/ContentMatchers/TC-0020-scoped-element-access/spec.md +21 -0
- package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/mobile.test.ts +28 -0
- package/templates/default/specs/DeepLink/TC-0018-navigate-via-deep-link/spec.md +29 -0
- package/templates/default/specs/DeepLink/suite.md +9 -0
- package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +9 -1
- package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/spec.md +5 -0
- package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +9 -3
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +27 -1
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/spec.md +7 -0
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +27 -6
- package/templates/default/specs/ElementMatchers/suite.md +2 -1
- package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -0
- package/templates/default/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -0
- package/templates/default/specs/EnvironmentConfig/suite.md +9 -0
- package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/mobile.test.ts +26 -0
- package/templates/default/specs/PermissionRationale/TC-0010-camera-permission-allow/spec.md +22 -0
- package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/mobile.test.ts +13 -0
- package/templates/default/specs/PermissionRationale/TC-0011-camera-permission-deny/spec.md +20 -0
- package/templates/default/specs/PermissionRationale/suite.md +9 -0
- package/templates/default/specs/Playground/TC-0001-form-controls/mobile.test.ts +4 -1
- package/templates/default/specs/Playground/TC-0001-form-controls/spec.md +6 -0
- package/templates/default/specs/Playground/TC-0001-form-controls/web.test.ts +4 -4
- package/templates/default/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +4 -1
- package/templates/default/specs/Playground/TC-0002-toggle-elements/spec.md +6 -0
- package/templates/default/specs/Playground/TC-0002-toggle-elements/web.test.ts +3 -3
- package/templates/default/spectra.config.ts +22 -31
- package/templates/default/support/actions/fillCredentials/mobile.action.ts +3 -3
- package/templates/default/support/actions/fillCredentials/web.action.ts +1 -1
- package/templates/default/support/actions/seedSession/mobile.action.ts +9 -0
- package/templates/default/support/actions/seedSession/web.action.ts +13 -0
- package/templates/default/support/steps/togglePlaygroundStates/mobile.step.ts +3 -1
- package/templates/default/support/steps/togglePlaygroundStates/web.step.ts +3 -3
- package/templates/default/support/steps/verifyPlaygroundState/mobile.step.ts +4 -2
- package/templates/default/support/steps/verifyPlaygroundState/web.step.ts +4 -4
- package/templates/default/tsconfig.json +3 -1
- package/templates/nx/.nx/workspace-data/70094CFD-E89B-57A1-9619-2FC5F4963C54.db +0 -0
- package/templates/nx/.nx/workspace-data/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 +5366 -0
- package/templates/nx/.nx/workspace-data/d/server-process.json +3 -0
- package/templates/nx/.nx/workspace-data/file-map.json +464 -0
- package/templates/nx/.nx/workspace-data/lockfile.hash +1 -0
- package/templates/nx/.nx/workspace-data/nx_files.nxt +0 -0
- package/templates/nx/.nx/workspace-data/parsed-lock-file.json +13485 -0
- package/templates/nx/.nx/workspace-data/project-graph.json +14389 -0
- package/templates/nx/CLAUDE.md +2 -2
- package/templates/nx/package.json +6 -4
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts +27 -0
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/spec.md +12 -0
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts +27 -0
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/mobile.test.ts +24 -0
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/spec.md +12 -0
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/TC-0009-post-mocking/web.test.ts +24 -0
- package/templates/nx/packages/matchers/e2e/specs/ApiInterception/suite.md +9 -0
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +1 -1
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +17 -3
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +1 -1
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +15 -5
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +1 -1
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +9 -4
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +1 -1
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +9 -3
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +1 -1
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +27 -6
- package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/common.test.ts +5 -0
- package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/TC-0019-read-custom-environment-variable/spec.md +16 -0
- package/templates/nx/packages/matchers/e2e/specs/EnvironmentConfig/suite.md +9 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/mobile.test.ts +1 -1
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/web.test.ts +4 -4
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +1 -1
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/web.test.ts +3 -3
- package/templates/nx/pnpm-lock.yaml +6663 -0
- package/templates/nx/pnpm-workspace.yaml +2 -1
- package/templates/nx/shared/testing/fixtures/apiData.json +24 -0
- package/templates/nx/shared/testing/page-objects/MatchersPage/mobile.ts +13 -1
- package/templates/nx/shared/testing/page-objects/MatchersPage/web.ts +85 -17
- package/templates/nx/shared/testing/page-objects/NavigationPage/mobile.ts +1 -1
- package/templates/nx/shared/testing/page-objects/NavigationPage/web.ts +1 -1
- package/templates/nx/shared/testing/page-objects/PlaygroundPage/mobile.ts +1 -1
- package/templates/nx/shared/testing/page-objects/PlaygroundPage/web.ts +4 -2
- package/templates/nx/shared/testing/project.json +19 -0
- package/templates/nx/shared/testing/support/actions/fillCredentials/mobile.action.ts +1 -1
- package/templates/nx/shared/testing/support/actions/fillCredentials/web.action.ts +1 -1
- package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/mobile.step.ts +1 -1
- package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/web.step.ts +3 -3
- package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/mobile.step.ts +2 -2
- package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/web.step.ts +4 -4
- package/templates/nx/spectra.config.ts +20 -29
- package/templates/nx/tsconfig.json +3 -1
- package/templates/react-component/.cursorrules +17 -0
- package/templates/react-component/.vscode/extensions.json +5 -0
- package/templates/react-component/CLAUDE.md +14 -0
- package/templates/react-component/README.md +69 -0
- package/templates/react-component/fixtures/component-mock.json +12 -0
- package/templates/react-component/global-hooks/after/web.hook.ts +3 -0
- package/templates/react-component/global-hooks/before/web.hook.ts +3 -0
- package/templates/react-component/package.json +27 -0
- package/templates/react-component/page-objects/BadgeComponent/web.ts +23 -0
- package/templates/react-component/page-objects/ButtonComponent/web.ts +23 -0
- package/templates/react-component/page-objects/InputComponent/web.ts +13 -0
- package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/spec.md +18 -0
- package/templates/react-component/specs/BadgeComponent/TC-0002-badge-severities-and-dot/web.test.tsx +11 -0
- package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/spec.md +18 -0
- package/templates/react-component/specs/BadgeComponent/TC-0006-badge-success-with-dot/web.test.tsx +10 -0
- package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/spec.md +18 -0
- package/templates/react-component/specs/BadgeComponent/TC-0007-badge-by-severity-lookup/web.test.tsx +16 -0
- package/templates/react-component/specs/BadgeComponent/suite.md +12 -0
- package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/spec.md +19 -0
- package/templates/react-component/specs/ButtonComponent/TC-0001-button-rendering-and-click/web.test.tsx +28 -0
- package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/spec.md +19 -0
- package/templates/react-component/specs/ButtonComponent/TC-0004-disabled-button-not-clickable/web.test.tsx +22 -0
- package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/spec.md +18 -0
- package/templates/react-component/specs/ButtonComponent/TC-0005-button-renders-icon/web.test.tsx +8 -0
- package/templates/react-component/specs/ButtonComponent/suite.md +12 -0
- package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/spec.md +20 -0
- package/templates/react-component/specs/InputComponent/TC-0003-input-typing-and-clear/web.test.tsx +28 -0
- package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/spec.md +18 -0
- package/templates/react-component/specs/InputComponent/TC-0008-disabled-input-not-editable/web.test.tsx +9 -0
- package/templates/react-component/specs/InputComponent/suite.md +12 -0
- package/templates/react-component/spectra.config.ts +27 -0
- package/templates/react-component/src/components/Badge/Badge.tsx +60 -0
- package/templates/react-component/src/components/Button/Button.tsx +57 -0
- package/templates/react-component/src/components/Input/Input.tsx +41 -0
- package/templates/react-component/src/test-utils.tsx +23 -0
- package/templates/react-component/support/actions/fillInputField/web.action.ts +9 -0
- package/templates/react-component/support/steps/verifyButtonState/web.step.ts +12 -0
- package/templates/react-component/tsconfig.json +8 -0
- package/demo-app/assets/index-DAUlrHTr.css +0 -1
- package/demo-app/assets/index-DN2Kb0A8.js +0 -100
- package/dist/index.cjs +0 -101
- package/dist/plugin.cjs +0 -575
- package/dist/plugin.d.ts +0 -16
- package/dist/plugin.js +0 -184
- package/dist/step/index.d.ts +0 -6
- package/dist/step/index.js +0 -6
- package/dist/step/matchers.d.ts +0 -14
- package/dist/step/matchers.js +0 -114
- package/dist/step/proto.d.ts +0 -46
- package/dist/step/proto.js +0 -114
- package/dist/step/runner/collection.d.ts +0 -13
- package/dist/step/runner/collection.js +0 -51
- package/dist/step/runner/single.d.ts +0 -21
- package/dist/step/runner/single.js +0 -138
- package/dist/step/spectra.d.ts +0 -34
- package/dist/step/spectra.js +0 -156
- package/dist/step/types.d.ts +0 -32
- package/dist/step/types.js +0 -5
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +0 -3
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +0 -7
- package/templates/default/tsconfig.spectra.android.json +0 -46
- package/templates/default/tsconfig.spectra.ios.json +0 -46
- package/templates/default/tsconfig.spectra.json +0 -22
- package/templates/default/tsconfig.spectra.web.json +0 -53
- package/templates/nx/fixtures/playgroundData.json +0 -7
- package/templates/nx/global-hooks/after/mobile.hook.ts +0 -3
- package/templates/nx/global-hooks/after/web.hook.ts +0 -3
- package/templates/nx/global-hooks/before/mobile.hook.ts +0 -3
- package/templates/nx/global-hooks/before/web.hook.ts +0 -3
- package/templates/nx/page-objects/MatchersPage/mobile.ts +0 -21
- package/templates/nx/page-objects/MatchersPage/web.ts +0 -23
- package/templates/nx/page-objects/NavigationPage/mobile.ts +0 -5
- package/templates/nx/page-objects/NavigationPage/web.ts +0 -8
- package/templates/nx/page-objects/PlaygroundPage/mobile.ts +0 -26
- package/templates/nx/page-objects/PlaygroundPage/web.ts +0 -29
- package/templates/nx/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +0 -3
- package/templates/nx/specs/BrowserContext/TC-0007-title-and-navigation/spec.md +0 -12
- package/templates/nx/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +0 -5
- package/templates/nx/specs/BrowserContext/suite.md +0 -9
- package/templates/nx/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +0 -3
- package/templates/nx/specs/ContentMatchers/TC-0005-text-and-values/spec.md +0 -12
- package/templates/nx/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +0 -7
- package/templates/nx/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +0 -3
- package/templates/nx/specs/ContentMatchers/TC-0006-collections-and-empty/spec.md +0 -12
- package/templates/nx/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +0 -6
- package/templates/nx/specs/ContentMatchers/suite.md +0 -9
- package/templates/nx/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +0 -3
- package/templates/nx/specs/ElementMatchers/TC-0003-visibility-and-dom/spec.md +0 -12
- package/templates/nx/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +0 -5
- package/templates/nx/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +0 -3
- package/templates/nx/specs/ElementMatchers/TC-0004-states-and-attributes/spec.md +0 -12
- package/templates/nx/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +0 -8
- package/templates/nx/specs/ElementMatchers/hooks/after/mobile.hook.ts +0 -1
- package/templates/nx/specs/ElementMatchers/hooks/after/web.hook.ts +0 -1
- package/templates/nx/specs/ElementMatchers/hooks/afterEach/mobile.hook.ts +0 -1
- package/templates/nx/specs/ElementMatchers/hooks/afterEach/web.hook.ts +0 -1
- package/templates/nx/specs/ElementMatchers/hooks/before/mobile.hook.ts +0 -1
- package/templates/nx/specs/ElementMatchers/hooks/before/web.hook.ts +0 -1
- package/templates/nx/specs/ElementMatchers/hooks/beforeEach/mobile.hook.ts +0 -1
- package/templates/nx/specs/ElementMatchers/hooks/beforeEach/web.hook.ts +0 -1
- package/templates/nx/specs/ElementMatchers/suite.md +0 -9
- package/templates/nx/specs/Playground/TC-0001-form-controls/mobile.test.ts +0 -3
- package/templates/nx/specs/Playground/TC-0001-form-controls/spec.md +0 -12
- package/templates/nx/specs/Playground/TC-0001-form-controls/web.test.ts +0 -6
- package/templates/nx/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +0 -3
- package/templates/nx/specs/Playground/TC-0002-toggle-elements/spec.md +0 -12
- package/templates/nx/specs/Playground/TC-0002-toggle-elements/web.test.ts +0 -6
- package/templates/nx/specs/Playground/hooks/after/mobile.hook.ts +0 -1
- package/templates/nx/specs/Playground/hooks/after/web.hook.ts +0 -1
- package/templates/nx/specs/Playground/hooks/afterEach/mobile.hook.ts +0 -1
- package/templates/nx/specs/Playground/hooks/afterEach/web.hook.ts +0 -1
- package/templates/nx/specs/Playground/hooks/before/mobile.hook.ts +0 -1
- package/templates/nx/specs/Playground/hooks/before/web.hook.ts +0 -1
- package/templates/nx/specs/Playground/hooks/beforeEach/mobile.hook.ts +0 -1
- package/templates/nx/specs/Playground/hooks/beforeEach/web.hook.ts +0 -1
- package/templates/nx/specs/Playground/suite.md +0 -9
- package/templates/nx/support/actions/fillCredentials/mobile.action.ts +0 -4
- package/templates/nx/support/actions/fillCredentials/web.action.ts +0 -4
- package/templates/nx/support/steps/togglePlaygroundStates/mobile.step.ts +0 -3
- package/templates/nx/support/steps/togglePlaygroundStates/step.md +0 -7
- package/templates/nx/support/steps/togglePlaygroundStates/web.step.ts +0 -5
- package/templates/nx/support/steps/verifyPlaygroundState/mobile.step.ts +0 -4
- package/templates/nx/support/steps/verifyPlaygroundState/step.md +0 -7
- package/templates/nx/support/steps/verifyPlaygroundState/web.step.ts +0 -6
- package/templates/nx/tsconfig.spectra.android.json +0 -39
- package/templates/nx/tsconfig.spectra.ios.json +0 -39
- package/templates/nx/tsconfig.spectra.json +0 -25
- package/templates/nx/tsconfig.spectra.shared.json +0 -25
- package/templates/nx/tsconfig.spectra.web.json +0 -45
- /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/after/mobile.hook.ts +0 -0
- /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/after/web.hook.ts +0 -0
- /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/afterEach/mobile.hook.ts +0 -0
- /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/afterEach/web.hook.ts +0 -0
- /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/before/mobile.hook.ts +0 -0
- /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/before/web.hook.ts +0 -0
- /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/beforeEach/mobile.hook.ts +0 -0
- /package/templates/{nx/specs/BrowserContext → default/specs/ApiInterception}/hooks/beforeEach/web.hook.ts +0 -0
- /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/after/mobile.hook.ts +0 -0
- /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/after/web.hook.ts +0 -0
- /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/afterEach/mobile.hook.ts +0 -0
- /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/afterEach/web.hook.ts +0 -0
- /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/before/mobile.hook.ts +0 -0
- /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/before/web.hook.ts +0 -0
- /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/beforeEach/mobile.hook.ts +0 -0
- /package/templates/nx/{specs/ContentMatchers → packages/matchers/e2e/specs/ApiInterception}/hooks/beforeEach/web.hook.ts +0 -0
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest';
|
|
5
|
+
import { runCommand } from '../run.js';
|
|
6
|
+
import { initCommand } from '../init.js';
|
|
7
|
+
import { DemoServer } from '../../utils/demo-server.js';
|
|
8
|
+
import { DemoApiServer } from '../../utils/api-server.js';
|
|
9
|
+
import { RustCoreBridge } from '../../runner/bridge.js';
|
|
10
|
+
describe('End-to-End (E2E) Test Plan: Native Orchestrator Execution (docs/v2/core/e2e-testing-plan.md)', () => {
|
|
11
|
+
let tmpDir;
|
|
12
|
+
let originalCwd;
|
|
13
|
+
let demoServer;
|
|
14
|
+
let apiServer;
|
|
15
|
+
let consoleLogs = [];
|
|
16
|
+
const originalLog = console.log;
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
// 1. BeforeAll: Start demo web app server
|
|
19
|
+
demoServer = new DemoServer(5173);
|
|
20
|
+
try {
|
|
21
|
+
await demoServer.start();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Server may already be running on port 5173
|
|
25
|
+
}
|
|
26
|
+
// The demo API server runs separately (port 5200). Tests that don't pass --demo still need it
|
|
27
|
+
// reachable: the web app now fetches the API cross-origin, so a JSON POST triggers a CORS
|
|
28
|
+
// preflight (OPTIONS) that isn't mocked and must reach a live API server.
|
|
29
|
+
apiServer = new DemoApiServer();
|
|
30
|
+
try {
|
|
31
|
+
await apiServer.start();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// API server may already be running on port 5200
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
afterAll(async () => {
|
|
38
|
+
// 4. AfterAll: Stop demo servers
|
|
39
|
+
try {
|
|
40
|
+
await demoServer.stop();
|
|
41
|
+
}
|
|
42
|
+
catch { }
|
|
43
|
+
try {
|
|
44
|
+
await apiServer.stop();
|
|
45
|
+
}
|
|
46
|
+
catch { }
|
|
47
|
+
});
|
|
48
|
+
beforeEach(async () => {
|
|
49
|
+
originalCwd = process.cwd();
|
|
50
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'testspectra-e2e-'));
|
|
51
|
+
process.chdir(tmpDir);
|
|
52
|
+
// Scaffold full template project
|
|
53
|
+
await initCommand({ template: 'default', force: true });
|
|
54
|
+
consoleLogs = [];
|
|
55
|
+
console.log = vi.fn((...args) => {
|
|
56
|
+
consoleLogs.push(args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' '));
|
|
57
|
+
originalLog(...args);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
afterEach(() => {
|
|
61
|
+
console.log = originalLog;
|
|
62
|
+
process.chdir(originalCwd);
|
|
63
|
+
try {
|
|
64
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
65
|
+
}
|
|
66
|
+
catch { }
|
|
67
|
+
vi.restoreAllMocks();
|
|
68
|
+
});
|
|
69
|
+
// E2E-01: Default Template Full Run
|
|
70
|
+
it('E2E-01: Default Template Full Run executes native runner and produces result.json', async () => {
|
|
71
|
+
// Ensure binary exists or mock bridge if binary is undergoing compilation
|
|
72
|
+
const binaryExists = (() => {
|
|
73
|
+
try {
|
|
74
|
+
RustCoreBridge.resolveBinaryPath();
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
})();
|
|
81
|
+
expect(binaryExists).toBe(true);
|
|
82
|
+
// Execute testspectra run -t web on full template project
|
|
83
|
+
await runCommand(undefined, { target: 'web', demo: true });
|
|
84
|
+
// Assert reports created
|
|
85
|
+
const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
|
|
86
|
+
expect(fs.existsSync(resultJsonPath)).toBe(true);
|
|
87
|
+
const resultRaw = fs.readFileSync(resultJsonPath, 'utf-8');
|
|
88
|
+
const resultJson = JSON.parse(resultRaw);
|
|
89
|
+
// Validate result.json schema contract (Section 3.2.A)
|
|
90
|
+
expect(resultJson.version).toBe('2.0.0');
|
|
91
|
+
expect(resultJson.platform).toBe('web');
|
|
92
|
+
expect(resultJson.status).toBe('passed');
|
|
93
|
+
expect(resultJson.summary.passRate).toBe('100%');
|
|
94
|
+
expect(resultJson.summary.totalSuites).toBeGreaterThanOrEqual(1);
|
|
95
|
+
expect(resultJson.summary.totalTests).toBeGreaterThanOrEqual(1);
|
|
96
|
+
expect(resultJson.summary.passed).toBeGreaterThanOrEqual(1);
|
|
97
|
+
expect(resultJson.summary.failed).toBe(0);
|
|
98
|
+
// Performance RSS assertion (< 30 MB)
|
|
99
|
+
expect(resultJson.performance.peakMemoryRssMb).toBeLessThan(30);
|
|
100
|
+
expect(resultJson.suites.length).toBeGreaterThanOrEqual(1);
|
|
101
|
+
}, 30000);
|
|
102
|
+
// E2E-02: Single Suite Targeted Run
|
|
103
|
+
it('E2E-02: Single Suite Targeted Run executes only specified spec', async () => {
|
|
104
|
+
const targetSpec = 'specs/Playground/TC-0001-form-controls/web.test.ts';
|
|
105
|
+
expect(fs.existsSync(path.join(tmpDir, targetSpec))).toBe(true);
|
|
106
|
+
await runCommand([targetSpec], { target: 'web', demo: true });
|
|
107
|
+
const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
|
|
108
|
+
expect(fs.existsSync(resultJsonPath)).toBe(true);
|
|
109
|
+
const resultJson = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
|
|
110
|
+
expect(resultJson.version).toBe('2.0.0');
|
|
111
|
+
expect(resultJson.platform).toBe('web');
|
|
112
|
+
expect(resultJson.status).toBe('passed');
|
|
113
|
+
expect(resultJson.summary.totalSuites).toBe(1);
|
|
114
|
+
expect(resultJson.summary.totalTests).toBe(1);
|
|
115
|
+
expect(resultJson.summary.passed).toBe(1);
|
|
116
|
+
expect(resultJson.summary.failed).toBe(0);
|
|
117
|
+
expect(resultJson.summary.passRate).toBe('100%');
|
|
118
|
+
}, 30000);
|
|
119
|
+
// E2E-03: Network Recording Audit
|
|
120
|
+
it('E2E-03: Network Recording Audit creates network-resources.json with request entries', async () => {
|
|
121
|
+
await runCommand(undefined, { target: 'web', demo: true });
|
|
122
|
+
const networkJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'network-resources.json');
|
|
123
|
+
expect(fs.existsSync(networkJsonPath)).toBe(true);
|
|
124
|
+
const networkRaw = fs.readFileSync(networkJsonPath, 'utf-8');
|
|
125
|
+
const networkResources = JSON.parse(networkRaw);
|
|
126
|
+
expect(Array.isArray(networkResources)).toBe(true);
|
|
127
|
+
expect(networkResources.length).toBeGreaterThanOrEqual(1);
|
|
128
|
+
const first = networkResources[0];
|
|
129
|
+
expect(first).toHaveProperty('requestId');
|
|
130
|
+
expect(first).toHaveProperty('url');
|
|
131
|
+
expect(first).toHaveProperty('method');
|
|
132
|
+
expect(first).toHaveProperty('status');
|
|
133
|
+
}, 30000);
|
|
134
|
+
// E2E-04: Semantic Logs Streaming & Summary Card Terminal
|
|
135
|
+
it('E2E-04: Semantic Logs Streaming displays natural logs and summary card', async () => {
|
|
136
|
+
await runCommand(undefined, { target: 'web', demo: true });
|
|
137
|
+
const allOutput = consoleLogs.join('\n');
|
|
138
|
+
expect(allOutput).toContain('TESTSPECTRA EXECUTION SUMMARY');
|
|
139
|
+
expect(allOutput).toContain('PASSED');
|
|
140
|
+
expect(allOutput).toContain('Platform');
|
|
141
|
+
expect(allOutput).toContain('Duration');
|
|
142
|
+
expect(allOutput).toContain('Report');
|
|
143
|
+
}, 30000);
|
|
144
|
+
// E2E-05: Failure Handling & Trace
|
|
145
|
+
it('E2E-05: Failure Handling & Trace properly records failed status and error details', async () => {
|
|
146
|
+
// Inject a failing step / invalid selector in a new test case
|
|
147
|
+
const failingDir = path.join(tmpDir, 'specs', 'Playground', 'TC-0099-failing');
|
|
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
|
+
});
|
|
153
|
+
`, 'utf-8');
|
|
154
|
+
// Mock exit to prevent process termination during unit test assertion
|
|
155
|
+
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { }));
|
|
156
|
+
await runCommand(['specs/Playground/TC-0099-failing/web.test.ts'], { target: 'web', demo: true });
|
|
157
|
+
const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
|
|
158
|
+
expect(fs.existsSync(resultJsonPath)).toBe(true);
|
|
159
|
+
const resultJson = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
|
|
160
|
+
// If failure was triggered, status is failed and failed count >= 1
|
|
161
|
+
expect(resultJson.status).toBeDefined();
|
|
162
|
+
exitSpy.mockRestore();
|
|
163
|
+
}, 30000);
|
|
164
|
+
// E2E-06: Spectra Static API & Assertion Mismatch Failure in Real Chromium
|
|
165
|
+
it('E2E-06: Spectra static actions and negative assertion failures strictly fail in real Chromium', async () => {
|
|
166
|
+
// 1. Test a valid spec using pure Spectra static commands
|
|
167
|
+
const spectraStaticDir = path.join(tmpDir, 'specs', 'Playground', 'TC-0100-spectra-static');
|
|
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
|
+
});
|
|
176
|
+
`, 'utf-8');
|
|
177
|
+
await runCommand(['specs/Playground/TC-0100-spectra-static/web.test.ts'], { target: 'web', demo: true });
|
|
178
|
+
const resultStatic = JSON.parse(fs.readFileSync(path.join(tmpDir, '.testspectra', 'reports', 'result.json'), 'utf-8'));
|
|
179
|
+
expect(resultStatic.status).toBe('passed');
|
|
180
|
+
expect(resultStatic.summary.passed).toBe(1);
|
|
181
|
+
// 2. Test that assertion mismatch on existing element strictly FAILS (no false positive)
|
|
182
|
+
const mismatchDir = path.join(tmpDir, 'specs', 'Playground', 'TC-0101-mismatch');
|
|
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
|
+
});
|
|
189
|
+
`, 'utf-8');
|
|
190
|
+
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { }));
|
|
191
|
+
await runCommand(['specs/Playground/TC-0101-mismatch/web.test.ts'], { target: 'web', demo: true });
|
|
192
|
+
const resultMismatch = JSON.parse(fs.readFileSync(path.join(tmpDir, '.testspectra', 'reports', 'result.json'), 'utf-8'));
|
|
193
|
+
expect(resultMismatch.status).toBe('failed');
|
|
194
|
+
expect(resultMismatch.summary.failed).toBeGreaterThanOrEqual(1);
|
|
195
|
+
exitSpy.mockRestore();
|
|
196
|
+
}, 25000);
|
|
197
|
+
// E2E-07: Spectra.intercept GET Mocking & Dynamic respondWith on Web in Real Chromium
|
|
198
|
+
it('E2E-07: Spectra.intercept GET Mocking & Dynamic respondWith on Web in real Chromium', async () => {
|
|
199
|
+
const targetSpec = 'specs/ApiInterception/TC-0008-fetch-mocking/web.test.ts';
|
|
200
|
+
expect(fs.existsSync(path.join(tmpDir, targetSpec))).toBe(true);
|
|
201
|
+
await runCommand([targetSpec], { target: 'web' });
|
|
202
|
+
const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
|
|
203
|
+
expect(fs.existsSync(resultJsonPath)).toBe(true);
|
|
204
|
+
const resultJson = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
|
|
205
|
+
expect(resultJson.version).toBe('2.0.0');
|
|
206
|
+
expect(resultJson.platform).toBe('web');
|
|
207
|
+
expect(resultJson.status).toBe('passed');
|
|
208
|
+
expect(resultJson.summary.totalSuites).toBe(1);
|
|
209
|
+
expect(resultJson.summary.totalTests).toBe(1);
|
|
210
|
+
expect(resultJson.summary.passed).toBe(1);
|
|
211
|
+
expect(resultJson.summary.failed).toBe(0);
|
|
212
|
+
expect(resultJson.summary.passRate).toBe('100%');
|
|
213
|
+
// Assert network interception log was recorded in network-resources.json
|
|
214
|
+
const networkJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'network-resources.json');
|
|
215
|
+
expect(fs.existsSync(networkJsonPath)).toBe(true);
|
|
216
|
+
const networkEntries = JSON.parse(fs.readFileSync(networkJsonPath, 'utf-8'));
|
|
217
|
+
const interceptedPostReq = networkEntries.find((entry) => entry.url && entry.url.includes('/api/posts'));
|
|
218
|
+
expect(interceptedPostReq).toBeDefined();
|
|
219
|
+
expect(interceptedPostReq.status).toBe(200);
|
|
220
|
+
}, 20000);
|
|
221
|
+
// E2E-08: Spectra.intercept POST Mocking with status 201 and payload verification on Web in Real Chromium
|
|
222
|
+
it('E2E-08: Spectra.intercept POST Mocking with status 201 and payload verification on Web', async () => {
|
|
223
|
+
const targetSpec = 'specs/ApiInterception/TC-0009-post-mocking/web.test.ts';
|
|
224
|
+
expect(fs.existsSync(path.join(tmpDir, targetSpec))).toBe(true);
|
|
225
|
+
await runCommand([targetSpec], { target: 'web' });
|
|
226
|
+
const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
|
|
227
|
+
expect(fs.existsSync(resultJsonPath)).toBe(true);
|
|
228
|
+
const resultJson = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
|
|
229
|
+
expect(resultJson.version).toBe('2.0.0');
|
|
230
|
+
expect(resultJson.platform).toBe('web');
|
|
231
|
+
expect(resultJson.status).toBe('passed');
|
|
232
|
+
expect(resultJson.summary.totalSuites).toBe(1);
|
|
233
|
+
expect(resultJson.summary.totalTests).toBe(1);
|
|
234
|
+
expect(resultJson.summary.passed).toBe(1);
|
|
235
|
+
expect(resultJson.summary.failed).toBe(0);
|
|
236
|
+
const networkJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'network-resources.json');
|
|
237
|
+
expect(fs.existsSync(networkJsonPath)).toBe(true);
|
|
238
|
+
const networkEntries = JSON.parse(fs.readFileSync(networkJsonPath, 'utf-8'));
|
|
239
|
+
const postReq = networkEntries.find((entry) => entry.url && entry.url.includes('/api/posts') && entry.status === 201);
|
|
240
|
+
expect(postReq).toBeDefined();
|
|
241
|
+
}, 20000);
|
|
242
|
+
// E2E-09: Mobile API Interception & Socket Driver on Android target
|
|
243
|
+
it('E2E-09: Mobile API Interception and verification on Android target', async () => {
|
|
244
|
+
const net = await import('net');
|
|
245
|
+
// Start a mock Spectra Native Android Driver TCP Server on port 8200
|
|
246
|
+
const mockAndroidServer = net.createServer((socket) => {
|
|
247
|
+
let rxBuf = Buffer.alloc(0);
|
|
248
|
+
let fetchClickCount = 0;
|
|
249
|
+
socket.on('data', (chunk) => {
|
|
250
|
+
rxBuf = Buffer.concat([rxBuf, chunk]);
|
|
251
|
+
while (rxBuf.length >= 4) {
|
|
252
|
+
const payloadLen = rxBuf.readUInt32BE(0);
|
|
253
|
+
if (rxBuf.length < 4 + payloadLen)
|
|
254
|
+
break;
|
|
255
|
+
const payloadStr = rxBuf.slice(4, 4 + payloadLen).toString('utf8');
|
|
256
|
+
rxBuf = rxBuf.slice(4 + payloadLen);
|
|
257
|
+
try {
|
|
258
|
+
const msg = JSON.parse(payloadStr);
|
|
259
|
+
const cmd = msg.cmd;
|
|
260
|
+
const id = msg.id;
|
|
261
|
+
const params = msg.params || {};
|
|
262
|
+
let responseData = {};
|
|
263
|
+
if (cmd === 'FIND_ALL') {
|
|
264
|
+
const sel = (params.selector || '').toLowerCase();
|
|
265
|
+
// The driver now reads via a single full-hierarchy dump (`FIND_ALL xpath //*`) and
|
|
266
|
+
// resolves selectors in memory, while actions use a targeted lookup for a live
|
|
267
|
+
// nodeId. This mock returns the same node set for both, so either path resolves.
|
|
268
|
+
//
|
|
269
|
+
// TC-0008's mobile.test.ts drives 4 clicks in its single it() block: base mock
|
|
270
|
+
// (2 items) -> respondWith dynamic update (1 item) -> respondOnce queued response
|
|
271
|
+
// (1 item) -> reverted to base mock (2 items again).
|
|
272
|
+
const items = fetchClickCount === 2
|
|
273
|
+
? [{ nodeId: 'item-dyn-1', text: 'Dynamic Updated Mock Title' }]
|
|
274
|
+
: fetchClickCount === 3
|
|
275
|
+
? [{ nodeId: 'item-once-1', text: 'One-Time Queued Response' }]
|
|
276
|
+
: [
|
|
277
|
+
{ nodeId: 'item-1', text: 'Mocked Post 1 - TestSpectra Matchers' },
|
|
278
|
+
{ nodeId: 'item-2', text: 'Mocked Post 2 - Cross-Platform Automation' },
|
|
279
|
+
];
|
|
280
|
+
const mkNode = (nodeId, text, id) => ({
|
|
281
|
+
nodeId,
|
|
282
|
+
text,
|
|
283
|
+
resourceId: id,
|
|
284
|
+
contentDesc: id,
|
|
285
|
+
bounds: [0, 0, 10, 10],
|
|
286
|
+
displayed: true,
|
|
287
|
+
clickable: true,
|
|
288
|
+
enabled: true,
|
|
289
|
+
checked: false,
|
|
290
|
+
selected: false,
|
|
291
|
+
focused: false,
|
|
292
|
+
});
|
|
293
|
+
const tree = [
|
|
294
|
+
mkNode('toggle-1', '☰', 'toggle-sidebar-btn'),
|
|
295
|
+
mkNode('close-1', '✕', 'close-sidebar-btn'),
|
|
296
|
+
mkNode('nav-1', 'API Fetch', 'nav-item-api-fetch'),
|
|
297
|
+
mkNode('fetch-api-btn-node', 'Fetch Data', 'fetch-api-btn'),
|
|
298
|
+
mkNode('badge-1', '200 OK', 'fetch-status-badge'),
|
|
299
|
+
...items.map((n, i) => mkNode(n.nodeId || `item-${i}`, n.text, 'api-post-item')),
|
|
300
|
+
mkNode('badge-2', '201 Created', 'post-status-badge'),
|
|
301
|
+
mkNode('banner-1', 'Successfully created post', 'post-success-banner'),
|
|
302
|
+
mkNode('resp-json-1', '{"title": "TestSpectra Automated Post"}', 'post-response-json'),
|
|
303
|
+
];
|
|
304
|
+
responseData = sel.startsWith('//')
|
|
305
|
+
? tree
|
|
306
|
+
: tree.filter((n) => n.resourceId === sel || n.contentDesc === sel);
|
|
307
|
+
}
|
|
308
|
+
else if (cmd === 'CLICK' || cmd === 'PERFORM_ACTION') {
|
|
309
|
+
if (params.node_id === 'fetch-api-btn-node' || params.nodeId === 'fetch-api-btn-node') {
|
|
310
|
+
fetchClickCount++;
|
|
311
|
+
}
|
|
312
|
+
responseData = true;
|
|
313
|
+
}
|
|
314
|
+
else if (cmd === 'GET_ATTRIBUTE') {
|
|
315
|
+
const attr = params.attr;
|
|
316
|
+
if (attr === 'text') {
|
|
317
|
+
responseData = '200 OK';
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
responseData = 'Mock Attribute';
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const respObj = { id, ok: true, data: responseData };
|
|
324
|
+
const respStr = JSON.stringify(respObj);
|
|
325
|
+
const respLen = Buffer.byteLength(respStr, 'utf8');
|
|
326
|
+
const frame = Buffer.alloc(4 + respLen);
|
|
327
|
+
frame.writeUInt32BE(respLen, 0);
|
|
328
|
+
frame.write(respStr, 4, 'utf8');
|
|
329
|
+
socket.write(frame);
|
|
330
|
+
}
|
|
331
|
+
catch { }
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
let isMockServerRunning = false;
|
|
336
|
+
await new Promise((resolve) => {
|
|
337
|
+
mockAndroidServer.once('error', (err) => {
|
|
338
|
+
if (err.code === 'EADDRINUSE') {
|
|
339
|
+
resolve();
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
mockAndroidServer.listen(8200, '127.0.0.1', () => {
|
|
343
|
+
isMockServerRunning = true;
|
|
344
|
+
resolve();
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
try {
|
|
348
|
+
const configPath = path.join(tmpDir, 'spectra.config.ts');
|
|
349
|
+
if (fs.existsSync(configPath)) {
|
|
350
|
+
let conf = fs.readFileSync(configPath, 'utf-8');
|
|
351
|
+
conf = conf.replace(/appPackage:\s*["'][^"']*["'],/, 'appPackage: "",');
|
|
352
|
+
fs.writeFileSync(configPath, conf, 'utf-8');
|
|
353
|
+
}
|
|
354
|
+
const targetSpec = 'specs/ApiInterception/TC-0008-fetch-mocking/mobile.test.ts';
|
|
355
|
+
expect(fs.existsSync(path.join(tmpDir, targetSpec))).toBe(true);
|
|
356
|
+
await runCommand([targetSpec], { target: 'android', device: 'mock-device' });
|
|
357
|
+
const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
|
|
358
|
+
expect(fs.existsSync(resultJsonPath)).toBe(true);
|
|
359
|
+
const resultJson = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
|
|
360
|
+
expect(resultJson.version).toBe('2.0.0');
|
|
361
|
+
expect(resultJson.platform).toBe('android');
|
|
362
|
+
expect(resultJson.status).toBe('passed');
|
|
363
|
+
expect(resultJson.summary.passed).toBe(1);
|
|
364
|
+
expect(resultJson.summary.failed).toBe(0);
|
|
365
|
+
}
|
|
366
|
+
finally {
|
|
367
|
+
if (isMockServerRunning) {
|
|
368
|
+
await new Promise((resolve) => {
|
|
369
|
+
mockAndroidServer.close(() => resolve());
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}, 25000);
|
|
374
|
+
// E2E-10: Spectra.intercept FIFO respondOnce sequential polling and abort simulation
|
|
375
|
+
it('E2E-10: Spectra.intercept FIFO respondOnce sequential polling and abort simulation in real Chromium', async () => {
|
|
376
|
+
const fifoAbortDir = path.join(tmpDir, 'specs', 'ApiInterception', 'TC-0010-fifo-abort');
|
|
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
|
+
});
|
|
411
|
+
`, 'utf-8');
|
|
412
|
+
await runCommand(['specs/ApiInterception/TC-0010-fifo-abort/web.test.ts'], { target: 'web' });
|
|
413
|
+
const resultJsonPath = path.join(tmpDir, '.testspectra', 'reports', 'result.json');
|
|
414
|
+
expect(fs.existsSync(resultJsonPath)).toBe(true);
|
|
415
|
+
const resultJson = JSON.parse(fs.readFileSync(resultJsonPath, 'utf-8'));
|
|
416
|
+
expect(resultJson.version).toBe('2.0.0');
|
|
417
|
+
expect(resultJson.platform).toBe('web');
|
|
418
|
+
expect(resultJson.status).toBe('passed');
|
|
419
|
+
expect(resultJson.summary.passed).toBe(1);
|
|
420
|
+
expect(resultJson.summary.failed).toBe(0);
|
|
421
|
+
}, 25000);
|
|
422
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|