@testspectra/cli 1.0.58 → 1.0.62
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/bin/testspectra-runner +0 -0
- package/dist/commands/add.js +3 -9
- package/dist/commands/init.js +15 -15
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.js +22 -13
- package/dist/index.js +1 -0
- package/dist/server/demo.d.ts +8 -0
- package/dist/server/demo.js +104 -0
- package/dist/types/generator.d.ts +1 -1
- package/dist/types/generator.js +50 -15
- package/package.json +2 -2
- package/templates/default/.cursorrules +18 -0
- package/templates/default/CLAUDE.md +10 -0
- package/templates/default/fixtures/playgroundData.json +7 -0
- package/templates/default/global-hooks/after/mobile.hook.ts +3 -0
- package/templates/default/global-hooks/after/web.hook.ts +3 -0
- package/templates/default/global-hooks/before/mobile.hook.ts +3 -0
- package/templates/default/global-hooks/before/web.hook.ts +3 -0
- package/templates/default/package.json +5 -1
- package/templates/default/page-objects/MatchersPage/mobile.ts +21 -0
- package/templates/default/page-objects/MatchersPage/web.ts +23 -0
- package/templates/default/page-objects/NavigationPage/mobile.ts +5 -0
- package/templates/default/page-objects/NavigationPage/web.ts +8 -0
- package/templates/default/page-objects/PlaygroundPage/mobile.ts +26 -0
- package/templates/default/page-objects/PlaygroundPage/web.ts +29 -0
- package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +3 -0
- package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/spec.md +12 -0
- package/templates/default/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +5 -0
- package/templates/default/specs/BrowserContext/hooks/after/mobile.hook.ts +1 -0
- package/templates/default/specs/BrowserContext/hooks/after/web.hook.ts +1 -0
- package/templates/default/specs/BrowserContext/hooks/afterEach/mobile.hook.ts +1 -0
- package/templates/default/specs/BrowserContext/hooks/afterEach/web.hook.ts +1 -0
- package/templates/default/specs/BrowserContext/hooks/before/mobile.hook.ts +1 -0
- package/templates/default/specs/BrowserContext/hooks/before/web.hook.ts +1 -0
- package/templates/default/specs/BrowserContext/hooks/beforeEach/mobile.hook.ts +1 -0
- package/templates/default/specs/BrowserContext/hooks/beforeEach/web.hook.ts +1 -0
- package/templates/default/specs/BrowserContext/suite.md +9 -0
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +3 -0
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/spec.md +12 -0
- package/templates/default/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +7 -0
- package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +3 -0
- package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/spec.md +12 -0
- package/templates/default/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +6 -0
- package/templates/default/specs/ContentMatchers/hooks/after/mobile.hook.ts +1 -0
- package/templates/default/specs/ContentMatchers/hooks/after/web.hook.ts +1 -0
- package/templates/default/specs/ContentMatchers/hooks/afterEach/mobile.hook.ts +1 -0
- package/templates/default/specs/ContentMatchers/hooks/afterEach/web.hook.ts +1 -0
- package/templates/default/specs/ContentMatchers/hooks/before/mobile.hook.ts +1 -0
- package/templates/default/specs/ContentMatchers/hooks/before/web.hook.ts +1 -0
- package/templates/default/specs/ContentMatchers/hooks/beforeEach/mobile.hook.ts +1 -0
- package/templates/default/specs/ContentMatchers/hooks/beforeEach/web.hook.ts +1 -0
- package/templates/default/specs/ContentMatchers/suite.md +9 -0
- package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +3 -0
- package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/spec.md +12 -0
- package/templates/default/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +5 -0
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +3 -0
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/spec.md +12 -0
- package/templates/default/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +8 -0
- package/templates/default/specs/ElementMatchers/hooks/after/mobile.hook.ts +1 -0
- package/templates/default/specs/ElementMatchers/hooks/after/web.hook.ts +1 -0
- package/templates/default/specs/ElementMatchers/hooks/afterEach/mobile.hook.ts +1 -0
- package/templates/default/specs/ElementMatchers/hooks/afterEach/web.hook.ts +1 -0
- package/templates/default/specs/ElementMatchers/hooks/before/mobile.hook.ts +1 -0
- package/templates/default/specs/ElementMatchers/hooks/before/web.hook.ts +1 -0
- package/templates/default/specs/ElementMatchers/hooks/beforeEach/mobile.hook.ts +1 -0
- package/templates/default/specs/ElementMatchers/hooks/beforeEach/web.hook.ts +1 -0
- package/templates/default/specs/ElementMatchers/suite.md +9 -0
- package/templates/default/specs/Playground/TC-0001-form-controls/mobile.test.ts +3 -0
- package/templates/default/specs/Playground/TC-0001-form-controls/spec.md +12 -0
- package/templates/default/specs/Playground/TC-0001-form-controls/web.test.ts +6 -0
- package/templates/default/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +3 -0
- package/templates/default/specs/Playground/TC-0002-toggle-elements/spec.md +12 -0
- package/templates/default/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -0
- package/templates/default/specs/Playground/hooks/after/mobile.hook.ts +1 -0
- package/templates/default/specs/Playground/hooks/after/web.hook.ts +1 -0
- package/templates/default/specs/Playground/hooks/afterEach/mobile.hook.ts +1 -0
- package/templates/default/specs/Playground/hooks/afterEach/web.hook.ts +1 -0
- package/templates/default/specs/Playground/hooks/before/mobile.hook.ts +1 -0
- package/templates/default/specs/Playground/hooks/before/web.hook.ts +1 -0
- package/templates/default/specs/Playground/hooks/beforeEach/mobile.hook.ts +1 -0
- package/templates/default/specs/Playground/hooks/beforeEach/web.hook.ts +1 -0
- package/templates/default/specs/Playground/suite.md +9 -0
- package/templates/default/spectra.config.ts +1 -1
- package/templates/default/support/actions/fillCredentials/mobile.action.ts +4 -0
- package/templates/default/support/actions/fillCredentials/web.action.ts +4 -0
- package/templates/default/support/steps/togglePlaygroundStates/mobile.step.ts +3 -0
- package/templates/default/support/steps/togglePlaygroundStates/step.md +7 -0
- package/templates/default/support/steps/togglePlaygroundStates/web.step.ts +5 -0
- package/templates/default/support/steps/verifyPlaygroundState/mobile.step.ts +4 -0
- package/templates/default/support/steps/verifyPlaygroundState/step.md +7 -0
- package/templates/default/support/steps/verifyPlaygroundState/web.step.ts +6 -0
- package/templates/default/tsconfig.spectra.android.json +7 -4
- package/templates/default/tsconfig.spectra.ios.json +7 -4
- package/templates/default/tsconfig.spectra.web.json +10 -6
- package/templates/nx/.cursorrules +18 -0
- package/templates/nx/CLAUDE.md +10 -0
- package/templates/nx/fixtures/playgroundData.json +7 -0
- package/templates/nx/global-hooks/after/mobile.hook.ts +3 -0
- package/templates/nx/global-hooks/after/web.hook.ts +3 -0
- package/templates/nx/global-hooks/before/mobile.hook.ts +3 -0
- package/templates/nx/global-hooks/before/web.hook.ts +3 -0
- package/templates/nx/package.json +1 -1
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +3 -0
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/spec.md +12 -0
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +5 -0
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/hooks/after/mobile.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/hooks/after/web.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/hooks/afterEach/mobile.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/hooks/afterEach/web.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/hooks/before/mobile.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/hooks/before/web.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/hooks/beforeEach/mobile.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/hooks/beforeEach/web.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/BrowserContext/suite.md +9 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +3 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/spec.md +12 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +7 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +3 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/spec.md +12 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +6 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/hooks/after/mobile.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/hooks/after/web.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/hooks/afterEach/mobile.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/hooks/afterEach/web.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/hooks/before/mobile.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/hooks/before/web.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/hooks/beforeEach/mobile.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/hooks/beforeEach/web.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ContentMatchers/suite.md +9 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +3 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/spec.md +12 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +5 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +3 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/spec.md +12 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +8 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/hooks/after/mobile.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/hooks/after/web.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/hooks/afterEach/mobile.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/hooks/afterEach/web.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/hooks/before/mobile.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/hooks/before/web.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/hooks/beforeEach/mobile.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/hooks/beforeEach/web.hook.ts +1 -0
- package/templates/nx/packages/matchers/e2e/specs/ElementMatchers/suite.md +9 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/mobile.test.ts +3 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/spec.md +12 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0001-form-controls/web.test.ts +6 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +3 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/spec.md +12 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/hooks/after/mobile.hook.ts +1 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/hooks/after/web.hook.ts +1 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/hooks/afterEach/mobile.hook.ts +1 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/hooks/afterEach/web.hook.ts +1 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/hooks/before/mobile.hook.ts +1 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/hooks/before/web.hook.ts +1 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/hooks/beforeEach/mobile.hook.ts +1 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/hooks/beforeEach/web.hook.ts +1 -0
- package/templates/nx/packages/playground/e2e/specs/Playground/suite.md +9 -0
- package/templates/nx/page-objects/MatchersPage/mobile.ts +21 -0
- package/templates/nx/page-objects/MatchersPage/web.ts +23 -0
- package/templates/nx/page-objects/NavigationPage/mobile.ts +5 -0
- package/templates/nx/page-objects/NavigationPage/web.ts +8 -0
- package/templates/nx/page-objects/PlaygroundPage/mobile.ts +26 -0
- package/templates/nx/page-objects/PlaygroundPage/web.ts +29 -0
- package/templates/nx/shared/testing/fixtures/playgroundData.json +7 -0
- package/templates/nx/shared/testing/global-hooks/after/mobile.hook.ts +3 -0
- package/templates/nx/shared/testing/global-hooks/after/web.hook.ts +3 -0
- package/templates/nx/shared/testing/global-hooks/before/mobile.hook.ts +3 -0
- package/templates/nx/shared/testing/global-hooks/before/web.hook.ts +3 -0
- package/templates/nx/shared/testing/page-objects/MatchersPage/mobile.ts +21 -0
- package/templates/nx/shared/testing/page-objects/MatchersPage/web.ts +23 -0
- package/templates/nx/shared/testing/page-objects/NavigationPage/mobile.ts +5 -0
- package/templates/nx/shared/testing/page-objects/NavigationPage/web.ts +8 -0
- package/templates/nx/shared/testing/page-objects/PlaygroundPage/mobile.ts +26 -0
- package/templates/nx/shared/testing/page-objects/PlaygroundPage/web.ts +29 -0
- package/templates/nx/shared/testing/support/actions/fillCredentials/mobile.action.ts +4 -0
- package/templates/nx/shared/testing/support/actions/fillCredentials/web.action.ts +4 -0
- package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/mobile.step.ts +3 -0
- package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/step.md +7 -0
- package/templates/nx/shared/testing/support/steps/togglePlaygroundStates/web.step.ts +5 -0
- package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/mobile.step.ts +4 -0
- package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/step.md +7 -0
- package/templates/nx/shared/testing/support/steps/verifyPlaygroundState/web.step.ts +6 -0
- package/templates/nx/specs/BrowserContext/TC-0007-title-and-navigation/mobile.test.ts +3 -0
- package/templates/nx/specs/BrowserContext/TC-0007-title-and-navigation/spec.md +12 -0
- package/templates/nx/specs/BrowserContext/TC-0007-title-and-navigation/web.test.ts +5 -0
- package/templates/nx/specs/BrowserContext/hooks/after/mobile.hook.ts +1 -0
- package/templates/nx/specs/BrowserContext/hooks/after/web.hook.ts +1 -0
- package/templates/nx/specs/BrowserContext/hooks/afterEach/mobile.hook.ts +1 -0
- package/templates/nx/specs/BrowserContext/hooks/afterEach/web.hook.ts +1 -0
- package/templates/nx/specs/BrowserContext/hooks/before/mobile.hook.ts +1 -0
- package/templates/nx/specs/BrowserContext/hooks/before/web.hook.ts +1 -0
- package/templates/nx/specs/BrowserContext/hooks/beforeEach/mobile.hook.ts +1 -0
- package/templates/nx/specs/BrowserContext/hooks/beforeEach/web.hook.ts +1 -0
- package/templates/nx/specs/BrowserContext/suite.md +9 -0
- package/templates/nx/specs/ContentMatchers/TC-0005-text-and-values/mobile.test.ts +3 -0
- package/templates/nx/specs/ContentMatchers/TC-0005-text-and-values/spec.md +12 -0
- package/templates/nx/specs/ContentMatchers/TC-0005-text-and-values/web.test.ts +7 -0
- package/templates/nx/specs/ContentMatchers/TC-0006-collections-and-empty/mobile.test.ts +3 -0
- package/templates/nx/specs/ContentMatchers/TC-0006-collections-and-empty/spec.md +12 -0
- package/templates/nx/specs/ContentMatchers/TC-0006-collections-and-empty/web.test.ts +6 -0
- package/templates/nx/specs/ContentMatchers/hooks/after/mobile.hook.ts +1 -0
- package/templates/nx/specs/ContentMatchers/hooks/after/web.hook.ts +1 -0
- package/templates/nx/specs/ContentMatchers/hooks/afterEach/mobile.hook.ts +1 -0
- package/templates/nx/specs/ContentMatchers/hooks/afterEach/web.hook.ts +1 -0
- package/templates/nx/specs/ContentMatchers/hooks/before/mobile.hook.ts +1 -0
- package/templates/nx/specs/ContentMatchers/hooks/before/web.hook.ts +1 -0
- package/templates/nx/specs/ContentMatchers/hooks/beforeEach/mobile.hook.ts +1 -0
- package/templates/nx/specs/ContentMatchers/hooks/beforeEach/web.hook.ts +1 -0
- package/templates/nx/specs/ContentMatchers/suite.md +9 -0
- package/templates/nx/specs/ElementMatchers/TC-0003-visibility-and-dom/mobile.test.ts +3 -0
- package/templates/nx/specs/ElementMatchers/TC-0003-visibility-and-dom/spec.md +12 -0
- package/templates/nx/specs/ElementMatchers/TC-0003-visibility-and-dom/web.test.ts +5 -0
- package/templates/nx/specs/ElementMatchers/TC-0004-states-and-attributes/mobile.test.ts +3 -0
- package/templates/nx/specs/ElementMatchers/TC-0004-states-and-attributes/spec.md +12 -0
- package/templates/nx/specs/ElementMatchers/TC-0004-states-and-attributes/web.test.ts +8 -0
- package/templates/nx/specs/ElementMatchers/hooks/after/mobile.hook.ts +1 -0
- package/templates/nx/specs/ElementMatchers/hooks/after/web.hook.ts +1 -0
- package/templates/nx/specs/ElementMatchers/hooks/afterEach/mobile.hook.ts +1 -0
- package/templates/nx/specs/ElementMatchers/hooks/afterEach/web.hook.ts +1 -0
- package/templates/nx/specs/ElementMatchers/hooks/before/mobile.hook.ts +1 -0
- package/templates/nx/specs/ElementMatchers/hooks/before/web.hook.ts +1 -0
- package/templates/nx/specs/ElementMatchers/hooks/beforeEach/mobile.hook.ts +1 -0
- package/templates/nx/specs/ElementMatchers/hooks/beforeEach/web.hook.ts +1 -0
- package/templates/nx/specs/ElementMatchers/suite.md +9 -0
- package/templates/nx/specs/Playground/TC-0001-form-controls/mobile.test.ts +3 -0
- package/templates/nx/specs/Playground/TC-0001-form-controls/spec.md +12 -0
- package/templates/nx/specs/Playground/TC-0001-form-controls/web.test.ts +6 -0
- package/templates/nx/specs/Playground/TC-0002-toggle-elements/mobile.test.ts +3 -0
- package/templates/nx/specs/Playground/TC-0002-toggle-elements/spec.md +12 -0
- package/templates/nx/specs/Playground/TC-0002-toggle-elements/web.test.ts +6 -0
- package/templates/nx/specs/Playground/hooks/after/mobile.hook.ts +1 -0
- package/templates/nx/specs/Playground/hooks/after/web.hook.ts +1 -0
- package/templates/nx/specs/Playground/hooks/afterEach/mobile.hook.ts +1 -0
- package/templates/nx/specs/Playground/hooks/afterEach/web.hook.ts +1 -0
- package/templates/nx/specs/Playground/hooks/before/mobile.hook.ts +1 -0
- package/templates/nx/specs/Playground/hooks/before/web.hook.ts +1 -0
- package/templates/nx/specs/Playground/hooks/beforeEach/mobile.hook.ts +1 -0
- package/templates/nx/specs/Playground/hooks/beforeEach/web.hook.ts +1 -0
- package/templates/nx/specs/Playground/suite.md +9 -0
- package/templates/nx/spectra.config.ts +1 -1
- package/templates/nx/support/actions/fillCredentials/mobile.action.ts +4 -0
- package/templates/nx/support/actions/fillCredentials/web.action.ts +4 -0
- package/templates/nx/support/steps/togglePlaygroundStates/mobile.step.ts +3 -0
- package/templates/nx/support/steps/togglePlaygroundStates/step.md +7 -0
- package/templates/nx/support/steps/togglePlaygroundStates/web.step.ts +5 -0
- package/templates/nx/support/steps/verifyPlaygroundState/mobile.step.ts +4 -0
- package/templates/nx/support/steps/verifyPlaygroundState/step.md +7 -0
- package/templates/nx/support/steps/verifyPlaygroundState/web.step.ts +6 -0
- package/templates/default/actions/applyDiscount/mobile.action.ts +0 -6
- package/templates/default/actions/applyDiscount/web.action.ts +0 -6
- package/templates/default/actions/verifyOtp/mobile.action.ts +0 -6
- package/templates/default/actions/verifyOtp/web.action.ts +0 -6
- package/templates/default/fixtures/couponCode.json +0 -1
- package/templates/default/fixtures/searchQuery.json +0 -1
- package/templates/default/fixtures/userData.json +0 -1
- package/templates/default/global-hooks/before.web.hook.ts +0 -4
- package/templates/default/page-objects/LoginPage/mobile.ts +0 -32
- package/templates/default/page-objects/LoginPage/web.ts +0 -32
- package/templates/default/page-objects/ProductPage/mobile.ts +0 -39
- package/templates/default/page-objects/ProductPage/web.ts +0 -38
- package/templates/default/page-objects/SettingsPage/common.ts +0 -26
- package/templates/default/specs/Auth/TC-AUTH-01/android.test.ts +0 -6
- package/templates/default/specs/Auth/TC-AUTH-01/ios.test.ts +0 -6
- package/templates/default/specs/Auth/TC-AUTH-01/web.test.ts +0 -13
- package/templates/default/specs/Auth/TC-AUTH-02/android.test.ts +0 -5
- package/templates/default/specs/Auth/TC-AUTH-02/web.test.ts +0 -5
- package/templates/default/specs/Auth/hooks/before.android.hook.ts +0 -3
- package/templates/default/specs/Auth/hooks/before.ios.hook.ts +0 -3
- package/templates/default/specs/Auth/hooks/before.web.hook.ts +0 -3
- package/templates/default/specs/Product/TC-PROD-01/mobile.test.ts +0 -8
- package/templates/default/specs/Product/TC-PROD-01/web.test.ts +0 -8
- package/templates/default/specs/Product/TC-PROD-02/mobile.test.ts +0 -6
- package/templates/default/specs/Product/TC-PROD-02/web.test.ts +0 -6
- package/templates/default/specs/Product/hooks/before.web.hook.ts +0 -3
- package/templates/default/specs/Settings/TC-SET-01/common.test.ts +0 -8
- package/templates/default/steps/loginUser/mobile.step.ts +0 -4
- package/templates/default/steps/loginUser/web.step.ts +0 -4
- package/templates/default/steps/searchProduct/mobile.step.ts +0 -5
- package/templates/default/steps/searchProduct/web.step.ts +0 -5
- package/templates/nx/modules/auth/e2e/actions/verifyOtp/mobile.action.ts +0 -6
- package/templates/nx/modules/auth/e2e/actions/verifyOtp/web.action.ts +0 -6
- package/templates/nx/modules/auth/e2e/fixtures/userData.json +0 -1
- package/templates/nx/modules/auth/e2e/global-hooks/before.web.hook.ts +0 -4
- package/templates/nx/modules/auth/e2e/package.json +0 -19
- package/templates/nx/modules/auth/e2e/page-objects/LoginPage/mobile.ts +0 -32
- package/templates/nx/modules/auth/e2e/page-objects/LoginPage/web.ts +0 -32
- package/templates/nx/modules/auth/e2e/page-objects/SettingsPage/common.ts +0 -26
- package/templates/nx/modules/auth/e2e/project.json +0 -34
- package/templates/nx/modules/auth/e2e/specs/Auth/TC-AUTH-01/android.test.ts +0 -6
- package/templates/nx/modules/auth/e2e/specs/Auth/TC-AUTH-01/ios.test.ts +0 -6
- package/templates/nx/modules/auth/e2e/specs/Auth/TC-AUTH-01/web.test.ts +0 -19
- package/templates/nx/modules/auth/e2e/specs/Auth/TC-AUTH-02/android.test.ts +0 -5
- package/templates/nx/modules/auth/e2e/specs/Auth/TC-AUTH-02/web.test.ts +0 -5
- package/templates/nx/modules/auth/e2e/specs/Auth/hooks/before.android.hook.ts +0 -3
- package/templates/nx/modules/auth/e2e/specs/Auth/hooks/before.ios.hook.ts +0 -3
- package/templates/nx/modules/auth/e2e/specs/Auth/hooks/before.web.hook.ts +0 -3
- package/templates/nx/modules/auth/e2e/specs/Settings/TC-SET-01/common.test.ts +0 -8
- package/templates/nx/modules/auth/e2e/steps/loginUser/mobile.step.ts +0 -4
- package/templates/nx/modules/auth/e2e/steps/loginUser/web.step.ts +0 -4
- package/templates/nx/modules/auth/package.json +0 -7
- package/templates/nx/modules/auth/src/index.ts +0 -3
- package/templates/nx/modules/catalog/package.json +0 -7
- package/templates/nx/modules/catalog/src/index.ts +0 -6
- package/templates/nx/shared/testing/actions/dismissBanner/common.action.ts +0 -6
- package/templates/nx/shared/testing/fixtures/appConfig.json +0 -4
- package/templates/nx/shared/testing/package.json +0 -8
- package/templates/nx/shared/testing/page-objects/NavigationBar/common.ts +0 -13
- package/templates/nx/shared/testing/steps/loginAsAdmin/common.step.ts +0 -4
package/bin/testspectra-runner
CHANGED
|
Binary file
|
package/dist/commands/add.js
CHANGED
|
@@ -151,18 +151,12 @@ export async function addCommand(moduleNameArg, options = {}) {
|
|
|
151
151
|
cur = rootDir;
|
|
152
152
|
while (cur !== path.dirname(cur)) {
|
|
153
153
|
if (fs.existsSync(path.join(cur, "pnpm-workspace.yaml"))) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (wsContent.includes("cli")) {
|
|
157
|
-
isInternalWorkspace = true;
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
catch { }
|
|
154
|
+
isInternalWorkspace = true;
|
|
155
|
+
break;
|
|
162
156
|
}
|
|
163
157
|
cur = path.dirname(cur);
|
|
164
158
|
}
|
|
165
|
-
const cliDepVersion = isInternalWorkspace ? "workspace:*" : cliVersion;
|
|
159
|
+
const cliDepVersion = isInternalWorkspace ? "workspace:*" : (cliVersion.startsWith("^") ? cliVersion : `^${cliVersion}`);
|
|
166
160
|
function copyRecursive(src, dest, replacements = {}) {
|
|
167
161
|
const base = path.basename(src);
|
|
168
162
|
if (base === "node_modules" || base === "dist" || base === ".testspectra" || base === ".git") {
|
package/dist/commands/init.js
CHANGED
|
@@ -256,19 +256,13 @@ export async function initCommand(options = {}) {
|
|
|
256
256
|
let cur = cwd;
|
|
257
257
|
while (cur !== path.dirname(cur)) {
|
|
258
258
|
if (fs.existsSync(path.join(cur, "pnpm-workspace.yaml"))) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (wsContent.includes("cli")) {
|
|
262
|
-
isInternalWorkspace = true;
|
|
263
|
-
break;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
catch { }
|
|
259
|
+
isInternalWorkspace = true;
|
|
260
|
+
break;
|
|
267
261
|
}
|
|
268
262
|
cur = path.dirname(cur);
|
|
269
263
|
}
|
|
270
264
|
const projectName = path.basename(cwd);
|
|
271
|
-
const cliDepVersion = isInternalWorkspace ? "workspace:*" : cliVersion;
|
|
265
|
+
const cliDepVersion = isInternalWorkspace ? "workspace:*" : (cliVersion.startsWith("^") ? cliVersion : `^${cliVersion}`);
|
|
272
266
|
// Helper: Copy directory contents recursively
|
|
273
267
|
function copyRecursive(src, dest, replacements = {}) {
|
|
274
268
|
const base = path.basename(src);
|
|
@@ -442,6 +436,14 @@ export async function initCommand(options = {}) {
|
|
|
442
436
|
rootPkg.devDependencies["@testspectra/matchers"] = cliDepVersion;
|
|
443
437
|
if (!rootPkg.devDependencies["typescript"])
|
|
444
438
|
rootPkg.devDependencies["typescript"] = "^5.4.5";
|
|
439
|
+
if (!rootPkg.devDependencies["@wdio/cli"])
|
|
440
|
+
rootPkg.devDependencies["@wdio/cli"] = "^9.2.8";
|
|
441
|
+
if (!rootPkg.devDependencies["@wdio/local-runner"])
|
|
442
|
+
rootPkg.devDependencies["@wdio/local-runner"] = "^9.2.8";
|
|
443
|
+
if (!rootPkg.devDependencies["@wdio/spec-reporter"])
|
|
444
|
+
rootPkg.devDependencies["@wdio/spec-reporter"] = "^9.2.8";
|
|
445
|
+
if (!rootPkg.devDependencies["@wdio/devtools-service"])
|
|
446
|
+
rootPkg.devDependencies["@wdio/devtools-service"] = "^9.2.8";
|
|
445
447
|
if (!rootPkg.devDependencies["@wdio/globals"])
|
|
446
448
|
rootPkg.devDependencies["@wdio/globals"] = "^9.2.8";
|
|
447
449
|
if (!rootPkg.devDependencies["@wdio/mocha-framework"])
|
|
@@ -704,12 +706,10 @@ pnpm type-check
|
|
|
704
706
|
const s = p.spinner();
|
|
705
707
|
s.start("Scaffolding standalone TestSpectra project...");
|
|
706
708
|
copyRecursive(templateDir, cwd);
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
fs.writeFileSync(pnpmWorkspacePath, pnpmWsContent, "utf-8");
|
|
712
|
-
}
|
|
709
|
+
const standaloneWsPath = path.join(cwd, "pnpm-workspace.yaml");
|
|
710
|
+
if (!fs.existsSync(standaloneWsPath) || options.force) {
|
|
711
|
+
const pnpmWsContent = `allowBuilds:\n chromedriver: true\n edgedriver: true\n geckodriver: true\n`;
|
|
712
|
+
fs.writeFileSync(standaloneWsPath, pnpmWsContent, "utf-8");
|
|
713
713
|
}
|
|
714
714
|
TypeGenerator.writeDeclarationFiles(cwd);
|
|
715
715
|
ensureVsCodeExtensions(cwd);
|
package/dist/commands/run.d.ts
CHANGED
package/dist/commands/run.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Reporter } from "../runner/reporter.js";
|
|
|
6
6
|
import { TypeGenerator } from "../types/generator.js";
|
|
7
7
|
export async function runCommand(specPath, options = {}) {
|
|
8
8
|
const cwd = process.cwd();
|
|
9
|
+
const effectiveSpecPath = specPath || options.spec;
|
|
9
10
|
// Ensure latest ambient types are up-to-date
|
|
10
11
|
TypeGenerator.writeDeclarationFiles(cwd);
|
|
11
12
|
const config = await ConfigLoader.loadConfig(cwd);
|
|
@@ -13,11 +14,11 @@ export async function runCommand(specPath, options = {}) {
|
|
|
13
14
|
config.webConfig.headlessMode = options.headless;
|
|
14
15
|
}
|
|
15
16
|
const platform = options.target || "web";
|
|
16
|
-
// Find workspace root for centralized .testspectra
|
|
17
|
+
// Find workspace root for centralized .testspectra (monorepo root or current test project)
|
|
17
18
|
let workspaceRoot = cwd;
|
|
18
19
|
let cur = cwd;
|
|
19
20
|
while (cur !== path.dirname(cur)) {
|
|
20
|
-
if (fs.existsSync(path.join(cur, "pnpm-workspace.yaml")) || fs.existsSync(path.join(cur, "nx.json"))
|
|
21
|
+
if (fs.existsSync(path.join(cur, "pnpm-workspace.yaml")) || fs.existsSync(path.join(cur, "nx.json"))) {
|
|
21
22
|
workspaceRoot = cur;
|
|
22
23
|
break;
|
|
23
24
|
}
|
|
@@ -30,18 +31,25 @@ export async function runCommand(specPath, options = {}) {
|
|
|
30
31
|
// Determine suite/test case name
|
|
31
32
|
let suiteName = "default";
|
|
32
33
|
let testCases = [];
|
|
33
|
-
if (
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
34
|
+
if (effectiveSpecPath) {
|
|
35
|
+
const relSpec = path.isAbsolute(effectiveSpecPath) ? path.relative(cwd, effectiveSpecPath) : effectiveSpecPath;
|
|
36
|
+
const normalized = relSpec.replace(/\\/g, "/");
|
|
37
|
+
const parts = normalized.split("/");
|
|
38
|
+
if (parts.length >= 2 && (parts[0] === "specs" || parts[0] === "suites")) {
|
|
39
|
+
suiteName = parts[1];
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const parsed = path.parse(normalized);
|
|
43
|
+
suiteName = parsed.name.replace(/\.(web|android|ios|common|test|spec)/g, "") || "default";
|
|
44
|
+
}
|
|
37
45
|
testCases.push({
|
|
38
|
-
id:
|
|
39
|
-
title:
|
|
46
|
+
id: normalized,
|
|
47
|
+
title: normalized,
|
|
40
48
|
executionOrder: 1,
|
|
41
49
|
});
|
|
42
50
|
}
|
|
43
51
|
else {
|
|
44
|
-
// 1. Scan specs/ directory (
|
|
52
|
+
// 1. Scan specs/ directory (specs/<Suite>/<CaseId>/web.test.ts)
|
|
45
53
|
const specsDir = path.join(cwd, "specs");
|
|
46
54
|
if (fs.existsSync(specsDir)) {
|
|
47
55
|
const suiteEntries = fs.readdirSync(specsDir, { withFileTypes: true });
|
|
@@ -55,11 +63,11 @@ export async function runCommand(specPath, options = {}) {
|
|
|
55
63
|
if (cEntry.name.startsWith(".") || cEntry.name === "hooks")
|
|
56
64
|
continue;
|
|
57
65
|
if (cEntry.isDirectory()) {
|
|
58
|
-
testCases.push({ id: cEntry.name, title: `${currentSuite} / ${cEntry.name}` });
|
|
66
|
+
testCases.push({ id: path.join("specs", currentSuite, cEntry.name).replace(/\\/g, "/"), title: `${currentSuite} / ${cEntry.name}` });
|
|
59
67
|
}
|
|
60
68
|
else if (cEntry.isFile() && (cEntry.name.endsWith(".test.ts") || cEntry.name.endsWith(".spec.ts"))) {
|
|
61
|
-
const id = cEntry.name.replace(
|
|
62
|
-
testCases.push({ id, title: `${currentSuite} / ${
|
|
69
|
+
const id = path.join("specs", currentSuite, cEntry.name).replace(/\\/g, "/");
|
|
70
|
+
testCases.push({ id, title: `${currentSuite} / ${cEntry.name}` });
|
|
63
71
|
}
|
|
64
72
|
}
|
|
65
73
|
}
|
|
@@ -77,7 +85,7 @@ export async function runCommand(specPath, options = {}) {
|
|
|
77
85
|
for (const cEntry of caseEntries) {
|
|
78
86
|
if (cEntry.name.startsWith(".") || cEntry.name === "hooks" || !cEntry.isDirectory())
|
|
79
87
|
continue;
|
|
80
|
-
testCases.push({ id: cEntry.name, title: `${currentSuite} / ${cEntry.name}` });
|
|
88
|
+
testCases.push({ id: path.join("suites", currentSuite, cEntry.name).replace(/\\/g, "/"), title: `${currentSuite} / ${cEntry.name}` });
|
|
81
89
|
}
|
|
82
90
|
}
|
|
83
91
|
}
|
|
@@ -93,6 +101,7 @@ export async function runCommand(specPath, options = {}) {
|
|
|
93
101
|
? path.resolve(options.output)
|
|
94
102
|
: path.join(appDataPath, "reports/result.json");
|
|
95
103
|
console.log(`\x1b[36m[TestSpectra]\x1b[0m Starting execution on target \x1b[1m${platform}\x1b[0m...`);
|
|
104
|
+
console.log(`\x1b[36m[TestSpectra]\x1b[0m Base URL: \x1b[33m${config.webConfig.baseUrl}\x1b[0m`);
|
|
96
105
|
console.log(`\x1b[36m[TestSpectra]\x1b[0m Suite: ${suiteName} (${testCases.length} case(s))\n`);
|
|
97
106
|
const reporter = new Reporter();
|
|
98
107
|
try {
|
package/dist/index.js
CHANGED
|
@@ -45,6 +45,7 @@ export function createCliProgram() {
|
|
|
45
45
|
program
|
|
46
46
|
.command("run [path]")
|
|
47
47
|
.description("Execute test suite or individual test case")
|
|
48
|
+
.option("-s, --spec <path>", "Spec file path to execute")
|
|
48
49
|
.option("-t, --target <target>", "Target platform: web, android, ios, common", "web")
|
|
49
50
|
.option("-d, --device <device>", "Target device name or UDID")
|
|
50
51
|
.option("--headless", "Run in headless mode")
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import http from "http";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
export class DemoAppServer {
|
|
10
|
+
server = null;
|
|
11
|
+
port = 0;
|
|
12
|
+
url = "";
|
|
13
|
+
static findDemoAppDir(cwd) {
|
|
14
|
+
// 1. Check upward from cwd
|
|
15
|
+
let cur = cwd;
|
|
16
|
+
while (cur !== path.dirname(cur)) {
|
|
17
|
+
const cand = path.join(cur, "apps", "demo-web-app");
|
|
18
|
+
if (fs.existsSync(cand) && fs.existsSync(path.join(cand, "package.json"))) {
|
|
19
|
+
return cand;
|
|
20
|
+
}
|
|
21
|
+
cur = path.dirname(cur);
|
|
22
|
+
}
|
|
23
|
+
// 2. Check relative to CLI package location
|
|
24
|
+
const relCand = path.resolve(__dirname, "../../../apps/demo-web-app");
|
|
25
|
+
if (fs.existsSync(relCand) && fs.existsSync(path.join(relCand, "package.json"))) {
|
|
26
|
+
return relCand;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
async start(cwd) {
|
|
31
|
+
const demoDir = DemoAppServer.findDemoAppDir(cwd);
|
|
32
|
+
if (!demoDir) {
|
|
33
|
+
throw new Error("Could not locate 'apps/demo-web-app'. Ensure you are running within the TestSpectra workspace.");
|
|
34
|
+
}
|
|
35
|
+
const distDir = path.join(demoDir, "dist");
|
|
36
|
+
if (!fs.existsSync(distDir) || !fs.existsSync(path.join(distDir, "index.html"))) {
|
|
37
|
+
console.log(chalk.cyan("[TestSpectra] ") +
|
|
38
|
+
chalk.dim("Building demo web app bundle (apps/demo-web-app)..."));
|
|
39
|
+
execSync("pnpm --filter @testspectra/demo-web-app build", {
|
|
40
|
+
cwd: path.resolve(demoDir, "../.."),
|
|
41
|
+
stdio: "inherit",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const mimeTypes = {
|
|
45
|
+
".html": "text/html; charset=utf-8",
|
|
46
|
+
".js": "text/javascript; charset=utf-8",
|
|
47
|
+
".css": "text/css; charset=utf-8",
|
|
48
|
+
".json": "application/json",
|
|
49
|
+
".svg": "image/svg+xml",
|
|
50
|
+
".png": "image/png",
|
|
51
|
+
".jpg": "image/jpeg",
|
|
52
|
+
".jpeg": "image/jpeg",
|
|
53
|
+
".ico": "image/x-icon",
|
|
54
|
+
".woff": "font/woff",
|
|
55
|
+
".woff2": "font/woff2",
|
|
56
|
+
".ttf": "font/ttf",
|
|
57
|
+
};
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
this.server = http.createServer((req, res) => {
|
|
60
|
+
const rawUrl = req.url || "/";
|
|
61
|
+
const cleanPath = rawUrl.split("?")[0].split("#")[0];
|
|
62
|
+
let filePath = path.join(distDir, cleanPath === "/" ? "index.html" : cleanPath);
|
|
63
|
+
// Fallback to index.html for SPA client-side routing
|
|
64
|
+
if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {
|
|
65
|
+
filePath = path.join(distDir, "index.html");
|
|
66
|
+
}
|
|
67
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
68
|
+
const contentType = mimeTypes[ext] || "application/octet-stream";
|
|
69
|
+
fs.readFile(filePath, (err, content) => {
|
|
70
|
+
if (err) {
|
|
71
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
72
|
+
res.end("Internal Server Error");
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
res.writeHead(200, {
|
|
76
|
+
"Content-Type": contentType,
|
|
77
|
+
"Cache-Control": "no-cache",
|
|
78
|
+
"Access-Control-Allow-Origin": "*",
|
|
79
|
+
});
|
|
80
|
+
res.end(content);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
this.server.listen(0, "127.0.0.1", () => {
|
|
85
|
+
const addr = this.server?.address();
|
|
86
|
+
if (addr && typeof addr === "object") {
|
|
87
|
+
this.port = addr.port;
|
|
88
|
+
this.url = `http://127.0.0.1:${this.port}`;
|
|
89
|
+
resolve(this.url);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
reject(new Error("Failed to bind demo preview server address"));
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
this.server.on("error", reject);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
stop() {
|
|
99
|
+
if (this.server) {
|
|
100
|
+
this.server.close();
|
|
101
|
+
this.server = null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -12,6 +12,6 @@ export declare class TypeGenerator {
|
|
|
12
12
|
static getPageObjectsDirsForScope(scopeDir: string, sharedDir?: string): string[];
|
|
13
13
|
static getEntityDirsForScope(entityType: "actions" | "steps" | "fixtures", scopeDir: string, sharedDir?: string): string[];
|
|
14
14
|
static generateDeclarationsForScope(cwd: string, outputTypesDir: string, scopeDir: string, sharedDir?: string): Record<string, string>;
|
|
15
|
-
static generateFixturesDeclarationForScope(scopeDir: string, sharedDir?: string): string;
|
|
15
|
+
static generateFixturesDeclarationForScope(cwd: string, outputTypesDir: string, scopeDir: string, sharedDir?: string): string;
|
|
16
16
|
static writeDeclarationFiles(cwd: string): void;
|
|
17
17
|
}
|
package/dist/types/generator.js
CHANGED
|
@@ -2,10 +2,10 @@ import fs from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
const PLATFORM_HIERARCHY = {
|
|
4
4
|
web: ["web", "common"],
|
|
5
|
-
ios: ["ios", "mobile", "common"],
|
|
6
|
-
android: ["android", "mobile", "common"],
|
|
7
|
-
mobile: ["mobile", "common"],
|
|
8
|
-
common: ["common"],
|
|
5
|
+
ios: ["ios", "mobile", "common", "web"],
|
|
6
|
+
android: ["android", "mobile", "common", "web"],
|
|
7
|
+
mobile: ["mobile", "common", "android", "ios", "web"],
|
|
8
|
+
common: ["common", "web", "mobile", "android", "ios"],
|
|
9
9
|
};
|
|
10
10
|
export class TypeGenerator {
|
|
11
11
|
/**
|
|
@@ -21,7 +21,12 @@ export class TypeGenerator {
|
|
|
21
21
|
path.join(cwd, "testing"),
|
|
22
22
|
];
|
|
23
23
|
for (const cand of sharedCandidates) {
|
|
24
|
-
if (fs.existsSync(cand) &&
|
|
24
|
+
if (fs.existsSync(cand) &&
|
|
25
|
+
(fs.existsSync(path.join(cand, "page-objects")) ||
|
|
26
|
+
fs.existsSync(path.join(cand, "support")) ||
|
|
27
|
+
fs.existsSync(path.join(cand, "steps")) ||
|
|
28
|
+
fs.existsSync(path.join(cand, "actions")) ||
|
|
29
|
+
fs.existsSync(path.join(cand, "fixtures")))) {
|
|
25
30
|
if (!scopes.some((s) => s.dir === cand)) {
|
|
26
31
|
scopes.push({ name: "shared", dir: cand, isShared: true });
|
|
27
32
|
}
|
|
@@ -124,9 +129,15 @@ export class TypeGenerator {
|
|
|
124
129
|
scanDir(scopeDir, 6);
|
|
125
130
|
}
|
|
126
131
|
else {
|
|
127
|
-
const candidates = [
|
|
132
|
+
const candidates = [
|
|
133
|
+
path.join(scopeDir, entityType),
|
|
134
|
+
path.join(scopeDir, "support", entityType),
|
|
135
|
+
path.join(scopeDir, "support", "testing", entityType),
|
|
136
|
+
];
|
|
128
137
|
if (sharedDir) {
|
|
129
138
|
candidates.push(path.join(sharedDir, entityType));
|
|
139
|
+
candidates.push(path.join(sharedDir, "support", entityType));
|
|
140
|
+
candidates.push(path.join(sharedDir, "support", "testing", entityType));
|
|
130
141
|
}
|
|
131
142
|
for (const c of candidates) {
|
|
132
143
|
if (fs.existsSync(c) && !dirs.includes(c))
|
|
@@ -244,7 +255,11 @@ export class TypeGenerator {
|
|
|
244
255
|
if (matchedFile) {
|
|
245
256
|
if (!matchedFile.startsWith("."))
|
|
246
257
|
matchedFile = `./${matchedFile}`;
|
|
247
|
-
|
|
258
|
+
const camel = actionName.replace(/[-_]+([a-zA-Z0-9])/g, (_, char) => char.toUpperCase());
|
|
259
|
+
content += ` "${actionName}": (typeof import('${matchedFile}') extends { default: infer T } ? T : typeof import('${matchedFile}'));\n`;
|
|
260
|
+
if (camel !== actionName && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(camel)) {
|
|
261
|
+
content += ` ${camel}: (typeof import('${matchedFile}') extends { default: infer T } ? T : typeof import('${matchedFile}'));\n`;
|
|
262
|
+
}
|
|
248
263
|
declaredActions.add(actionName);
|
|
249
264
|
}
|
|
250
265
|
}
|
|
@@ -258,7 +273,11 @@ export class TypeGenerator {
|
|
|
258
273
|
let rel = path.relative(outputTypesDir, path.join(actionsDir, entry.name)).replace(/\\/g, "/").replace(/\.ts$/, ".js");
|
|
259
274
|
if (!rel.startsWith("."))
|
|
260
275
|
rel = `./${rel}`;
|
|
261
|
-
|
|
276
|
+
const camel = actionName.replace(/[-_]+([a-zA-Z0-9])/g, (_, char) => char.toUpperCase());
|
|
277
|
+
content += ` "${actionName}": (typeof import('${rel}') extends { default: infer T } ? T : typeof import('${rel}'));\n`;
|
|
278
|
+
if (camel !== actionName && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(camel)) {
|
|
279
|
+
content += ` ${camel}: (typeof import('${rel}') extends { default: infer T } ? T : typeof import('${rel}'));\n`;
|
|
280
|
+
}
|
|
262
281
|
declaredActions.add(actionName);
|
|
263
282
|
}
|
|
264
283
|
}
|
|
@@ -298,7 +317,11 @@ export class TypeGenerator {
|
|
|
298
317
|
if (matchedFile) {
|
|
299
318
|
if (!matchedFile.startsWith("."))
|
|
300
319
|
matchedFile = `./${matchedFile}`;
|
|
301
|
-
|
|
320
|
+
const camel = stepName.replace(/[-_]+([a-zA-Z0-9])/g, (_, char) => char.toUpperCase());
|
|
321
|
+
content += ` "${stepName}": (typeof import('${matchedFile}') extends { default: infer T } ? T : typeof import('${matchedFile}'));\n`;
|
|
322
|
+
if (camel !== stepName && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(camel)) {
|
|
323
|
+
content += ` ${camel}: (typeof import('${matchedFile}') extends { default: infer T } ? T : typeof import('${matchedFile}'));\n`;
|
|
324
|
+
}
|
|
302
325
|
declaredSteps.add(stepName);
|
|
303
326
|
}
|
|
304
327
|
}
|
|
@@ -312,7 +335,11 @@ export class TypeGenerator {
|
|
|
312
335
|
let rel = path.relative(outputTypesDir, path.join(stepsDir, entry.name)).replace(/\\/g, "/").replace(/\.ts$/, ".js");
|
|
313
336
|
if (!rel.startsWith("."))
|
|
314
337
|
rel = `./${rel}`;
|
|
315
|
-
|
|
338
|
+
const camel = stepName.replace(/[-_]+([a-zA-Z0-9])/g, (_, char) => char.toUpperCase());
|
|
339
|
+
content += ` "${stepName}": (typeof import('${rel}') extends { default: infer T } ? T : typeof import('${rel}'));\n`;
|
|
340
|
+
if (camel !== stepName && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(camel)) {
|
|
341
|
+
content += ` ${camel}: (typeof import('${rel}') extends { default: infer T } ? T : typeof import('${rel}'));\n`;
|
|
342
|
+
}
|
|
316
343
|
declaredSteps.add(stepName);
|
|
317
344
|
}
|
|
318
345
|
}
|
|
@@ -324,7 +351,7 @@ export class TypeGenerator {
|
|
|
324
351
|
}
|
|
325
352
|
return results;
|
|
326
353
|
}
|
|
327
|
-
static generateFixturesDeclarationForScope(scopeDir, sharedDir) {
|
|
354
|
+
static generateFixturesDeclarationForScope(cwd, outputTypesDir, scopeDir, sharedDir) {
|
|
328
355
|
const fixturesDirs = this.getEntityDirsForScope("fixtures", scopeDir, sharedDir);
|
|
329
356
|
let content = `// Auto-generated ambient fixture declarations for TestSpectra\n`;
|
|
330
357
|
content += `// Managed automatically by TestSpectra Language Service Plugin.\n\n`;
|
|
@@ -346,7 +373,15 @@ export class TypeGenerator {
|
|
|
346
373
|
const varName = parsed.name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
347
374
|
if (declaredFixtures.has(varName))
|
|
348
375
|
continue;
|
|
349
|
-
|
|
376
|
+
if (entry.name.endsWith(".json")) {
|
|
377
|
+
let rel = path.relative(outputTypesDir, path.join(dir, entry.name)).replace(/\\/g, "/");
|
|
378
|
+
if (!rel.startsWith("."))
|
|
379
|
+
rel = `./${rel}`;
|
|
380
|
+
content += ` readonly ${varName}: typeof import('${rel}');\n`;
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
content += ` readonly ${varName}: string;\n`;
|
|
384
|
+
}
|
|
350
385
|
declaredFixtures.add(varName);
|
|
351
386
|
}
|
|
352
387
|
}
|
|
@@ -368,7 +403,7 @@ export class TypeGenerator {
|
|
|
368
403
|
const scopes = this.getEntityScopes(cwd);
|
|
369
404
|
const sharedScope = scopes.find((s) => s.isShared);
|
|
370
405
|
// 1. Generate Global / Standalone Root Types (or fallback)
|
|
371
|
-
const rootFixtures = this.generateFixturesDeclarationForScope(cwd);
|
|
406
|
+
const rootFixtures = this.generateFixturesDeclarationForScope(cwd, rootTypesDir, cwd);
|
|
372
407
|
fs.writeFileSync(path.join(rootTypesDir, "fixtures.d.ts"), rootFixtures, "utf-8");
|
|
373
408
|
const rootDeclarations = this.generateDeclarationsForScope(cwd, rootTypesDir, cwd);
|
|
374
409
|
for (const [platform, content] of Object.entries(rootDeclarations)) {
|
|
@@ -379,7 +414,7 @@ export class TypeGenerator {
|
|
|
379
414
|
const sharedTypesDir = path.join(rootTypesDir, "shared");
|
|
380
415
|
if (!fs.existsSync(sharedTypesDir))
|
|
381
416
|
fs.mkdirSync(sharedTypesDir, { recursive: true });
|
|
382
|
-
const sharedFixtures = this.generateFixturesDeclarationForScope(sharedScope.dir);
|
|
417
|
+
const sharedFixtures = this.generateFixturesDeclarationForScope(cwd, sharedTypesDir, sharedScope.dir);
|
|
383
418
|
fs.writeFileSync(path.join(sharedTypesDir, "fixtures.d.ts"), sharedFixtures, "utf-8");
|
|
384
419
|
// Shared scope has NO feature dependencies (only shared itself)
|
|
385
420
|
const sharedDeclarations = this.generateDeclarationsForScope(cwd, sharedTypesDir, sharedScope.dir);
|
|
@@ -393,7 +428,7 @@ export class TypeGenerator {
|
|
|
393
428
|
const featureTypesDir = path.join(rootTypesDir, "features", feature.name);
|
|
394
429
|
if (!fs.existsSync(featureTypesDir))
|
|
395
430
|
fs.mkdirSync(featureTypesDir, { recursive: true });
|
|
396
|
-
const featureFixtures = this.generateFixturesDeclarationForScope(feature.dir, sharedScope?.dir);
|
|
431
|
+
const featureFixtures = this.generateFixturesDeclarationForScope(cwd, featureTypesDir, feature.dir, sharedScope?.dir);
|
|
397
432
|
fs.writeFileSync(path.join(featureTypesDir, "fixtures.d.ts"), featureFixtures, "utf-8");
|
|
398
433
|
// Feature scope gets (feature + shared) ONLY
|
|
399
434
|
const featureDeclarations = this.generateDeclarationsForScope(cwd, featureTypesDir, feature.dir, sharedScope?.dir);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testspectra/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.62",
|
|
4
4
|
"description": "TestSpectra Zero-Config Cross-Platform Test Runner CLI",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@clack/prompts": "^1.7.0",
|
|
32
|
-
"@testspectra/matchers": "^1.0.
|
|
32
|
+
"@testspectra/matchers": "^1.0.62",
|
|
33
33
|
"chalk": "^5.3.0",
|
|
34
34
|
"commander": "^12.1.0",
|
|
35
35
|
"dotenv": "^16.4.5",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# TestSpectra AI Instructions for Cursor
|
|
2
|
+
|
|
3
|
+
You are developing end-to-end automated tests with the TestSpectra testing framework.
|
|
4
|
+
|
|
5
|
+
## Key Rules & Conventions
|
|
6
|
+
1. **Zero-Import Standard**: Never add manual `import` statements for Page Objects (`PlaygroundPage`, `MatchersPage`, `NavigationPage`), Steps (`Step.*`), Fixtures (`Fixture.*`), or Actions (`Spectra.*`). All typings are provided globally and ambiently via `.testspectra/types/`.
|
|
7
|
+
2. **Platform Separation**:
|
|
8
|
+
- `web.test.ts` / `web.ts` / `web.step.ts` / `web.hook.ts`: Web platform implementations.
|
|
9
|
+
- `mobile.test.ts` / `mobile.ts` / `mobile.step.ts` / `mobile.hook.ts`: Mobile platform implementations.
|
|
10
|
+
- `common.test.ts` / `common.ts` / `common.step.ts` / `common.hook.ts`: Cross-platform shared implementations.
|
|
11
|
+
3. **CamelCase Naming Convention**:
|
|
12
|
+
- Shared Steps (`support/steps/loginAsAdmin/`, `support/steps/verifyPlaygroundState/`)
|
|
13
|
+
- Custom Actions (`support/actions/applyDiscount/`, `support/actions/verifyOtp/`)
|
|
14
|
+
- Lifecycle Hooks (`global-hooks/before/`, `global-hooks/after/`, `specs/<suite>/hooks/before/`, `specs/<suite>/hooks/beforeEach/`, `specs/<suite>/hooks/after/`, `specs/<suite>/hooks/afterEach/`)
|
|
15
|
+
4. **Spec-as-Code**:
|
|
16
|
+
- Suites are defined in `specs/<suite-name>/suite.md` with YAML frontmatter (`id`, `name`, `executionOrder`, `tags`).
|
|
17
|
+
- Test cases are defined in `specs/<suite-name>/<TC-ID-slug>/spec.md` with YAML frontmatter (`id`, `title`, `priority`, `coverage`).
|
|
18
|
+
- Shared steps are defined in `support/steps/<stepName>/step.md`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# CLAUDE.md - TestSpectra Framework Conventions
|
|
2
|
+
|
|
3
|
+
## Commands
|
|
4
|
+
- Run all Web tests against live demo app: `pnpm spectra run --demo`
|
|
5
|
+
- Run specific spec with demo app: `pnpm spectra run --spec specs/authentication/TC-0001-valid-login/web.test.ts --demo`
|
|
6
|
+
- Run Web tests: `pnpm test`
|
|
7
|
+
- Run Mobile tests: `pnpm spectra run --target android` or `pnpm spectra run --target ios`
|
|
8
|
+
- Type checking: `pnpm type-check` (or `tsc -b`)
|
|
9
|
+
- Preflight doctor: `pnpm spectra doctor`
|
|
10
|
+
- List devices: `pnpm spectra devices`
|
|
@@ -9,8 +9,12 @@
|
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
11
|
"@testspectra/cli": "workspace:*",
|
|
12
|
-
"@testspectra/matchers": "^1.0.
|
|
12
|
+
"@testspectra/matchers": "^1.0.62",
|
|
13
13
|
"@types/node": "^20.14.0",
|
|
14
|
+
"@wdio/cli": "^9.2.8",
|
|
15
|
+
"@wdio/local-runner": "^9.2.8",
|
|
16
|
+
"@wdio/spec-reporter": "^9.2.8",
|
|
17
|
+
"@wdio/devtools-service": "^8.46.0",
|
|
14
18
|
"@wdio/globals": "^9.2.8",
|
|
15
19
|
"@wdio/mocha-framework": "^9.2.8",
|
|
16
20
|
"webdriverio": "^9.2.8",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class MatchersPage {
|
|
2
|
+
get visibleElement() { return Spectra.get("~visible_element"); }
|
|
3
|
+
get hiddenElement() { return Spectra.get("~hidden_element"); }
|
|
4
|
+
get existingElement() { return Spectra.get("~existing_element"); }
|
|
5
|
+
get submitButton() { return Spectra.get("~submit_button"); }
|
|
6
|
+
get disabledButton() { return Spectra.get("~disabled_button"); }
|
|
7
|
+
get checkbox() { return Spectra.get("~checkbox"); }
|
|
8
|
+
get radio() { return Spectra.get("~radio"); }
|
|
9
|
+
get emailInput() { return Spectra.get("~email_input"); }
|
|
10
|
+
get tokenInput() { return Spectra.get("~token_input"); }
|
|
11
|
+
get exactText() { return Spectra.get("~exact_text"); }
|
|
12
|
+
get partialText() { return Spectra.get("~partial_text"); }
|
|
13
|
+
get activeTab() { return Spectra.get("~active_tab"); }
|
|
14
|
+
get navLink() { return Spectra.get("~nav_link"); }
|
|
15
|
+
get productCards() { return Spectra.getAll("~product_card"); }
|
|
16
|
+
get emptyList() { return Spectra.getAll("~empty_list"); }
|
|
17
|
+
|
|
18
|
+
async open(_section = "") {}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default new MatchersPage();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export class MatchersPage {
|
|
2
|
+
get visibleElement() { return Spectra.get("#demo-visible-el"); }
|
|
3
|
+
get hiddenElement() { return Spectra.get("#demo-hidden-el"); }
|
|
4
|
+
get existingElement() { return Spectra.get("#demo-existing-el"); }
|
|
5
|
+
get submitButton() { return Spectra.get("#demo-submit-btn"); }
|
|
6
|
+
get disabledButton() { return Spectra.get("#demo-disabled-btn"); }
|
|
7
|
+
get checkbox() { return Spectra.get("#demo-checkbox"); }
|
|
8
|
+
get radio() { return Spectra.get("#demo-radio"); }
|
|
9
|
+
get emailInput() { return Spectra.get("#demo-email-input"); }
|
|
10
|
+
get tokenInput() { return Spectra.get("#demo-token-input"); }
|
|
11
|
+
get exactText() { return Spectra.get("#demo-exact-text"); }
|
|
12
|
+
get partialText() { return Spectra.get("#demo-partial-text"); }
|
|
13
|
+
get activeTab() { return Spectra.get("#demo-active-tab"); }
|
|
14
|
+
get navLink() { return Spectra.get("#demo-nav-link"); }
|
|
15
|
+
get productCards() { return Spectra.getAll(".demo-product-card"); }
|
|
16
|
+
get emptyList() { return Spectra.getAll("#demo-empty-list > *"); }
|
|
17
|
+
|
|
18
|
+
async open(section = "") {
|
|
19
|
+
await browser.url("/" + (section ? "#" + section : ""));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default new MatchersPage();
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export class NavigationPage {
|
|
2
|
+
get sidebarLinks() { return Spectra.getAll("aside a"); }
|
|
3
|
+
get overviewLink() { return Spectra.get("aside a[href='#overview']"); }
|
|
4
|
+
get visibilityLink() { return Spectra.get("aside a[href='#visibility']"); }
|
|
5
|
+
get playgroundLink() { return Spectra.get("aside a[href='#playground']"); }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default new NavigationPage();
|