swipium 1.4.0 → 2.0.0
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/CHANGELOG.md +313 -0
- package/README.md +175 -219
- package/THREAT_MODEL.md +223 -47
- package/dist/appMap/automationLink.js +39 -26
- package/dist/appMap/automationLink.js.map +1 -1
- package/dist/appMap/build.js +68 -20
- package/dist/appMap/build.js.map +1 -1
- package/dist/appMap/codeIndex.js +31 -5
- package/dist/appMap/codeIndex.js.map +1 -1
- package/dist/appMap/featureIndex.js +23 -5
- package/dist/appMap/featureIndex.js.map +1 -1
- package/dist/appMap/featureModel.js +44 -8
- package/dist/appMap/featureModel.js.map +1 -1
- package/dist/appMap/firstRunApply.js +24 -6
- package/dist/appMap/firstRunApply.js.map +1 -1
- package/dist/appMap/fsWalk.js +19 -2
- package/dist/appMap/fsWalk.js.map +1 -1
- package/dist/appMap/issues.js +10 -30
- package/dist/appMap/issues.js.map +1 -1
- package/dist/appMap/migrations.js +6 -6
- package/dist/appMap/migrations.js.map +1 -1
- package/dist/appMap/prelaunch.js +2 -2
- package/dist/appMap/prelaunch.js.map +1 -1
- package/dist/appMap/projectRegistry.js +83 -11
- package/dist/appMap/projectRegistry.js.map +1 -1
- package/dist/appMap/provenance.js +2 -2
- package/dist/appMap/provenance.js.map +1 -1
- package/dist/appMap/query.js +21 -5
- package/dist/appMap/query.js.map +1 -1
- package/dist/appMap/runtimeMerge.js +12 -5
- package/dist/appMap/runtimeMerge.js.map +1 -1
- package/dist/appMap/schema.js +1 -1
- package/dist/appMap/schema.js.map +1 -1
- package/dist/appMap/screenMatch.js +6 -2
- package/dist/appMap/screenMatch.js.map +1 -1
- package/dist/appMap/staticScan.js +151 -39
- package/dist/appMap/staticScan.js.map +1 -1
- package/dist/appMap/store.js +58 -85
- package/dist/appMap/store.js.map +1 -1
- package/dist/appMap/tsAstScan.js +34 -12
- package/dist/appMap/tsAstScan.js.map +1 -1
- package/dist/artifacts/bundletool.js +49 -12
- package/dist/artifacts/bundletool.js.map +1 -1
- package/dist/artifacts/resolve.js +41 -14
- package/dist/artifacts/resolve.js.map +1 -1
- package/dist/automation/capabilities.js +112 -30
- package/dist/automation/capabilities.js.map +1 -1
- package/dist/automation/gestures.js +6 -2
- package/dist/automation/gestures.js.map +1 -1
- package/dist/automation/plan.js +71 -23
- package/dist/automation/plan.js.map +1 -1
- package/dist/automation/report.js +15 -5
- package/dist/automation/report.js.map +1 -1
- package/dist/automation/selectors.js +22 -28
- package/dist/automation/selectors.js.map +1 -1
- package/dist/automation/types.js +1 -1
- package/dist/automation/types.js.map +1 -1
- package/dist/automation/waits.js +14 -8
- package/dist/automation/waits.js.map +1 -1
- package/dist/automation/webview.js +9 -3
- package/dist/automation/webview.js.map +1 -1
- package/dist/automationGen/appiumModel.js +16 -8
- package/dist/automationGen/appiumModel.js.map +1 -1
- package/dist/automationGen/ciEmitter.js +6 -6
- package/dist/automationGen/ciEmitter.js.map +1 -1
- package/dist/automationGen/identifiers.js +224 -0
- package/dist/automationGen/identifiers.js.map +1 -0
- package/dist/automationGen/jsEmitter.js +288 -121
- package/dist/automationGen/jsEmitter.js.map +1 -1
- package/dist/automationGen/packagePatch.js +2 -2
- package/dist/automationGen/packagePatch.js.map +1 -1
- package/dist/automationGen/platformResolve.js +36 -0
- package/dist/automationGen/platformResolve.js.map +1 -0
- package/dist/automationGen/projectProfile.js +76 -27
- package/dist/automationGen/projectProfile.js.map +1 -1
- package/dist/automationGen/pythonEmitter.js +278 -107
- package/dist/automationGen/pythonEmitter.js.map +1 -1
- package/dist/automationGen/readmeEmitter.js +7 -6
- package/dist/automationGen/readmeEmitter.js.map +1 -1
- package/dist/automationGen/run.js +388 -0
- package/dist/automationGen/run.js.map +1 -0
- package/dist/automationGen/suitePlan.js +17 -10
- package/dist/automationGen/suitePlan.js.map +1 -1
- package/dist/automationGen/validation.js +52 -12
- package/dist/automationGen/validation.js.map +1 -1
- package/dist/build/parseBuildLog.js +235 -31
- package/dist/build/parseBuildLog.js.map +1 -1
- package/dist/build/plan.js +39 -18
- package/dist/build/plan.js.map +1 -1
- package/dist/ci/preflight.js +13 -4
- package/dist/ci/preflight.js.map +1 -1
- package/dist/cli/gc.js +74 -0
- package/dist/cli/gc.js.map +1 -0
- package/dist/cli/init.js +217 -26
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/main.js +50 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/report.js +218 -0
- package/dist/cli/report.js.map +1 -0
- package/dist/cli/scan.js +36 -15
- package/dist/cli/scan.js.map +1 -1
- package/dist/cli/suite.js +7 -7
- package/dist/cli/suite.js.map +1 -1
- package/dist/cli/verify.js +7 -7
- package/dist/cli/verify.js.map +1 -1
- package/dist/consent/consent.js +162 -5
- package/dist/consent/consent.js.map +1 -1
- package/dist/context/detect.js +55 -18
- package/dist/context/detect.js.map +1 -1
- package/dist/context/findApps.js +22 -8
- package/dist/context/findApps.js.map +1 -1
- package/dist/context/projectRoot.js +174 -19
- package/dist/context/projectRoot.js.map +1 -1
- package/dist/context/scan.js +1 -1
- package/dist/context/scan.js.map +1 -1
- package/dist/core/capabilityGroups.js +90 -0
- package/dist/core/capabilityGroups.js.map +1 -0
- package/dist/core/target.js +10 -8
- package/dist/core/target.js.map +1 -1
- package/dist/core/targetPlan.js +160 -39
- package/dist/core/targetPlan.js.map +1 -1
- package/dist/drivers/DirectDriver.js +317 -43
- package/dist/drivers/DirectDriver.js.map +1 -1
- package/dist/drivers/SimctlDriver.js +124 -8
- package/dist/drivers/SimctlDriver.js.map +1 -1
- package/dist/drivers/WdaDriver.js +365 -55
- package/dist/drivers/WdaDriver.js.map +1 -1
- package/dist/explore/candidates.js +7 -3
- package/dist/explore/candidates.js.map +1 -1
- package/dist/explore/graph.js +15 -6
- package/dist/explore/graph.js.map +1 -1
- package/dist/explore/memory.js +4 -1
- package/dist/explore/memory.js.map +1 -1
- package/dist/explore/planner.js +54 -9
- package/dist/explore/planner.js.map +1 -1
- package/dist/explore/policy.js +11 -6
- package/dist/explore/policy.js.map +1 -1
- package/dist/explore/runner.js +363 -190
- package/dist/explore/runner.js.map +1 -1
- package/dist/explore/signatures.js +4 -2
- package/dist/explore/signatures.js.map +1 -1
- package/dist/explore/suite.js +3 -1
- package/dist/explore/suite.js.map +1 -1
- package/dist/featureTesting/executionBootstrap.js +137 -29
- package/dist/featureTesting/executionBootstrap.js.map +1 -1
- package/dist/featureTesting/featureMap.js +6 -3
- package/dist/featureTesting/featureMap.js.map +1 -1
- package/dist/featureTesting/featureScope.js +101 -20
- package/dist/featureTesting/featureScope.js.map +1 -1
- package/dist/featureTesting/mapFeatureScope.js +44 -15
- package/dist/featureTesting/mapFeatureScope.js.map +1 -1
- package/dist/featureTesting/objectiveModel.js +15 -11
- package/dist/featureTesting/objectiveModel.js.map +1 -1
- package/dist/featureTesting/resultMerge.js +16 -8
- package/dist/featureTesting/resultMerge.js.map +1 -1
- package/dist/featureTesting/sources.js +8 -5
- package/dist/featureTesting/sources.js.map +1 -1
- package/dist/featureTesting/suiteBridge.js +8 -2
- package/dist/featureTesting/suiteBridge.js.map +1 -1
- package/dist/featureTesting/synonyms.js +32 -4
- package/dist/featureTesting/synonyms.js.map +1 -1
- package/dist/featureTesting/testCaseFactory.js +27 -15
- package/dist/featureTesting/testCaseFactory.js.map +1 -1
- package/dist/featureTesting/testPlan.js +34 -12
- package/dist/featureTesting/testPlan.js.map +1 -1
- package/dist/firstRun/authStateMachine.js +1 -1
- package/dist/firstRun/authStateMachine.js.map +1 -1
- package/dist/firstRun/classifyScreen.js +9 -6
- package/dist/firstRun/classifyScreen.js.map +1 -1
- package/dist/firstRun/firstRunPlanner.js +16 -13
- package/dist/firstRun/firstRunPlanner.js.map +1 -1
- package/dist/firstRun/firstRunRunner.js +92 -31
- package/dist/firstRun/firstRunRunner.js.map +1 -1
- package/dist/firstRun/generatedDataPolicy.js +25 -15
- package/dist/firstRun/generatedDataPolicy.js.map +1 -1
- package/dist/firstRun/inputPlanner.js +45 -11
- package/dist/firstRun/inputPlanner.js.map +1 -1
- package/dist/firstRun/onboardingStateMachine.js +1 -1
- package/dist/firstRun/onboardingStateMachine.js.map +1 -1
- package/dist/firstRun/paywallPolicy.js +3 -3
- package/dist/firstRun/paywallPolicy.js.map +1 -1
- package/dist/firstRun/types.js +1 -1
- package/dist/firstRun/types.js.map +1 -1
- package/dist/fixtures/catalog.js +72 -9
- package/dist/fixtures/catalog.js.map +1 -1
- package/dist/fixtures/load.js +50 -0
- package/dist/fixtures/load.js.map +1 -0
- package/dist/flows/discover.js +2 -2
- package/dist/flows/discover.js.map +1 -1
- package/dist/flows/generate.js +68 -17
- package/dist/flows/generate.js.map +1 -1
- package/dist/flows/lint.js +88 -14
- package/dist/flows/lint.js.map +1 -1
- package/dist/flows/pack.js +21 -5
- package/dist/flows/pack.js.map +1 -1
- package/dist/flows/paths.js +57 -0
- package/dist/flows/paths.js.map +1 -0
- package/dist/flows/repair.js +176 -37
- package/dist/flows/repair.js.map +1 -1
- package/dist/flows/run.js +308 -99
- package/dist/flows/run.js.map +1 -1
- package/dist/flows/schema.js +107 -25
- package/dist/flows/schema.js.map +1 -1
- package/dist/flows/seedExec.js +8 -4
- package/dist/flows/seedExec.js.map +1 -1
- package/dist/flows/templates.js +5 -5
- package/dist/index.js +50 -8
- package/dist/index.js.map +1 -1
- package/dist/ios/signing.js +32 -3
- package/dist/ios/signing.js.map +1 -1
- package/dist/issues/classify.js +28 -20
- package/dist/issues/classify.js.map +1 -1
- package/dist/issues/fingerprint.js +35 -9
- package/dist/issues/fingerprint.js.map +1 -1
- package/dist/issues/index.js +93 -46
- package/dist/issues/index.js.map +1 -1
- package/dist/issues/metrics.js +6 -5
- package/dist/issues/metrics.js.map +1 -1
- package/dist/issues/recurrence.js +35 -8
- package/dist/issues/recurrence.js.map +1 -1
- package/dist/issues/report.js +12 -8
- package/dist/issues/report.js.map +1 -1
- package/dist/issues/reportBridge.js +40 -9
- package/dist/issues/reportBridge.js.map +1 -1
- package/dist/issues/schema.js +2 -2
- package/dist/issues/schema.js.map +1 -1
- package/dist/issues/sourceRevision.js +2 -2
- package/dist/issues/sourceRevision.js.map +1 -1
- package/dist/issues/store.js +110 -32
- package/dist/issues/store.js.map +1 -1
- package/dist/lib/abortScope.js +48 -0
- package/dist/lib/abortScope.js.map +1 -0
- package/dist/lib/android.js +84 -8
- package/dist/lib/android.js.map +1 -1
- package/dist/lib/coordSpace.js +1 -1
- package/dist/lib/coordSpace.js.map +1 -1
- package/dist/lib/device.js +53 -12
- package/dist/lib/device.js.map +1 -1
- package/dist/lib/gestures.js +141 -0
- package/dist/lib/gestures.js.map +1 -0
- package/dist/lib/gitignore.js +4 -3
- package/dist/lib/gitignore.js.map +1 -1
- package/dist/lib/image.js +12 -3
- package/dist/lib/image.js.map +1 -1
- package/dist/lib/lockfile.js +399 -0
- package/dist/lib/lockfile.js.map +1 -0
- package/dist/lib/needsInput.js +9 -6
- package/dist/lib/needsInput.js.map +1 -1
- package/dist/lib/png.js +4 -3
- package/dist/lib/png.js.map +1 -1
- package/dist/lib/redact.js +170 -9
- package/dist/lib/redact.js.map +1 -1
- package/dist/lib/result.js +94 -10
- package/dist/lib/result.js.map +1 -1
- package/dist/lib/schemaHash.js +7 -5
- package/dist/lib/schemaHash.js.map +1 -1
- package/dist/lib/sensitive.js +6 -3
- package/dist/lib/sensitive.js.map +1 -1
- package/dist/lib/simctl.js +3 -3
- package/dist/lib/simctl.js.map +1 -1
- package/dist/lib/spawn.js +78 -17
- package/dist/lib/spawn.js.map +1 -1
- package/dist/lib/toolAnnotations.js +114 -0
- package/dist/lib/toolAnnotations.js.map +1 -0
- package/dist/lib/wda.js +321 -25
- package/dist/lib/wda.js.map +1 -1
- package/dist/lib/wdaConfig.js +12 -5
- package/dist/lib/wdaConfig.js.map +1 -1
- package/dist/lib/wdaTune.js +52 -23
- package/dist/lib/wdaTune.js.map +1 -1
- package/dist/mobileAudit/checks.js +150 -28
- package/dist/mobileAudit/checks.js.map +1 -1
- package/dist/mobileAudit/evidence.js +4 -4
- package/dist/mobileAudit/evidence.js.map +1 -1
- package/dist/mobileAudit/profiles.js +121 -20
- package/dist/mobileAudit/profiles.js.map +1 -1
- package/dist/mobileAudit/results.js +9 -4
- package/dist/mobileAudit/results.js.map +1 -1
- package/dist/mobileAudit/runner.js +214 -35
- package/dist/mobileAudit/runner.js.map +1 -1
- package/dist/oracle/auth.js +1 -1
- package/dist/oracle/auth.js.map +1 -1
- package/dist/oracle/failures.js +1085 -145
- package/dist/oracle/failures.js.map +1 -1
- package/dist/oracle/health.js +148 -26
- package/dist/oracle/health.js.map +1 -1
- package/dist/oracle/locator.js +148 -19
- package/dist/oracle/locator.js.map +1 -1
- package/dist/oracle/record.js +3 -3
- package/dist/oracle/record.js.map +1 -1
- package/dist/orchestration/envelope.js +2 -2
- package/dist/orchestration/envelope.js.map +1 -1
- package/dist/orchestration/goal.js +7 -13
- package/dist/orchestration/goal.js.map +1 -1
- package/dist/orchestration/testThis/execute.js +167 -0
- package/dist/orchestration/testThis/execute.js.map +1 -0
- package/dist/orchestration/testThis/pipeline.js +413 -0
- package/dist/orchestration/testThis/pipeline.js.map +1 -0
- package/dist/orchestration/testThis/plan.js +519 -0
- package/dist/orchestration/testThis/plan.js.map +1 -0
- package/dist/orchestration/testThis/sessionIntent.js +72 -0
- package/dist/orchestration/testThis/sessionIntent.js.map +1 -0
- package/dist/orchestration/testThis/terminal.js +136 -0
- package/dist/orchestration/testThis/terminal.js.map +1 -0
- package/dist/orchestration/testThis/types.js +12 -0
- package/dist/orchestration/testThis/types.js.map +1 -0
- package/dist/plan/plan.js +9 -9
- package/dist/plan/plan.js.map +1 -1
- package/dist/prompts/index.js +24 -24
- package/dist/prompts/index.js.map +1 -1
- package/dist/report/coverage.js +0 -1
- package/dist/report/coverage.js.map +1 -1
- package/dist/report/evidence.js.map +1 -1
- package/dist/report/export.js +449 -72
- package/dist/report/export.js.map +1 -1
- package/dist/report/findingsDedupe.js +41 -0
- package/dist/report/findingsDedupe.js.map +1 -0
- package/dist/report/flake.js +13 -14
- package/dist/report/flake.js.map +1 -1
- package/dist/report/history.js +72 -38
- package/dist/report/history.js.map +1 -1
- package/dist/report/policy.js +18 -7
- package/dist/report/policy.js.map +1 -1
- package/dist/report/qaLevel.js +10 -16
- package/dist/report/qaLevel.js.map +1 -1
- package/dist/report/readiness.js +42 -12
- package/dist/report/readiness.js.map +1 -1
- package/dist/report/sarifSources.js +160 -0
- package/dist/report/sarifSources.js.map +1 -0
- package/dist/report/summary.js +2 -2
- package/dist/report/summary.js.map +1 -1
- package/dist/report/testCatalog.js +4 -4
- package/dist/report/testCatalog.js.map +1 -1
- package/dist/report/timing.js +23 -23
- package/dist/report/timing.js.map +1 -1
- package/dist/report/toolHealth.js +124 -0
- package/dist/report/toolHealth.js.map +1 -0
- package/dist/server.js +501 -69
- package/dist/server.js.map +1 -1
- package/dist/services/automationGenerate.js +8 -2
- package/dist/services/automationGenerate.js.map +1 -1
- package/dist/services/build.js +10 -4
- package/dist/services/build.js.map +1 -1
- package/dist/services/flowGenerate.js +75 -0
- package/dist/services/flowGenerate.js.map +1 -0
- package/dist/services/preflight.js +40 -8
- package/dist/services/preflight.js.map +1 -1
- package/dist/services/prepareAndroid.js +92 -18
- package/dist/services/prepareAndroid.js.map +1 -1
- package/dist/services/prepareIos.js +94 -17
- package/dist/services/prepareIos.js.map +1 -1
- package/dist/services/report.js +271 -80
- package/dist/services/report.js.map +1 -1
- package/dist/services/smoke.js +68 -22
- package/dist/services/smoke.js.map +1 -1
- package/dist/services/suiteGenerate.js +43 -6
- package/dist/services/suiteGenerate.js.map +1 -1
- package/dist/services/testSuiteKnowledge.js +78 -33
- package/dist/services/testSuiteKnowledge.js.map +1 -1
- package/dist/session/attach.js +284 -4
- package/dist/session/attach.js.map +1 -1
- package/dist/session/processRegistry.js +433 -0
- package/dist/session/processRegistry.js.map +1 -0
- package/dist/session/progress.js +1 -1
- package/dist/session/progress.js.map +1 -1
- package/dist/session/retention.js +201 -0
- package/dist/session/retention.js.map +1 -0
- package/dist/session/store.js +459 -59
- package/dist/session/store.js.map +1 -1
- package/dist/snapshot/overlays.js +60 -10
- package/dist/snapshot/overlays.js.map +1 -1
- package/dist/snapshot/parse.js +30 -12
- package/dist/snapshot/parse.js.map +1 -1
- package/dist/snapshot/present.js +36 -6
- package/dist/snapshot/present.js.map +1 -1
- package/dist/snapshot/settle.js +39 -9
- package/dist/snapshot/settle.js.map +1 -1
- package/dist/state/consent.js +99 -0
- package/dist/state/consent.js.map +1 -0
- package/dist/state/profile.js +82 -11
- package/dist/state/profile.js.map +1 -1
- package/dist/suite/compile.js +15 -6
- package/dist/suite/compile.js.map +1 -1
- package/dist/suite/lint.js +22 -4
- package/dist/suite/lint.js.map +1 -1
- package/dist/suite/pom.js +75 -44
- package/dist/suite/pom.js.map +1 -1
- package/dist/suite/secretGuard.js +266 -0
- package/dist/suite/secretGuard.js.map +1 -0
- package/dist/suite/testcase.js +15 -11
- package/dist/suite/testcase.js.map +1 -1
- package/dist/testSuite/exporter.js +7 -3
- package/dist/testSuite/exporter.js.map +1 -1
- package/dist/testSuite/generator.js +43 -14
- package/dist/testSuite/generator.js.map +1 -1
- package/dist/testSuite/history.js +1 -1
- package/dist/testSuite/history.js.map +1 -1
- package/dist/testSuite/issueLinks.js +3 -3
- package/dist/testSuite/issueLinks.js.map +1 -1
- package/dist/testSuite/lint.js +4 -4
- package/dist/testSuite/lint.js.map +1 -1
- package/dist/testSuite/merge.js +13 -7
- package/dist/testSuite/merge.js.map +1 -1
- package/dist/testSuite/schema.js +21 -16
- package/dist/testSuite/schema.js.map +1 -1
- package/dist/testSuite/store.js +12 -4
- package/dist/testSuite/store.js.map +1 -1
- package/dist/testSuite/traceability.js +2 -2
- package/dist/testSuite/traceability.js.map +1 -1
- package/dist/tools/act.js +868 -174
- package/dist/tools/act.js.map +1 -1
- package/dist/tools/agent.js +521 -112
- package/dist/tools/agent.js.map +1 -1
- package/dist/tools/appControl.js +82 -19
- package/dist/tools/appControl.js.map +1 -1
- package/dist/tools/appMap.js +393 -189
- package/dist/tools/appMap.js.map +1 -1
- package/dist/tools/build.js +92 -51
- package/dist/tools/build.js.map +1 -1
- package/dist/tools/bundletool.js +141 -40
- package/dist/tools/bundletool.js.map +1 -1
- package/dist/tools/clearOverlay.js +98 -18
- package/dist/tools/clearOverlay.js.map +1 -1
- package/dist/tools/device.js +108 -19
- package/dist/tools/device.js.map +1 -1
- package/dist/tools/doctor.js +98 -35
- package/dist/tools/doctor.js.map +1 -1
- package/dist/tools/explore.js +110 -42
- package/dist/tools/explore.js.map +1 -1
- package/dist/tools/featureTesting.js +152 -130
- package/dist/tools/featureTesting.js.map +1 -1
- package/dist/tools/firstRun.js +94 -66
- package/dist/tools/firstRun.js.map +1 -1
- package/dist/tools/flow.js +347 -132
- package/dist/tools/flow.js.map +1 -1
- package/dist/tools/flowRepair.js +42 -10
- package/dist/tools/flowRepair.js.map +1 -1
- package/dist/tools/generate.js +199 -0
- package/dist/tools/generate.js.map +1 -0
- package/dist/tools/getArtifact.js +37 -8
- package/dist/tools/getArtifact.js.map +1 -1
- package/dist/tools/health.js +17 -12
- package/dist/tools/health.js.map +1 -1
- package/dist/tools/ios.js +148 -194
- package/dist/tools/ios.js.map +1 -1
- package/dist/tools/issues.js +256 -171
- package/dist/tools/issues.js.map +1 -1
- package/dist/tools/jobs.js +55 -11
- package/dist/tools/jobs.js.map +1 -1
- package/dist/tools/metro.js +84 -38
- package/dist/tools/metro.js.map +1 -1
- package/dist/tools/mobileAudit.js +83 -26
- package/dist/tools/mobileAudit.js.map +1 -1
- package/dist/tools/network.js +36 -14
- package/dist/tools/network.js.map +1 -1
- package/dist/tools/note.js +31 -17
- package/dist/tools/note.js.map +1 -1
- package/dist/tools/prepareIosTarget.js +31 -14
- package/dist/tools/prepareIosTarget.js.map +1 -1
- package/dist/tools/prepareTarget.js +193 -47
- package/dist/tools/prepareTarget.js.map +1 -1
- package/dist/tools/report.js +16 -8
- package/dist/tools/report.js.map +1 -1
- package/dist/tools/resolveArtifact.js +12 -12
- package/dist/tools/resolveArtifact.js.map +1 -1
- package/dist/tools/resolveTarget.js +81 -18
- package/dist/tools/resolveTarget.js.map +1 -1
- package/dist/tools/screenRecord.js +85 -20
- package/dist/tools/screenRecord.js.map +1 -1
- package/dist/tools/screenshot.js +42 -18
- package/dist/tools/screenshot.js.map +1 -1
- package/dist/tools/smoke.js +29 -12
- package/dist/tools/smoke.js.map +1 -1
- package/dist/tools/snapshot.js +157 -107
- package/dist/tools/snapshot.js.map +1 -1
- package/dist/tools/startSession.js +117 -84
- package/dist/tools/startSession.js.map +1 -1
- package/dist/tools/suite.js +391 -310
- package/dist/tools/suite.js.map +1 -1
- package/dist/tools/testSuite.js +112 -36
- package/dist/tools/testSuite.js.map +1 -1
- package/dist/tools/testThis.js +35 -802
- package/dist/tools/testThis.js.map +1 -1
- package/dist/tools/visual.js +698 -116
- package/dist/tools/visual.js.map +1 -1
- package/dist/tools/wait.js +9 -23
- package/dist/tools/wait.js.map +1 -1
- package/dist/tools/wda.js +312 -54
- package/dist/tools/wda.js.map +1 -1
- package/dist/version.js +37 -64
- package/dist/version.js.map +1 -1
- package/dist/visual/ocr.js +65 -8
- package/dist/visual/ocr.js.map +1 -1
- package/dist/visual/provider.js +87 -15
- package/dist/visual/provider.js.map +1 -1
- package/docs/README.md +24 -10
- package/docs/ci-reports.md +328 -0
- package/docs/concepts.md +227 -0
- package/docs/flows.md +153 -0
- package/docs/mcp-server.md +210 -104
- package/docs/physical-devices.md +84 -0
- package/docs/tools.md +808 -217
- package/package.json +16 -6
- package/dist/appMap/diff.js +0 -60
- package/dist/appMap/diff.js.map +0 -1
- package/dist/automation/maestroIr.js +0 -282
- package/dist/automation/maestroIr.js.map +0 -1
- package/dist/interop/maestro.js +0 -151
- package/dist/interop/maestro.js.map +0 -1
- package/dist/tools/assertVisual.js +0 -74
- package/dist/tools/assertVisual.js.map +0 -1
- package/dist/tools/automationGenerate.js +0 -394
- package/dist/tools/automationGenerate.js.map +0 -1
- package/dist/tools/capabilities.js +0 -209
- package/dist/tools/capabilities.js.map +0 -1
- package/dist/tools/detectContext.js +0 -41
- package/dist/tools/detectContext.js.map +0 -1
- package/dist/tools/flowGenerate.js +0 -55
- package/dist/tools/flowGenerate.js.map +0 -1
- package/dist/tools/history.js +0 -56
- package/dist/tools/history.js.map +0 -1
- package/dist/tools/idling.js +0 -104
- package/dist/tools/idling.js.map +0 -1
- package/dist/tools/inputCapabilities.js +0 -30
- package/dist/tools/inputCapabilities.js.map +0 -1
- package/dist/tools/locator.js +0 -57
- package/dist/tools/locator.js.map +0 -1
- package/dist/tools/maestro.js +0 -78
- package/dist/tools/maestro.js.map +0 -1
- package/dist/tools/permissions.js +0 -149
- package/dist/tools/permissions.js.map +0 -1
- package/dist/tools/plan.js +0 -49
- package/dist/tools/plan.js.map +0 -1
- package/dist/tools/screenInfo.js +0 -70
- package/dist/tools/screenInfo.js.map +0 -1
- package/dist/tools/seed.js +0 -101
- package/dist/tools/seed.js.map +0 -1
- package/dist/tools/state.js +0 -170
- package/dist/tools/state.js.map +0 -1
- package/dist/tools/visualText.js +0 -61
- package/dist/tools/visualText.js.map +0 -1
|
@@ -1,16 +1,75 @@
|
|
|
1
|
-
//
|
|
1
|
+
// JS/TS WebdriverIO + Appium emitter. Pure: turns the shared Appium model into a
|
|
2
2
|
// runnable WebdriverIO POM suite (page/screen classes with locator getters + action methods, env-
|
|
3
3
|
// driven capabilities, structured waits). No XPath is ever emitted; coordinate fallbacks are marked
|
|
4
4
|
// non-release-grade. Aligns with the Appium JS quickstart (WebdriverIO) locator-strategy guidance.
|
|
5
|
+
import { defaultPlatformOf } from './platformResolve.js';
|
|
6
|
+
import { NameAllocator, UnemittableStepError, checkDirection, checkKey, className, commentSafe, isJsIdentifier, jsMemberName, pascalWords, screensOf, } from './identifiers.js';
|
|
5
7
|
const DEFAULT_TIMEOUT = 15000;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
const MAX_SCROLLS = 8;
|
|
9
|
+
/** Every member BaseScreen declares. A generated field/method must never shadow one (a field named
|
|
10
|
+
* `tap` would replace the tap() helper, and in TS it is a type error). */
|
|
11
|
+
const JS_BASE_MEMBERS = [
|
|
12
|
+
'constructor',
|
|
13
|
+
'element',
|
|
14
|
+
'tap',
|
|
15
|
+
'type',
|
|
16
|
+
'typeFocused',
|
|
17
|
+
'scrollTo',
|
|
18
|
+
'scrollToText',
|
|
19
|
+
'scrollOnce',
|
|
20
|
+
'isVisible',
|
|
21
|
+
'assertVisible',
|
|
22
|
+
'assertTextVisible',
|
|
23
|
+
'textSelector',
|
|
24
|
+
'tapAt',
|
|
25
|
+
'pressKey',
|
|
26
|
+
'swipe',
|
|
27
|
+
'openUrl',
|
|
28
|
+
];
|
|
29
|
+
function methodKey(action, element) {
|
|
30
|
+
return `${action}|${element}`;
|
|
9
31
|
}
|
|
10
|
-
|
|
32
|
+
function planJsNames(model) {
|
|
33
|
+
const classes = new NameAllocator(['BaseScreen', 'TestData', 'PlatformLocator'], (s) => s.toLowerCase());
|
|
34
|
+
const instances = new NameAllocator(['testData', 'driver', 'browser', 'describe', 'it', 'expect', '$', '$$']);
|
|
35
|
+
const plan = new Map();
|
|
36
|
+
for (const screen of screensOf(model)) {
|
|
37
|
+
const cls = classes.alloc(className(screen.className));
|
|
38
|
+
const instance = instances.alloc(cls[0].toLowerCase() + cls.slice(1));
|
|
39
|
+
const members = new NameAllocator(JS_BASE_MEMBERS);
|
|
40
|
+
const fields = new Map();
|
|
41
|
+
for (const el of screen.elements)
|
|
42
|
+
if (!fields.has(el.name))
|
|
43
|
+
fields.set(el.name, members.alloc(jsMemberName(el.name)));
|
|
44
|
+
const methods = new Map();
|
|
45
|
+
for (const s of model.steps) {
|
|
46
|
+
if (s.screen !== screen.className || !s.element)
|
|
47
|
+
continue;
|
|
48
|
+
const field = fields.get(s.element);
|
|
49
|
+
if (!field) {
|
|
50
|
+
throw new UnemittableStepError(`cannot emit ${s.action} step on ${screen.className}: element ${JSON.stringify(s.element)} is not declared on that screen`);
|
|
51
|
+
}
|
|
52
|
+
const key = methodKey(s.action, s.element);
|
|
53
|
+
if (methods.has(key))
|
|
54
|
+
continue;
|
|
55
|
+
methods.set(key, members.alloc(methodBase(s.action, pascalWords(field) || 'Element')));
|
|
56
|
+
}
|
|
57
|
+
plan.set(screen.className, { cls, instance, fields, methods });
|
|
58
|
+
}
|
|
59
|
+
return plan;
|
|
60
|
+
}
|
|
61
|
+
function jsProp(obj, key) {
|
|
62
|
+
return isJsIdentifier(key) ? `${obj}.${key}` : `${obj}[${JSON.stringify(key)}]`;
|
|
63
|
+
}
|
|
64
|
+
/** Env var (SWIPIUM_TEST_PASSWORD) > a stable testData key (password). */
|
|
11
65
|
export function varToKey(v) {
|
|
12
66
|
const stripped = v.replace(/^SWIPIUM_(TEST_)?/i, '');
|
|
13
|
-
const parts = stripped
|
|
67
|
+
const parts = stripped
|
|
68
|
+
.replace(/[^A-Za-z0-9]+/g, ' ')
|
|
69
|
+
.trim()
|
|
70
|
+
.toLowerCase()
|
|
71
|
+
.split(/\s+/)
|
|
72
|
+
.filter(Boolean);
|
|
14
73
|
if (!parts.length)
|
|
15
74
|
return 'value';
|
|
16
75
|
return parts.map((p, i) => (i === 0 ? p : p[0].toUpperCase() + p.slice(1))).join('');
|
|
@@ -51,8 +110,11 @@ function platformLocatorLiteral(el) {
|
|
|
51
110
|
export function emitJsSuite(input) {
|
|
52
111
|
const ts = input.language === 'typescript';
|
|
53
112
|
const ext = ts ? 'ts' : 'js';
|
|
54
|
-
const t = (annotation) => (ts ? annotation : '');
|
|
55
113
|
const files = [];
|
|
114
|
+
// Plan every class/field/method name up front (H4) and validate every step is emittable (§4);
|
|
115
|
+
// generation fails loudly here rather than writing a suite with silent no-op steps.
|
|
116
|
+
const names = planJsNames(input.model);
|
|
117
|
+
const smoke = smokeTest(input.model, names);
|
|
56
118
|
files.push({ path: 'package.json', content: packageJson(input) });
|
|
57
119
|
if (ts)
|
|
58
120
|
files.push({ path: 'tsconfig.json', content: tsconfigJson() });
|
|
@@ -61,11 +123,12 @@ export function emitJsSuite(input) {
|
|
|
61
123
|
files.push({ path: `src/utils/locators.${ext}`, content: locatorsUtil(ts) });
|
|
62
124
|
files.push({ path: `src/utils/waits.${ext}`, content: waitsUtil(ts) });
|
|
63
125
|
files.push({ path: `src/screens/BaseScreen.${ext}`, content: baseScreen(ts) });
|
|
64
|
-
for (const screen of input.model
|
|
65
|
-
|
|
126
|
+
for (const screen of screensOf(input.model)) {
|
|
127
|
+
const n = names.get(screen.className);
|
|
128
|
+
files.push({ path: `src/screens/${n.cls}.${ext}`, content: screenClass(screen, n, input.model.steps, ts) });
|
|
66
129
|
}
|
|
67
130
|
files.push({ path: `src/data/testData.${ext}`, content: testData(input.model, ts) });
|
|
68
|
-
files.push({ path: `test/smoke.e2e.${ext}`, content:
|
|
131
|
+
files.push({ path: `test/smoke.e2e.${ext}`, content: smoke });
|
|
69
132
|
return files;
|
|
70
133
|
}
|
|
71
134
|
function packageJson(input) {
|
|
@@ -75,7 +138,7 @@ function packageJson(input) {
|
|
|
75
138
|
name,
|
|
76
139
|
private: true,
|
|
77
140
|
type: 'module',
|
|
78
|
-
description: 'Generated by Swipium
|
|
141
|
+
description: 'Generated by Swipium: WebdriverIO + Appium POM suite (review before committing).',
|
|
79
142
|
scripts: {
|
|
80
143
|
test: 'wdio run ./wdio.conf.' + (ts ? 'ts' : 'js'),
|
|
81
144
|
},
|
|
@@ -92,7 +155,7 @@ function packageJson(input) {
|
|
|
92
155
|
return JSON.stringify(pkg, null, 2) + '\n';
|
|
93
156
|
}
|
|
94
157
|
function tsconfigJson() {
|
|
95
|
-
return JSON.stringify({
|
|
158
|
+
return (JSON.stringify({
|
|
96
159
|
compilerOptions: {
|
|
97
160
|
target: 'ES2022',
|
|
98
161
|
module: 'NodeNext',
|
|
@@ -103,10 +166,10 @@ function tsconfigJson() {
|
|
|
103
166
|
skipLibCheck: true,
|
|
104
167
|
},
|
|
105
168
|
include: ['src/**/*.ts', 'test/**/*.ts', 'wdio.conf.ts'],
|
|
106
|
-
}, null, 2) + '\n';
|
|
169
|
+
}, null, 2) + '\n');
|
|
107
170
|
}
|
|
108
171
|
function wdioConf(ts, ext) {
|
|
109
|
-
const header = '// Generated by Swipium. WebdriverIO + Appium config
|
|
172
|
+
const header = '// Generated by Swipium. WebdriverIO + Appium config. Capabilities come from env (see src/config/capabilities).';
|
|
110
173
|
const importLine = `import { buildCapabilities } from './src/config/capabilities.js';`;
|
|
111
174
|
const typeAnno = ts ? `\nimport type { Options } from '@wdio/types';\n` : '\n';
|
|
112
175
|
const cfgType = ts ? ': WebdriverIO.Config' : '';
|
|
@@ -130,38 +193,39 @@ export const config${cfgType} = {
|
|
|
130
193
|
`;
|
|
131
194
|
}
|
|
132
195
|
function capabilities(input, ts) {
|
|
133
|
-
const
|
|
134
|
-
const ios = input.model.platforms.ios;
|
|
196
|
+
const primary = defaultPlatformOf(input.profile, input.model);
|
|
135
197
|
const ret = ts ? ': WebdriverIO.Capabilities' : '';
|
|
136
198
|
const appId = input.appId ?? '';
|
|
137
199
|
const lines = [];
|
|
138
|
-
lines.push('// Generated by Swipium. ALL device/app config is environment-driven
|
|
139
|
-
lines.push(
|
|
200
|
+
lines.push('// Generated by Swipium. ALL device/app config is environment-driven: no secrets, no hardcoded paths.');
|
|
201
|
+
lines.push(primary === 'ios'
|
|
202
|
+
? '// iOS (XCUITest) is the default for this suite; set SWIPIUM_PLATFORM=android to target Android (UiAutomator2).'
|
|
203
|
+
: '// Android (UiAutomator2) is the default for this suite; set SWIPIUM_PLATFORM=ios to target iOS (XCUITest).');
|
|
140
204
|
lines.push('');
|
|
141
205
|
lines.push(`export function buildCapabilities()${ret} {`);
|
|
142
|
-
lines.push(` const platform = (process.env.SWIPIUM_PLATFORM || ${JSON.stringify(
|
|
143
|
-
lines.push(
|
|
206
|
+
lines.push(` const platform = (process.env.SWIPIUM_PLATFORM || ${JSON.stringify(primary)}).toLowerCase();`);
|
|
207
|
+
lines.push(" if (platform === 'ios') {");
|
|
144
208
|
lines.push(' return {');
|
|
145
|
-
lines.push(
|
|
146
|
-
lines.push(
|
|
147
|
-
lines.push(
|
|
148
|
-
lines.push(
|
|
209
|
+
lines.push(" platformName: 'iOS',");
|
|
210
|
+
lines.push(" 'appium:automationName': 'XCUITest',");
|
|
211
|
+
lines.push(" 'appium:deviceName': process.env.IOS_DEVICE_NAME || 'iPhone 15',");
|
|
212
|
+
lines.push(" 'appium:platformVersion': process.env.IOS_PLATFORM_VERSION,");
|
|
149
213
|
lines.push(` 'appium:bundleId': process.env.IOS_BUNDLE_ID${appId ? ` || ${JSON.stringify(appId)}` : ''},`);
|
|
150
|
-
lines.push(
|
|
151
|
-
lines.push(
|
|
152
|
-
lines.push(
|
|
214
|
+
lines.push(" 'appium:app': process.env.IOS_APP_PATH,");
|
|
215
|
+
lines.push(" 'appium:udid': process.env.IOS_UDID,");
|
|
216
|
+
lines.push(" 'appium:noReset': process.env.SWIPIUM_NO_RESET === 'true',");
|
|
153
217
|
lines.push(' };');
|
|
154
218
|
lines.push(' }');
|
|
155
|
-
lines.push(' // Android UiAutomator2
|
|
219
|
+
lines.push(' // Android UiAutomator2.');
|
|
156
220
|
lines.push(' return {');
|
|
157
|
-
lines.push(
|
|
158
|
-
lines.push(
|
|
159
|
-
lines.push(
|
|
221
|
+
lines.push(" platformName: 'Android',");
|
|
222
|
+
lines.push(" 'appium:automationName': 'UiAutomator2',");
|
|
223
|
+
lines.push(" 'appium:deviceName': process.env.ANDROID_DEVICE_NAME || 'Android Emulator',");
|
|
160
224
|
lines.push(` 'appium:appPackage': process.env.ANDROID_APP_PACKAGE${appId ? ` || ${JSON.stringify(appId)}` : ''},`);
|
|
161
|
-
lines.push(
|
|
162
|
-
lines.push(
|
|
163
|
-
lines.push(
|
|
164
|
-
lines.push(
|
|
225
|
+
lines.push(" 'appium:appActivity': process.env.ANDROID_APP_ACTIVITY,");
|
|
226
|
+
lines.push(" 'appium:app': process.env.ANDROID_APP_PATH,");
|
|
227
|
+
lines.push(" 'appium:udid': process.env.ANDROID_UDID,");
|
|
228
|
+
lines.push(" 'appium:noReset': process.env.SWIPIUM_NO_RESET === 'true',");
|
|
165
229
|
lines.push(' };');
|
|
166
230
|
lines.push('}');
|
|
167
231
|
lines.push('');
|
|
@@ -169,7 +233,7 @@ function capabilities(input, ts) {
|
|
|
169
233
|
}
|
|
170
234
|
function locatorsUtil(ts) {
|
|
171
235
|
const typeBlock = ts
|
|
172
|
-
? `export interface PlatformLocator {\n android?: string;\n ios?: string;\n /** "x,y" coordinate fallback
|
|
236
|
+
? `export interface PlatformLocator {\n android?: string;\n ios?: string;\n /** "x,y" coordinate fallback, non-release-grade. */\n coordinate?: string;\n}\n\n`
|
|
173
237
|
: '';
|
|
174
238
|
const param = ts ? 'loc: PlatformLocator' : 'loc';
|
|
175
239
|
const ret = ts ? ': string' : '';
|
|
@@ -179,7 +243,7 @@ ${typeBlock}export function resolveSelector(${param})${ret} {
|
|
|
179
243
|
const android = typeof driver !== 'undefined' ? driver.isAndroid : true;
|
|
180
244
|
const selector = android ? loc.android : loc.ios;
|
|
181
245
|
if (!selector) {
|
|
182
|
-
throw new Error('No durable selector for this platform
|
|
246
|
+
throw new Error('No durable selector for this platform. Element relies on a coordinate fallback (non-release-grade). Add an accessibility id / resource-id / testID.');
|
|
183
247
|
}
|
|
184
248
|
return selector;
|
|
185
249
|
}
|
|
@@ -194,7 +258,7 @@ export function coordinateOf(${param})${ts ? ': [number, number] | undefined' :
|
|
|
194
258
|
function waitsUtil(ts) {
|
|
195
259
|
const elAnno = ts ? ': WebdriverIO.Element' : '';
|
|
196
260
|
const numAnno = ts ? ': number' : '';
|
|
197
|
-
return `// Generated by Swipium. Structured waits
|
|
261
|
+
return `// Generated by Swipium. Structured waits, never a bare sleep. Auto-wait before interaction
|
|
198
262
|
// (Playwright-style resilience, applied to mobile Appium).
|
|
199
263
|
export const DEFAULT_TIMEOUT = ${DEFAULT_TIMEOUT};
|
|
200
264
|
|
|
@@ -212,36 +276,92 @@ export async function waitEnabled(el${elAnno}, timeout${numAnno} = DEFAULT_TIMEO
|
|
|
212
276
|
function baseScreen(ts) {
|
|
213
277
|
const locImport = ts ? `import type { PlatformLocator } from '../utils/locators.js';\n` : '';
|
|
214
278
|
const p = (name, type) => (ts ? `${name}: ${type}` : name);
|
|
279
|
+
// H3: TS-only modifiers/annotations are gated on the TypeScript flag so the .js output parses.
|
|
280
|
+
const prot = ts ? 'protected ' : '';
|
|
281
|
+
const dirType = ts ? `: 'up' | 'down' | 'left' | 'right'` : '';
|
|
215
282
|
return `// Generated by Swipium. BaseScreen centralizes all element interaction so selectors are
|
|
216
283
|
// resolved per-platform in ONE place and tests never touch raw selectors.
|
|
217
|
-
${locImport}import { resolveSelector
|
|
218
|
-
import { waitVisible, waitEnabled
|
|
284
|
+
${locImport}import { resolveSelector } from '../utils/locators.js';
|
|
285
|
+
import { waitVisible, waitEnabled } from '../utils/waits.js';
|
|
286
|
+
|
|
287
|
+
/** Upper bound on scroll gestures while looking for an element, so never an endless loop. */
|
|
288
|
+
export const MAX_SCROLLS = ${MAX_SCROLLS};
|
|
219
289
|
|
|
220
290
|
export class BaseScreen {
|
|
221
|
-
|
|
291
|
+
${prot}async element(${p('loc', 'PlatformLocator')}) {
|
|
222
292
|
// @ts-ignore wdio global
|
|
223
293
|
return $(resolveSelector(loc));
|
|
224
294
|
}
|
|
225
295
|
|
|
226
|
-
|
|
296
|
+
${prot}async tap(${p('loc', 'PlatformLocator')}) {
|
|
227
297
|
const el = await this.element(loc);
|
|
228
298
|
await waitEnabled(await waitVisible(el));
|
|
229
299
|
await el.click();
|
|
230
300
|
}
|
|
231
301
|
|
|
232
|
-
|
|
302
|
+
${prot}async type(${p('loc', 'PlatformLocator')}, ${p('value', 'string')}) {
|
|
233
303
|
const el = await this.element(loc);
|
|
234
304
|
await waitVisible(el);
|
|
235
305
|
await el.setValue(value);
|
|
236
306
|
}
|
|
237
307
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
await
|
|
308
|
+
/** Type into whichever field currently has focus (the recording had no locator for it). */
|
|
309
|
+
async typeFocused(${p('value', 'string')}) {
|
|
310
|
+
// @ts-ignore wdio global
|
|
311
|
+
const active = await $(await driver.getActiveElement());
|
|
312
|
+
await active.addValue(value);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* One real content-scroll gesture. direction is the CONTENT direction: 'down' reveals what is below.
|
|
317
|
+
* Android: UiAutomator2 \`mobile: scrollGesture\` (returns false once the end is reached).
|
|
318
|
+
* iOS: XCUITest \`mobile: scroll\`.
|
|
319
|
+
*/
|
|
320
|
+
async scrollOnce(${p('direction', 'string')} = 'down')${ts ? ': Promise<boolean>' : ''} {
|
|
321
|
+
// @ts-ignore wdio global
|
|
322
|
+
if (driver.isAndroid) {
|
|
323
|
+
// @ts-ignore wdio global
|
|
324
|
+
const { width, height } = await driver.getWindowSize();
|
|
325
|
+
// @ts-ignore wdio global
|
|
326
|
+
const canScrollMore = await driver.execute('mobile: scrollGesture', {
|
|
327
|
+
left: Math.round(width * 0.1),
|
|
328
|
+
top: Math.round(height * 0.2),
|
|
329
|
+
width: Math.round(width * 0.8),
|
|
330
|
+
height: Math.round(height * 0.6),
|
|
331
|
+
direction,
|
|
332
|
+
percent: 0.75,
|
|
333
|
+
});
|
|
334
|
+
return canScrollMore !== false;
|
|
335
|
+
}
|
|
336
|
+
// @ts-ignore wdio global
|
|
337
|
+
await driver.execute('mobile: scroll', { direction });
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** Scroll (bounded by maxScrolls) until the element is displayed; throws if it never appears. */
|
|
342
|
+
${prot}async scrollTo(${p('loc', 'PlatformLocator')}, ${p('direction', 'string')} = 'down', ${p('maxScrolls', 'number')} = MAX_SCROLLS) {
|
|
343
|
+
let atEnd = false;
|
|
344
|
+
for (let i = 0; i <= maxScrolls; i++) {
|
|
345
|
+
if (await this.isVisible(loc)) return;
|
|
346
|
+
if (i === maxScrolls || atEnd) break;
|
|
347
|
+
atEnd = !(await this.scrollOnce(direction));
|
|
348
|
+
}
|
|
349
|
+
throw new Error('Element not visible after scrolling ' + direction + ' (max ' + maxScrolls + ' scrolls)');
|
|
242
350
|
}
|
|
243
351
|
|
|
244
|
-
|
|
352
|
+
/** Scroll (bounded) until an element whose text/label contains \`text\` is displayed. */
|
|
353
|
+
async scrollToText(${p('text', 'string')}, ${p('direction', 'string')} = 'down', ${p('maxScrolls', 'number')} = MAX_SCROLLS) {
|
|
354
|
+
let atEnd = false;
|
|
355
|
+
for (let i = 0; i <= maxScrolls; i++) {
|
|
356
|
+
// @ts-ignore wdio global
|
|
357
|
+
if (await $(this.textSelector(text)).isDisplayed()) return;
|
|
358
|
+
if (i === maxScrolls || atEnd) break;
|
|
359
|
+
atEnd = !(await this.scrollOnce(direction));
|
|
360
|
+
}
|
|
361
|
+
throw new Error('Text ' + JSON.stringify(text) + ' not visible after scrolling ' + direction + ' (max ' + maxScrolls + ' scrolls)');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
${prot}async isVisible(${p('loc', 'PlatformLocator')})${ts ? ': Promise<boolean>' : ''} {
|
|
245
365
|
const el = await this.element(loc);
|
|
246
366
|
return el.isDisplayed();
|
|
247
367
|
}
|
|
@@ -251,28 +371,60 @@ export class BaseScreen {
|
|
|
251
371
|
await waitVisible(el);
|
|
252
372
|
}
|
|
253
373
|
|
|
374
|
+
/** Platform-appropriate "text contains" selector. JSON.stringify escapes backslashes and quotes. */
|
|
375
|
+
textSelector(${p('text', 'string')})${ts ? ': string' : ''} {
|
|
376
|
+
// @ts-ignore wdio global
|
|
377
|
+
if (driver.isAndroid) return 'android=new UiSelector().textContains(' + JSON.stringify(text) + ')';
|
|
378
|
+
return '-ios predicate string:label CONTAINS ' + JSON.stringify(text) + ' OR name CONTAINS ' + JSON.stringify(text);
|
|
379
|
+
}
|
|
380
|
+
|
|
254
381
|
async assertTextVisible(${p('text', 'string')}) {
|
|
255
|
-
// Non-release-grade text/OCR assertion
|
|
382
|
+
// Non-release-grade text/OCR assertion. Prefer a structured locator.
|
|
256
383
|
// @ts-ignore wdio global
|
|
257
|
-
const el = await $(
|
|
384
|
+
const el = await $(this.textSelector(text));
|
|
258
385
|
await waitVisible(el);
|
|
259
386
|
}
|
|
260
387
|
|
|
261
|
-
// Coordinate tap
|
|
388
|
+
// Coordinate tap: brittle, non-release-grade fallback. Only used when no durable locator exists.
|
|
262
389
|
async tapAt(${p('x', 'number')}, ${p('y', 'number')}) {
|
|
263
390
|
// @ts-ignore wdio global
|
|
264
|
-
await driver.action('pointer')
|
|
391
|
+
await driver.action('pointer', { parameters: { pointerType: 'touch' } })
|
|
265
392
|
.move({ x, y }).down().pause(50).up().perform();
|
|
266
393
|
}
|
|
267
394
|
|
|
268
|
-
async pressKey(${p('key', '
|
|
269
|
-
// @ts-ignore wdio global
|
|
270
|
-
if (driver.isAndroid && key === 'back') { await driver.back(); return; }
|
|
395
|
+
async pressKey(${p('key', `'back' | 'home' | 'enter'`)}) {
|
|
271
396
|
// @ts-ignore wdio global
|
|
272
|
-
|
|
397
|
+
const android = driver.isAndroid;
|
|
398
|
+
if (key === 'back') {
|
|
399
|
+
// @ts-ignore wdio global
|
|
400
|
+
if (android) { await driver.back(); return; }
|
|
401
|
+
// iOS has no system back key: swipe in from the left edge (UINavigationController back gesture).
|
|
402
|
+
// @ts-ignore wdio global
|
|
403
|
+
const { width, height } = await driver.getWindowSize();
|
|
404
|
+
// @ts-ignore wdio global
|
|
405
|
+
await driver.action('pointer', { parameters: { pointerType: 'touch' } })
|
|
406
|
+
.move({ x: 2, y: Math.round(height / 2) }).down().pause(50)
|
|
407
|
+
.move({ duration: 400, x: Math.round(width * 0.7), y: Math.round(height / 2) }).up().perform();
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (key === 'home') {
|
|
411
|
+
// @ts-ignore wdio global
|
|
412
|
+
if (android) await driver.execute('mobile: pressKey', { keycode: 3 });
|
|
413
|
+
// @ts-ignore wdio global
|
|
414
|
+
else await driver.execute('mobile: pressButton', { name: 'home' });
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (key === 'enter') {
|
|
418
|
+
// @ts-ignore wdio global
|
|
419
|
+
if (android) { await driver.execute('mobile: pressKey', { keycode: 66 }); return; }
|
|
420
|
+
await this.typeFocused('\\n');
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
throw new Error('Unsupported key: ' + key);
|
|
273
424
|
}
|
|
274
425
|
|
|
275
|
-
|
|
426
|
+
/** Real window-size-relative swipe. direction is the FINGER direction ('up' drags from bottom to top). */
|
|
427
|
+
async swipe(${p('direction', dirType.slice(2) || 'string')}) {
|
|
276
428
|
// @ts-ignore wdio global
|
|
277
429
|
const { width, height } = await driver.getWindowSize();
|
|
278
430
|
const cx = Math.round(width / 2), cy = Math.round(height / 2);
|
|
@@ -282,9 +434,11 @@ export class BaseScreen {
|
|
|
282
434
|
left: [Math.round(width * 0.8), cy, Math.round(width * 0.2), cy],
|
|
283
435
|
right: [Math.round(width * 0.2), cy, Math.round(width * 0.8), cy],
|
|
284
436
|
};
|
|
285
|
-
const
|
|
437
|
+
const vec = map[direction];
|
|
438
|
+
if (!vec) throw new Error('Unsupported swipe direction: ' + direction);
|
|
439
|
+
const [x1, y1, x2, y2] = vec;
|
|
286
440
|
// @ts-ignore wdio global
|
|
287
|
-
await driver.action('pointer')
|
|
441
|
+
await driver.action('pointer', { parameters: { pointerType: 'touch' } })
|
|
288
442
|
.move({ x: x1, y: y1 }).down().pause(100).move({ duration: 600, x: x2, y: y2 }).up().perform();
|
|
289
443
|
}
|
|
290
444
|
|
|
@@ -295,9 +449,8 @@ export class BaseScreen {
|
|
|
295
449
|
}
|
|
296
450
|
`;
|
|
297
451
|
}
|
|
298
|
-
/**
|
|
299
|
-
function
|
|
300
|
-
const E = pascal(element);
|
|
452
|
+
/** Base (pre-dedupe) action-method name for an (action, element) pair. */
|
|
453
|
+
function methodBase(action, E) {
|
|
301
454
|
switch (action) {
|
|
302
455
|
case 'tap':
|
|
303
456
|
return `tap${E}`;
|
|
@@ -308,22 +461,23 @@ function methodName(action, element) {
|
|
|
308
461
|
case 'assertVisible':
|
|
309
462
|
return `assert${E}Visible`;
|
|
310
463
|
default:
|
|
311
|
-
|
|
464
|
+
throw new UnemittableStepError(`cannot emit a ${action} step bound to an element: no generated action exists for it`);
|
|
312
465
|
}
|
|
313
466
|
}
|
|
314
|
-
function screenClass(screen, steps, ts
|
|
315
|
-
const locImport = ts ? `import type { PlatformLocator } from '../utils/locators.js';\n` : '';
|
|
467
|
+
function screenClass(screen, names, steps, ts) {
|
|
316
468
|
const lines = [];
|
|
317
|
-
lines.push(`// Generated by Swipium. Screen object for ${screen.pageName}${screen.screenSignature ? ` (${screen.screenSignature})` : ''}.`);
|
|
469
|
+
lines.push(`// Generated by Swipium. Screen object for ${commentSafe(screen.pageName)}${screen.screenSignature ? ` (${commentSafe(screen.screenSignature)})` : ''}.`);
|
|
318
470
|
lines.push('// Selectors are centralized here; tests call the action methods below.');
|
|
319
|
-
|
|
471
|
+
if (ts)
|
|
472
|
+
lines.push(`import type { PlatformLocator } from '../utils/locators.js';`);
|
|
320
473
|
lines.push(`import { BaseScreen } from './BaseScreen.js';`);
|
|
321
474
|
lines.push('');
|
|
322
|
-
lines.push(`export class ${
|
|
323
|
-
// Locators (centralized).
|
|
475
|
+
lines.push(`export class ${names.cls} extends BaseScreen {`);
|
|
476
|
+
// Locators (centralized). H3: `readonly` + the type annotation are TypeScript-only.
|
|
324
477
|
for (const el of screen.elements) {
|
|
325
|
-
const
|
|
326
|
-
|
|
478
|
+
const field = names.fields.get(el.name);
|
|
479
|
+
const durabilityNote = el.durability === 'durable' ? '' : ` // ${el.durability} locator${el.remediation ? `: ${commentSafe(el.remediation)}` : ''}`;
|
|
480
|
+
lines.push(` ${ts ? 'readonly ' : ''}${field}${ts ? ': PlatformLocator' : ''} = ${platformLocatorLiteral(el)};${durabilityNote}`);
|
|
327
481
|
}
|
|
328
482
|
lines.push('');
|
|
329
483
|
// Action methods for each (element, action) used in steps on this screen.
|
|
@@ -331,47 +485,47 @@ function screenClass(screen, steps, ts, t) {
|
|
|
331
485
|
for (const s of steps) {
|
|
332
486
|
if (s.screen !== screen.className || !s.element)
|
|
333
487
|
continue;
|
|
334
|
-
const
|
|
335
|
-
if (
|
|
488
|
+
const key = methodKey(s.action, s.element);
|
|
489
|
+
if (seen.has(key))
|
|
336
490
|
continue;
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
seen.add(name);
|
|
491
|
+
seen.add(key);
|
|
492
|
+
const name = names.methods.get(key);
|
|
493
|
+
const field = `this.${names.fields.get(s.element)}`;
|
|
341
494
|
if (s.action === 'inputText') {
|
|
342
495
|
lines.push(` async ${name}(${ts ? 'value: string' : 'value'}) {`);
|
|
343
|
-
lines.push(` await this.type(
|
|
344
|
-
lines.push(' }');
|
|
496
|
+
lines.push(` await this.type(${field}, value);`);
|
|
345
497
|
}
|
|
346
498
|
else if (s.action === 'tap') {
|
|
347
499
|
lines.push(` async ${name}() {`);
|
|
348
|
-
lines.push(` await this.tap(
|
|
349
|
-
lines.push(' }');
|
|
500
|
+
lines.push(` await this.tap(${field});`);
|
|
350
501
|
}
|
|
351
502
|
else if (s.action === 'scrollTo') {
|
|
352
|
-
lines.push(` async ${name}() {`);
|
|
353
|
-
lines.push(` await this.scrollTo(
|
|
354
|
-
lines.push(' }');
|
|
503
|
+
lines.push(` async ${name}(${ts ? "direction: string = 'down'" : "direction = 'down'"}) {`);
|
|
504
|
+
lines.push(` await this.scrollTo(${field}, direction);`);
|
|
355
505
|
}
|
|
356
|
-
else
|
|
506
|
+
else {
|
|
357
507
|
lines.push(` async ${name}() {`);
|
|
358
|
-
lines.push(` await this.assertVisible(
|
|
359
|
-
lines.push(' }');
|
|
508
|
+
lines.push(` await this.assertVisible(${field});`);
|
|
360
509
|
}
|
|
510
|
+
lines.push(' }');
|
|
361
511
|
}
|
|
362
512
|
lines.push('}');
|
|
363
513
|
lines.push('');
|
|
364
514
|
return lines.join('\n');
|
|
365
515
|
}
|
|
516
|
+
function envRef(v) {
|
|
517
|
+
return jsProp('process.env', v);
|
|
518
|
+
}
|
|
366
519
|
function testData(model, ts) {
|
|
367
520
|
const lines = [];
|
|
368
|
-
|
|
521
|
+
const key = (v) => (isJsIdentifier(varToKey(v)) ? varToKey(v) : JSON.stringify(varToKey(v)));
|
|
522
|
+
lines.push('// Generated by Swipium. Test data comes from the ENVIRONMENT. Secrets are never inlined.');
|
|
369
523
|
lines.push('export const testData = {');
|
|
370
524
|
for (const v of model.variables) {
|
|
371
|
-
lines.push(` ${
|
|
525
|
+
lines.push(` ${key(v)}: ${envRef(v)} || '',`);
|
|
372
526
|
}
|
|
373
527
|
for (const v of model.secrets) {
|
|
374
|
-
lines.push(` ${
|
|
528
|
+
lines.push(` ${key(v)}: ${envRef(v)} || '', // secret, provide via environment only`);
|
|
375
529
|
}
|
|
376
530
|
lines.push('};');
|
|
377
531
|
lines.push('');
|
|
@@ -380,61 +534,74 @@ function testData(model, ts) {
|
|
|
380
534
|
lines.push('');
|
|
381
535
|
return lines.join('\n');
|
|
382
536
|
}
|
|
383
|
-
function smokeTest(model,
|
|
537
|
+
function smokeTest(model, names) {
|
|
384
538
|
const classes = [...new Set(model.steps.map((s) => s.screen))];
|
|
385
539
|
const lines = [];
|
|
386
540
|
lines.push('// Generated by Swipium. Smoke test driving the recorded flow through the screen objects.');
|
|
387
541
|
for (const c of classes) {
|
|
388
|
-
|
|
542
|
+
const cls = names.get(c).cls;
|
|
543
|
+
lines.push(`import { ${cls} } from '../src/screens/${cls}.js';`);
|
|
389
544
|
}
|
|
390
545
|
lines.push(`import { testData } from '../src/data/testData.js';`);
|
|
391
546
|
lines.push('');
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
for (const c of classes)
|
|
396
|
-
const
|
|
397
|
-
inst.set(c, v);
|
|
398
|
-
}
|
|
399
|
-
lines.push(` it('completes the recorded flow', async () => {`);
|
|
400
|
-
for (const [c, v] of inst)
|
|
401
|
-
lines.push(` const ${v} = new ${c}();`);
|
|
547
|
+
// H3: titles come from recorded data, so always a JSON-escaped literal, never raw interpolation.
|
|
548
|
+
lines.push(`describe(${JSON.stringify(`${model.testName} smoke`)}, () => {`);
|
|
549
|
+
lines.push(` it(${JSON.stringify('completes the recorded flow')}, async () => {`);
|
|
550
|
+
for (const c of classes)
|
|
551
|
+
lines.push(` const ${names.get(c).instance} = new ${names.get(c).cls}();`);
|
|
402
552
|
for (const s of model.steps) {
|
|
403
|
-
|
|
404
|
-
lines.push(' ' + stepCall(v, s));
|
|
553
|
+
lines.push(' ' + stepCall(names.get(s.screen), s));
|
|
405
554
|
}
|
|
406
555
|
lines.push(' });');
|
|
407
556
|
lines.push('});');
|
|
408
557
|
lines.push('');
|
|
409
558
|
return lines.join('\n');
|
|
410
559
|
}
|
|
411
|
-
function
|
|
560
|
+
function dataArg(s) {
|
|
561
|
+
return s.varName ? jsProp('testData', varToKey(s.varName)) : JSON.stringify(s.text ?? '');
|
|
562
|
+
}
|
|
563
|
+
function num(n) {
|
|
564
|
+
return Number.isFinite(n) ? Math.round(n) : 0;
|
|
565
|
+
}
|
|
566
|
+
function stepCall(n, s) {
|
|
567
|
+
const v = n.instance;
|
|
412
568
|
if (s.element) {
|
|
413
|
-
const name =
|
|
414
|
-
if (s.action === 'inputText')
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
return `await ${
|
|
569
|
+
const name = n.methods.get(methodKey(s.action, s.element));
|
|
570
|
+
if (s.action === 'inputText')
|
|
571
|
+
return `await ${v}.${name}(${dataArg(s)});`;
|
|
572
|
+
if (s.action === 'scrollTo')
|
|
573
|
+
return `await ${v}.${name}(${JSON.stringify(checkDirection(s.direction ?? 'down', 'scrollTo step'))});`;
|
|
574
|
+
return `await ${v}.${name}();`;
|
|
419
575
|
}
|
|
420
576
|
// No element: generic BaseScreen helpers.
|
|
421
577
|
switch (s.action) {
|
|
422
578
|
case 'tapAt':
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
579
|
+
if (!s.coords)
|
|
580
|
+
throw new UnemittableStepError('cannot emit a coordinate tap without coordinates');
|
|
581
|
+
return `await ${v}.tapAt(${num(s.coords[0])}, ${num(s.coords[1])}); // coordinate fallback, non-release-grade`;
|
|
582
|
+
case 'inputText':
|
|
583
|
+
return `await ${v}.typeFocused(${dataArg(s)}); // focused-field input`;
|
|
428
584
|
case 'press':
|
|
429
|
-
return `await ${
|
|
585
|
+
return `await ${v}.pressKey(${JSON.stringify(checkKey(s.key))});`;
|
|
430
586
|
case 'swipe':
|
|
431
|
-
return `await ${
|
|
587
|
+
return `await ${v}.swipe(${JSON.stringify(checkDirection(s.direction ?? 'up', 'swipe step'))});`;
|
|
588
|
+
case 'scrollTo':
|
|
589
|
+
if (!s.text)
|
|
590
|
+
throw new UnemittableStepError('cannot emit a scrollTo step with neither an element nor a target text');
|
|
591
|
+
return `await ${v}.scrollToText(${JSON.stringify(s.text)}, ${JSON.stringify(checkDirection(s.direction ?? 'down', 'scrollTo step'))});`;
|
|
432
592
|
case 'openUrl':
|
|
433
|
-
|
|
593
|
+
if (!s.url)
|
|
594
|
+
throw new UnemittableStepError('cannot emit an openUrl step without a URL');
|
|
595
|
+
return `await ${v}.openUrl(${JSON.stringify(s.url)});`;
|
|
434
596
|
case 'assertVisible':
|
|
435
|
-
|
|
597
|
+
if (!s.text)
|
|
598
|
+
throw new UnemittableStepError('cannot emit a text assertion without text');
|
|
599
|
+
return `await ${v}.assertTextVisible(${JSON.stringify(s.text)});`;
|
|
600
|
+
case 'visualCheck':
|
|
601
|
+
// Visual judgement prose is not on-screen text. It's a manual checkpoint, never a failing text check.
|
|
602
|
+
return `// TODO(manual visual check, not automated): ${commentSafe(s.text ?? 'visual checkpoint')}`;
|
|
436
603
|
default:
|
|
437
|
-
|
|
604
|
+
throw new UnemittableStepError(`cannot emit step action ${JSON.stringify(s.action)}: no WebdriverIO equivalent is generated for it`);
|
|
438
605
|
}
|
|
439
606
|
}
|
|
440
607
|
//# sourceMappingURL=jsEmitter.js.map
|