swipium 1.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/LICENSE +21 -0
- package/README.md +295 -0
- package/dist/appMap/automationLink.js +74 -0
- package/dist/appMap/automationLink.js.map +1 -0
- package/dist/appMap/build.js +193 -0
- package/dist/appMap/build.js.map +1 -0
- package/dist/appMap/codeIndex.js +73 -0
- package/dist/appMap/codeIndex.js.map +1 -0
- package/dist/appMap/featureModel.js +83 -0
- package/dist/appMap/featureModel.js.map +1 -0
- package/dist/appMap/firstRunApply.js +131 -0
- package/dist/appMap/firstRunApply.js.map +1 -0
- package/dist/appMap/fsWalk.js +75 -0
- package/dist/appMap/fsWalk.js.map +1 -0
- package/dist/appMap/issues.js +227 -0
- package/dist/appMap/issues.js.map +1 -0
- package/dist/appMap/migrations.js +87 -0
- package/dist/appMap/migrations.js.map +1 -0
- package/dist/appMap/prelaunch.js +66 -0
- package/dist/appMap/prelaunch.js.map +1 -0
- package/dist/appMap/projectRegistry.js +70 -0
- package/dist/appMap/projectRegistry.js.map +1 -0
- package/dist/appMap/provenance.js +52 -0
- package/dist/appMap/provenance.js.map +1 -0
- package/dist/appMap/query.js +118 -0
- package/dist/appMap/query.js.map +1 -0
- package/dist/appMap/runtimeMerge.js +207 -0
- package/dist/appMap/runtimeMerge.js.map +1 -0
- package/dist/appMap/schema.js +65 -0
- package/dist/appMap/schema.js.map +1 -0
- package/dist/appMap/screenMatch.js +75 -0
- package/dist/appMap/screenMatch.js.map +1 -0
- package/dist/appMap/staticScan.js +614 -0
- package/dist/appMap/staticScan.js.map +1 -0
- package/dist/appMap/store.js +173 -0
- package/dist/appMap/store.js.map +1 -0
- package/dist/appMap/tsAstScan.js +184 -0
- package/dist/appMap/tsAstScan.js.map +1 -0
- package/dist/artifacts/bundletool.js +199 -0
- package/dist/artifacts/bundletool.js.map +1 -0
- package/dist/artifacts/resolve.js +388 -0
- package/dist/artifacts/resolve.js.map +1 -0
- package/dist/automationGen/appiumModel.js +129 -0
- package/dist/automationGen/appiumModel.js.map +1 -0
- package/dist/automationGen/ciEmitter.js +80 -0
- package/dist/automationGen/ciEmitter.js.map +1 -0
- package/dist/automationGen/jsEmitter.js +440 -0
- package/dist/automationGen/jsEmitter.js.map +1 -0
- package/dist/automationGen/packagePatch.js +56 -0
- package/dist/automationGen/packagePatch.js.map +1 -0
- package/dist/automationGen/projectProfile.js +317 -0
- package/dist/automationGen/projectProfile.js.map +1 -0
- package/dist/automationGen/pythonEmitter.js +386 -0
- package/dist/automationGen/pythonEmitter.js.map +1 -0
- package/dist/automationGen/readmeEmitter.js +66 -0
- package/dist/automationGen/readmeEmitter.js.map +1 -0
- package/dist/automationGen/suitePlan.js +94 -0
- package/dist/automationGen/suitePlan.js.map +1 -0
- package/dist/automationGen/validation.js +150 -0
- package/dist/automationGen/validation.js.map +1 -0
- package/dist/build/parseBuildLog.js +119 -0
- package/dist/build/parseBuildLog.js.map +1 -0
- package/dist/build/plan.js +150 -0
- package/dist/build/plan.js.map +1 -0
- package/dist/ci/preflight.js +85 -0
- package/dist/ci/preflight.js.map +1 -0
- package/dist/cli/init.js +105 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/scan.js +85 -0
- package/dist/cli/scan.js.map +1 -0
- package/dist/cli/suite.js +85 -0
- package/dist/cli/suite.js.map +1 -0
- package/dist/cli/verify.js +62 -0
- package/dist/cli/verify.js.map +1 -0
- package/dist/consent/consent.js +43 -0
- package/dist/consent/consent.js.map +1 -0
- package/dist/context/detect.js +156 -0
- package/dist/context/detect.js.map +1 -0
- package/dist/context/findApps.js +198 -0
- package/dist/context/findApps.js.map +1 -0
- package/dist/context/projectRoot.js +41 -0
- package/dist/context/projectRoot.js.map +1 -0
- package/dist/context/scan.js +167 -0
- package/dist/context/scan.js.map +1 -0
- package/dist/core/target.js +115 -0
- package/dist/core/target.js.map +1 -0
- package/dist/core/targetPlan.js +140 -0
- package/dist/core/targetPlan.js.map +1 -0
- package/dist/drivers/DirectDriver.js +228 -0
- package/dist/drivers/DirectDriver.js.map +1 -0
- package/dist/drivers/Driver.js +4 -0
- package/dist/drivers/Driver.js.map +1 -0
- package/dist/drivers/SimctlDriver.js +127 -0
- package/dist/drivers/SimctlDriver.js.map +1 -0
- package/dist/drivers/WdaDriver.js +250 -0
- package/dist/drivers/WdaDriver.js.map +1 -0
- package/dist/explore/candidates.js +93 -0
- package/dist/explore/candidates.js.map +1 -0
- package/dist/explore/graph.js +116 -0
- package/dist/explore/graph.js.map +1 -0
- package/dist/explore/memory.js +35 -0
- package/dist/explore/memory.js.map +1 -0
- package/dist/explore/planner.js +105 -0
- package/dist/explore/planner.js.map +1 -0
- package/dist/explore/policy.js +85 -0
- package/dist/explore/policy.js.map +1 -0
- package/dist/explore/runner.js +475 -0
- package/dist/explore/runner.js.map +1 -0
- package/dist/explore/signatures.js +35 -0
- package/dist/explore/signatures.js.map +1 -0
- package/dist/explore/suite.js +55 -0
- package/dist/explore/suite.js.map +1 -0
- package/dist/featureTesting/executionBootstrap.js +140 -0
- package/dist/featureTesting/executionBootstrap.js.map +1 -0
- package/dist/firstRun/authStateMachine.js +83 -0
- package/dist/firstRun/authStateMachine.js.map +1 -0
- package/dist/firstRun/classifyScreen.js +201 -0
- package/dist/firstRun/classifyScreen.js.map +1 -0
- package/dist/firstRun/firstRunPlanner.js +207 -0
- package/dist/firstRun/firstRunPlanner.js.map +1 -0
- package/dist/firstRun/firstRunRunner.js +331 -0
- package/dist/firstRun/firstRunRunner.js.map +1 -0
- package/dist/firstRun/generatedDataPolicy.js +215 -0
- package/dist/firstRun/generatedDataPolicy.js.map +1 -0
- package/dist/firstRun/inputPlanner.js +67 -0
- package/dist/firstRun/inputPlanner.js.map +1 -0
- package/dist/firstRun/onboardingStateMachine.js +46 -0
- package/dist/firstRun/onboardingStateMachine.js.map +1 -0
- package/dist/firstRun/paywallPolicy.js +54 -0
- package/dist/firstRun/paywallPolicy.js.map +1 -0
- package/dist/firstRun/types.js +7 -0
- package/dist/firstRun/types.js.map +1 -0
- package/dist/fixtures/catalog.js +131 -0
- package/dist/fixtures/catalog.js.map +1 -0
- package/dist/flows/discover.js +18 -0
- package/dist/flows/discover.js.map +1 -0
- package/dist/flows/generate.js +211 -0
- package/dist/flows/generate.js.map +1 -0
- package/dist/flows/lint.js +97 -0
- package/dist/flows/lint.js.map +1 -0
- package/dist/flows/pack.js +68 -0
- package/dist/flows/pack.js.map +1 -0
- package/dist/flows/run.js +738 -0
- package/dist/flows/run.js.map +1 -0
- package/dist/flows/schema.js +240 -0
- package/dist/flows/schema.js.map +1 -0
- package/dist/flows/seedExec.js +108 -0
- package/dist/flows/seedExec.js.map +1 -0
- package/dist/flows/templates.js +155 -0
- package/dist/flows/templates.js.map +1 -0
- package/dist/flows/waitGuards.js +12 -0
- package/dist/flows/waitGuards.js.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/ios/signing.js +182 -0
- package/dist/ios/signing.js.map +1 -0
- package/dist/issues/classify.js +186 -0
- package/dist/issues/classify.js.map +1 -0
- package/dist/issues/fingerprint.js +130 -0
- package/dist/issues/fingerprint.js.map +1 -0
- package/dist/issues/index.js +240 -0
- package/dist/issues/index.js.map +1 -0
- package/dist/issues/metrics.js +155 -0
- package/dist/issues/metrics.js.map +1 -0
- package/dist/issues/recurrence.js +193 -0
- package/dist/issues/recurrence.js.map +1 -0
- package/dist/issues/report.js +110 -0
- package/dist/issues/report.js.map +1 -0
- package/dist/issues/reportBridge.js +119 -0
- package/dist/issues/reportBridge.js.map +1 -0
- package/dist/issues/schema.js +90 -0
- package/dist/issues/schema.js.map +1 -0
- package/dist/issues/sourceRevision.js +108 -0
- package/dist/issues/sourceRevision.js.map +1 -0
- package/dist/issues/store.js +155 -0
- package/dist/issues/store.js.map +1 -0
- package/dist/lib/android.js +278 -0
- package/dist/lib/android.js.map +1 -0
- package/dist/lib/commandTemplate.js +55 -0
- package/dist/lib/commandTemplate.js.map +1 -0
- package/dist/lib/coordSpace.js +23 -0
- package/dist/lib/coordSpace.js.map +1 -0
- package/dist/lib/device.js +87 -0
- package/dist/lib/device.js.map +1 -0
- package/dist/lib/gitignore.js +22 -0
- package/dist/lib/gitignore.js.map +1 -0
- package/dist/lib/image.js +97 -0
- package/dist/lib/image.js.map +1 -0
- package/dist/lib/logger.js +8 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/metroState.js +45 -0
- package/dist/lib/metroState.js.map +1 -0
- package/dist/lib/needsInput.js +201 -0
- package/dist/lib/needsInput.js.map +1 -0
- package/dist/lib/png.js +103 -0
- package/dist/lib/png.js.map +1 -0
- package/dist/lib/redact.js +21 -0
- package/dist/lib/redact.js.map +1 -0
- package/dist/lib/result.js +87 -0
- package/dist/lib/result.js.map +1 -0
- package/dist/lib/schemaHash.js +65 -0
- package/dist/lib/schemaHash.js.map +1 -0
- package/dist/lib/sensitive.js +13 -0
- package/dist/lib/sensitive.js.map +1 -0
- package/dist/lib/simctl.js +149 -0
- package/dist/lib/simctl.js.map +1 -0
- package/dist/lib/spawn.js +144 -0
- package/dist/lib/spawn.js.map +1 -0
- package/dist/lib/wda.js +347 -0
- package/dist/lib/wda.js.map +1 -0
- package/dist/lib/wdaConfig.js +69 -0
- package/dist/lib/wdaConfig.js.map +1 -0
- package/dist/lib/wdaTune.js +58 -0
- package/dist/lib/wdaTune.js.map +1 -0
- package/dist/oracle/auth.js +22 -0
- package/dist/oracle/auth.js.map +1 -0
- package/dist/oracle/failures.js +193 -0
- package/dist/oracle/failures.js.map +1 -0
- package/dist/oracle/health.js +123 -0
- package/dist/oracle/health.js.map +1 -0
- package/dist/oracle/record.js +36 -0
- package/dist/oracle/record.js.map +1 -0
- package/dist/orchestration/envelope.js +52 -0
- package/dist/orchestration/envelope.js.map +1 -0
- package/dist/orchestration/goal.js +98 -0
- package/dist/orchestration/goal.js.map +1 -0
- package/dist/plan/plan.js +124 -0
- package/dist/plan/plan.js.map +1 -0
- package/dist/prompts/index.js +63 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/report/artifactManifest.js +44 -0
- package/dist/report/artifactManifest.js.map +1 -0
- package/dist/report/coverage.js +60 -0
- package/dist/report/coverage.js.map +1 -0
- package/dist/report/evidence.js +84 -0
- package/dist/report/evidence.js.map +1 -0
- package/dist/report/export.js +447 -0
- package/dist/report/export.js.map +1 -0
- package/dist/report/flake.js +61 -0
- package/dist/report/flake.js.map +1 -0
- package/dist/report/history.js +564 -0
- package/dist/report/history.js.map +1 -0
- package/dist/report/policy.js +98 -0
- package/dist/report/policy.js.map +1 -0
- package/dist/report/qaLevel.js +96 -0
- package/dist/report/qaLevel.js.map +1 -0
- package/dist/report/readiness.js +213 -0
- package/dist/report/readiness.js.map +1 -0
- package/dist/report/summary.js +42 -0
- package/dist/report/summary.js.map +1 -0
- package/dist/report/testCatalog.js +93 -0
- package/dist/report/testCatalog.js.map +1 -0
- package/dist/report/timing.js +37 -0
- package/dist/report/timing.js.map +1 -0
- package/dist/server.js +180 -0
- package/dist/server.js.map +1 -0
- package/dist/services/automationGenerate.js +60 -0
- package/dist/services/automationGenerate.js.map +1 -0
- package/dist/services/build.js +66 -0
- package/dist/services/build.js.map +1 -0
- package/dist/services/preflight.js +51 -0
- package/dist/services/preflight.js.map +1 -0
- package/dist/services/prepareAndroid.js +183 -0
- package/dist/services/prepareAndroid.js.map +1 -0
- package/dist/services/prepareIos.js +162 -0
- package/dist/services/prepareIos.js.map +1 -0
- package/dist/services/report.js +537 -0
- package/dist/services/report.js.map +1 -0
- package/dist/services/smoke.js +120 -0
- package/dist/services/smoke.js.map +1 -0
- package/dist/services/suiteGenerate.js +123 -0
- package/dist/services/suiteGenerate.js.map +1 -0
- package/dist/services/testSuiteKnowledge.js +150 -0
- package/dist/services/testSuiteKnowledge.js.map +1 -0
- package/dist/session/attach.js +47 -0
- package/dist/session/attach.js.map +1 -0
- package/dist/session/progress.js +56 -0
- package/dist/session/progress.js.map +1 -0
- package/dist/session/store.js +329 -0
- package/dist/session/store.js.map +1 -0
- package/dist/snapshot/overlays.js +132 -0
- package/dist/snapshot/overlays.js.map +1 -0
- package/dist/snapshot/parse.js +204 -0
- package/dist/snapshot/parse.js.map +1 -0
- package/dist/snapshot/present.js +12 -0
- package/dist/snapshot/present.js.map +1 -0
- package/dist/snapshot/settle.js +36 -0
- package/dist/snapshot/settle.js.map +1 -0
- package/dist/state/profile.js +318 -0
- package/dist/state/profile.js.map +1 -0
- package/dist/suite/compile.js +176 -0
- package/dist/suite/compile.js.map +1 -0
- package/dist/suite/lint.js +45 -0
- package/dist/suite/lint.js.map +1 -0
- package/dist/suite/pom.js +285 -0
- package/dist/suite/pom.js.map +1 -0
- package/dist/suite/testcase.js +87 -0
- package/dist/suite/testcase.js.map +1 -0
- package/dist/testSuite/generator.js +222 -0
- package/dist/testSuite/generator.js.map +1 -0
- package/dist/testSuite/history.js +44 -0
- package/dist/testSuite/history.js.map +1 -0
- package/dist/testSuite/issueLinks.js +96 -0
- package/dist/testSuite/issueLinks.js.map +1 -0
- package/dist/testSuite/merge.js +190 -0
- package/dist/testSuite/merge.js.map +1 -0
- package/dist/testSuite/schema.js +86 -0
- package/dist/testSuite/schema.js.map +1 -0
- package/dist/testSuite/store.js +116 -0
- package/dist/testSuite/store.js.map +1 -0
- package/dist/testSuite/traceability.js +55 -0
- package/dist/testSuite/traceability.js.map +1 -0
- package/dist/tools/act.js +457 -0
- package/dist/tools/act.js.map +1 -0
- package/dist/tools/agent.js +225 -0
- package/dist/tools/agent.js.map +1 -0
- package/dist/tools/appMap.js +317 -0
- package/dist/tools/appMap.js.map +1 -0
- package/dist/tools/assertVisual.js +74 -0
- package/dist/tools/assertVisual.js.map +1 -0
- package/dist/tools/automationGenerate.js +394 -0
- package/dist/tools/automationGenerate.js.map +1 -0
- package/dist/tools/capabilities.js +114 -0
- package/dist/tools/capabilities.js.map +1 -0
- package/dist/tools/clearOverlay.js +150 -0
- package/dist/tools/clearOverlay.js.map +1 -0
- package/dist/tools/detectContext.js +41 -0
- package/dist/tools/detectContext.js.map +1 -0
- package/dist/tools/doctor.js +116 -0
- package/dist/tools/doctor.js.map +1 -0
- package/dist/tools/explore.js +289 -0
- package/dist/tools/explore.js.map +1 -0
- package/dist/tools/firstRun.js +136 -0
- package/dist/tools/firstRun.js.map +1 -0
- package/dist/tools/flow.js +331 -0
- package/dist/tools/flow.js.map +1 -0
- package/dist/tools/flowGenerate.js +55 -0
- package/dist/tools/flowGenerate.js.map +1 -0
- package/dist/tools/getArtifact.js +39 -0
- package/dist/tools/getArtifact.js.map +1 -0
- package/dist/tools/health.js +41 -0
- package/dist/tools/health.js.map +1 -0
- package/dist/tools/ios.js +492 -0
- package/dist/tools/ios.js.map +1 -0
- package/dist/tools/jobs.js +20 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/network.js +50 -0
- package/dist/tools/network.js.map +1 -0
- package/dist/tools/note.js +69 -0
- package/dist/tools/note.js.map +1 -0
- package/dist/tools/plan.js +49 -0
- package/dist/tools/plan.js.map +1 -0
- package/dist/tools/prepareIosTarget.js +99 -0
- package/dist/tools/prepareIosTarget.js.map +1 -0
- package/dist/tools/prepareTarget.js +257 -0
- package/dist/tools/prepareTarget.js.map +1 -0
- package/dist/tools/report.js +42 -0
- package/dist/tools/report.js.map +1 -0
- package/dist/tools/screenRecord.js +24 -0
- package/dist/tools/screenRecord.js.map +1 -0
- package/dist/tools/screenshot.js +63 -0
- package/dist/tools/screenshot.js.map +1 -0
- package/dist/tools/smoke.js +39 -0
- package/dist/tools/smoke.js.map +1 -0
- package/dist/tools/snapshot.js +129 -0
- package/dist/tools/snapshot.js.map +1 -0
- package/dist/tools/startSession.js +169 -0
- package/dist/tools/startSession.js.map +1 -0
- package/dist/tools/state.js +87 -0
- package/dist/tools/state.js.map +1 -0
- package/dist/tools/suite.js +412 -0
- package/dist/tools/suite.js.map +1 -0
- package/dist/tools/testThis.js +774 -0
- package/dist/tools/testThis.js.map +1 -0
- package/dist/tools/wda.js +477 -0
- package/dist/tools/wda.js.map +1 -0
- package/dist/version.js +62 -0
- package/dist/version.js.map +1 -0
- package/dist/visual/ocr.js +72 -0
- package/dist/visual/ocr.js.map +1 -0
- package/dist/visual/provider.js +75 -0
- package/dist/visual/provider.js.map +1 -0
- package/docs/README.md +19 -0
- package/docs/assets/swipium-lockup-ink-on-light.png +0 -0
- package/docs/mcp-server.md +135 -0
- package/docs/tools.md +130 -0
- package/package.json +70 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// iOS Simulator helpers (PHASE3-PLAN Phase 11) via `xcrun simctl` — local, macOS host only. Covers
|
|
2
|
+
// the lifecycle + screenshot + deep links + privacy/erase that simctl supports natively. UI-tree
|
|
3
|
+
// reads and input injection are NOT available through simctl (they need an XCUITest backend, a
|
|
4
|
+
// attach WebDriverAgent for structured iOS automation, so SimctlDriver reports those as
|
|
5
|
+
// unsupported rather than faking them.
|
|
6
|
+
// All spawns use arg arrays (injection-safe). Tool/binary names (xcrun/simctl/plutil) are CLI
|
|
7
|
+
// invocations, not configuration to surface.
|
|
8
|
+
import { readFileSync, rmSync } from 'node:fs';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { run } from './spawn.js';
|
|
12
|
+
const X = 'xcrun';
|
|
13
|
+
/** Is the simulator toolchain usable on this host (macOS + the `xcrun simctl` command-line tools)? */
|
|
14
|
+
export async function simctlAvailable() {
|
|
15
|
+
if (process.platform !== 'darwin')
|
|
16
|
+
return false;
|
|
17
|
+
try {
|
|
18
|
+
const r = await run(X, ['simctl', 'help'], { timeoutMs: 8000 });
|
|
19
|
+
return r.code === 0;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function prettyRuntime(key) {
|
|
26
|
+
// runtime key '<reverse-dns>.SimRuntime.iOS-18-0' → 'iOS 18.0'
|
|
27
|
+
const m = key.match(/SimRuntime\.([A-Za-z]+)-([\d-]+)$/);
|
|
28
|
+
return m ? `${m[1]} ${m[2].replace(/-/g, '.')}` : key;
|
|
29
|
+
}
|
|
30
|
+
export async function listSimulators() {
|
|
31
|
+
const r = await run(X, ['simctl', 'list', 'devices', 'available', '--json'], { timeoutMs: 15000 });
|
|
32
|
+
const out = [];
|
|
33
|
+
try {
|
|
34
|
+
const j = JSON.parse(r.stdout);
|
|
35
|
+
for (const [rt, list] of Object.entries(j.devices)) {
|
|
36
|
+
for (const d of list)
|
|
37
|
+
out.push({ udid: d.udid, name: d.name, state: d.state, runtime: prettyRuntime(rt) });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
/* malformed */
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
export async function boot(udid) {
|
|
46
|
+
// 'Unable to boot ... current state: Booted' is fine — treat already-booted as success.
|
|
47
|
+
const r = await run(X, ['simctl', 'boot', udid], { timeoutMs: 120000 });
|
|
48
|
+
if (r.code !== 0 && !/current state: Booted/i.test(r.stderr)) {
|
|
49
|
+
throw new Error(`boot failed: ${r.stderr.trim() || r.stdout.trim()}`);
|
|
50
|
+
}
|
|
51
|
+
const ready = await run(X, ['simctl', 'bootstatus', udid, '-b'], { timeoutMs: 120000 });
|
|
52
|
+
if (ready.code !== 0) {
|
|
53
|
+
throw new Error(`bootstatus failed: ${ready.stderr.trim() || ready.stdout.trim()}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export async function shutdown(udid) {
|
|
57
|
+
const r = await run(X, ['simctl', 'shutdown', udid], { timeoutMs: 60000 });
|
|
58
|
+
if (r.code !== 0 && !/current state: Shutdown/i.test(r.stderr))
|
|
59
|
+
throw new Error(`shutdown failed: ${r.stderr.trim()}`);
|
|
60
|
+
}
|
|
61
|
+
export async function installApp(udid, appPath) {
|
|
62
|
+
await run(X, ['simctl', 'install', udid, appPath], { timeoutMs: 120000, rejectOnNonZero: true });
|
|
63
|
+
}
|
|
64
|
+
export async function uninstallApp(udid, bundleId) {
|
|
65
|
+
await run(X, ['simctl', 'uninstall', udid, bundleId], { timeoutMs: 60000 });
|
|
66
|
+
}
|
|
67
|
+
export function classifyIosInstallFailure(message) {
|
|
68
|
+
if (/wrong architecture|unsupported architecture|missing required architecture|mach-o/i.test(message) ||
|
|
69
|
+
/not built .*simulator|built for .*device|built for iOS(?! Simulator)|iphoneos/i.test(message)) {
|
|
70
|
+
return 'WRONG_ARCH';
|
|
71
|
+
}
|
|
72
|
+
return 'INSTALL_FAILED';
|
|
73
|
+
}
|
|
74
|
+
export async function launchApp(udid, bundleId) {
|
|
75
|
+
await run(X, ['simctl', 'launch', udid, bundleId], { timeoutMs: 30000, rejectOnNonZero: true });
|
|
76
|
+
}
|
|
77
|
+
export async function launchAppWithArgs(udid, bundleId, args) {
|
|
78
|
+
const argv = Object.entries(args).flatMap(([key, value]) => [`--${key}`, String(value)]);
|
|
79
|
+
await run(X, ['simctl', 'launch', '--terminate-running-process', udid, bundleId, ...argv], { timeoutMs: 30000, rejectOnNonZero: true });
|
|
80
|
+
}
|
|
81
|
+
export async function terminateApp(udid, bundleId) {
|
|
82
|
+
// Not-running is not an error for our purposes.
|
|
83
|
+
await run(X, ['simctl', 'terminate', udid, bundleId], { timeoutMs: 15000 });
|
|
84
|
+
}
|
|
85
|
+
export async function isInstalled(udid, bundleId) {
|
|
86
|
+
const r = await run(X, ['simctl', 'get_app_container', udid, bundleId], { timeoutMs: 10000 });
|
|
87
|
+
return r.code === 0 && r.stdout.trim().length > 0;
|
|
88
|
+
}
|
|
89
|
+
/** Erase requires the device be shut down first. */
|
|
90
|
+
export async function erase(udid) {
|
|
91
|
+
await shutdown(udid).catch(() => { });
|
|
92
|
+
await run(X, ['simctl', 'erase', udid], { timeoutMs: 60000, rejectOnNonZero: true });
|
|
93
|
+
}
|
|
94
|
+
export async function screenshot(udid) {
|
|
95
|
+
// NOTE: `simctl io screenshot -` (stdout) is unreliable across toolchain versions (it can try to
|
|
96
|
+
// "save" to a file literally named "-" on a read-only volume). Write to a temp file + read back.
|
|
97
|
+
const tmp = join(tmpdir(), `swipium-ios-${udid}-${Date.now()}.png`);
|
|
98
|
+
try {
|
|
99
|
+
await run(X, ['simctl', 'io', udid, 'screenshot', '--type', 'png', tmp], { timeoutMs: 20000, rejectOnNonZero: true });
|
|
100
|
+
return readFileSync(tmp);
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
try {
|
|
104
|
+
rmSync(tmp, { force: true });
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
/* best-effort cleanup */
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export async function openUrl(udid, url) {
|
|
112
|
+
await run(X, ['simctl', 'openurl', udid, url], { timeoutMs: 15000, rejectOnNonZero: true });
|
|
113
|
+
}
|
|
114
|
+
export function simulatorLogArgs(udid, opts = {}) {
|
|
115
|
+
const args = ['simctl', 'spawn', udid, 'log', 'show', '--style', 'compact', '--last', opts.last ?? '5m'];
|
|
116
|
+
if (opts.bundleId) {
|
|
117
|
+
args.push('--predicate', `eventMessage CONTAINS[c] "${opts.bundleId}" OR processImagePath CONTAINS[c] "${opts.bundleId}" OR subsystem CONTAINS[c] "${opts.bundleId}"`);
|
|
118
|
+
}
|
|
119
|
+
return args;
|
|
120
|
+
}
|
|
121
|
+
export async function simulatorLogs(udid, opts = {}) {
|
|
122
|
+
const r = await run(X, simulatorLogArgs(udid, opts), { timeoutMs: 20000, rejectOnNonZero: true });
|
|
123
|
+
return [r.stdout, r.stderr].filter(Boolean).join('\n');
|
|
124
|
+
}
|
|
125
|
+
/** Reset a privacy permission (service e.g. location, photos, camera, contacts, all). */
|
|
126
|
+
export async function privacyReset(udid, service, bundleId) {
|
|
127
|
+
const args = ['simctl', 'privacy', udid, 'reset', service];
|
|
128
|
+
if (bundleId)
|
|
129
|
+
args.push(bundleId);
|
|
130
|
+
await run(X, args, { timeoutMs: 15000, rejectOnNonZero: true });
|
|
131
|
+
}
|
|
132
|
+
export async function privacySet(udid, action, service, bundleId) {
|
|
133
|
+
const args = ['simctl', 'privacy', udid, action, service];
|
|
134
|
+
if (bundleId)
|
|
135
|
+
args.push(bundleId);
|
|
136
|
+
await run(X, args, { timeoutMs: 15000, rejectOnNonZero: true });
|
|
137
|
+
}
|
|
138
|
+
/** Read CFBundleIdentifier from a built .app via plutil. */
|
|
139
|
+
export async function bundleIdFromApp(appPath) {
|
|
140
|
+
try {
|
|
141
|
+
const r = await run('plutil', ['-extract', 'CFBundleIdentifier', 'raw', '-o', '-', `${appPath}/Info.plist`], { timeoutMs: 8000 });
|
|
142
|
+
const id = r.stdout.trim();
|
|
143
|
+
return id && r.code === 0 ? id : null;
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=simctl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simctl.js","sourceRoot":"","sources":["../../src/lib/simctl.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Safe process execution: arg arrays only (never shell strings → injection-safe,
|
|
2
|
+
// DESIGN §10.1), with timeout + AbortSignal threading.
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { basename } from 'node:path';
|
|
5
|
+
export class GitScopeForbiddenError extends Error {
|
|
6
|
+
code = 'GIT_SCOPE_FORBIDDEN';
|
|
7
|
+
constructor(command) {
|
|
8
|
+
super(`Git is outside Swipium's QA scope; refused command "${command}"`);
|
|
9
|
+
this.name = 'GitScopeForbiddenError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function executableName(command) {
|
|
13
|
+
const base = basename(command).toLowerCase();
|
|
14
|
+
return base.replace(/\.(exe|cmd|bat)$/i, '');
|
|
15
|
+
}
|
|
16
|
+
function isShell(command) {
|
|
17
|
+
return ['sh', 'bash', 'zsh', 'fish', 'cmd', 'powershell', 'pwsh'].includes(executableName(command));
|
|
18
|
+
}
|
|
19
|
+
function shellCommandPayload(command, args) {
|
|
20
|
+
if (!isShell(command))
|
|
21
|
+
return null;
|
|
22
|
+
const shell = executableName(command);
|
|
23
|
+
const cIndex = args.findIndex((arg) => (arg === '-c'
|
|
24
|
+
|| arg === '/c'
|
|
25
|
+
|| arg === '/C'
|
|
26
|
+
|| arg.toLowerCase() === '-command'
|
|
27
|
+
|| (['sh', 'bash', 'zsh', 'fish'].includes(shell) && /^-[A-Za-z]*c[A-Za-z]*$/.test(arg))));
|
|
28
|
+
return cIndex >= 0 && typeof args[cIndex + 1] === 'string' ? args[cIndex + 1] : null;
|
|
29
|
+
}
|
|
30
|
+
function stripTokenQuotes(token) {
|
|
31
|
+
let out = token.trim();
|
|
32
|
+
while ((out.startsWith('"') && out.endsWith('"')) || (out.startsWith("'") && out.endsWith("'"))) {
|
|
33
|
+
out = out.slice(1, -1).trim();
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
function shellPayloadGitToken(payload) {
|
|
38
|
+
for (const raw of payload.split(/[\s;&|(){}<>]+/)) {
|
|
39
|
+
const token = stripTokenQuotes(raw);
|
|
40
|
+
if (!token || /^[A-Za-z_][A-Za-z0-9_]*=/.test(token))
|
|
41
|
+
continue;
|
|
42
|
+
if (executableName(token) === 'git')
|
|
43
|
+
return token;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
export function gitScopeViolation(argv) {
|
|
48
|
+
const command = Array.isArray(argv) ? argv[0] : argv;
|
|
49
|
+
if (!command)
|
|
50
|
+
return null;
|
|
51
|
+
if (executableName(command) === 'git')
|
|
52
|
+
return command;
|
|
53
|
+
if (Array.isArray(argv)) {
|
|
54
|
+
const payload = shellCommandPayload(command, argv.slice(1));
|
|
55
|
+
const shellGit = payload ? shellPayloadGitToken(payload) : null;
|
|
56
|
+
if (shellGit)
|
|
57
|
+
return shellGit;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
export function assertNoGitScope(command, args = []) {
|
|
62
|
+
const bad = gitScopeViolation([command, ...args]);
|
|
63
|
+
if (bad)
|
|
64
|
+
throw new GitScopeForbiddenError(bad);
|
|
65
|
+
}
|
|
66
|
+
export function run(cmd, args, opts = {}) {
|
|
67
|
+
try {
|
|
68
|
+
assertNoGitScope(cmd, args);
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
return Promise.reject(e);
|
|
72
|
+
}
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const child = spawn(cmd, args, { cwd: opts.cwd, signal: opts.signal });
|
|
75
|
+
let stdout = '';
|
|
76
|
+
let stderr = '';
|
|
77
|
+
let timedOut = false;
|
|
78
|
+
const timer = opts.timeoutMs
|
|
79
|
+
? setTimeout(() => {
|
|
80
|
+
timedOut = true;
|
|
81
|
+
child.kill('SIGKILL');
|
|
82
|
+
}, opts.timeoutMs)
|
|
83
|
+
: undefined;
|
|
84
|
+
child.stdout.on('data', (d) => (stdout += d.toString()));
|
|
85
|
+
child.stderr.on('data', (d) => (stderr += d.toString()));
|
|
86
|
+
child.on('error', (err) => {
|
|
87
|
+
if (timer)
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
reject(err);
|
|
90
|
+
});
|
|
91
|
+
child.on('close', (code) => {
|
|
92
|
+
if (timer)
|
|
93
|
+
clearTimeout(timer);
|
|
94
|
+
const result = { code, stdout, stderr, timedOut };
|
|
95
|
+
if (opts.rejectOnNonZero && code !== 0) {
|
|
96
|
+
reject(new Error(`\`${cmd} ${args.join(' ')}\` exited ${code}${timedOut ? ' (timed out)' : ''}: ${stderr.trim()}`));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
resolve(result);
|
|
100
|
+
});
|
|
101
|
+
if (opts.input !== undefined) {
|
|
102
|
+
child.stdin.write(opts.input);
|
|
103
|
+
child.stdin.end();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/** Like run(), but collects stdout as raw bytes (for screenshots etc.). */
|
|
108
|
+
export function runBinary(cmd, args, opts = {}) {
|
|
109
|
+
try {
|
|
110
|
+
assertNoGitScope(cmd, args);
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
return Promise.reject(e);
|
|
114
|
+
}
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
const child = spawn(cmd, args, { cwd: opts.cwd, signal: opts.signal });
|
|
117
|
+
const chunks = [];
|
|
118
|
+
let stderr = '';
|
|
119
|
+
let timedOut = false;
|
|
120
|
+
const timer = opts.timeoutMs
|
|
121
|
+
? setTimeout(() => {
|
|
122
|
+
timedOut = true;
|
|
123
|
+
child.kill('SIGKILL');
|
|
124
|
+
}, opts.timeoutMs)
|
|
125
|
+
: undefined;
|
|
126
|
+
child.stdout.on('data', (d) => chunks.push(d));
|
|
127
|
+
child.stderr.on('data', (d) => (stderr += d.toString()));
|
|
128
|
+
child.on('error', (err) => {
|
|
129
|
+
if (timer)
|
|
130
|
+
clearTimeout(timer);
|
|
131
|
+
reject(err);
|
|
132
|
+
});
|
|
133
|
+
child.on('close', (code) => {
|
|
134
|
+
if (timer)
|
|
135
|
+
clearTimeout(timer);
|
|
136
|
+
if (opts.rejectOnNonZero && code !== 0) {
|
|
137
|
+
reject(new Error(`\`${cmd}\` exited ${code}: ${stderr.trim()}`));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
resolve({ code, stdout: Buffer.concat(chunks), stderr, timedOut });
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=spawn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.js","sourceRoot":"","sources":["../../src/lib/spawn.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/wda.js
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
2
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { run } from './spawn.js';
|
|
5
|
+
const ELEMENT_KEY = 'element-6066-11e4-a52e-4f735466cecf';
|
|
6
|
+
function normalizeUrl(url) {
|
|
7
|
+
return url.replace(/\/+$/, '');
|
|
8
|
+
}
|
|
9
|
+
async function wdaFetch(baseUrl, path, init) {
|
|
10
|
+
const res = await fetch(`${normalizeUrl(baseUrl)}${path}`, {
|
|
11
|
+
...init,
|
|
12
|
+
headers: { 'content-type': 'application/json', ...(init?.headers ?? {}) },
|
|
13
|
+
});
|
|
14
|
+
const body = (await res.text()) || '{}';
|
|
15
|
+
let json;
|
|
16
|
+
try {
|
|
17
|
+
json = JSON.parse(body);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
throw new Error(`WDA returned non-JSON ${res.status}: ${body.slice(0, 200)}`);
|
|
21
|
+
}
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
const msg = typeof json.value?.message === 'string'
|
|
24
|
+
? json.value.message
|
|
25
|
+
: typeof json.message === 'string'
|
|
26
|
+
? json.message
|
|
27
|
+
: body.slice(0, 200);
|
|
28
|
+
throw new Error(`WDA HTTP ${res.status}: ${msg}`);
|
|
29
|
+
}
|
|
30
|
+
return json;
|
|
31
|
+
}
|
|
32
|
+
function valueOf(json) {
|
|
33
|
+
return json.value ?? json;
|
|
34
|
+
}
|
|
35
|
+
function settingsCapabilities(settings) {
|
|
36
|
+
if (!settings)
|
|
37
|
+
return {};
|
|
38
|
+
return Object.fromEntries(Object.entries(settings).map(([key, value]) => [`settings[${key}]`, value]));
|
|
39
|
+
}
|
|
40
|
+
export async function checkWda(baseUrl, timeoutMs = 5000) {
|
|
41
|
+
const controller = new AbortController();
|
|
42
|
+
const t = setTimeout(() => controller.abort(), timeoutMs);
|
|
43
|
+
try {
|
|
44
|
+
const res = await fetch(`${normalizeUrl(baseUrl)}/status`, { signal: controller.signal });
|
|
45
|
+
const body = (await res.text()) || '{}';
|
|
46
|
+
const json = JSON.parse(body);
|
|
47
|
+
const value = (json.value ?? json);
|
|
48
|
+
return {
|
|
49
|
+
reachable: res.ok,
|
|
50
|
+
ready: res.ok && (value.ready === true || json.status === 0 || value.state === 'success'),
|
|
51
|
+
message: typeof value.message === 'string' ? value.message : undefined,
|
|
52
|
+
build: typeof value.build === 'object' && value.build ? value.build : undefined,
|
|
53
|
+
os: typeof value.os === 'object' && value.os ? value.os : undefined,
|
|
54
|
+
ios: typeof value.ios === 'object' && value.ios ? value.ios : undefined,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
return { reachable: false, ready: false, error: String(e.message ?? e) };
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
clearTimeout(t);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export async function waitForWdaReady(baseUrl, timeoutMs, intervalMs = 1000) {
|
|
65
|
+
const started = Date.now();
|
|
66
|
+
let status = { reachable: false, ready: false, error: 'not checked yet' };
|
|
67
|
+
while (Date.now() - started < timeoutMs) {
|
|
68
|
+
status = await checkWda(baseUrl, Math.min(1500, Math.max(250, intervalMs)));
|
|
69
|
+
if (status.ready)
|
|
70
|
+
return { ready: true, status, durationMs: Date.now() - started };
|
|
71
|
+
const remaining = timeoutMs - (Date.now() - started);
|
|
72
|
+
if (remaining <= 0)
|
|
73
|
+
break;
|
|
74
|
+
await new Promise((r) => setTimeout(r, Math.min(intervalMs, remaining)));
|
|
75
|
+
}
|
|
76
|
+
return { ready: false, status, durationMs: Date.now() - started };
|
|
77
|
+
}
|
|
78
|
+
export async function xcodeAvailable() {
|
|
79
|
+
if (process.platform !== 'darwin')
|
|
80
|
+
return { available: false, error: 'WDA requires macOS with Xcode command line tools.' };
|
|
81
|
+
try {
|
|
82
|
+
const r = await run('xcodebuild', ['-version'], { timeoutMs: 8000 });
|
|
83
|
+
return r.code === 0 ? { available: true, version: r.stdout.trim() } : { available: false, error: r.stderr.trim() || r.stdout.trim() };
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
return { available: false, error: String(e) };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function discoverWdaProjects(root, extraCandidates = []) {
|
|
90
|
+
const directCandidates = [
|
|
91
|
+
...extraCandidates,
|
|
92
|
+
process.env.WDA_PROJECT_PATH,
|
|
93
|
+
process.env.WEBDRIVERAGENT_PROJECT,
|
|
94
|
+
join(root, 'WebDriverAgent.xcodeproj'),
|
|
95
|
+
join(root, 'WebDriverAgent', 'WebDriverAgent.xcodeproj'),
|
|
96
|
+
join(root, 'ios', 'WebDriverAgent.xcodeproj'),
|
|
97
|
+
join(root, 'ios', 'WebDriverAgent', 'WebDriverAgent.xcodeproj'),
|
|
98
|
+
join(root, 'node_modules', 'appium-webdriveragent', 'WebDriverAgent.xcodeproj'),
|
|
99
|
+
join(root, 'node_modules', 'appium-xcuitest-driver', 'node_modules', 'appium-webdriveragent', 'WebDriverAgent.xcodeproj'),
|
|
100
|
+
].filter((p) => !!p && p.trim().length > 0);
|
|
101
|
+
const found = new Set();
|
|
102
|
+
for (const p of directCandidates) {
|
|
103
|
+
if (existsSync(p) && /WebDriverAgent\.xcodeproj$/i.test(p))
|
|
104
|
+
found.add(p);
|
|
105
|
+
}
|
|
106
|
+
const searchedRoots = [root];
|
|
107
|
+
const skip = new Set(['.git', 'dist', 'build', 'DerivedData', '.swipium', 'node_modules']);
|
|
108
|
+
const stack = [{ path: root, depth: 0 }];
|
|
109
|
+
while (stack.length) {
|
|
110
|
+
const cur = stack.pop();
|
|
111
|
+
if (cur.depth > 4)
|
|
112
|
+
continue;
|
|
113
|
+
let entries;
|
|
114
|
+
try {
|
|
115
|
+
entries = readdirSync(cur.path);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
for (const name of entries) {
|
|
121
|
+
if (skip.has(name))
|
|
122
|
+
continue;
|
|
123
|
+
const p = join(cur.path, name);
|
|
124
|
+
let st;
|
|
125
|
+
try {
|
|
126
|
+
st = statSync(p);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (!st.isDirectory())
|
|
132
|
+
continue;
|
|
133
|
+
if (/WebDriverAgent\.xcodeproj$/i.test(name)) {
|
|
134
|
+
found.add(p);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
stack.push({ path: p, depth: cur.depth + 1 });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { candidates: [...found].sort(), searchedRoots };
|
|
141
|
+
}
|
|
142
|
+
export function managedWdaBuildArgs(opts) {
|
|
143
|
+
return [
|
|
144
|
+
'-project', opts.projectPath,
|
|
145
|
+
'-scheme', opts.scheme ?? 'WebDriverAgentRunner',
|
|
146
|
+
'-destination', `id=${opts.udid}`,
|
|
147
|
+
...(opts.derivedDataPath ? ['-derivedDataPath', opts.derivedDataPath] : []),
|
|
148
|
+
...(opts.allowProvisioningUpdates ? ['-allowProvisioningUpdates'] : []),
|
|
149
|
+
...(opts.allowProvisioningDeviceRegistration ? ['-allowProvisioningDeviceRegistration'] : []),
|
|
150
|
+
...(opts.authenticationKeyPath ? ['-authenticationKeyPath', opts.authenticationKeyPath] : []),
|
|
151
|
+
...(opts.authenticationKeyId ? ['-authenticationKeyID', opts.authenticationKeyId] : []),
|
|
152
|
+
...(opts.authenticationKeyIssuerId ? ['-authenticationKeyIssuerID', opts.authenticationKeyIssuerId] : []),
|
|
153
|
+
...(opts.developmentTeam ? [`DEVELOPMENT_TEAM=${opts.developmentTeam}`] : []),
|
|
154
|
+
...(opts.bundleId ? [`PRODUCT_BUNDLE_IDENTIFIER=${opts.bundleId}`] : []),
|
|
155
|
+
...(opts.codeSignStyle ? [`CODE_SIGN_STYLE=${opts.codeSignStyle}`] : []),
|
|
156
|
+
'build-for-testing',
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
export function managedWdaStartArgs(opts) {
|
|
160
|
+
return [
|
|
161
|
+
'-project', opts.projectPath,
|
|
162
|
+
'-scheme', opts.scheme ?? 'WebDriverAgentRunner',
|
|
163
|
+
'-destination', `id=${opts.udid}`,
|
|
164
|
+
...(opts.derivedDataPath ? ['-derivedDataPath', opts.derivedDataPath] : []),
|
|
165
|
+
...(opts.developmentTeam ? [`DEVELOPMENT_TEAM=${opts.developmentTeam}`] : []),
|
|
166
|
+
...(opts.bundleId ? [`PRODUCT_BUNDLE_IDENTIFIER=${opts.bundleId}`] : []),
|
|
167
|
+
...(opts.codeSignStyle ? [`CODE_SIGN_STYLE=${opts.codeSignStyle}`] : []),
|
|
168
|
+
'test-without-building',
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
export function classifyWdaBuildFailure(log) {
|
|
172
|
+
if (/Signing for .* requires a development team|No profiles for|provisioning profile|Code signing is required|requires a provisioning profile|No signing certificate|No Accounts|Development Team/i.test(log)) {
|
|
173
|
+
return 'WDA_SIGNING_FAILED';
|
|
174
|
+
}
|
|
175
|
+
return 'WDA_BUILD_FAILED';
|
|
176
|
+
}
|
|
177
|
+
export function classifyWdaConnectionFailure(message) {
|
|
178
|
+
if (/EADDRINUSE|address already in use|port .*in use|bind.*address/i.test(message))
|
|
179
|
+
return 'WDA_PORT_CONFLICT';
|
|
180
|
+
if (/ECONNREFUSED|fetch failed|timed out|aborted|network|socket|unreachable/i.test(message))
|
|
181
|
+
return 'WDA_UNREACHABLE';
|
|
182
|
+
if (/bundle id|bundle identifier|application.*not.*installed|app.*not.*installed|no such application|could not.*launch.*app|failed to launch.*app/i.test(message))
|
|
183
|
+
return 'BUNDLE_ID_NOT_FOUND';
|
|
184
|
+
return 'WDA_SESSION_FAILED';
|
|
185
|
+
}
|
|
186
|
+
export async function createWdaSession(baseUrl, opts = {}) {
|
|
187
|
+
const alwaysMatch = {
|
|
188
|
+
...(opts.capabilities ?? {}),
|
|
189
|
+
...settingsCapabilities(opts.settings),
|
|
190
|
+
};
|
|
191
|
+
if (opts.bundleId)
|
|
192
|
+
alwaysMatch.bundleId = opts.bundleId;
|
|
193
|
+
if (opts.udid)
|
|
194
|
+
alwaysMatch.udid = opts.udid;
|
|
195
|
+
const json = await wdaFetch(baseUrl, '/session', {
|
|
196
|
+
method: 'POST',
|
|
197
|
+
body: JSON.stringify({ capabilities: { alwaysMatch } }),
|
|
198
|
+
});
|
|
199
|
+
const v = valueOf(json);
|
|
200
|
+
const sessionId = String(json.sessionId ?? v.sessionId ?? '');
|
|
201
|
+
if (!sessionId)
|
|
202
|
+
throw new Error('WDA did not return a sessionId.');
|
|
203
|
+
return { sessionId, capabilities: typeof v.capabilities === 'object' && v.capabilities ? v.capabilities : undefined };
|
|
204
|
+
}
|
|
205
|
+
export function wdaSessionUdid(capabilities) {
|
|
206
|
+
if (!capabilities)
|
|
207
|
+
return undefined;
|
|
208
|
+
for (const key of ['udid', 'deviceUDID', 'deviceUdid', 'appium:udid', 'appium:deviceUDID']) {
|
|
209
|
+
const value = capabilities[key];
|
|
210
|
+
if (typeof value === 'string' && value.trim())
|
|
211
|
+
return value;
|
|
212
|
+
}
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
export function wdaSessionUdidMismatch(capabilities, expectedUdid) {
|
|
216
|
+
if (!expectedUdid)
|
|
217
|
+
return undefined;
|
|
218
|
+
const actual = wdaSessionUdid(capabilities);
|
|
219
|
+
return actual && actual !== expectedUdid ? actual : undefined;
|
|
220
|
+
}
|
|
221
|
+
export async function deleteWdaSession(baseUrl, sessionId) {
|
|
222
|
+
await wdaFetch(baseUrl, `/session/${sessionId}`, { method: 'DELETE' });
|
|
223
|
+
}
|
|
224
|
+
export async function wdaScreenshot(baseUrl, sessionId) {
|
|
225
|
+
const json = await wdaFetch(baseUrl, `/session/${sessionId}/screenshot`);
|
|
226
|
+
return Buffer.from(String(valueOf(json)), 'base64');
|
|
227
|
+
}
|
|
228
|
+
export async function wdaSource(baseUrl, sessionId) {
|
|
229
|
+
const json = await wdaFetch(baseUrl, `/session/${sessionId}/source`);
|
|
230
|
+
return String(valueOf(json));
|
|
231
|
+
}
|
|
232
|
+
export async function wdaActiveAppInfo(baseUrl, sessionId) {
|
|
233
|
+
let json;
|
|
234
|
+
try {
|
|
235
|
+
json = await wdaFetch(baseUrl, `/session/${sessionId}/wda/activeAppInfo`);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
json = await wdaFetch(baseUrl, '/wda/activeAppInfo');
|
|
239
|
+
}
|
|
240
|
+
const v = valueOf(json);
|
|
241
|
+
return {
|
|
242
|
+
bundleId: typeof v.bundleId === 'string' ? v.bundleId : undefined,
|
|
243
|
+
name: typeof v.name === 'string' ? v.name : undefined,
|
|
244
|
+
pid: typeof v.pid === 'number' ? v.pid : undefined,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
export async function findWdaElement(baseUrl, sessionId, using, value) {
|
|
248
|
+
const json = await wdaFetch(baseUrl, `/session/${sessionId}/element`, {
|
|
249
|
+
method: 'POST',
|
|
250
|
+
body: JSON.stringify({ using, value }),
|
|
251
|
+
});
|
|
252
|
+
const v = valueOf(json);
|
|
253
|
+
const elementId = String(v[ELEMENT_KEY] ?? v.ELEMENT ?? v.elementId ?? '');
|
|
254
|
+
if (!elementId)
|
|
255
|
+
throw new Error(`WDA could not resolve element using ${using}=${value}.`);
|
|
256
|
+
return { elementId };
|
|
257
|
+
}
|
|
258
|
+
export async function tapWdaElement(baseUrl, sessionId, elementId) {
|
|
259
|
+
await wdaFetch(baseUrl, `/session/${sessionId}/element/${elementId}/click`, { method: 'POST', body: '{}' });
|
|
260
|
+
}
|
|
261
|
+
export async function typeWdaElement(baseUrl, sessionId, elementId, text) {
|
|
262
|
+
await wdaFetch(baseUrl, `/session/${sessionId}/element/${elementId}/value`, {
|
|
263
|
+
method: 'POST',
|
|
264
|
+
body: JSON.stringify({ value: [...text], text }),
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
export async function clearWdaElement(baseUrl, sessionId, elementId) {
|
|
268
|
+
await wdaFetch(baseUrl, `/session/${sessionId}/element/${elementId}/clear`, { method: 'POST', body: '{}' });
|
|
269
|
+
}
|
|
270
|
+
export async function tapWdaPoint(baseUrl, sessionId, x, y) {
|
|
271
|
+
await wdaFetch(baseUrl, `/session/${sessionId}/wda/tap/0`, { method: 'POST', body: JSON.stringify({ x, y }) });
|
|
272
|
+
}
|
|
273
|
+
export async function dragWdaPoint(baseUrl, sessionId, x1, y1, x2, y2, duration = 0.3) {
|
|
274
|
+
await wdaFetch(baseUrl, `/session/${sessionId}/wda/dragfromtoforduration`, {
|
|
275
|
+
method: 'POST',
|
|
276
|
+
body: JSON.stringify({ fromX: x1, fromY: y1, toX: x2, toY: y2, duration }),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
export async function pressWdaHome(baseUrl, sessionId) {
|
|
280
|
+
await wdaFetch(baseUrl, `/session/${sessionId}/wda/homescreen`, { method: 'POST', body: '{}' });
|
|
281
|
+
}
|
|
282
|
+
export async function pressWdaBack(baseUrl, sessionId) {
|
|
283
|
+
await wdaFetch(baseUrl, `/session/${sessionId}/back`, { method: 'POST', body: '{}' });
|
|
284
|
+
}
|
|
285
|
+
function bool(v) {
|
|
286
|
+
return v === true || v === 'true' || v === 1 || v === '1';
|
|
287
|
+
}
|
|
288
|
+
function iosBounds(attrs) {
|
|
289
|
+
const x = Number(attrs.x ?? 0);
|
|
290
|
+
const y = Number(attrs.y ?? 0);
|
|
291
|
+
const w = Number(attrs.width ?? 0);
|
|
292
|
+
const h = Number(attrs.height ?? 0);
|
|
293
|
+
return `[${Math.round(x)},${Math.round(y)}][${Math.round(x + w)},${Math.round(y + h)}]`;
|
|
294
|
+
}
|
|
295
|
+
function normalizeNode(node) {
|
|
296
|
+
const type = String(node.type ?? node.name ?? 'XCUIElementTypeOther');
|
|
297
|
+
const id = String(node.identifier ?? '');
|
|
298
|
+
const label = String(node.label ?? node.name ?? '');
|
|
299
|
+
const value = String(node.value ?? '');
|
|
300
|
+
const enabled = node.enabled == null ? true : bool(node.enabled);
|
|
301
|
+
const visible = node.visible == null ? true : bool(node.visible);
|
|
302
|
+
const typeLooksInteractive = /Button|Cell|Link|Switch|Tab|Image|TextField|SecureTextField/i.test(type);
|
|
303
|
+
const clickable = node.hittable == null ? enabled && visible && typeLooksInteractive : bool(node.hittable);
|
|
304
|
+
const childValues = Object.entries(node)
|
|
305
|
+
.filter(([k]) => k === 'children' || k.startsWith('XCUIElementType'))
|
|
306
|
+
.flatMap(([, v]) => Array.isArray(v) ? v : v ? [v] : []);
|
|
307
|
+
const out = {
|
|
308
|
+
class: type,
|
|
309
|
+
text: value || label,
|
|
310
|
+
'content-desc': label,
|
|
311
|
+
'resource-id': id,
|
|
312
|
+
bounds: iosBounds(node),
|
|
313
|
+
clickable,
|
|
314
|
+
'long-clickable': false,
|
|
315
|
+
scrollable: /ScrollView|Table|CollectionView|Picker|WebView/i.test(type),
|
|
316
|
+
focusable: /TextField|SecureTextField|TextView/i.test(type),
|
|
317
|
+
focused: bool(node.focused),
|
|
318
|
+
enabled,
|
|
319
|
+
password: /SecureTextField/i.test(type),
|
|
320
|
+
};
|
|
321
|
+
if (childValues.length)
|
|
322
|
+
out.node = childValues.map((c) => normalizeNode(c));
|
|
323
|
+
return out;
|
|
324
|
+
}
|
|
325
|
+
function esc(v) {
|
|
326
|
+
return String(v ?? '').replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
327
|
+
}
|
|
328
|
+
function nodeXml(node) {
|
|
329
|
+
const children = Array.isArray(node.node) ? node.node : [];
|
|
330
|
+
const attrs = Object.entries(node)
|
|
331
|
+
.filter(([k]) => k !== 'node')
|
|
332
|
+
.map(([k, v]) => `${k}="${esc(v)}"`)
|
|
333
|
+
.join(' ');
|
|
334
|
+
return `<node ${attrs}>${children.map(nodeXml).join('')}</node>`;
|
|
335
|
+
}
|
|
336
|
+
export function normalizeWdaSource(xml) {
|
|
337
|
+
const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '', isArray: (name) => name.startsWith('XCUIElementType') || name === 'children' });
|
|
338
|
+
const doc = parser.parse(xml);
|
|
339
|
+
const rootKey = Object.keys(doc).find((k) => k.startsWith('XCUIElementType'));
|
|
340
|
+
if (!rootKey)
|
|
341
|
+
return xml;
|
|
342
|
+
const rootRaw = doc[rootKey];
|
|
343
|
+
const root = (Array.isArray(rootRaw) ? rootRaw[0] : rootRaw);
|
|
344
|
+
const normalized = normalizeNode({ ...root, type: root.type ?? rootKey });
|
|
345
|
+
return `<hierarchy>${nodeXml(normalized)}</hierarchy>`;
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=wda.js.map
|