@sun-asterisk/sungen 3.2.31 → 3.2.32
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/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +3 -1
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.d.ts.map +1 -1
- package/dist/cli/commands/capability.js +11 -0
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +2 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/setup-mobile-env.d.ts +15 -0
- package/dist/cli/commands/setup-mobile-env.d.ts.map +1 -0
- package/dist/cli/commands/setup-mobile-env.js +65 -0
- package/dist/cli/commands/setup-mobile-env.js.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-assertion.hbs +1 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-element.hbs +8 -5
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/count-assertion.hbs +5 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/list-item-count-assertion.hbs +5 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/state-with-filter-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-filtered-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/partials/await-elements.hbs +12 -0
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +26 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +7 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/row-scoped-column-assertion.hbs +23 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +4 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +4 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +13 -2
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +14 -0
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +56 -13
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +39 -2
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +38 -1
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +1 -1
- package/dist/generators/test-generator/patterns/index.js +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +35 -0
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +103 -4
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
- package/dist/generators/test-generator/template-engine.js +18 -0
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +47 -0
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +17 -3
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +23 -4
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +2 -1
- package/dist/harness/next-step.js.map +1 -1
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +42 -7
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/spec-branches.d.ts +7 -2
- package/dist/harness/spec-branches.d.ts.map +1 -1
- package/dist/harness/spec-branches.js +62 -11
- package/dist/harness/spec-branches.js.map +1 -1
- package/dist/harness/spec-coverage.d.ts +16 -0
- package/dist/harness/spec-coverage.d.ts.map +1 -1
- package/dist/harness/spec-coverage.js +119 -10
- package/dist/harness/spec-coverage.js.map +1 -1
- package/dist/orchestrator/appium-environment-setup.d.ts +24 -0
- package/dist/orchestrator/appium-environment-setup.d.ts.map +1 -0
- package/dist/orchestrator/appium-environment-setup.js +157 -0
- package/dist/orchestrator/appium-environment-setup.js.map +1 -0
- package/dist/orchestrator/mobile-runtime-scaffolder.d.ts +2 -0
- package/dist/orchestrator/mobile-runtime-scaffolder.d.ts.map +1 -1
- package/dist/orchestrator/mobile-runtime-scaffolder.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +1 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +14 -0
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +5 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +55 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +20 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +3 -3
- package/dist/orchestrator/templates/appium-environment/README.md +83 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/install-appium.ps1 +44 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/install-appium.sh +50 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/lib.ps1 +108 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/lib.sh +141 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/verify-appium.ps1 +39 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/verify-appium.sh +43 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/verify-node.ps1 +13 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/verify-node.sh +16 -0
- package/dist/orchestrator/templates/appium-environment/scripts/macos/install-android.sh +110 -0
- package/dist/orchestrator/templates/appium-environment/scripts/macos/install-ios.sh +32 -0
- package/dist/orchestrator/templates/appium-environment/scripts/macos/verify-android.sh +32 -0
- package/dist/orchestrator/templates/appium-environment/scripts/macos/verify-ios.sh +15 -0
- package/dist/orchestrator/templates/appium-environment/scripts/windows/install-android.ps1 +153 -0
- package/dist/orchestrator/templates/appium-environment/scripts/windows/verify-android.ps1 +26 -0
- package/dist/orchestrator/templates/appium-environment/setup.ps1 +58 -0
- package/dist/orchestrator/templates/appium-environment/setup.sh +88 -0
- package/dist/orchestrator/templates/specs-checked-state.d.ts +1 -1
- package/dist/orchestrator/templates/specs-checked-state.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-checked-state.js +26 -3
- package/dist/orchestrator/templates/specs-checked-state.js.map +1 -1
- package/dist/orchestrator/templates/specs-checked-state.ts +31 -6
- package/dist/orchestrator/templates/specs-mock.d.ts +2 -1
- package/dist/orchestrator/templates/specs-mock.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-mock.js +10 -3
- package/dist/orchestrator/templates/specs-mock.js.map +1 -1
- package/dist/orchestrator/templates/specs-mock.ts +12 -4
- package/package.json +3 -3
- package/src/cli/commands/audit.ts +2 -1
- package/src/cli/commands/capability.ts +12 -1
- package/src/cli/commands/init.ts +3 -0
- package/src/cli/commands/setup-mobile-env.ts +64 -0
- package/src/cli/index.ts +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-assertion.hbs +1 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-element.hbs +8 -5
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/count-assertion.hbs +5 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/list-item-count-assertion.hbs +5 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/state-with-filter-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-filtered-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/partials/await-elements.hbs +12 -0
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +26 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +7 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/row-scoped-column-assertion.hbs +23 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +4 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +4 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +13 -2
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +14 -0
- package/src/generators/test-generator/code-generator.ts +66 -17
- package/src/generators/test-generator/diagnostics.ts +39 -2
- package/src/generators/test-generator/patterns/index.ts +1 -1
- package/src/generators/test-generator/step-mapper.ts +106 -5
- package/src/generators/test-generator/template-engine.ts +18 -0
- package/src/generators/test-generator/utils/selector-resolver.ts +18 -3
- package/src/harness/audit.ts +24 -5
- package/src/harness/next-step.ts +2 -1
- package/src/harness/parse.ts +42 -7
- package/src/harness/spec-branches.ts +67 -12
- package/src/harness/spec-coverage.ts +121 -11
- package/src/orchestrator/appium-environment-setup.ts +127 -0
- package/src/orchestrator/mobile-runtime-scaffolder.ts +2 -0
- package/src/orchestrator/project-initializer.ts +15 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +5 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +55 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +20 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +3 -3
- package/src/orchestrator/templates/appium-environment/README.md +83 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/install-appium.ps1 +44 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/install-appium.sh +50 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/lib.ps1 +108 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/lib.sh +141 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/verify-appium.ps1 +39 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/verify-appium.sh +43 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/verify-node.ps1 +13 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/verify-node.sh +16 -0
- package/src/orchestrator/templates/appium-environment/scripts/macos/install-android.sh +110 -0
- package/src/orchestrator/templates/appium-environment/scripts/macos/install-ios.sh +32 -0
- package/src/orchestrator/templates/appium-environment/scripts/macos/verify-android.sh +32 -0
- package/src/orchestrator/templates/appium-environment/scripts/macos/verify-ios.sh +15 -0
- package/src/orchestrator/templates/appium-environment/scripts/windows/install-android.ps1 +153 -0
- package/src/orchestrator/templates/appium-environment/scripts/windows/verify-android.ps1 +26 -0
- package/src/orchestrator/templates/appium-environment/setup.ps1 +58 -0
- package/src/orchestrator/templates/appium-environment/setup.sh +88 -0
- package/src/orchestrator/templates/specs-checked-state.ts +31 -6
- package/src/orchestrator/templates/specs-mock.ts +12 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs-checked-state.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-checked-state.ts"],"names":[],"mappings":";;AA+DA,
|
|
1
|
+
{"version":3,"file":"specs-checked-state.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-checked-state.ts"],"names":[],"mappings":";;AA+DA,oCAoEC;AA8CD,0CAgHC;AAYD,gDA+CC;AApUD;;;;;GAKG;AACH,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;AACjE,qGAAqG;AACrG,qDAAqD;AACrD,MAAM,iBAAiB,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAK,CAAC;AAChG,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAe7B;;;;;;;;;;;;;GAaG;AACH,SAAgB,YAAY,CAAC,IAAa,EAAE,QAAyB;IACnE,MAAM,EAAE,GAAG,IAKV,CAAC;IACF,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAG,gCAAgC;IAC7F,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACxD,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACxE,MAAM,iBAAiB,GAAG,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,CAAC;IACvF,MAAM,UAAU,GAAG,GAAG,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ;WACnD,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC;IAEvF,iGAAiG;IACjG,iGAAiG;IACjG,iGAAiG;IACjG,8FAA8F;IAC9F,mGAAmG;IACnG,gGAAgG;IAChG,yFAAyF;IACzF,IAAI,QAAQ,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,EAAE,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC7E,MAAM,OAAO,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,aAAa,CAAC,WAAW,GAAG,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC;QACzF,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC;IAChE,CAAC;IACD,kGAAkG;IAClG,gGAAgG;IAChG,4FAA4F;IAC5F,IAAI,QAAQ,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,UAAU,EAAE,CAAC;IACpG,CAAC;IAED,iFAAiF;IACjF,IAAI,GAAG,KAAK,OAAO,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IACrH,iGAAiG;IACjG,gGAAgG;IAChG,6FAA6F;IAC7F,2EAA2E;IAC3E,IAAI,iBAAiB,IAAI,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAC7G,IAAI,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,UAAU,EAAE,CAAC;IACnI,CAAC;IACD,IAAI,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,UAAU,EAAE,CAAC;IACnI,CAAC;IACD,gGAAgG;IAChG,IAAI,iBAAiB;QAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAC1E,4FAA4F;IAC5F,+FAA+F;IAC/F,gGAAgG;IAChG,iGAAiG;IACjG,kGAAkG;IAClG,cAAc;IACd,EAAE;IACF,kGAAkG;IAClG,6FAA6F;IAC7F,4FAA4F;IAC5F,qBAAqB;IACrB,IAAI,EAAE,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,UAAU,EAAE,CAAC;IACrI,CAAC;IACD,IAAI,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC/E,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,OAAO,EAAE,UAAU,EAAE,CAAC;IACnF,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AACnD,CAAC;AAED;4FAC4F;AAC5F,MAAM,cAAc,GAAG,CACrB,OAA8D,EAClC,EAAE,CAAC,CAC/B,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,YAAY,EAAE;IACrD,CAAC,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,gBAAgB,EAAE;QACnE,CAAC,CAAC,SAAS,CAChB,CAAC;AAEF,MAAM,KAAK,GAAG,CAAC,EAAU,EAAiB,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AACnF;;;;;GAKG;AACH,MAAM,IAAI,GAAG,CAAC,QAAgB,EAAU,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;AAKhF;kGACkG;AAClG,KAAK,UAAU,MAAM,CAAC,MAAe,EAAE,IAAqB,EAAE,QAAgB;IAC5E,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5G,iGAAiG;IACjG,gGAAgG;IAChG,wDAAwD;IACxD,IAAI,IAAI,KAAK,cAAc;QAAE,OAAO,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,OAAO,CAAC;IAClE,OAAO,GAAG,KAAK,MAAM,CAAC;AACxB,CAAC;AAED;;;;;;;;;;GAUG;AACI,KAAK,UAAU,eAAe,CACnC,MAAe,EAAE,IAAa,EAAE,KAAa,EAC7C,OAAgF;IAEhF,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,iBAAiB,CAAC;IACtD,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACzC,+FAA+F;IAC/F,4FAA4F;IAC5F,+EAA+E;IAC/E,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;IAE9C,mGAAmG;IACnG,iGAAiG;IACjG,gFAAgF;IAChF,IAAI,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;IAC/F,OAAO,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,EAAE,CAAC;QACnF,MAAM,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAC9B,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,IAAI;YAAE,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;;YACrC,MAAM,MAAM,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QACvC,OAAO;IACT,CAAC;IAED,+FAA+F;IAC/F,gGAAgG;IAChG,2DAA2D;IAC3D,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;QAC7D,IAAI,KAAK,CAAC,EAAE,KAAK,IAAI;YAAE,OAAO;QAC9B,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAChC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC9B,MAAM,aAAa,GAAG,UAAU,GAAG,OAAO,CAAC;QAC3C,SAAS,CAAC;YACR,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAC9F,IAAI,KAAK,CAAC,EAAE,KAAK,IAAI;gBAAE,OAAO;YAC9B,IAAI,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,gBAAgB,EAAE,CAAC;gBACnD,MAAM,IAAI,KAAK,CACb,IAAI,KAAK,oBAAoB,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,WAAW,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,QAAQ;sBACnG,gCAAgC,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,KAAK,IAAI,QAAQ,EAAE,SAAS,gBAAgB;sBACvG,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,wCAAwC,CAAC,CAAC,CAAC,EAAE,IAAI;sBACrG,mFAAmF;sBACnF,GAAG,KAAK,CAAC,MAAM,6CAA6C,CAC/D,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC5B,kGAAkG;QAClG,0FAA0F;QAC1F,8FAA8F;QAC9F,+FAA+F;QAC/F,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACrB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,0FAA0F;gBAC1F,oFAAoF;gBACpF,OAAO,CAAC,IAAI,CACV,qBAAqB,KAAK,mEAAmE;sBAC3F,qFAAqF;sBACrF,mFAAmF,CACtF,CAAC;gBACF,OAAO;YACT,CAAC;YACD,0FAA0F;YAC1F,gGAAgG;YAChG,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YAChC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAChC,MAAM,eAAe,GAAG,YAAY,GAAG,OAAO,CAAC;YAC/C,SAAS,CAAC;gBACR,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;gBACjG,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,EAAE,KAAK,IAAI;oBAAE,OAAO;gBACzD,IAAI,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,gBAAgB,EAAE,CAAC;oBACrD,MAAM,IAAI,KAAK,CACb,iBAAiB,KAAK,qEAAqE;0BACzF,cAAc,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,qDAAqD;0BAC5F,uEAAuE,KAAK,WAAW;0BACvF,uCAAuC,CAC1C,CAAC;gBACJ,CAAC;gBACD,MAAM,KAAK,CAAC,gBAAgB,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;QACD,oGAAoG;QACpG,MAAM,IAAI,KAAK,CACb,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,kDAAkD;cAC9F,6HAA6H;cAC7H,qEAAqE,CACxE,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAoB,KAAK,CAAC,MAAyB,CAAC;IAC9D,4FAA4F;IAC5F,IAAI,KAAK,CAAC,EAAE,KAAK,IAAI;QAAE,OAAO;IAC9B,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAEhC,yFAAyF;IACzF,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC/B,MAAM,cAAc,GAAG,WAAW,GAAG,OAAO,CAAC;IAC7C,SAAS,CAAC;QACR,IAAI,MAAM,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,KAAK,IAAI;YAAE,OAAO;QAChE,IAAI,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,gBAAgB,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CACb,IAAI,KAAK,oBAAoB,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,WAAW,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,cAAc;kBAC1G,aAAa,IAAI,gBAAgB,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,qDAAqD;kBACzG,uDAAuD,CAC1D,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACI,KAAK,UAAU,kBAAkB,CACtC,MAAe,EAAE,QAAiB,EAAE,KAAa,EACjD,OAAgF;IAEhF,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,iBAAiB,CAAC;IACtD,gGAAgG;IAChG,+FAA+F;IAC/F,sDAAsD;IACtD,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC;IACjC,SAAS,CAAC;QACR,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACzF,IAAI,MAAM,GAAmB,IAAI,CAAC;QAClC,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ;YAAE,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;aACvF,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM;YAAE,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC;aAC/C,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YAC1B,6FAA6F;YAC7F,6FAA6F;YAC7F,2FAA2F;YAC3F,6EAA6E;YAC7E,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,OAAO,CAAC,IAAI,CACV,iBAAiB,KAAK,sEAAsE;sBAC1F,2FAA2F;sBAC3F,oFAAoF,CACvF,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC;QACD,IAAI,MAAM,KAAK,QAAQ;YAAE,OAAO;QAChC,IAAI,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,gBAAgB,EAAE,CAAC;YAC9C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CACb,kBAAkB,KAAK,QAAQ,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,iBAAiB;sBAChF,4FAA4F;sBAC5F,0FAA0F;sBAC1F,mBAAmB,CACtB,CAAC;YACJ,CAAC;YACD,MAAM,IAAI,KAAK,CACb,aAAa,KAAK,WAAW,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,cAAc;kBAC3E,GAAG,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,UAAU,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,KAAK,CACvE,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAChC,CAAC;AACH,CAAC"}
|
|
@@ -38,7 +38,7 @@ const POLL_INTERVAL_MS = 100;
|
|
|
38
38
|
export interface DeclaredSignal { class?: string; attribute?: string }
|
|
39
39
|
|
|
40
40
|
/** Where the control publishes its on/off state — decided in the page, from the real element. */
|
|
41
|
-
export type StateSource = 'native' | 'aria-pressed' | 'aria-checked' | 'class' | 'attribute' | 'none';
|
|
41
|
+
export type StateSource = 'native' | 'aria-pressed' | 'aria-checked' | 'aria-selected' | 'aria-current' | 'class' | 'attribute' | 'none';
|
|
42
42
|
export interface ControlProbe {
|
|
43
43
|
source: StateSource;
|
|
44
44
|
/** Current state, already normalised. Meaningless when `source` is 'native' (Playwright reads it). */
|
|
@@ -110,6 +110,24 @@ export function probeControl(node: unknown, declared?: DeclaredSignal): ControlP
|
|
|
110
110
|
}
|
|
111
111
|
// Role claims checkable but nothing is published — let Playwright raise its own, precise error.
|
|
112
112
|
if (ariaCheckableRole) return { source: 'native', on: false, buttonLike };
|
|
113
|
+
// `is selected` on a tab, an option, a chip or a nav item. These publish `aria-selected` or
|
|
114
|
+
// `aria-current`, which nothing above reads: the probe fell straight through to 'none' and the
|
|
115
|
+
// step failed saying the control has no on/off state to set, on a control that publishes one in
|
|
116
|
+
// the standard place. Read here rather than in the templates so a suite already generated in the
|
|
117
|
+
// field picks the fix up without regenerating — the same reasoning the URL predicate is a runtime
|
|
118
|
+
// helper for.
|
|
119
|
+
//
|
|
120
|
+
// `aria-current` is not a boolean: the spec allows 'page', 'step', 'location', 'date', 'time' and
|
|
121
|
+
// 'true', and only the literal 'false' (or the attribute's absence) means off. Read that way
|
|
122
|
+
// round, so `aria-current="page"` on the active nav item reads as ON rather than as a stray
|
|
123
|
+
// non-'true' string.
|
|
124
|
+
if (el.hasAttribute('aria-selected')) {
|
|
125
|
+
return { source: 'aria-selected', on: String(el.getAttribute('aria-selected') ?? '').trim().toLowerCase() === 'true', buttonLike };
|
|
126
|
+
}
|
|
127
|
+
if (el.hasAttribute('aria-current')) {
|
|
128
|
+
const raw = String(el.getAttribute('aria-current') ?? '').trim().toLowerCase();
|
|
129
|
+
return { source: 'aria-current', on: raw !== '' && raw !== 'false', buttonLike };
|
|
130
|
+
}
|
|
113
131
|
return { source: 'none', on: false, buttonLike };
|
|
114
132
|
}
|
|
115
133
|
|
|
@@ -132,11 +150,18 @@ const sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms
|
|
|
132
150
|
*/
|
|
133
151
|
const left = (deadline: number): number => Math.max(250, deadline - Date.now());
|
|
134
152
|
|
|
153
|
+
/** The sources that name an ATTRIBUTE the live state can be re-read from. */
|
|
154
|
+
type AttributeSource = 'aria-pressed' | 'aria-checked' | 'aria-selected' | 'aria-current';
|
|
155
|
+
|
|
135
156
|
/** Read the live state. A REMOVED attribute means off: `aria-pressed={on || undefined}` is a common
|
|
136
157
|
* React/Vue idiom, so requiring a literal "false" would fail a control that is correctly off. */
|
|
137
|
-
async function readOn(target: Locator, attr:
|
|
138
|
-
const raw = await target.getAttribute(attr, { timeout: left(deadline) });
|
|
139
|
-
|
|
158
|
+
async function readOn(target: Locator, attr: AttributeSource, deadline: number): Promise<boolean> {
|
|
159
|
+
const raw = String(await target.getAttribute(attr, { timeout: left(deadline) }) ?? '').trim().toLowerCase();
|
|
160
|
+
// `aria-current` is the one non-boolean of the four: the spec allows 'page', 'step', 'location',
|
|
161
|
+
// 'date', 'time' and 'true', so only the literal 'false' (or absence) means off. Read the other
|
|
162
|
+
// way round it would report the active nav item as OFF.
|
|
163
|
+
if (attr === 'aria-current') return raw !== '' && raw !== 'false';
|
|
164
|
+
return raw === 'true';
|
|
140
165
|
}
|
|
141
166
|
|
|
142
167
|
/**
|
|
@@ -238,12 +263,12 @@ export async function setCheckedState(
|
|
|
238
263
|
// Not pressable and publishes nothing — there is no reading of this that makes the step meaningful.
|
|
239
264
|
throw new Error(
|
|
240
265
|
`Cannot ${want ? 'check' : 'uncheck'} [${label}] — it is not a checkbox/radio/switch, is not a `
|
|
241
|
-
+ `button, and publishes no aria-pressed/aria-checked, so it has no on/off state to set. Point the `
|
|
266
|
+
+ `button, and publishes no aria-pressed/aria-checked/aria-selected/aria-current, so it has no on/off state to set. Point the `
|
|
242
267
|
+ `selector at the control itself, or give it a real ARIA toggle role.`,
|
|
243
268
|
);
|
|
244
269
|
}
|
|
245
270
|
|
|
246
|
-
const attr = probe.source;
|
|
271
|
+
const attr: AttributeSource = probe.source as AttributeSource;
|
|
247
272
|
// Already in the requested state → nothing to do. This is the idempotence `check` promises.
|
|
248
273
|
if (probe.on === want) return;
|
|
249
274
|
await target.click({ timeout });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs-mock.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-mock.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,IAAI,EAAS,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA0BD,cAAM,UAAU;IACd,OAAO,CAAC,KAAK,CAAkC;IAE/C,OAAO,CAAC,QAAQ;IAShB;;;;;gDAK4C;IAC5C,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAMhC,yFAAyF;IACnF,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"specs-mock.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-mock.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,IAAI,EAAS,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA0BD,cAAM,UAAU;IACd,OAAO,CAAC,KAAK,CAAkC;IAE/C,OAAO,CAAC,QAAQ;IAShB;;;;;gDAK4C;IAC5C,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAMhC,yFAAyF;IACnF,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAyCzD;;2CAEuC;IACvC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAQ3C;;6FAEyF;IACnF,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAUjD;;;;;sBAKkB;IAClB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAMpC;;;2FAGuF;IACjF,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CAoBvC;AAED,eAAO,MAAM,IAAI,YAAmB,CAAC"}
|
|
@@ -55,10 +55,17 @@ class MockRouter {
|
|
|
55
55
|
st.counts.set(cfg.name, (st.counts.get(cfg.name) ?? 0) + 1);
|
|
56
56
|
if (cfg.delay_ms)
|
|
57
57
|
await new Promise((r) => setTimeout(r, cfg.delay_ms));
|
|
58
|
-
// A delayed
|
|
59
|
-
// not surface as an unhandled rejection from inside a route handler.
|
|
58
|
+
// A delayed response can race a page close / reset at teardown — that race must
|
|
59
|
+
// not surface as an unhandled rejection from inside a route handler. It wraps BOTH
|
|
60
|
+
// outcomes: an abort races teardown exactly like a fulfill does.
|
|
60
61
|
try {
|
|
61
|
-
|
|
62
|
+
// `abort` is the request that never reaches the server; `status` is the one the
|
|
63
|
+
// server answered. The catalog refuses an entry carrying both, so this is a real
|
|
64
|
+
// either/or, not a precedence rule (#676).
|
|
65
|
+
if (cfg.abort)
|
|
66
|
+
await route.abort(cfg.abort);
|
|
67
|
+
else
|
|
68
|
+
await route.fulfill({ status: cfg.status, headers: cfg.headers, body: cfg.body });
|
|
62
69
|
}
|
|
63
70
|
catch { /* page closing */ }
|
|
64
71
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs-mock.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-mock.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"specs-mock.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-mock.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,7 +25,8 @@ export interface MockConfig {
|
|
|
25
25
|
name: string;
|
|
26
26
|
method: string; // HTTP verb or '*'
|
|
27
27
|
url: string; // page.route glob, or 're:<pattern>' for a regex
|
|
28
|
-
status
|
|
28
|
+
status?: number; // the server ANSWERED — absent exactly when `abort` is set
|
|
29
|
+
abort?: string; // the request NEVER ARRIVED — a route.abort() errorCode
|
|
29
30
|
headers?: Record<string, string>;
|
|
30
31
|
body?: string; // pre-serialized by the compiler (JSON string)
|
|
31
32
|
delay_ms?: number;
|
|
@@ -104,9 +105,16 @@ class MockRouter {
|
|
|
104
105
|
if (cfg.method !== '*' && route.request().method() !== cfg.method) return route.fallback();
|
|
105
106
|
st.counts.set(cfg.name, (st.counts.get(cfg.name) ?? 0) + 1);
|
|
106
107
|
if (cfg.delay_ms) await new Promise((r) => setTimeout(r, cfg.delay_ms));
|
|
107
|
-
// A delayed
|
|
108
|
-
// not surface as an unhandled rejection from inside a route handler.
|
|
109
|
-
|
|
108
|
+
// A delayed response can race a page close / reset at teardown — that race must
|
|
109
|
+
// not surface as an unhandled rejection from inside a route handler. It wraps BOTH
|
|
110
|
+
// outcomes: an abort races teardown exactly like a fulfill does.
|
|
111
|
+
try {
|
|
112
|
+
// `abort` is the request that never reaches the server; `status` is the one the
|
|
113
|
+
// server answered. The catalog refuses an entry carrying both, so this is a real
|
|
114
|
+
// either/or, not a precedence rule (#676).
|
|
115
|
+
if (cfg.abort) await route.abort(cfg.abort);
|
|
116
|
+
else await route.fulfill({ status: cfg.status, headers: cfg.headers, body: cfg.body });
|
|
117
|
+
} catch { /* page closing */ }
|
|
110
118
|
};
|
|
111
119
|
await page.route(pattern, handler);
|
|
112
120
|
st.routes.push({ pattern, handler });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sun-asterisk/sungen",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.32",
|
|
4
4
|
"description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"@babel/types": "^7.28.5",
|
|
41
41
|
"@cucumber/gherkin": "^37.0.0",
|
|
42
42
|
"@cucumber/messages": "^31.0.0",
|
|
43
|
-
"@sungen/driver-data-factory": "3.2.
|
|
44
|
-
"@sungen/driver-ui": "3.2.
|
|
43
|
+
"@sungen/driver-data-factory": "3.2.32",
|
|
44
|
+
"@sungen/driver-ui": "3.2.32",
|
|
45
45
|
"chalk": "^5.6.2",
|
|
46
46
|
"commander": "^14.0.2",
|
|
47
47
|
"dotenv": "^17.2.3",
|
|
@@ -109,7 +109,8 @@ function render(r: AuditReport): void {
|
|
|
109
109
|
L(` ⑦ Spec coverage — FR ${r.spec.frCovered}/${r.spec.frTotal} covered [${r.spec.verdict.toUpperCase()}]`);
|
|
110
110
|
for (const g of r.spec.triggerGaps) L(` ✗ TRIGGER-UNCOVERED: "${g.constraint}"${g.code ? ` (${g.code})` : ''} mandated on [${g.required.join(', ')}], only tested on [${g.found.join(', ') || 'none'}] → missing ${g.missing.join(', ')}`);
|
|
111
111
|
for (const u of r.spec.uncoveredMust.slice(0, 6)) L(` ✗ SPEC-UNCOVERED: ${u.id} (MUST) — "${u.text}"`);
|
|
112
|
-
|
|
112
|
+
for (const c of r.spec.clauseGaps.slice(0, 6)) L(` ✗ SPEC-CLAUSE-UNCOVERED: ${c.id} (${c.polarity === 'negate' ? 'MUST NOT' : 'MUST'}) — "${c.text}"`);
|
|
113
|
+
if (!r.spec.triggerGaps.length && !r.spec.uncoveredMust.length && !r.spec.clauseGaps.length) L(' ✓ every MUST FR + per-constraint trigger covered');
|
|
113
114
|
L('');
|
|
114
115
|
}
|
|
115
116
|
if (r.ledger.hasViewpoint && r.ledger.total > 0) {
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { buildPlan, MANUAL_REASONS } from '../../harness/capability-plan';
|
|
10
10
|
import { adapterRegistry } from '../../generators/test-generator/adapters';
|
|
11
11
|
import { emitMobileRuntime, installMobileDriver } from '../../orchestrator/mobile-runtime-scaffolder';
|
|
12
|
+
import { emitAppiumEnvironment, runAppiumEnvironmentSetup } from '../../orchestrator/appium-environment-setup';
|
|
12
13
|
import { coreVersion, resolveDriverVersion } from '../../orchestrator/lockstep-version';
|
|
13
14
|
import { findUnitDir, resolveUnit } from '../resolve-unit';
|
|
14
15
|
|
|
@@ -157,7 +158,8 @@ export function registerCapabilityCommand(program: Command): void {
|
|
|
157
158
|
.description('Enable a driver: install its package (if external) + record it in qa/capabilities.yaml')
|
|
158
159
|
.option('--skip-install', 'Only record in the profile; do not run npm install')
|
|
159
160
|
.option('--force', 'Switch the project platform even if a different one is already set (sungen is one platform per project)')
|
|
160
|
-
.
|
|
161
|
+
.option('--skip-env-setup', 'Mobile: emit appium-environment/ but do not run it now — re-run later: ./appium-environment/setup.sh (Windows: setup.ps1)')
|
|
162
|
+
.action((driver: string, o: { skipInstall?: boolean; force?: boolean; skipEnvSetup?: boolean }) => {
|
|
161
163
|
try {
|
|
162
164
|
const cwd = process.cwd();
|
|
163
165
|
const meta = driverMeta(driver);
|
|
@@ -240,8 +242,17 @@ export function registerCapabilityCommand(program: Command): void {
|
|
|
240
242
|
console.log(' – skipped package.json scripts[test:mobile] (already exists)');
|
|
241
243
|
}
|
|
242
244
|
|
|
245
|
+
// appium-environment/ provisioning bundle (idempotent per file) — emitted before the driver
|
|
246
|
+
// install, run after it so its Appium step finds the project-local CLI.
|
|
247
|
+
const env = emitAppiumEnvironment(cwd);
|
|
248
|
+
if (env.created.length > 0) console.log(` ✓ created appium-environment/ (${env.created.length} files)`);
|
|
249
|
+
else console.log(' – skipped appium-environment/ (already exists)');
|
|
250
|
+
|
|
243
251
|
// Best-effort driver install — non-fatal so capability add still succeeds offline.
|
|
244
252
|
installMobileDriver(cwd);
|
|
253
|
+
|
|
254
|
+
// --skip-install means "record only" — then provisioning the machine is not wanted either.
|
|
255
|
+
runAppiumEnvironmentSetup(cwd, { skip: !!(o.skipEnvSetup || o.skipInstall) });
|
|
245
256
|
}
|
|
246
257
|
|
|
247
258
|
console.log('');
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -13,6 +13,7 @@ export function registerInitCommand(program: Command): void {
|
|
|
13
13
|
.option('--ios-bundle-id <id>', 'iOS bundle id for mobile (e.g. com.example.app.dev) — default app when MOBILE_PLATFORM=ios|both. Used with --mobile')
|
|
14
14
|
.option('--ios-device <name>', "iOS Simulator device name for mobile (default: 'iPhone 16'). Used with --mobile")
|
|
15
15
|
.option('--ios-version <ver>', "iOS Simulator platform version for mobile (default: '18.0'). Used with --mobile")
|
|
16
|
+
.option('--skip-env-setup', 'Mobile: emit appium-environment/ (JDK, Android SDK, Xcode CLT, env vars, Appium drivers) but do not run it now — re-run later: ./appium-environment/setup.sh (Windows: setup.ps1). Used with --mobile')
|
|
16
17
|
.option(
|
|
17
18
|
'--assistant <name>',
|
|
18
19
|
'AI assistant to install for: claude | copilot | codex | all (repeatable). Default: claude',
|
|
@@ -30,6 +31,7 @@ export function registerInitCommand(program: Command): void {
|
|
|
30
31
|
iosBundleId?: string;
|
|
31
32
|
iosDevice?: string;
|
|
32
33
|
iosVersion?: string;
|
|
34
|
+
skipEnvSetup?: boolean;
|
|
33
35
|
assistant?: string[];
|
|
34
36
|
force?: boolean;
|
|
35
37
|
}) => {
|
|
@@ -55,6 +57,7 @@ export function registerInitCommand(program: Command): void {
|
|
|
55
57
|
iosBundleId: options.iosBundleId,
|
|
56
58
|
iosDevice: options.iosDevice,
|
|
57
59
|
iosVersion: options.iosVersion,
|
|
60
|
+
skipEnvSetup: options.skipEnvSetup,
|
|
58
61
|
});
|
|
59
62
|
await initializer.initialize(projectName);
|
|
60
63
|
} catch (error) {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `sungen setup-mobile-env` — provision this machine for Appium, in a project that already exists.
|
|
5
|
+
*
|
|
6
|
+
* `sungen init --mobile` runs the same bundle, but only the person who created the project ever runs
|
|
7
|
+
* init. The second QA clones a ready-made repo and had no command to reach for: `capability add
|
|
8
|
+
* mobile` re-records a capability that is already recorded, and knowing to run
|
|
9
|
+
* `./appium-environment/setup.sh` by hand assumes they know the folder exists. This is that command.
|
|
10
|
+
*
|
|
11
|
+
* Emits `appium-environment/` when it is missing — a project initialised before the bundle shipped,
|
|
12
|
+
* or one where the folder was deleted — then runs it. Both halves are idempotent, so running it on a
|
|
13
|
+
* fully provisioned machine reports a wall of skips and changes nothing.
|
|
14
|
+
*/
|
|
15
|
+
export function registerSetupMobileEnvCommand(program: Command): void {
|
|
16
|
+
program
|
|
17
|
+
.command('setup-mobile-env')
|
|
18
|
+
.description('Install the Appium environment on THIS machine (JDK, Android SDK, env vars, Xcode CLT, Appium drivers) for an existing mobile project — idempotent, safe to re-run')
|
|
19
|
+
.option('--check', 'Report what is missing and install nothing')
|
|
20
|
+
.option('--skip-android', 'Skip the JDK / Android SDK / uiautomator2 steps')
|
|
21
|
+
.option('--skip-ios', 'Skip the Xcode CLT / applesimutils / xcuitest steps (macOS only)')
|
|
22
|
+
.option('--no-profile', 'Do not write ANDROID_HOME / JAVA_HOME / PATH to your shell profile')
|
|
23
|
+
.option('--emit-only', 'Write appium-environment/ into the project but do not run it')
|
|
24
|
+
.action((o: { check?: boolean; skipAndroid?: boolean; skipIos?: boolean; profile?: boolean; emitOnly?: boolean }) => {
|
|
25
|
+
try {
|
|
26
|
+
const cwd = process.cwd();
|
|
27
|
+
const {
|
|
28
|
+
emitAppiumEnvironment,
|
|
29
|
+
runAppiumEnvironmentSetup,
|
|
30
|
+
appiumEnvironmentRerunCommand,
|
|
31
|
+
} = require('../../orchestrator/appium-environment-setup');
|
|
32
|
+
|
|
33
|
+
// Idempotent per file, so an edited script (a bumped cmdline-tools build, a local hint)
|
|
34
|
+
// survives. A project that already has the folder just sees the skip line.
|
|
35
|
+
const emitted = emitAppiumEnvironment(cwd);
|
|
36
|
+
if (emitted.created.length > 0) {
|
|
37
|
+
console.log(`✓ created appium-environment/ (${emitted.created.length} files)`);
|
|
38
|
+
} else {
|
|
39
|
+
console.log('– skipped appium-environment/ (already present)');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (o.emitOnly) {
|
|
43
|
+
console.log(`\nRun it when you are ready: ${appiumEnvironmentRerunCommand()}`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// commander maps `--no-profile` to `profile: false`, so the flag is absent when profile is
|
|
48
|
+
// anything but an explicit false.
|
|
49
|
+
const flags: string[] = [];
|
|
50
|
+
if (o.check) flags.push('--check');
|
|
51
|
+
if (o.skipAndroid) flags.push('--skip-android');
|
|
52
|
+
if (o.skipIos) flags.push('--skip-ios');
|
|
53
|
+
if (o.profile === false) flags.push('--no-profile');
|
|
54
|
+
|
|
55
|
+
const outcome = runAppiumEnvironmentSetup(cwd, { flags });
|
|
56
|
+
// The bundle already printed its own per-step report and summary; exit non-zero so CI and a
|
|
57
|
+
// `&&` chain can act on a machine that is not ready.
|
|
58
|
+
if (outcome === 'failed') process.exitCode = 1;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('❌ setup-mobile-env failed:', error);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { registerInitCommand } from './commands/init';
|
|
|
9
9
|
import { registerAddCommand } from './commands/add';
|
|
10
10
|
import { registerGenerateCommand } from './commands/generate';
|
|
11
11
|
import { registerMakeauthCommand } from './commands/makeauth';
|
|
12
|
+
import { registerSetupMobileEnvCommand } from './commands/setup-mobile-env';
|
|
12
13
|
import { registerUpdateCommand } from './commands/update';
|
|
13
14
|
import { registerDeliveryCommand } from './commands/delivery';
|
|
14
15
|
import { registerFigmaCommand } from './commands/figma';
|
|
@@ -61,6 +62,7 @@ async function main() {
|
|
|
61
62
|
registerAddCommand(program);
|
|
62
63
|
registerGenerateCommand(program);
|
|
63
64
|
registerMakeauthCommand(program);
|
|
65
|
+
registerSetupMobileEnvCommand(program);
|
|
64
66
|
registerUpdateCommand(program);
|
|
65
67
|
registerDeliveryCommand(program);
|
|
66
68
|
registerFigmaCommand(program);
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
as contain-text-assertion), substring check on every match — no CSS filter() combinator
|
|
3
3
|
exists on Appium, so this loop replaces it. --}}
|
|
4
4
|
{
|
|
5
|
-
const __items_{{stepCounter}} =
|
|
6
|
-
if (__items_{{stepCounter}}.length === 0) throw new Error('[sungen] Expected at least one [{{escapeQuotes selectorRef}}]');
|
|
5
|
+
const __items_{{stepCounter}} = {{> await-elements mustExist=true}};
|
|
7
6
|
const __expected_{{stepCounter}} = String('{{escapeQuotes expectedText}}').trim();
|
|
8
7
|
for (const __el_{{stepCounter}} of __items_{{stepCounter}}) {
|
|
9
8
|
const __sources_{{stepCounter}} = driver.isIOS
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{{!-- Q2 all-card structural assertion (mobile): every container element must hold the child.
|
|
2
2
|
WebdriverIO's $$() returns a live element ARRAY, not a lazy Locator — there is no `.nth()`
|
|
3
3
|
(that is Playwright's API). Enumerate by index and chain off the array element itself via
|
|
4
|
-
`.$()` (WDIO findElementFromElement).
|
|
4
|
+
`.$()` (WDIO findElementFromElement).
|
|
5
|
+
|
|
6
|
+
`appium-selector-expr`, not a hand-quoted `appium-selector`: the expr partial emits its own
|
|
7
|
+
quotes, and it is the only form that can carry a per-OS `android:`/`ios:` entry (it renders
|
|
8
|
+
that as a runtime ternary). The raw partial silently emitted the Android side alone, so an
|
|
9
|
+
iOS run drove Android selectors; wrapping the expr output in quotes of our own instead
|
|
10
|
+
produces `$$(''~card'')`, a syntax error that costs the WHOLE .spec.ts, not just this step. --}}
|
|
5
11
|
{
|
|
6
|
-
const {{containerVar}} =
|
|
7
|
-
if ({{containerVar}}.length === 0) {
|
|
8
|
-
throw new Error('[sungen] Expected at least one [{{escapeQuotes selectorRef}}]');
|
|
9
|
-
}
|
|
12
|
+
const {{containerVar}} = {{> await-elements mustExist=true}};
|
|
10
13
|
for (let {{containerIndexVar}} = 0; {{containerIndexVar}} < {{containerVar}}.length; {{containerIndexVar}}++) {
|
|
11
14
|
await expect({{#with child}}{{> locator parentLocator=(add (add (add ../containerVar "[") ../containerIndexVar) "]")}}{{/with}}).toBeDisplayed();
|
|
12
15
|
}
|
package/src/generators/test-generator/adapters/appium/templates/steps/assertions/count-assertion.hbs
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
{{!-- `appium-selector-expr` emits its own quotes and is the only form that can carry a per-OS
|
|
2
|
+
`android:`/`ios:` entry (rendered as a runtime ternary). The raw partial silently emitted the
|
|
3
|
+
Android side alone; quoting the expr output by hand instead produces `$$(''~card'')`, a syntax
|
|
4
|
+
error that costs the whole .spec.ts. --}}
|
|
5
|
+
await expect($$({{> appium-selector-expr}})).toBeElementsArrayOfSize({{expectedCount}});
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
has no such exemption — absent is not disabled.) --}}
|
|
14
14
|
{
|
|
15
15
|
const __expected = String('{{escapeQuotes dataValue}}').trim();
|
|
16
|
-
const __items =
|
|
16
|
+
const __items = {{> await-elements mustExist=true}};
|
|
17
17
|
let __found = false;
|
|
18
18
|
for (const __el of __items) {
|
|
19
19
|
const __sources = driver.isIOS
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
{{#if name}}
|
|
6
6
|
{
|
|
7
7
|
const __expected = String('{{escapeQuotes dataValue}}').trim();
|
|
8
|
-
const __items =
|
|
8
|
+
const __items = {{> await-elements useRoleName=true mustExist=true}};
|
|
9
9
|
let __found = false;
|
|
10
10
|
for (const __el of __items) {
|
|
11
11
|
const __s = driver.isIOS ? [await __el.getAttribute('label'), await __el.getAttribute('value')] : [await __el.getText(), await __el.getAttribute('content-desc')];
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
is hand-rolled. Assert none of the matching elements is displayed. --}}
|
|
4
4
|
{
|
|
5
5
|
const __expected = String('{{escapeQuotes dataValue}}').trim();
|
|
6
|
-
const __items =
|
|
6
|
+
const __items = {{> await-elements}};
|
|
7
7
|
for (const __el of __items) {
|
|
8
8
|
const __sources = driver.isIOS
|
|
9
9
|
? [await __el.getAttribute('label'), await __el.getAttribute('value')]
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
{{#if name}}
|
|
7
7
|
{
|
|
8
8
|
const __expected = String('{{escapeQuotes dataValue}}').trim();
|
|
9
|
-
const __items =
|
|
9
|
+
const __items = {{> await-elements useRoleName=true}};
|
|
10
10
|
for (const __el of __items) {
|
|
11
11
|
const __s = driver.isIOS ? [await __el.getAttribute('label'), await __el.getAttribute('value')] : [await __el.getText(), await __el.getAttribute('content-desc')];
|
|
12
12
|
if (String(__s.find(s => s && String(s).trim() !== '') ?? '').trim() === __expected) await expect(__el).not.toBeDisplayed();
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
{{!-- Count the elements matching the list locator (mobile has no role="listitem").
|
|
2
|
-
|
|
1
|
+
{{!-- Count the elements matching the list locator (mobile has no role="listitem").
|
|
2
|
+
`appium-selector-expr` emits its own quotes and is the only form that can carry a per-OS
|
|
3
|
+
`android:`/`ios:` entry; quoting it by hand produced `$$(''~card'')`, a syntax error that
|
|
4
|
+
takes the whole .spec.ts down. --}}
|
|
5
|
+
await expect($$({{> appium-selector-expr}})).toBeElementsArrayOfSize({{expectedCount}});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
house style. --}}
|
|
8
8
|
{
|
|
9
9
|
const __expected = String('{{escapeQuotes dataValue}}').trim();
|
|
10
|
-
const __items =
|
|
10
|
+
const __items = {{> await-elements mustExist=true}};
|
|
11
11
|
let __el: WebdriverIO.Element | undefined;
|
|
12
12
|
for (const __c of __items) {
|
|
13
13
|
const __s = driver.isIOS ? [await __c.getAttribute('label'), await __c.getAttribute('value')] : [await __c.getText(), await __c.getAttribute('content-desc')];
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
{
|
|
8
8
|
const __expected = String('{{escapeQuotes dataValue}}').trim();
|
|
9
9
|
const __norm = (s: string) => s.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
10
|
-
const __items =
|
|
10
|
+
const __items = {{> await-elements mustExist=true}};
|
|
11
11
|
let __found = false;
|
|
12
12
|
for (const __el of __items) {
|
|
13
13
|
const __sources = driver.isIOS
|
package/src/generators/test-generator/adapters/appium/templates/steps/partials/await-elements.hbs
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{{~!--
|
|
2
|
+
The settled read behind every collection assertion on this adapter. `await $$(sel)` is a one-shot
|
|
3
|
+
read, so a loop over its result asserts nothing at all when the list has not painted yet — the
|
|
4
|
+
false-green class #672 closed on the Playwright side and left here.
|
|
5
|
+
|
|
6
|
+
Hash params:
|
|
7
|
+
mustExist the assertion is meaningless over an empty set (positive assertions). Omitted means
|
|
8
|
+
emptiness is a legitimate verdict (the negated `is hidden` forms).
|
|
9
|
+
useRoleName the two role-variable templates search by accessibility id built from `name:`
|
|
10
|
+
rather than through the selector entry.
|
|
11
|
+
--~}}
|
|
12
|
+
await __settleElements({{#if useRoleName}}'~{{escapeQuotes name}}'{{else}}{{> appium-selector-expr}}{{/if}}, {{#if mustExist}}true{{else}}false{{/if}}, '{{escapeQuotes selectorRef}}')
|
|
@@ -119,6 +119,32 @@ async function __ensureVisible(selector: string): Promise<WebdriverIO.Element> {
|
|
|
119
119
|
return $(selector);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// Read a live element ARRAY after giving the tree a chance to render.
|
|
123
|
+
//
|
|
124
|
+
// `await $$(sel)` is a ONE-SHOT read — unlike a Playwright Locator it does not re-query, so an
|
|
125
|
+
// assertion that loops over the result silently asserts NOTHING when the list has not painted yet:
|
|
126
|
+
// zero matches, zero iterations, green. #672 fixed that class on the Playwright side and left the
|
|
127
|
+
// appium adapter untouched, so every collection assertion here still raced the first render.
|
|
128
|
+
//
|
|
129
|
+
// `mustExist` says which verdict emptiness is. A positive assertion ("all of them contain X",
|
|
130
|
+
// "every match is disabled") is meaningless over an empty set, so it waits the full budget and
|
|
131
|
+
// then fails naming the reference. A negated one ("none of them is displayed") is legitimately
|
|
132
|
+
// satisfied by absence, so it only waits long enough to outlast a render race and then proceeds
|
|
133
|
+
// with whatever it found — waiting the full budget there would add 15s to every correct `is
|
|
134
|
+
// hidden` step, which is a worse trade than the race it would close.
|
|
135
|
+
async function __settleElements(
|
|
136
|
+
selector: string, mustExist: boolean, ref: string,
|
|
137
|
+
): Promise<WebdriverIO.Element[]> {
|
|
138
|
+
try {
|
|
139
|
+
await (await $(selector)).waitForExist({ timeout: mustExist ? 15000 : 3000 });
|
|
140
|
+
} catch {
|
|
141
|
+
if (mustExist) {
|
|
142
|
+
throw new Error(`[sungen] Expected at least one ${ref ? `[${ref}]` : `element matching ${selector}`} — none appeared within 15s`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return $$(selector);
|
|
146
|
+
}
|
|
147
|
+
|
|
122
148
|
// Visibility oracle for simple-case `see [X]` assertions. Android: WDIO's toBeDisplayed()
|
|
123
149
|
// (reliable on UiAutomator2). iOS: XCUITest's `visible` attribute false-negatives on real,
|
|
124
150
|
// on-screen elements (snapshot artifact — seen live even on a screen title every prior run
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
{{!-- `.first()` unless the entry pinned an index. A capture reads ONE element; without it,
|
|
2
|
+
Playwright's strict mode turned any selector matching more than one node into a hard failure
|
|
3
|
+
naming the count rather than the step — and the documented workaround was to add `nth: 0`,
|
|
4
|
+
i.e. to ask for exactly this. `{{> locator}}` already appends `.nth(n)` when `nth:` is
|
|
5
|
+
declared, so the two never stack. The compiler warns (SG-W036) when the selector is a SHAPE
|
|
6
|
+
that routinely matches a list, so "the first one" stays a decision rather than an accident. --}}
|
|
7
|
+
testData.set('{{varName}}', ((await {{> locator}}{{#unless (isDefined nth)}}.first(){{/unless}}.{{capture}}()) ?? '').trim());
|
|
@@ -1 +1,23 @@
|
|
|
1
|
-
{{!-- Column assertion scoped to the current row (`tableRow`, bound by the row-in-table entry step
|
|
1
|
+
{{!-- Column assertion scoped to the current row (`tableRow`, bound by the row-in-table entry step
|
|
2
|
+
that ran before this one).
|
|
3
|
+
|
|
4
|
+
`cellLocator` is the DECLARED cell — `columns.<col>.elements.cell` on the table entry, already
|
|
5
|
+
rendered against `tableRow`. It comes first because it is the only branch that works on a row
|
|
6
|
+
built from <div>s: those rows carry no ARIA `cell` role, so both branches below match zero
|
|
7
|
+
elements there, and `columns.<col>.index` cannot help — it indexes that same empty set. The
|
|
8
|
+
compiler warns (SG-W034) when a `rows:` shape is declared and no cell is.
|
|
9
|
+
|
|
10
|
+
columnIndex uses `isDefined` rather than a bare truthy check: column 0 is a valid index.
|
|
11
|
+
|
|
12
|
+
includeHidden + visible filter: when a modal (e.g. Radix Dialog) is open the background gets
|
|
13
|
+
aria-hidden="true", which empties plain getByRole() even though the cells are still rendered
|
|
14
|
+
— measure what the user SEES, not the a11y tree. --}}
|
|
15
|
+
{{~#if cellLocator~}}
|
|
16
|
+
{{#if isGiven}}await {{cellLocator}}.filter({ hasText: '{{escapeQuotes cellValue}}' }).waitFor();{{else}}await expect({{cellLocator}}).toHaveText('{{escapeQuotes cellValue}}');{{/if}}
|
|
17
|
+
{{~else if (isDefined columnIndex)~}}
|
|
18
|
+
await expect(tableRow.getByRole('cell', { includeHidden: true }).filter({ visible: true }).nth({{columnIndex}})).toHaveText('{{escapeQuotes cellValue}}');
|
|
19
|
+
{{~else if isGiven~}}
|
|
20
|
+
await tableRow.getByRole('cell', { includeHidden: true }).filter({ hasText: '{{escapeQuotes cellValue}}' }).filter({ visible: true }).waitFor();
|
|
21
|
+
{{~else~}}
|
|
22
|
+
await expect(tableRow.getByRole('cell', { includeHidden: true }).filter({ hasText: '{{escapeQuotes cellValue}}' }).filter({ visible: true })).toBeVisible();
|
|
23
|
+
{{~/if~}}
|