swipium 1.1.0 → 1.3.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/README.md +14 -9
- package/THREAT_MODEL.md +59 -0
- package/dist/appMap/diff.js +60 -0
- package/dist/appMap/diff.js.map +1 -0
- package/dist/appMap/featureIndex.js +183 -0
- package/dist/appMap/featureIndex.js.map +1 -0
- package/dist/automation/capabilities.js +167 -0
- package/dist/automation/capabilities.js.map +1 -0
- package/dist/automation/gestures.js +94 -0
- package/dist/automation/gestures.js.map +1 -0
- package/dist/automation/maestroIr.js +282 -0
- package/dist/automation/maestroIr.js.map +1 -0
- package/dist/automation/plan.js +268 -0
- package/dist/automation/plan.js.map +1 -0
- package/dist/automation/report.js +83 -0
- package/dist/automation/report.js.map +1 -0
- package/dist/automation/selectors.js +201 -0
- package/dist/automation/selectors.js.map +1 -0
- package/dist/automation/types.js +13 -0
- package/dist/automation/types.js.map +1 -0
- package/dist/automation/waits.js +91 -0
- package/dist/automation/waits.js.map +1 -0
- package/dist/automation/webview.js +81 -0
- package/dist/automation/webview.js.map +1 -0
- package/dist/featureTesting/executionBootstrap.js +14 -10
- package/dist/featureTesting/executionBootstrap.js.map +1 -1
- package/dist/featureTesting/featureMap.js +53 -0
- package/dist/featureTesting/featureMap.js.map +1 -0
- package/dist/featureTesting/featureScope.js +354 -0
- package/dist/featureTesting/featureScope.js.map +1 -0
- package/dist/featureTesting/mapFeatureScope.js +143 -0
- package/dist/featureTesting/mapFeatureScope.js.map +1 -0
- package/dist/featureTesting/objectiveModel.js +129 -0
- package/dist/featureTesting/objectiveModel.js.map +1 -0
- package/dist/featureTesting/resultMerge.js +112 -0
- package/dist/featureTesting/resultMerge.js.map +1 -0
- package/dist/featureTesting/sources.js +96 -0
- package/dist/featureTesting/sources.js.map +1 -0
- package/dist/featureTesting/suiteBridge.js +51 -0
- package/dist/featureTesting/suiteBridge.js.map +1 -0
- package/dist/featureTesting/synonyms.js +73 -0
- package/dist/featureTesting/synonyms.js.map +1 -0
- package/dist/featureTesting/testCaseFactory.js +107 -0
- package/dist/featureTesting/testCaseFactory.js.map +1 -0
- package/dist/featureTesting/testPlan.js +50 -0
- package/dist/featureTesting/testPlan.js.map +1 -0
- package/dist/flows/repair.js +363 -0
- package/dist/flows/repair.js.map +1 -0
- package/dist/interop/maestro.js +151 -0
- package/dist/interop/maestro.js.map +1 -0
- package/dist/mobileAudit/checks.js +251 -0
- package/dist/mobileAudit/checks.js.map +1 -0
- package/dist/mobileAudit/evidence.js +49 -0
- package/dist/mobileAudit/evidence.js.map +1 -0
- package/dist/mobileAudit/profiles.js +73 -0
- package/dist/mobileAudit/profiles.js.map +1 -0
- package/dist/mobileAudit/results.js +41 -0
- package/dist/mobileAudit/results.js.map +1 -0
- package/dist/mobileAudit/runner.js +229 -0
- package/dist/mobileAudit/runner.js.map +1 -0
- package/dist/oracle/locator.js +177 -0
- package/dist/oracle/locator.js.map +1 -0
- package/dist/server.js +31 -0
- package/dist/server.js.map +1 -1
- package/dist/testSuite/exporter.js +70 -0
- package/dist/testSuite/exporter.js.map +1 -0
- package/dist/testSuite/lint.js +53 -0
- package/dist/testSuite/lint.js.map +1 -0
- package/dist/tools/build.js +172 -0
- package/dist/tools/build.js.map +1 -0
- package/dist/tools/bundletool.js +145 -0
- package/dist/tools/bundletool.js.map +1 -0
- package/dist/tools/capabilities.js +66 -4
- package/dist/tools/capabilities.js.map +1 -1
- package/dist/tools/featureTesting.js +344 -0
- package/dist/tools/featureTesting.js.map +1 -0
- package/dist/tools/flow.js +67 -1
- package/dist/tools/flow.js.map +1 -1
- package/dist/tools/flowRepair.js +66 -0
- package/dist/tools/flowRepair.js.map +1 -0
- package/dist/tools/idling.js +104 -0
- package/dist/tools/idling.js.map +1 -0
- package/dist/tools/inputCapabilities.js +30 -0
- package/dist/tools/inputCapabilities.js.map +1 -0
- package/dist/tools/issues.js +218 -0
- package/dist/tools/issues.js.map +1 -0
- package/dist/tools/jobs.js +12 -1
- package/dist/tools/jobs.js.map +1 -1
- package/dist/tools/locator.js +57 -0
- package/dist/tools/locator.js.map +1 -0
- package/dist/tools/maestro.js +78 -0
- package/dist/tools/maestro.js.map +1 -0
- package/dist/tools/mobileAudit.js +115 -0
- package/dist/tools/mobileAudit.js.map +1 -0
- package/dist/tools/resolveArtifact.js +80 -0
- package/dist/tools/resolveArtifact.js.map +1 -0
- package/dist/tools/resolveTarget.js +67 -0
- package/dist/tools/resolveTarget.js.map +1 -0
- package/dist/tools/suite.js +49 -1
- package/dist/tools/suite.js.map +1 -1
- package/dist/tools/testSuite.js +233 -0
- package/dist/tools/testSuite.js.map +1 -0
- package/dist/tools/wait.js +58 -0
- package/dist/tools/wait.js.map +1 -0
- package/dist/version.js +41 -2
- package/dist/version.js.map +1 -1
- package/docs/mcp-server.md +1 -1
- package/docs/tools.md +86 -3
- package/package.json +3 -2
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Automation Kernel V2 — Workstream 4: Wait and Stability Engine. Explicit wait contracts that
|
|
2
|
+
// replace scattered sleeps. Assertions act as efficient condition waits (Maestro parity); idle waits
|
|
3
|
+
// label their source as app_declared | backend | heuristic (Espresso parity); tap retry is explicit
|
|
4
|
+
// and recorded, never a hidden loop. All executors take an injectable clock so they are deterministic
|
|
5
|
+
// in tests and never block on real wall-clock during unit runs.
|
|
6
|
+
export const realClock = {
|
|
7
|
+
now: () => Date.now(),
|
|
8
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Poll `condition` until it returns true or `timeoutMs` elapses. Exits early the instant the
|
|
12
|
+
* condition holds. Always evaluates the condition at least once.
|
|
13
|
+
*/
|
|
14
|
+
export async function waitForCondition(condition, opts) {
|
|
15
|
+
const clock = opts.clock ?? realClock;
|
|
16
|
+
const interval = Math.max(1, opts.intervalMs ?? 250);
|
|
17
|
+
const start = clock.now();
|
|
18
|
+
let polls = 0;
|
|
19
|
+
// First, immediate check.
|
|
20
|
+
polls += 1;
|
|
21
|
+
if (await condition())
|
|
22
|
+
return { ok: true, elapsedMs: clock.now() - start, polls, timedOut: false };
|
|
23
|
+
while (clock.now() - start < opts.timeoutMs) {
|
|
24
|
+
await clock.sleep(interval);
|
|
25
|
+
polls += 1;
|
|
26
|
+
if (await condition())
|
|
27
|
+
return { ok: true, elapsedMs: clock.now() - start, polls, timedOut: false };
|
|
28
|
+
}
|
|
29
|
+
return { ok: false, elapsedMs: clock.now() - start, polls, timedOut: true };
|
|
30
|
+
}
|
|
31
|
+
/** Wait until an element is visible (assertion-as-wait). */
|
|
32
|
+
export async function waitForVisible(isVisible, opts) {
|
|
33
|
+
const r = await waitForCondition(isVisible, opts);
|
|
34
|
+
return { ...r, visible: r.ok };
|
|
35
|
+
}
|
|
36
|
+
/** Wait until an element is NOT visible — e.g. a spinner / modal disappears. */
|
|
37
|
+
export async function waitForNotVisible(isVisible, opts) {
|
|
38
|
+
const r = await waitForCondition(async () => !(await isVisible()), opts);
|
|
39
|
+
return { ...r, visible: !r.ok };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Wait for the UI to settle. The caller declares which idle source is in play:
|
|
43
|
+
* - app_declared: an Espresso/RN idling resource the app exposes (the only true proof)
|
|
44
|
+
* - backend: WDA/XCTest waitForIdle or driver-level idle
|
|
45
|
+
* - heuristic: best-effort settling (screenshot stability etc.) — labeled as NOT app proof
|
|
46
|
+
*/
|
|
47
|
+
export async function waitForIdle(isIdle, opts) {
|
|
48
|
+
const r = await waitForCondition(isIdle, opts);
|
|
49
|
+
const appProof = opts.source === 'app_declared' && r.ok;
|
|
50
|
+
const detail = opts.source === 'app_declared'
|
|
51
|
+
? r.ok ? 'app-declared idling resource reported idle' : 'app-declared idling resource did not settle within timeout'
|
|
52
|
+
: opts.source === 'backend'
|
|
53
|
+
? r.ok ? 'backend (WDA/XCTest) idle reported' : 'backend idle did not settle within timeout'
|
|
54
|
+
: r.ok ? 'heuristic settling reached (not app-declared proof)' : 'heuristic settling did not stabilize within timeout';
|
|
55
|
+
return { ok: r.ok, source: opts.source, elapsedMs: r.elapsedMs, polls: r.polls, timedOut: r.timedOut, appProof, detail };
|
|
56
|
+
}
|
|
57
|
+
/** waitForAnimationToEnd — on timeout, pass with a warning rather than fail. */
|
|
58
|
+
export async function waitForAnimationToEnd(isAnimating, opts) {
|
|
59
|
+
const r = await waitForCondition(async () => !(await isAnimating()), opts);
|
|
60
|
+
if (r.ok)
|
|
61
|
+
return { ok: true, timedOut: false, elapsedMs: r.elapsedMs };
|
|
62
|
+
return {
|
|
63
|
+
ok: true,
|
|
64
|
+
timedOut: true,
|
|
65
|
+
elapsedMs: r.elapsedMs,
|
|
66
|
+
warning: `animation did not end within ${opts.timeoutMs}ms; continuing (pass-with-warning).`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Tap with explicit `retryTapIfNoChange` semantics. Taps once; if the UI did not change AND retry is
|
|
71
|
+
* enabled, taps exactly one more time. Every attempt (with before/after signatures) is recorded so
|
|
72
|
+
* the retry is visible in the plan and evidence — never a hidden loop.
|
|
73
|
+
*/
|
|
74
|
+
export async function retryTapIfNoChange(doTap, screenSignature, opts) {
|
|
75
|
+
const attempts = [];
|
|
76
|
+
const before1 = await screenSignature();
|
|
77
|
+
await doTap();
|
|
78
|
+
const after1 = await screenSignature();
|
|
79
|
+
const changed1 = before1 !== after1;
|
|
80
|
+
attempts.push({ index: 0, changed: changed1, signatureBefore: before1, signatureAfter: after1 });
|
|
81
|
+
if (changed1 || !opts.enabled) {
|
|
82
|
+
return { ok: true, attempts, retried: false, changed: changed1 };
|
|
83
|
+
}
|
|
84
|
+
const before2 = after1;
|
|
85
|
+
await doTap();
|
|
86
|
+
const after2 = await screenSignature();
|
|
87
|
+
const changed2 = before2 !== after2;
|
|
88
|
+
attempts.push({ index: 1, changed: changed2, signatureBefore: before2, signatureAfter: after2 });
|
|
89
|
+
return { ok: true, attempts, retried: true, changed: changed2 };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=waits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waits.js","sourceRoot":"","sources":["../../src/automation/waits.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Automation Kernel V2 — Workstream 7 (Stage 1): Hybrid / WebView awareness. Pure detection of
|
|
2
|
+
// probable WebView / canvas / map surfaces from UI-tree element classes, plus the diagnostics that
|
|
3
|
+
// tell a developer whether structured automation is possible or whether Appium context support (or a
|
|
4
|
+
// native accessibility id) is required. Stage 1 is detection + read-only Appium context inventory; it
|
|
5
|
+
// does NOT add arbitrary web JS execution.
|
|
6
|
+
// Class/role signals. Matching is case-insensitive substring so framework-prefixed classes still hit.
|
|
7
|
+
const WEBVIEW_SIGNALS = ['webview', 'wkwebview', 'uiwebview', 'org.chromium', 'xwalk', 'xcuielementtypewebview', 'rctwebview'];
|
|
8
|
+
const CANVAS_SIGNALS = ['surfaceview', 'textureview', 'glsurfaceview', 'flutterview', 'unityplayer', 'canvas'];
|
|
9
|
+
const MAP_SIGNALS = ['mapview', 'com.google.android.maps', 'mkmapview', 'gmsmapview'];
|
|
10
|
+
function classify(value) {
|
|
11
|
+
const v = value.toLowerCase();
|
|
12
|
+
if (WEBVIEW_SIGNALS.some((s) => v.includes(s)))
|
|
13
|
+
return 'webview';
|
|
14
|
+
if (MAP_SIGNALS.some((s) => v.includes(s)))
|
|
15
|
+
return 'map';
|
|
16
|
+
if (CANVAS_SIGNALS.some((s) => v.includes(s)))
|
|
17
|
+
return 'canvas';
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
/** Detect probable hybrid surfaces from a parsed UI tree and emit the right diagnostic. */
|
|
21
|
+
export function detectHybridSurface(elements, opts = {}) {
|
|
22
|
+
const surfaces = [];
|
|
23
|
+
for (const el of elements) {
|
|
24
|
+
for (const value of [el.className, el.role]) {
|
|
25
|
+
if (!value)
|
|
26
|
+
continue;
|
|
27
|
+
const kind = classify(value);
|
|
28
|
+
if (kind && !surfaces.some((s) => s.kind === kind && s.signal === value)) {
|
|
29
|
+
surfaces.push({ kind, signal: value });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// A high visual-fallback ratio with no structured matches is a soft hybrid/canvas signal.
|
|
34
|
+
const softHybrid = surfaces.length === 0 && (opts.visualFallbackRatio ?? 0) >= 0.5;
|
|
35
|
+
const hybrid = surfaces.length > 0 || softHybrid;
|
|
36
|
+
if (!hybrid) {
|
|
37
|
+
return { hybrid: false, surfaces, diagnostic: 'NATIVE', appiumRequired: false, agentMessage: 'This screen looks like a native UI tree; structured selectors should work.' };
|
|
38
|
+
}
|
|
39
|
+
const hasWebview = surfaces.some((s) => s.kind === 'webview') || softHybrid;
|
|
40
|
+
const nonNativeContexts = (opts.contexts ?? []).filter((c) => !/^native_app$/i.test(c));
|
|
41
|
+
const contextSwitchingPossible = !!opts.caps?.webviewContexts;
|
|
42
|
+
if (hasWebview && nonNativeContexts.length > 0) {
|
|
43
|
+
return {
|
|
44
|
+
hybrid: true,
|
|
45
|
+
surfaces,
|
|
46
|
+
diagnostic: 'WEBVIEW_CONTEXT_AVAILABLE',
|
|
47
|
+
appiumRequired: false,
|
|
48
|
+
agentMessage: `This screen is hybrid (WebView). Appium reports ${nonNativeContexts.length} web context(s) (${nonNativeContexts.join(', ')}); structural web automation is possible by switching context.`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (hasWebview && contextSwitchingPossible) {
|
|
52
|
+
return {
|
|
53
|
+
hybrid: true,
|
|
54
|
+
surfaces,
|
|
55
|
+
diagnostic: 'WEBVIEW_CONTEXT_MISSING',
|
|
56
|
+
appiumRequired: true,
|
|
57
|
+
agentMessage: 'This screen looks like a WebView. The backend can switch contexts, but none are currently reported; attach the WebView context or use a native accessibility id, otherwise only visual candidate evidence is possible.',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
hybrid: true,
|
|
62
|
+
surfaces,
|
|
63
|
+
diagnostic: 'WEBVIEW_VISUAL_ONLY',
|
|
64
|
+
appiumRequired: true,
|
|
65
|
+
agentMessage: 'This screen looks like a WebView. I need Appium context support or a native accessibility id to automate it structurally; otherwise I can only produce visual candidate evidence.',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Read-only Appium context inventory (Stage 2 deliverable — no arbitrary JS execution). */
|
|
69
|
+
export function summarizeAppiumContexts(contexts, current) {
|
|
70
|
+
const webviewContexts = contexts.filter((c) => !/^native_app$/i.test(c));
|
|
71
|
+
return { current: current ?? null, available: [...contexts], webviewContexts };
|
|
72
|
+
}
|
|
73
|
+
/** An actionable blocker for a structured step that targets a WebView-only surface. */
|
|
74
|
+
export function webviewSelectorBlocker(selector) {
|
|
75
|
+
return {
|
|
76
|
+
code: 'WEBVIEW_VISUAL_ONLY',
|
|
77
|
+
detail: `Selector ${selector.strategy}=${selector.value} targets a WebView surface that the current backend cannot reach structurally.`,
|
|
78
|
+
nextStep: 'This screen looks like a WebView. I need Appium context support or a native accessibility id to automate it structurally; otherwise I can only produce visual candidate evidence.',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=webview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webview.js","sourceRoot":"","sources":["../../src/automation/webview.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// SWIPIUM-REQ-03 Fix Group 4 — bootstrap a prepared device session for qa_test_feature execute/
|
|
2
|
+
// interactive when no prepared session exists. Reuses the SAME resolver/planner/prepare path as
|
|
3
|
+
// qa_test_this (resolveArtifact → planTarget → prepareAndroid/prepareIos), with the SAME consent
|
|
4
|
+
// gate so the high-level feature tool is never less safe than the lower-level prepare tools. When a
|
|
5
|
+
// device/artifact is not available it returns a typed, actionable target-preparation blocker that
|
|
6
|
+
// routes to qa_test_this; the complex lanes (build-from-source, .aab convert, real iOS) are routed
|
|
7
|
+
// to qa_test_this rather than duplicated here.
|
|
4
8
|
import { existsSync, readFileSync } from 'node:fs';
|
|
5
9
|
import { sep } from 'node:path';
|
|
6
10
|
import { createHash } from 'node:crypto';
|
|
@@ -33,19 +37,19 @@ export async function bootstrapFeatureExecution(a) {
|
|
|
33
37
|
ok: false,
|
|
34
38
|
result: qaError({
|
|
35
39
|
what, changedState: false, retrySafe: true, failureCode,
|
|
36
|
-
nextSteps: [...extraSteps, `Or run qa_test_this { projectRoot:"${root}", mode:"execute" } to prepare a
|
|
40
|
+
nextSteps: [...extraSteps, `Or run qa_test_this { projectRoot:"${root}", mode:"execute" } to prepare a device with consent, then qa_test_feature { sessionId:"${session.id}", feature:"${a.feature}", mode:"execute" }.`],
|
|
37
41
|
}, { sessionId: session.id }),
|
|
38
42
|
});
|
|
39
43
|
// ---- artifact (same resolver as qa_test_this) ----
|
|
40
44
|
const art = await resolveArtifact({ projectRoot: root, platform: a.platform ?? 'any' });
|
|
41
45
|
if (!art.best) {
|
|
42
46
|
return routeToTestThis('NO_BUILD_ARTIFACT', `No installable artifact under ${root} to test "${a.feature}".`, [
|
|
43
|
-
'Build
|
|
47
|
+
'Build it: qa_build_plan → qa_build.',
|
|
44
48
|
`Searched: ${art.searchedLocations.slice(0, 5).join('; ') || '(root only)'}.`,
|
|
45
49
|
]);
|
|
46
50
|
}
|
|
47
51
|
if (art.best.type === 'aab') {
|
|
48
|
-
return routeToTestThis('AAB_NEEDS_BUNDLETOOL', 'Only a .aab is present
|
|
52
|
+
return routeToTestThis('AAB_NEEDS_BUNDLETOOL', 'Only a .aab is present — convert it to an installable APK first.', [`Convert: qa_bundletool { aab:"${art.best.path}" }.`]);
|
|
49
53
|
}
|
|
50
54
|
// ---- target (same planner as qa_test_this) ----
|
|
51
55
|
const [adbPresent, simPresent] = await Promise.all([which('adb'), simctlAvailable()]);
|
|
@@ -74,7 +78,7 @@ export async function bootstrapFeatureExecution(a) {
|
|
|
74
78
|
};
|
|
75
79
|
}
|
|
76
80
|
if (target.selected === 'ios-real') {
|
|
77
|
-
return routeToTestThis('IPA_INSTALL_UNSUPPORTED', 'This artifact installs only on a real iOS device
|
|
81
|
+
return routeToTestThis('IPA_INSTALL_UNSUPPORTED', 'This artifact installs only on a real iOS device (signing/provisioning required).', ['Use qa_prepare_ios_real_target.']);
|
|
78
82
|
}
|
|
79
83
|
const isAndroid = SEL_TO_PLATFORM[target.selected] === 'android';
|
|
80
84
|
const effectiveApk = isAndroid ? art.best.path : undefined;
|
|
@@ -97,7 +101,7 @@ export async function bootstrapFeatureExecution(a) {
|
|
|
97
101
|
if (preflight.consentRequired) {
|
|
98
102
|
const gate = consumeConsent(a.consentId, a.approve, { action: 'test_this_plan', affects: preflight.consentAffects });
|
|
99
103
|
if (!gate.approved) {
|
|
100
|
-
a.sessions.recordMutation(session, { tool: '
|
|
104
|
+
a.sessions.recordMutation(session, { tool: 'qa_test_feature', action: 'test_this_plan', risk: preflight.risk, target: preflight.consentAffects, consent: { required: true, approved: false }, status: 'requested' });
|
|
101
105
|
return {
|
|
102
106
|
ok: false,
|
|
103
107
|
result: requireConsent({
|
|
@@ -107,7 +111,7 @@ export async function bootstrapFeatureExecution(a) {
|
|
|
107
111
|
};
|
|
108
112
|
}
|
|
109
113
|
mutationConsent = { required: true, consentId: a.consentId, approved: true };
|
|
110
|
-
a.sessions.recordMutation(session, { tool: '
|
|
114
|
+
a.sessions.recordMutation(session, { tool: 'qa_test_feature', action: 'test_this_plan', risk: preflight.risk, target: preflight.consentAffects, consent: mutationConsent, status: 'approved' });
|
|
111
115
|
}
|
|
112
116
|
// ---- prepare (boot/install/launch) ----
|
|
113
117
|
try {
|
|
@@ -134,7 +138,7 @@ export async function bootstrapFeatureExecution(a) {
|
|
|
134
138
|
}
|
|
135
139
|
const { driver } = await getDriver(session);
|
|
136
140
|
if (!driver)
|
|
137
|
-
return { ok: false, result: qaError({ what: 'No driver bound after device preparation.', changedState: true, retrySafe: true, failureCode: 'NO_DEVICE', nextSteps: ['Run qa_test_this { mode:"execute" } to prepare a
|
|
141
|
+
return { ok: false, result: qaError({ what: 'No driver bound after device preparation.', changedState: true, retrySafe: true, failureCode: 'NO_DEVICE', nextSteps: ['Run qa_test_this { mode:"execute" } to prepare a device, then qa_test_feature with that sessionId.'] }, { sessionId: session.id }) };
|
|
138
142
|
return { ok: true, session, driver };
|
|
139
143
|
}
|
|
140
144
|
//# sourceMappingURL=executionBootstrap.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executionBootstrap.js","sourceRoot":"","sources":["../../src/featureTesting/executionBootstrap.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,
|
|
1
|
+
{"version":3,"file":"executionBootstrap.js","sourceRoot":"","sources":["../../src/featureTesting/executionBootstrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Feature coverage CACHE (Vision Gap Fix 11 — a DERIVED, disposable cache, NOT a source of truth).
|
|
2
|
+
// The single durable QA business-context layer is the App Knowledge Map (.swipium/app-map.json) plus
|
|
3
|
+
// the persistent test suite (.swipium/test-suite.json); both are written on every feature run. This
|
|
4
|
+
// file only memoizes the last per-feature coverage snapshot (runtime/static screens, generated cases
|
|
5
|
+
// and outcomes, blockers, automation readiness, evidence) for fast re-display. It can be deleted and
|
|
6
|
+
// regenerated at any time without losing QA knowledge. Best-effort + bounded.
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
function safeName(value) {
|
|
10
|
+
return value.trim().replace(/[^a-zA-Z0-9._-]+/g, '_').slice(0, 120) || 'unknown-app';
|
|
11
|
+
}
|
|
12
|
+
/** Cache dir — under `.swipium/cache/` to make its disposable/derived status explicit (Fix 11). */
|
|
13
|
+
export function featureCoverageCacheDir(root) {
|
|
14
|
+
return join(root, '.swipium', 'cache', 'feature-coverage');
|
|
15
|
+
}
|
|
16
|
+
export function featureMapPath(root, appId) {
|
|
17
|
+
return join(featureCoverageCacheDir(root), `${safeName(appId ?? 'unknown-app')}.json`);
|
|
18
|
+
}
|
|
19
|
+
export function loadFeatureMap(root, appId) {
|
|
20
|
+
const path = featureMapPath(root, appId);
|
|
21
|
+
if (!existsSync(path))
|
|
22
|
+
return { schemaVersion: 1, appId: appId ?? 'unknown-app', features: [] };
|
|
23
|
+
try {
|
|
24
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
25
|
+
return {
|
|
26
|
+
schemaVersion: 1,
|
|
27
|
+
appId: parsed.appId ?? appId ?? 'unknown-app',
|
|
28
|
+
features: Array.isArray(parsed.features) ? parsed.features : [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return { schemaVersion: 1, appId: appId ?? 'unknown-app', features: [] };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Upsert a feature's coverage (by featureId) and persist. Returns the file path. */
|
|
36
|
+
export function upsertFeatureCoverage(root, appId, coverage) {
|
|
37
|
+
const path = featureMapPath(root, appId);
|
|
38
|
+
const map = loadFeatureMap(root, appId);
|
|
39
|
+
map.appId = appId ?? map.appId ?? 'unknown-app';
|
|
40
|
+
const idx = map.features.findIndex((f) => f.featureId === coverage.featureId);
|
|
41
|
+
if (idx >= 0)
|
|
42
|
+
map.features[idx] = coverage;
|
|
43
|
+
else
|
|
44
|
+
map.features.push(coverage);
|
|
45
|
+
map.features = map.features.slice(-200);
|
|
46
|
+
mkdirSync(featureCoverageCacheDir(root), { recursive: true });
|
|
47
|
+
writeFileSync(path, JSON.stringify(map, null, 2));
|
|
48
|
+
return path;
|
|
49
|
+
}
|
|
50
|
+
export function findFeatureCoverage(root, appId, featureId) {
|
|
51
|
+
return loadFeatureMap(root, appId).features.find((f) => f.featureId === featureId);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=featureMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"featureMap.js","sourceRoot":"","sources":["../../src/featureTesting/featureMap.ts"],"names":[],"mappings":"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"}
|