swipium 1.5.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 +171 -0
- package/README.md +173 -280
- package/THREAT_MODEL.md +223 -47
- package/dist/appMap/automationLink.js +30 -27
- package/dist/appMap/automationLink.js.map +1 -1
- package/dist/appMap/build.js +47 -21
- package/dist/appMap/build.js.map +1 -1
- package/dist/appMap/codeIndex.js +2 -2
- package/dist/appMap/codeIndex.js.map +1 -1
- package/dist/appMap/featureIndex.js +3 -3
- package/dist/appMap/featureIndex.js.map +1 -1
- package/dist/appMap/featureModel.js +2 -2
- package/dist/appMap/featureModel.js.map +1 -1
- package/dist/appMap/firstRunApply.js +2 -2
- package/dist/appMap/firstRunApply.js.map +1 -1
- package/dist/appMap/issues.js +10 -10
- package/dist/appMap/issues.js.map +1 -1
- package/dist/appMap/migrations.js +5 -5
- 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 +77 -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 +2 -2
- package/dist/appMap/query.js.map +1 -1
- package/dist/appMap/runtimeMerge.js +3 -3
- 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 +1 -1
- package/dist/appMap/screenMatch.js.map +1 -1
- package/dist/appMap/staticScan.js +17 -17
- package/dist/appMap/staticScan.js.map +1 -1
- package/dist/appMap/store.js +55 -7
- package/dist/appMap/store.js.map +1 -1
- package/dist/appMap/tsAstScan.js +3 -3
- package/dist/appMap/tsAstScan.js.map +1 -1
- package/dist/artifacts/bundletool.js +5 -5
- package/dist/artifacts/bundletool.js.map +1 -1
- package/dist/artifacts/resolve.js +10 -10
- package/dist/artifacts/resolve.js.map +1 -1
- package/dist/automation/capabilities.js +3 -3
- package/dist/automation/capabilities.js.map +1 -1
- package/dist/automation/gestures.js +1 -1
- package/dist/automation/gestures.js.map +1 -1
- package/dist/automation/plan.js +7 -7
- package/dist/automation/plan.js.map +1 -1
- package/dist/automation/report.js +3 -3
- package/dist/automation/report.js.map +1 -1
- package/dist/automation/selectors.js +14 -25
- 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 +5 -5
- package/dist/automation/waits.js.map +1 -1
- package/dist/automation/webview.js +2 -2
- package/dist/automation/webview.js.map +1 -1
- package/dist/automationGen/appiumModel.js +13 -7
- package/dist/automationGen/appiumModel.js.map +1 -1
- package/dist/automationGen/ciEmitter.js +5 -5
- 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 +265 -106
- 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 +71 -26
- package/dist/automationGen/projectProfile.js.map +1 -1
- package/dist/automationGen/pythonEmitter.js +276 -105
- 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/{tools/automationGenerate.js → automationGen/run.js} +72 -113
- package/dist/automationGen/run.js.map +1 -0
- package/dist/automationGen/suitePlan.js +6 -4
- package/dist/automationGen/suitePlan.js.map +1 -1
- package/dist/automationGen/validation.js +20 -8
- package/dist/automationGen/validation.js.map +1 -1
- package/dist/build/parseBuildLog.js +2 -2
- package/dist/build/parseBuildLog.js.map +1 -1
- package/dist/build/plan.js +7 -7
- 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 +35 -14
- package/dist/cli/scan.js.map +1 -1
- package/dist/cli/suite.js +6 -6
- 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 +45 -14
- package/dist/context/detect.js.map +1 -1
- package/dist/context/findApps.js +6 -6
- 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 +3 -3
- package/dist/core/target.js.map +1 -1
- package/dist/core/targetPlan.js +65 -62
- package/dist/core/targetPlan.js.map +1 -1
- package/dist/drivers/DirectDriver.js +300 -36
- package/dist/drivers/DirectDriver.js.map +1 -1
- package/dist/drivers/SimctlDriver.js +120 -7
- package/dist/drivers/SimctlDriver.js.map +1 -1
- package/dist/drivers/WdaDriver.js +350 -50
- package/dist/drivers/WdaDriver.js.map +1 -1
- package/dist/explore/candidates.js +2 -2
- package/dist/explore/candidates.js.map +1 -1
- package/dist/explore/graph.js +5 -5
- package/dist/explore/graph.js.map +1 -1
- package/dist/explore/policy.js +5 -5
- package/dist/explore/policy.js.map +1 -1
- package/dist/explore/runner.js +301 -284
- package/dist/explore/runner.js.map +1 -1
- package/dist/explore/signatures.js +1 -1
- package/dist/featureTesting/executionBootstrap.js +29 -30
- package/dist/featureTesting/executionBootstrap.js.map +1 -1
- package/dist/featureTesting/featureMap.js +2 -2
- package/dist/featureTesting/featureMap.js.map +1 -1
- package/dist/featureTesting/featureScope.js +12 -12
- package/dist/featureTesting/featureScope.js.map +1 -1
- package/dist/featureTesting/mapFeatureScope.js +7 -7
- package/dist/featureTesting/mapFeatureScope.js.map +1 -1
- package/dist/featureTesting/objectiveModel.js +10 -10
- package/dist/featureTesting/objectiveModel.js.map +1 -1
- package/dist/featureTesting/resultMerge.js +6 -6
- package/dist/featureTesting/resultMerge.js.map +1 -1
- package/dist/featureTesting/sources.js +4 -4
- package/dist/featureTesting/sources.js.map +1 -1
- package/dist/featureTesting/suiteBridge.js +1 -1
- package/dist/featureTesting/suiteBridge.js.map +1 -1
- package/dist/featureTesting/synonyms.js +1 -1
- package/dist/featureTesting/synonyms.js.map +1 -1
- package/dist/featureTesting/testCaseFactory.js +11 -11
- package/dist/featureTesting/testCaseFactory.js.map +1 -1
- package/dist/featureTesting/testPlan.js +3 -3
- 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 +5 -5
- package/dist/firstRun/classifyScreen.js.map +1 -1
- package/dist/firstRun/firstRunPlanner.js +11 -11
- package/dist/firstRun/firstRunPlanner.js.map +1 -1
- package/dist/firstRun/firstRunRunner.js +8 -8
- package/dist/firstRun/firstRunRunner.js.map +1 -1
- package/dist/firstRun/generatedDataPolicy.js +9 -9
- package/dist/firstRun/generatedDataPolicy.js.map +1 -1
- package/dist/firstRun/inputPlanner.js +5 -5
- 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 +33 -3
- 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 +20 -10
- package/dist/flows/generate.js.map +1 -1
- package/dist/flows/pack.js +3 -3
- 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 +111 -23
- package/dist/flows/repair.js.map +1 -1
- package/dist/flows/run.js +153 -57
- package/dist/flows/run.js.map +1 -1
- package/dist/flows/schema.js +50 -5
- package/dist/flows/schema.js.map +1 -1
- package/dist/flows/seedExec.js +4 -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/issues/classify.js +16 -16
- package/dist/issues/classify.js.map +1 -1
- package/dist/issues/fingerprint.js +24 -4
- package/dist/issues/fingerprint.js.map +1 -1
- package/dist/issues/index.js +94 -31
- package/dist/issues/index.js.map +1 -1
- package/dist/issues/metrics.js +4 -4
- 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 +4 -4
- package/dist/issues/report.js.map +1 -1
- package/dist/issues/reportBridge.js +4 -4
- 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 +75 -7
- 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 +32 -7
- 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 +1 -1
- package/dist/lib/image.js.map +1 -1
- package/dist/lib/lockfile.js +370 -31
- package/dist/lib/lockfile.js.map +1 -1
- package/dist/lib/needsInput.js +3 -3
- package/dist/lib/needsInput.js.map +1 -1
- package/dist/lib/png.js +2 -2
- 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 +79 -10
- package/dist/lib/result.js.map +1 -1
- package/dist/lib/schemaHash.js +3 -3
- package/dist/lib/schemaHash.js.map +1 -1
- package/dist/lib/sensitive.js +3 -2
- 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 +52 -10
- 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 +294 -15
- package/dist/lib/wda.js.map +1 -1
- package/dist/mobileAudit/checks.js +12 -12
- 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 +1 -1
- package/dist/mobileAudit/profiles.js.map +1 -1
- package/dist/mobileAudit/results.js +3 -3
- package/dist/mobileAudit/results.js.map +1 -1
- package/dist/mobileAudit/runner.js +72 -18
- 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 +219 -36
- package/dist/oracle/failures.js.map +1 -1
- package/dist/oracle/health.js +63 -17
- package/dist/oracle/health.js.map +1 -1
- package/dist/oracle/locator.js +13 -13
- 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 +4 -4
- package/dist/orchestration/goal.js.map +1 -1
- package/dist/orchestration/testThis/execute.js +62 -21
- package/dist/orchestration/testThis/execute.js.map +1 -1
- package/dist/orchestration/testThis/pipeline.js +119 -30
- package/dist/orchestration/testThis/pipeline.js.map +1 -1
- package/dist/orchestration/testThis/plan.js +162 -73
- package/dist/orchestration/testThis/plan.js.map +1 -1
- package/dist/orchestration/testThis/sessionIntent.js +72 -0
- package/dist/orchestration/testThis/sessionIntent.js.map +1 -0
- package/dist/orchestration/testThis/terminal.js +34 -16
- package/dist/orchestration/testThis/terminal.js.map +1 -1
- package/dist/orchestration/testThis/types.js +1 -1
- package/dist/orchestration/testThis/types.js.map +1 -1
- package/dist/plan/plan.js +9 -9
- package/dist/plan/plan.js.map +1 -1
- package/dist/prompts/index.js +22 -22
- package/dist/prompts/index.js.map +1 -1
- package/dist/report/export.js +392 -37
- 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/policy.js +7 -4
- package/dist/report/policy.js.map +1 -1
- package/dist/report/qaLevel.js +8 -8
- package/dist/report/qaLevel.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/toolHealth.js +124 -0
- package/dist/report/toolHealth.js.map +1 -0
- package/dist/server.js +472 -45
- 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 +2 -2
- package/dist/services/build.js.map +1 -1
- package/dist/{tools → services}/flowGenerate.js +29 -13
- package/dist/services/flowGenerate.js.map +1 -0
- package/dist/services/preflight.js +4 -4
- package/dist/services/preflight.js.map +1 -1
- package/dist/services/prepareAndroid.js +46 -8
- package/dist/services/prepareAndroid.js.map +1 -1
- package/dist/services/prepareIos.js +24 -9
- package/dist/services/prepareIos.js.map +1 -1
- package/dist/services/report.js +100 -45
- package/dist/services/report.js.map +1 -1
- package/dist/services/smoke.js +32 -16
- package/dist/services/smoke.js.map +1 -1
- package/dist/services/suiteGenerate.js +28 -3
- package/dist/services/suiteGenerate.js.map +1 -1
- package/dist/services/testSuiteKnowledge.js +40 -27
- package/dist/services/testSuiteKnowledge.js.map +1 -1
- package/dist/session/attach.js +270 -6
- package/dist/session/attach.js.map +1 -1
- package/dist/session/processRegistry.js +317 -48
- package/dist/session/processRegistry.js.map +1 -1
- package/dist/session/progress.js +1 -1
- package/dist/session/retention.js +201 -0
- package/dist/session/retention.js.map +1 -0
- package/dist/session/store.js +275 -48
- package/dist/session/store.js.map +1 -1
- package/dist/snapshot/overlays.js +53 -8
- package/dist/snapshot/overlays.js.map +1 -1
- package/dist/snapshot/parse.js +28 -4
- package/dist/snapshot/parse.js.map +1 -1
- package/dist/snapshot/present.js +11 -5
- 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 +46 -2
- package/dist/state/profile.js.map +1 -1
- package/dist/suite/compile.js +9 -5
- package/dist/suite/compile.js.map +1 -1
- package/dist/suite/lint.js +4 -4
- package/dist/suite/lint.js.map +1 -1
- package/dist/suite/pom.js +50 -41
- 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 +9 -7
- package/dist/suite/testcase.js.map +1 -1
- package/dist/testSuite/exporter.js +4 -4
- package/dist/testSuite/exporter.js.map +1 -1
- package/dist/testSuite/generator.js +13 -9
- 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 +5 -5
- package/dist/testSuite/merge.js.map +1 -1
- package/dist/testSuite/schema.js +5 -5
- package/dist/testSuite/schema.js.map +1 -1
- package/dist/testSuite/store.js +3 -3
- 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 +794 -164
- package/dist/tools/act.js.map +1 -1
- package/dist/tools/agent.js +451 -128
- package/dist/tools/agent.js.map +1 -1
- package/dist/tools/appControl.js +69 -19
- package/dist/tools/appControl.js.map +1 -1
- package/dist/tools/appMap.js +188 -130
- package/dist/tools/appMap.js.map +1 -1
- package/dist/tools/build.js +32 -42
- package/dist/tools/build.js.map +1 -1
- package/dist/tools/bundletool.js +21 -31
- package/dist/tools/bundletool.js.map +1 -1
- package/dist/tools/clearOverlay.js +55 -12
- package/dist/tools/clearOverlay.js.map +1 -1
- package/dist/tools/device.js +76 -24
- package/dist/tools/device.js.map +1 -1
- package/dist/tools/doctor.js +68 -28
- package/dist/tools/doctor.js.map +1 -1
- package/dist/tools/explore.js +41 -47
- package/dist/tools/explore.js.map +1 -1
- package/dist/tools/featureTesting.js +31 -48
- package/dist/tools/featureTesting.js.map +1 -1
- package/dist/tools/firstRun.js +34 -41
- package/dist/tools/firstRun.js.map +1 -1
- package/dist/tools/flow.js +183 -103
- package/dist/tools/flow.js.map +1 -1
- package/dist/tools/flowRepair.js +25 -13
- package/dist/tools/flowRepair.js.map +1 -1
- package/dist/tools/generate.js +52 -89
- package/dist/tools/generate.js.map +1 -1
- package/dist/tools/getArtifact.js +16 -5
- package/dist/tools/getArtifact.js.map +1 -1
- package/dist/tools/health.js +15 -12
- package/dist/tools/health.js.map +1 -1
- package/dist/tools/ios.js +45 -223
- package/dist/tools/ios.js.map +1 -1
- package/dist/tools/issues.js +251 -35
- package/dist/tools/issues.js.map +1 -1
- package/dist/tools/jobs.js +41 -16
- package/dist/tools/jobs.js.map +1 -1
- package/dist/tools/metro.js +36 -35
- package/dist/tools/metro.js.map +1 -1
- package/dist/tools/mobileAudit.js +50 -45
- package/dist/tools/mobileAudit.js.map +1 -1
- package/dist/tools/network.js +31 -14
- package/dist/tools/network.js.map +1 -1
- package/dist/tools/note.js +26 -26
- package/dist/tools/note.js.map +1 -1
- package/dist/tools/prepareIosTarget.js +10 -16
- package/dist/tools/prepareIosTarget.js.map +1 -1
- package/dist/tools/prepareTarget.js +117 -50
- package/dist/tools/prepareTarget.js.map +1 -1
- package/dist/tools/report.js +11 -22
- package/dist/tools/report.js.map +1 -1
- package/dist/tools/resolveArtifact.js +11 -15
- package/dist/tools/resolveArtifact.js.map +1 -1
- package/dist/tools/resolveTarget.js +78 -24
- package/dist/tools/resolveTarget.js.map +1 -1
- package/dist/tools/screenRecord.js +23 -21
- package/dist/tools/screenRecord.js.map +1 -1
- package/dist/tools/screenshot.js +23 -16
- package/dist/tools/screenshot.js.map +1 -1
- package/dist/tools/smoke.js +20 -16
- package/dist/tools/smoke.js.map +1 -1
- package/dist/tools/snapshot.js +155 -118
- package/dist/tools/snapshot.js.map +1 -1
- package/dist/tools/startSession.js +104 -116
- package/dist/tools/startSession.js.map +1 -1
- package/dist/tools/suite.js +72 -43
- package/dist/tools/suite.js.map +1 -1
- package/dist/tools/testSuite.js +30 -20
- package/dist/tools/testSuite.js.map +1 -1
- package/dist/tools/testThis.js +24 -28
- package/dist/tools/testThis.js.map +1 -1
- package/dist/tools/visual.js +686 -210
- package/dist/tools/visual.js.map +1 -1
- package/dist/tools/wait.js +9 -38
- package/dist/tools/wait.js.map +1 -1
- package/dist/tools/wda.js +187 -57
- package/dist/tools/wda.js.map +1 -1
- package/dist/version.js +31 -22
- package/dist/version.js.map +1 -1
- package/dist/visual/ocr.js +53 -6
- package/dist/visual/ocr.js.map +1 -1
- package/dist/visual/provider.js +80 -12
- 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 +194 -128
- package/docs/physical-devices.md +84 -0
- package/docs/tools.md +812 -152
- package/package.json +3 -2
- package/dist/tools/assertVisual.js +0 -84
- package/dist/tools/assertVisual.js.map +0 -1
- package/dist/tools/automationGenerate.js.map +0 -1
- package/dist/tools/capabilities.js +0 -179
- 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.map +0 -1
- package/dist/tools/history.js +0 -86
- package/dist/tools/history.js.map +0 -1
- package/dist/tools/locator.js +0 -85
- package/dist/tools/locator.js.map +0 -1
- package/dist/tools/permissions.js +0 -174
- package/dist/tools/permissions.js.map +0 -1
- package/dist/tools/plan.js +0 -52
- package/dist/tools/plan.js.map +0 -1
- package/dist/tools/screenInfo.js +0 -76
- package/dist/tools/screenInfo.js.map +0 -1
- package/dist/tools/seed.js +0 -133
- package/dist/tools/seed.js.map +0 -1
- package/dist/tools/state.js +0 -217
- package/dist/tools/state.js.map +0 -1
- package/dist/tools/visualText.js +0 -106
- package/dist/tools/visualText.js.map +0 -1
|
@@ -1,45 +1,108 @@
|
|
|
1
|
-
// qa_start_session
|
|
1
|
+
// qa_start_session: resolve projectRoot (src/context/projectRoot.ts) and open a session.
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
4
|
-
import { join } from 'node:path';
|
|
3
|
+
import { loadProjectFixtures } from '../fixtures/load.js';
|
|
5
4
|
import { qaOk, qaError } from '../lib/result.js';
|
|
6
|
-
import { resolveProjectRoot } from '../context/projectRoot.js';
|
|
5
|
+
import { resolveProjectRoot, unresolvedProjectRootError } from '../context/projectRoot.js';
|
|
7
6
|
import { loadProjectConfig } from '../cli/scan.js';
|
|
8
7
|
import { SWIPIUM_VERSION, TOOL_COUNT } from '../version.js';
|
|
9
8
|
import { getSchemaHash } from '../lib/schemaHash.js';
|
|
10
9
|
import { BUDGET_PROFILES } from '../session/store.js';
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
/** Full fixture shape. The tool's inputSchema advertises only `{ name, …passthrough }` (the nested
|
|
11
|
+
* seed/cleanup/fields schema tripled the tool's size in tools/list); fixtures are validated
|
|
12
|
+
* against this schema in the handler and rejected with INVALID_ARGUMENT, so nothing is lost. */
|
|
13
|
+
export const FIXTURE_SCHEMA = z.object({
|
|
14
|
+
name: z.string(),
|
|
15
|
+
description: z.string().optional(),
|
|
16
|
+
requiredState: z.string().optional(),
|
|
17
|
+
recommendedSetup: z.string().optional(),
|
|
18
|
+
testAccount: z.string().optional(),
|
|
19
|
+
apkPath: z.string().optional(),
|
|
20
|
+
value: z.string().optional().describe('Non-secret safe test input (e.g. flight number/search term) for exploration text entry.'),
|
|
21
|
+
disposable: z.boolean().optional().describe('True only for disposable accounts/data that destructive QA may mutate or delete.'),
|
|
22
|
+
environment: z.string().optional().describe('Environment label. Use "test" for non-production disposable test state.'),
|
|
23
|
+
fields: z
|
|
24
|
+
.record(z.object({
|
|
25
|
+
value: z.string().optional(),
|
|
26
|
+
var: z.string().optional().describe('Environment/secure-input variable name to read at runtime.'),
|
|
27
|
+
secret: z.boolean().optional(),
|
|
28
|
+
generator: z
|
|
29
|
+
.enum([
|
|
30
|
+
'email',
|
|
31
|
+
'email_address',
|
|
32
|
+
'person',
|
|
33
|
+
'person_name',
|
|
34
|
+
'full_name',
|
|
35
|
+
'display_name',
|
|
36
|
+
'number',
|
|
37
|
+
'numeric',
|
|
38
|
+
'text',
|
|
39
|
+
'city',
|
|
40
|
+
'city_name',
|
|
41
|
+
'country',
|
|
42
|
+
'country_name',
|
|
43
|
+
'color',
|
|
44
|
+
'phone',
|
|
45
|
+
'phone_number',
|
|
46
|
+
'mobile',
|
|
47
|
+
'date',
|
|
48
|
+
'date_iso',
|
|
49
|
+
])
|
|
50
|
+
.optional(),
|
|
51
|
+
role: z.string().optional(),
|
|
52
|
+
inputType: z.string().optional(),
|
|
53
|
+
}))
|
|
54
|
+
.optional()
|
|
55
|
+
.describe('Typed fixture catalog for form entry. Fields match by label/id/role and may use a fixed value, variable, or safe generator.'),
|
|
56
|
+
seed: z
|
|
57
|
+
.object({
|
|
58
|
+
type: z.enum(['deeplink', 'script', 'api']),
|
|
59
|
+
url: z.string().optional(),
|
|
60
|
+
command: z
|
|
61
|
+
.union([z.string(), z.array(z.string())])
|
|
62
|
+
.optional()
|
|
63
|
+
.describe('script: argv array preferred (string is deprecated).'),
|
|
64
|
+
method: z.string().optional(),
|
|
65
|
+
body: z.string().optional(),
|
|
66
|
+
headers: z.record(z.string()).optional(),
|
|
67
|
+
idempotent: z.boolean().optional().describe('True when re-running this seed safely converges to the same state.'),
|
|
68
|
+
cleanup: z
|
|
69
|
+
.object({
|
|
70
|
+
type: z.enum(['deeplink', 'script', 'api']),
|
|
71
|
+
url: z.string().optional(),
|
|
72
|
+
command: z
|
|
73
|
+
.union([z.string(), z.array(z.string())])
|
|
74
|
+
.optional()
|
|
75
|
+
.describe('script: argv array preferred (string is deprecated).'),
|
|
76
|
+
method: z.string().optional(),
|
|
77
|
+
body: z.string().optional(),
|
|
78
|
+
headers: z.record(z.string()).optional(),
|
|
79
|
+
})
|
|
80
|
+
.optional()
|
|
81
|
+
.describe('Optional teardown/rollback action used for state-profile transactions.'),
|
|
82
|
+
})
|
|
83
|
+
.optional()
|
|
84
|
+
.describe('Opt-in, consent-gated way to create this precondition during flows.'),
|
|
85
|
+
});
|
|
86
|
+
// Moved to src/fixtures/load.ts (store.ts re-reads it on rehydrate); re-exported for callers.
|
|
87
|
+
export { loadProjectFixtures } from '../fixtures/load.js';
|
|
25
88
|
export function registerStartSession(server, sessions) {
|
|
26
89
|
server.registerTool('qa_start_session', {
|
|
27
90
|
title: 'Start a QA session',
|
|
28
|
-
description: '
|
|
91
|
+
description: 'Open a QA session (only needed for low-level tools; qa_test_this creates one). projectRoot: arg, else MCP roots, else ' +
|
|
92
|
+
'SWIPIUM_PROJECT_ROOT / CLAUDE_PROJECT_DIR, else the server cwd if it is not / or $HOME and contains a project marker (package.json, app.json, pubspec.yaml, Gradle/Xcode files, Podfile, android/, ios/). Budget defaults to 8 min / 20 ' +
|
|
93
|
+
'actions / 8 screenshots; profile resizes it. fixtures (or .swipium/fixtures.json) declare preconditions so unmet ones ' +
|
|
94
|
+
'report as blocked, not failed. responseMode compact keeps transcripts small.',
|
|
29
95
|
inputSchema: {
|
|
30
|
-
projectRoot: z.string().optional().describe('Absolute
|
|
96
|
+
projectRoot: z.string().optional().describe('Absolute app path (optional with MCP roots).'),
|
|
31
97
|
responseMode: z
|
|
32
98
|
.enum(['compact', 'normal', 'verbose'])
|
|
33
99
|
.optional()
|
|
34
|
-
.describe('Text
|
|
35
|
-
sensitive: z
|
|
36
|
-
.boolean()
|
|
37
|
-
.optional()
|
|
38
|
-
.describe('Sensitive mode: refuse all screenshots, screen recordings, and on-screen-error evidence capture for this session (no pixels leave the device). For privacy-sensitive apps.'),
|
|
100
|
+
.describe('Text channel for every tool in this session: compact = summary + URIs; normal (default) = + JSON; verbose.'),
|
|
101
|
+
sensitive: z.boolean().optional().describe('Refuse all screenshots, recordings, and on-screen evidence capture for this session.'),
|
|
39
102
|
profile: z
|
|
40
103
|
.enum(['guardrail', 'login_smoke', 'full_smoke', 'install_smoke'])
|
|
41
104
|
.optional()
|
|
42
|
-
.describe('Budget class
|
|
105
|
+
.describe('Budget class sizing the time budget.'),
|
|
43
106
|
budget: z
|
|
44
107
|
.object({
|
|
45
108
|
maxMinutes: z.number().optional(),
|
|
@@ -51,100 +114,25 @@ export function registerStartSession(server, sessions) {
|
|
|
51
114
|
.optional()
|
|
52
115
|
.describe('Override default budget caps.'),
|
|
53
116
|
fixtures: z
|
|
54
|
-
.array(z.object({
|
|
55
|
-
name: z.string(),
|
|
56
|
-
description: z.string().optional(),
|
|
57
|
-
requiredState: z.string().optional(),
|
|
58
|
-
recommendedSetup: z.string().optional(),
|
|
59
|
-
testAccount: z.string().optional(),
|
|
60
|
-
apkPath: z.string().optional(),
|
|
61
|
-
value: z
|
|
62
|
-
.string()
|
|
63
|
-
.optional()
|
|
64
|
-
.describe('Non-secret safe test input (e.g. flight number/search term) for exploration text entry.'),
|
|
65
|
-
disposable: z
|
|
66
|
-
.boolean()
|
|
67
|
-
.optional()
|
|
68
|
-
.describe('True only for disposable accounts/data that destructive QA may mutate or delete.'),
|
|
69
|
-
environment: z.string().optional().describe('Environment label. Use "test" for non-production disposable test state.'),
|
|
70
|
-
fields: z
|
|
71
|
-
.record(z.object({
|
|
72
|
-
value: z.string().optional(),
|
|
73
|
-
var: z.string().optional().describe('Environment/secure-input variable name to read at runtime.'),
|
|
74
|
-
secret: z.boolean().optional(),
|
|
75
|
-
generator: z
|
|
76
|
-
.enum([
|
|
77
|
-
'email',
|
|
78
|
-
'email_address',
|
|
79
|
-
'person',
|
|
80
|
-
'person_name',
|
|
81
|
-
'full_name',
|
|
82
|
-
'display_name',
|
|
83
|
-
'number',
|
|
84
|
-
'numeric',
|
|
85
|
-
'text',
|
|
86
|
-
'city',
|
|
87
|
-
'city_name',
|
|
88
|
-
'country',
|
|
89
|
-
'country_name',
|
|
90
|
-
'color',
|
|
91
|
-
'phone',
|
|
92
|
-
'phone_number',
|
|
93
|
-
'mobile',
|
|
94
|
-
'date',
|
|
95
|
-
'date_iso',
|
|
96
|
-
])
|
|
97
|
-
.optional(),
|
|
98
|
-
role: z.string().optional(),
|
|
99
|
-
inputType: z.string().optional(),
|
|
100
|
-
}))
|
|
101
|
-
.optional()
|
|
102
|
-
.describe('Typed fixture catalog for form entry. Fields match by label/id/role and may use a fixed value, variable, or safe generator.'),
|
|
103
|
-
seed: z
|
|
104
|
-
.object({
|
|
105
|
-
type: z.enum(['deeplink', 'script', 'api']),
|
|
106
|
-
url: z.string().optional(),
|
|
107
|
-
command: z
|
|
108
|
-
.union([z.string(), z.array(z.string())])
|
|
109
|
-
.optional()
|
|
110
|
-
.describe('script: argv array preferred (string is deprecated).'),
|
|
111
|
-
method: z.string().optional(),
|
|
112
|
-
body: z.string().optional(),
|
|
113
|
-
headers: z.record(z.string()).optional(),
|
|
114
|
-
idempotent: z.boolean().optional().describe('True when re-running this seed safely converges to the same state.'),
|
|
115
|
-
cleanup: z
|
|
116
|
-
.object({
|
|
117
|
-
type: z.enum(['deeplink', 'script', 'api']),
|
|
118
|
-
url: z.string().optional(),
|
|
119
|
-
command: z
|
|
120
|
-
.union([z.string(), z.array(z.string())])
|
|
121
|
-
.optional()
|
|
122
|
-
.describe('script: argv array preferred (string is deprecated).'),
|
|
123
|
-
method: z.string().optional(),
|
|
124
|
-
body: z.string().optional(),
|
|
125
|
-
headers: z.record(z.string()).optional(),
|
|
126
|
-
})
|
|
127
|
-
.optional()
|
|
128
|
-
.describe('Optional teardown/rollback action used for state-profile transactions.'),
|
|
129
|
-
})
|
|
130
|
-
.optional()
|
|
131
|
-
.describe('Opt-in, consent-gated way to create this precondition during flows.'),
|
|
132
|
-
}))
|
|
117
|
+
.array(z.object({ name: z.string() }).passthrough())
|
|
133
118
|
.optional()
|
|
134
|
-
.describe('Declared preconditions
|
|
119
|
+
.describe('Declared preconditions (merged with .swipium/fixtures.json): {name, requiredState?, recommendedSetup?, value?, disposable?, fields?, seed?, …}. Full shape in docs/tools.md#qa_start_session.'),
|
|
135
120
|
},
|
|
136
121
|
}, async ({ projectRoot, profile, budget, fixtures, responseMode, sensitive }) => {
|
|
137
|
-
const
|
|
138
|
-
if (!
|
|
122
|
+
const parsedFixtures = z.array(FIXTURE_SCHEMA).optional().safeParse(fixtures);
|
|
123
|
+
if (!parsedFixtures.success)
|
|
139
124
|
return qaError({
|
|
140
|
-
what:
|
|
125
|
+
what: `Invalid fixtures: ${parsedFixtures.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ')}`,
|
|
141
126
|
changedState: false,
|
|
142
127
|
retrySafe: true,
|
|
143
|
-
|
|
144
|
-
|
|
128
|
+
failureCode: 'INVALID_ARGUMENT',
|
|
129
|
+
nextSteps: ['Fix the fixture shape (see docs/tools.md#qa_start_session) and re-call qa_start_session.'],
|
|
145
130
|
});
|
|
131
|
+
const resolved = await resolveProjectRoot(server, projectRoot);
|
|
132
|
+
if (!resolved.root) {
|
|
133
|
+
return unresolvedProjectRootError(resolved);
|
|
146
134
|
}
|
|
147
|
-
// Budget profile
|
|
135
|
+
// Budget profile sets recommended minutes; explicit profile sets the budget unless the
|
|
148
136
|
// caller also gave maxMinutes. Warn when the resulting time budget is below the class min.
|
|
149
137
|
const profileMinutes = profile ? BUDGET_PROFILES[profile] : undefined;
|
|
150
138
|
const effBudget = { ...(budget ?? {}) };
|
|
@@ -152,10 +140,10 @@ export function registerStartSession(server, sessions) {
|
|
|
152
140
|
effBudget.maxMinutes = profileMinutes;
|
|
153
141
|
const warnings = [];
|
|
154
142
|
if (profileMinutes != null && effBudget.maxMinutes != null && effBudget.maxMinutes < profileMinutes) {
|
|
155
|
-
warnings.push(`Requested ${effBudget.maxMinutes}m is below the ${profile} class (${profileMinutes}m)
|
|
143
|
+
warnings.push(`Requested ${effBudget.maxMinutes}m is below the ${profile} class (${profileMinutes}m), likely too short; consider raising maxMinutes.`);
|
|
156
144
|
}
|
|
157
145
|
// Surface a prior `swipium scan` (.swipium/config.json) so the agent knows the
|
|
158
|
-
// recommended profile / appId without re-scanning. Informational
|
|
146
|
+
// recommended profile / appId without re-scanning. Informational only, never auto-overrides
|
|
159
147
|
// an explicit profile choice.
|
|
160
148
|
const projectConfig = loadProjectConfig(resolved.root);
|
|
161
149
|
if (projectConfig && !profile && typeof projectConfig.recommendedProfile === 'string') {
|
|
@@ -166,7 +154,7 @@ export function registerStartSession(server, sessions) {
|
|
|
166
154
|
const byName = new Map();
|
|
167
155
|
for (const f of fileFixtures)
|
|
168
156
|
byName.set(f.name, f);
|
|
169
|
-
for (const f of
|
|
157
|
+
for (const f of parsedFixtures.data ?? [])
|
|
170
158
|
byName.set(f.name, f);
|
|
171
159
|
const mergedFixtures = [...byName.values()];
|
|
172
160
|
const session = sessions.create(resolved.root, effBudget, {
|
|
@@ -204,7 +192,7 @@ export function registerStartSession(server, sessions) {
|
|
|
204
192
|
: null,
|
|
205
193
|
declaredPreconditions: mergedFixtures,
|
|
206
194
|
warnings,
|
|
207
|
-
}, `Session ${session.id} started (Swipium v${SWIPIUM_VERSION}, ${TOOL_COUNT} tools
|
|
195
|
+
}, `Session ${session.id} started (Swipium v${SWIPIUM_VERSION}, ${TOOL_COUNT} tools; if your client lists fewer, restart it).\n` +
|
|
208
196
|
`projectRoot: ${session.root} (via ${resolved.source})\n` +
|
|
209
197
|
`budget: ${JSON.stringify(session.budget)}${profile ? ` (profile=${profile})` : ''}\n` +
|
|
210
198
|
(mergedFixtures.length ? `preconditions declared: ${mergedFixtures.map((f) => f.name).join(', ')}\n` : '') +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startSession.js","sourceRoot":"","sources":["../../src/tools/startSession.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"startSession.js","sourceRoot":"","sources":["../../src/tools/startSession.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/suite.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// Per-run suite generation cores
|
|
1
|
+
// Per-run suite generation cores + qa_flow_compile.
|
|
2
2
|
//
|
|
3
3
|
// These turn the actions recorded during a session (qa_act) into a MAINTAINABLE POM suite under
|
|
4
|
-
// .swipium
|
|
4
|
+
// .swipium/: page objects (selectors), tests (reference elements by name), a suite, a test-case
|
|
5
5
|
// catalog, and a locator audit. The generation entry points (page objects, full suite, test-case
|
|
6
6
|
// catalog) are exposed through qa_generate (src/tools/generate.ts); this file registers only
|
|
7
7
|
// qa_flow_compile, which compiles a generated POM suite into runnable Flow V2 for qa_flow_run.
|
|
@@ -9,7 +9,8 @@ import { z } from 'zod';
|
|
|
9
9
|
import { createHash } from 'node:crypto';
|
|
10
10
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
11
|
import { join, dirname } from 'node:path';
|
|
12
|
-
import { qaOk, qaError } from '../lib/result.js';
|
|
12
|
+
import { qaOk, qaError, unknownSessionError } from '../lib/result.js';
|
|
13
|
+
import { resolveProjectRoot, unresolvedProjectRootError } from '../context/projectRoot.js';
|
|
13
14
|
import { requireConsent, consumeConsent } from '../consent/consent.js';
|
|
14
15
|
import { generatePom } from '../suite/pom.js';
|
|
15
16
|
import { generateTestCases } from '../suite/testcase.js';
|
|
@@ -18,10 +19,11 @@ import { parseFlow } from '../flows/schema.js';
|
|
|
18
19
|
import { runFlow } from '../flows/run.js';
|
|
19
20
|
import { loadProjectConfig } from '../cli/scan.js';
|
|
20
21
|
import { generateAndCompileSuite } from '../services/suiteGenerate.js';
|
|
22
|
+
import { assertNoSecretLeaks, findSecretLeaks, inputBindings, secretSafeNotes, structuralLiterals } from '../suite/secretGuard.js';
|
|
21
23
|
import { getDriver } from '../session/attach.js';
|
|
22
24
|
import { readinessForSession } from '../report/readiness.js';
|
|
23
25
|
import { loadStateProfile, prepareStateProfile, teardownStateProfile, verifyStateProfile } from '../state/profile.js';
|
|
24
|
-
import { hasStateProfileMutation, stateProfileAffects, stateRisk } from '
|
|
26
|
+
import { hasStateProfileMutation, stateProfileAffects, stateRisk } from '../state/consent.js';
|
|
25
27
|
function stable(value) {
|
|
26
28
|
if (Array.isArray(value))
|
|
27
29
|
return value.map(stable);
|
|
@@ -61,7 +63,7 @@ function compiledFlowIdentity(c) {
|
|
|
61
63
|
function appIdOf(session) {
|
|
62
64
|
return session.appId ?? loadProjectConfig(session.root)?.appId ?? undefined;
|
|
63
65
|
}
|
|
64
|
-
/** Map the replay outcome to the test-case catalog's replay status (Deliverable 4
|
|
66
|
+
/** Map the replay outcome to the test-case catalog's replay status (Deliverable 4, honest plumbing). */
|
|
65
67
|
function catalogReplayStatus(mode, results, passed) {
|
|
66
68
|
if (mode === 'none' || results.length === 0)
|
|
67
69
|
return 'not_replayed';
|
|
@@ -84,6 +86,7 @@ function suiteDir(session) {
|
|
|
84
86
|
}
|
|
85
87
|
/** Write generated files under .swipium/, returning absolute paths written. */
|
|
86
88
|
function writeFiles(session, files) {
|
|
89
|
+
assertNoSecretLeaks(files, session.secrets, 'suite generation', { structural: structuralLiterals(session.recordedActions) }); // backstop: never write a secret
|
|
87
90
|
const base = suiteDir(session);
|
|
88
91
|
const written = [];
|
|
89
92
|
for (const f of files) {
|
|
@@ -94,12 +97,29 @@ function writeFiles(session, files) {
|
|
|
94
97
|
}
|
|
95
98
|
return written;
|
|
96
99
|
}
|
|
100
|
+
/** Loud refusal when generated files would carry a registered secret value (nothing is written). */
|
|
101
|
+
function secretLeakError(session, files) {
|
|
102
|
+
const leaks = findSecretLeaks(files, session.secrets, { structural: structuralLiterals(session.recordedActions) });
|
|
103
|
+
if (!leaks.length)
|
|
104
|
+
return null;
|
|
105
|
+
return qaError({
|
|
106
|
+
what: `Not generated: a registered secret value would be written in plaintext (${leaks
|
|
107
|
+
.slice(0, 5)
|
|
108
|
+
.map((l) => `${l.path}:${l.line}`)
|
|
109
|
+
.join(', ')})`,
|
|
110
|
+
changedState: false,
|
|
111
|
+
retrySafe: false,
|
|
112
|
+
failureCode: 'SECRET_IN_GENERATED_OUTPUT',
|
|
113
|
+
nextSteps: ['Nothing was written. Re-record the credential step so it is captured as a ${VAR}, then regenerate.'],
|
|
114
|
+
});
|
|
115
|
+
}
|
|
97
116
|
function requireActions(session) {
|
|
98
117
|
if (!session.recordedActions.length) {
|
|
99
118
|
return qaError({
|
|
100
119
|
what: 'No actions recorded in this session yet',
|
|
101
120
|
changedState: false,
|
|
102
121
|
retrySafe: true,
|
|
122
|
+
failureCode: 'NO_RECORDED_ACTIONS',
|
|
103
123
|
nextSteps: ['Drive the app with qa_act first (each action is recorded), then regenerate the suite.'],
|
|
104
124
|
});
|
|
105
125
|
}
|
|
@@ -108,47 +128,54 @@ function requireActions(session) {
|
|
|
108
128
|
function pomFor(session, name) {
|
|
109
129
|
const appId = appIdOf(session);
|
|
110
130
|
const flowName = (name ?? `${(appId ?? 'app').split('.').pop()}-smoke`).replace(/[^\w.-]+/g, '-');
|
|
111
|
-
const pom = generatePom(session.recordedActions, {
|
|
131
|
+
const pom = generatePom(session.recordedActions, {
|
|
132
|
+
name: flowName,
|
|
133
|
+
appId,
|
|
134
|
+
budgetProfile: session.budgetProfile,
|
|
135
|
+
secrets: session.secrets,
|
|
136
|
+
inputs: inputBindings(session), // typed text equal to a stored input maps to its ${SWIPIUM_TEST_*} placeholder
|
|
137
|
+
});
|
|
112
138
|
return { pom, flowName };
|
|
113
139
|
}
|
|
114
|
-
/** Core handler for qa_generate target:"pom"
|
|
140
|
+
/** Core handler for qa_generate target:"pom": page objects + locator audit from recorded actions. */
|
|
115
141
|
export async function runPomGenerate(sessions, { sessionId, name, save }) {
|
|
116
142
|
const session = sessions.get(sessionId);
|
|
117
143
|
if (!session)
|
|
118
|
-
return
|
|
119
|
-
what: `Unknown sessionId ${sessionId}`,
|
|
120
|
-
changedState: false,
|
|
121
|
-
retrySafe: true,
|
|
122
|
-
nextSteps: ['Call qa_start_session first.'],
|
|
123
|
-
});
|
|
144
|
+
return unknownSessionError(sessionId);
|
|
124
145
|
const noActions = requireActions(session);
|
|
125
146
|
if (noActions)
|
|
126
147
|
return noActions;
|
|
127
148
|
const { pom } = pomFor(session, name);
|
|
128
149
|
const pageFiles = pom.files.filter((f) => f.path.startsWith('pages/') || f.path.startsWith('locators/'));
|
|
150
|
+
const pomLeak = secretLeakError(session, pageFiles);
|
|
151
|
+
if (pomLeak)
|
|
152
|
+
return pomLeak;
|
|
129
153
|
const written = save ? writeFiles(session, pageFiles) : [];
|
|
130
154
|
const summary = `Generated ${pom.pages.length} page object(s): ${pom.pages.map((p) => p.name).join(', ')}\n` +
|
|
131
155
|
`locator audit: ${pom.audit.durable} durable / ${pom.audit.semi} semi / ${pom.audit.brittle} brittle (${pom.audit.brittlePct}% brittle)` +
|
|
132
|
-
(save ? `\nsaved ${written.length} files under .swipium/` : `\n(not saved
|
|
156
|
+
(save ? `\nsaved ${written.length} files under .swipium/` : `\n(not saved; pass save:true)`);
|
|
133
157
|
return qaOk({ pages: pom.pages, audit: pom.audit, files: pageFiles, written }, summary);
|
|
134
158
|
}
|
|
135
159
|
/**
|
|
136
|
-
* Core handler for qa_generate target:"suite"
|
|
160
|
+
* Core handler for qa_generate target:"suite": the full per-run .swipium/ suite from recorded
|
|
137
161
|
* actions: pages + tests + suite + test cases + locator audit, plus compile and replay gates.
|
|
138
162
|
*/
|
|
139
163
|
export async function runSuiteGenerate(sessions, { sessionId, name, save, compile, replay, stateProfile, consentId, approve }) {
|
|
140
164
|
const session = sessions.get(sessionId);
|
|
141
165
|
if (!session)
|
|
142
|
-
return
|
|
143
|
-
what: `Unknown sessionId ${sessionId}`,
|
|
144
|
-
changedState: false,
|
|
145
|
-
retrySafe: true,
|
|
146
|
-
nextSteps: ['Call qa_start_session first.'],
|
|
147
|
-
});
|
|
166
|
+
return unknownSessionError(sessionId);
|
|
148
167
|
const noActions = requireActions(session);
|
|
149
168
|
if (noActions)
|
|
150
169
|
return noActions;
|
|
151
170
|
const res = generateAndCompileSuite(sessions, session, { name, save: save !== false, compile: compile !== false });
|
|
171
|
+
if (res.failureCode)
|
|
172
|
+
return qaError({
|
|
173
|
+
what: `Suite not generated: ${res.skippedReason}`,
|
|
174
|
+
changedState: false,
|
|
175
|
+
retrySafe: false,
|
|
176
|
+
failureCode: 'SECRET_IN_GENERATED_OUTPUT',
|
|
177
|
+
nextSteps: [res.recommendation ?? 'Re-record the credential step so it is captured as a ${VAR}, then regenerate.'],
|
|
178
|
+
});
|
|
152
179
|
const audit = res.audit;
|
|
153
180
|
const ranOk = res.compiledFlows.filter((c) => c.ok).length;
|
|
154
181
|
const replayMode = replay ?? 'dry_run';
|
|
@@ -381,7 +408,7 @@ export async function runSuiteGenerate(sessions, { sessionId, name, save, compil
|
|
|
381
408
|
const tc = generateTestCases(pom, {
|
|
382
409
|
appId: appIdOf(session),
|
|
383
410
|
fixtures: session.fixtures,
|
|
384
|
-
notes: session.notes,
|
|
411
|
+
notes: secretSafeNotes(session.notes, session.secrets),
|
|
385
412
|
budgetProfile: session.budgetProfile,
|
|
386
413
|
replayStatus,
|
|
387
414
|
});
|
|
@@ -395,12 +422,12 @@ export async function runSuiteGenerate(sessions, { sessionId, name, save, compil
|
|
|
395
422
|
const summary = `✅ Suite "${res.name}" generated (${res.pages?.length ?? 0} pages, ${res.testCases?.length ?? 0} test case, suite + audit).\n` +
|
|
396
423
|
`durability: ${audit.durable} durable / ${audit.semi} semi / ${audit.brittle} brittle (${audit.brittlePct}% brittle)` +
|
|
397
424
|
(res.variables?.length ? `\nvariables: ${res.variables.join(', ')}` : '') +
|
|
398
|
-
(audit.brittle ? `\n⚠ ${audit.brittle} brittle locator(s)
|
|
399
|
-
(written.length ? `\nwrote ${written.length} files under ${suiteDir(session)}` : `\n(preview
|
|
425
|
+
(audit.brittle ? `\n⚠ ${audit.brittle} brittle locator(s), see locators/locator-audit.json for app-code fixes.` : '') +
|
|
426
|
+
(written.length ? `\nwrote ${written.length} files under ${suiteDir(session)}` : `\n(preview; pass save:true to write)`) +
|
|
400
427
|
(res.compiledFlows.length
|
|
401
|
-
? `\ncompiled ${ranOk}/${res.compiledFlows.length} runnable flow(s)
|
|
428
|
+
? `\ncompiled ${ranOk}/${res.compiledFlows.length} runnable flow(s) > run: swipium suite run (or qa_flow_run).`
|
|
402
429
|
: `\nNext: qa_flow_compile to produce runnable Flow V2.`) +
|
|
403
|
-
`\nreplay gate (${replayMode}): ${replayPassed ? 'passed' : replayResults.length ? 'not proven' : 'not run'}; readiness=${readiness}; labels=${readinessLabels.join('
|
|
430
|
+
`\nreplay gate (${replayMode}): ${replayPassed ? 'passed' : replayResults.length ? 'not proven' : 'not run'}; readiness=${readiness}; labels=${readinessLabels.join(' > ')}` +
|
|
404
431
|
(ciReady
|
|
405
432
|
? '\nci_ready: compiled + fresh-state replay + state evidence proved'
|
|
406
433
|
: replayMode === 'fresh_state'
|
|
@@ -426,16 +453,11 @@ export async function runSuiteGenerate(sessions, { sessionId, name, save, compil
|
|
|
426
453
|
ciReadyGate,
|
|
427
454
|
}, summary);
|
|
428
455
|
}
|
|
429
|
-
/** Core handler for qa_generate target:"testcases"
|
|
456
|
+
/** Core handler for qa_generate target:"testcases": industry-style test case catalog from recorded actions. */
|
|
430
457
|
export async function runTestcaseGenerate(sessions, { sessionId, name, format, save }) {
|
|
431
458
|
const session = sessions.get(sessionId);
|
|
432
459
|
if (!session)
|
|
433
|
-
return
|
|
434
|
-
what: `Unknown sessionId ${sessionId}`,
|
|
435
|
-
changedState: false,
|
|
436
|
-
retrySafe: true,
|
|
437
|
-
nextSteps: ['Call qa_start_session first.'],
|
|
438
|
-
});
|
|
460
|
+
return unknownSessionError(sessionId);
|
|
439
461
|
const noActions = requireActions(session);
|
|
440
462
|
if (noActions)
|
|
441
463
|
return noActions;
|
|
@@ -443,7 +465,7 @@ export async function runTestcaseGenerate(sessions, { sessionId, name, format, s
|
|
|
443
465
|
const tc = generateTestCases(pom, {
|
|
444
466
|
appId: appIdOf(session),
|
|
445
467
|
fixtures: session.fixtures,
|
|
446
|
-
notes: session.notes,
|
|
468
|
+
notes: secretSafeNotes(session.notes, session.secrets),
|
|
447
469
|
budgetProfile: session.budgetProfile,
|
|
448
470
|
});
|
|
449
471
|
const fmt = format ?? 'both';
|
|
@@ -452,6 +474,9 @@ export async function runTestcaseGenerate(sessions, { sessionId, name, format, s
|
|
|
452
474
|
files.push({ path: `testcases/${flowName}.cases.yaml`, content: tc.yaml });
|
|
453
475
|
if (fmt !== 'yaml')
|
|
454
476
|
files.push({ path: `testcases/${flowName}.cases.md`, content: tc.markdown });
|
|
477
|
+
const tcLeak = secretLeakError(session, files);
|
|
478
|
+
if (tcLeak)
|
|
479
|
+
return tcLeak;
|
|
455
480
|
const written = save ? writeFiles(session, files) : [];
|
|
456
481
|
const summary = `Generated ${tc.cases.length} test case(s): ${tc.cases.map((c) => `${c.id} ${c.title}`).join('; ')}` +
|
|
457
482
|
(written.length ? `\nsaved under .swipium/testcases` : '');
|
|
@@ -461,19 +486,23 @@ export function registerSuite(server, sessions) {
|
|
|
461
486
|
// ---- qa_flow_compile ----
|
|
462
487
|
server.registerTool('qa_flow_compile', {
|
|
463
488
|
title: 'Compile a POM suite to runnable flows',
|
|
464
|
-
description: 'Compile
|
|
489
|
+
description: 'Compile an existing POM suite on disk (.swipium/suites/<suite>.yaml, e.g. committed or hand-edited) into runnable Flow V2 ' +
|
|
490
|
+
'for qa_flow_run: resolves page-object refs to selectors, carries variables, writes .swipium/flows/<slug>.yaml (+ a copy ' +
|
|
491
|
+
'under .swipium/compiled/), and validates each flow. Needs no session or recorded actions; qa_generate target:"suite" ' +
|
|
492
|
+
'already compiles the suite it generates from a run.',
|
|
465
493
|
inputSchema: {
|
|
466
494
|
sessionId: z.string().optional(),
|
|
467
495
|
projectRoot: z.string().optional(),
|
|
468
496
|
suite: z.string().optional().describe('Suite file relative to .swipium/ (default suites/smoke.yaml).'),
|
|
469
497
|
},
|
|
470
498
|
}, async ({ sessionId, projectRoot, suite }) => {
|
|
471
|
-
let root;
|
|
472
|
-
if (
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
499
|
+
let root = sessionId ? sessions.get(sessionId)?.root : undefined;
|
|
500
|
+
if (!root) {
|
|
501
|
+
const resolved = await resolveProjectRoot(server, projectRoot);
|
|
502
|
+
if (!resolved.root)
|
|
503
|
+
return unresolvedProjectRootError(resolved);
|
|
504
|
+
root = resolved.root;
|
|
505
|
+
}
|
|
477
506
|
const result = compileSuite(root, suite ?? 'suites/smoke.yaml');
|
|
478
507
|
if (result.errors.length && result.flows.length === 0) {
|
|
479
508
|
return qaError({
|
|
@@ -494,7 +523,7 @@ export function registerSuite(server, sessions) {
|
|
|
494
523
|
let flowPath;
|
|
495
524
|
let compiledPath;
|
|
496
525
|
if (ok) {
|
|
497
|
-
flowPath = join(flowsDir, `${slug}.yaml`); // discoverable by
|
|
526
|
+
flowPath = join(flowsDir, `${slug}.yaml`); // discoverable by qa_flow_run flow:"<slug>"
|
|
498
527
|
compiledPath = join(compiledDir, `${slug}.flow.yaml`);
|
|
499
528
|
writeFileSync(flowPath, f.yaml);
|
|
500
529
|
writeFileSync(compiledPath, f.yaml);
|
|
@@ -504,7 +533,7 @@ export function registerSuite(server, sessions) {
|
|
|
504
533
|
const okCount = compiled.filter((c) => c.ok).length;
|
|
505
534
|
const summary = `Compiled ${okCount}/${compiled.length} flow(s) from ${result.suite}.\n` +
|
|
506
535
|
compiled
|
|
507
|
-
.map((c) => ` ${c.ok ? '✓' : '✗'} ${c.name}${c.ok ? `
|
|
536
|
+
.map((c) => ` ${c.ok ? '✓' : '✗'} ${c.name}${c.ok ? ` > flows/${c.slug}.yaml (run: qa_flow_run flow:"${c.slug}")` : `: ${c.errors.join('; ')}`}`)
|
|
508
537
|
.join('\n');
|
|
509
538
|
return qaOk({ suite: result.suite, flows: compiled, okCount }, summary);
|
|
510
539
|
});
|