swipium 1.2.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 +7 -8
- package/dist/appMap/featureIndex.js +183 -0
- package/dist/appMap/featureIndex.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/server.js +11 -0
- package/dist/server.js.map +1 -1
- 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 +20 -0
- 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/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/version.js +12 -2
- package/dist/version.js.map +1 -1
- package/docs/mcp-server.md +1 -1
- package/docs/tools.md +41 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -236,29 +236,28 @@ After setup, verify that the client lists `qa_test_this`, `qa_capabilities`, and
|
|
|
236
236
|
|
|
237
237
|
## Tool Docs
|
|
238
238
|
|
|
239
|
-
Swipium exposes
|
|
239
|
+
Swipium exposes 91 public MCP tools. Start with `qa_test_this` for low-context requests.
|
|
240
240
|
|
|
241
241
|
Full reference: [docs/tools.md](docs/tools.md)
|
|
242
242
|
|
|
243
|
-
New in 1.
|
|
243
|
+
New in 1.3.0 — 8 additional tools, all backward compatible:
|
|
244
244
|
|
|
245
|
-
- **
|
|
246
|
-
- **
|
|
247
|
-
- **Flow system and suite quality**: `qa_flow_plan`, `qa_flow_repair`, `qa_suite_lint`, `qa_pom_generate`.
|
|
248
|
-
- **Maestro interop**: `qa_maestro_import`, `qa_maestro_export`.
|
|
249
|
-
- **Agent-efficiency helpers**: `qa_locator_suggest`, `qa_input_capabilities`, `qa_wait`, `qa_idling_status`, `qa_job_cancel`.
|
|
245
|
+
- **Feature-focused testing**: `qa_feature_scope`, `qa_feature_test_plan`, `qa_test_feature` — test a specific feature by name ("the login flow"), backed by the app knowledge map.
|
|
246
|
+
- **Local build and artifact resolution**: `qa_resolve_target`, `qa_resolve_artifact`, `qa_build_plan`, `qa_build`, `qa_bundletool` — pick a device, find or build an installable artifact, and convert an `.aab` to an installable APK.
|
|
250
247
|
|
|
251
|
-
Earlier
|
|
248
|
+
Earlier releases added device-parity, local-first visual, seeded-state, report-history (1.1.0) and durable issue memory, persistent test suite, flow plan/repair, Maestro interop, and agent helpers (1.2.0).
|
|
252
249
|
|
|
253
250
|
| Group | Tools |
|
|
254
251
|
| --- | --- |
|
|
255
252
|
| Start | `qa_agent_brief`, `qa_capabilities`, `qa_test_this`, `qa_job_status`, `qa_job_cancel`, `qa_status`, `qa_explain_blocker`, `qa_continue_from_blocker`, `qa_get_artifact` |
|
|
256
253
|
| Setup | `qa_doctor`, `qa_start_session`, `qa_detect_context`, `qa_plan`, `qa_prepare_target`, `qa_prepare_ios_target`, `qa_ios`, `qa_wda` |
|
|
254
|
+
| Build | `qa_resolve_target`, `qa_resolve_artifact`, `qa_build_plan`, `qa_build`, `qa_bundletool` |
|
|
257
255
|
| Device | `qa_device_info`, `qa_permissions`, `qa_orientation`, `qa_geolocation`, `qa_network`, `qa_metro`, `qa_app_control`, `qa_screen_info`, `qa_screen_record` |
|
|
258
256
|
| Drive | `qa_snapshot`, `qa_act`, `qa_clear_overlay`, `qa_check_health`, `qa_screenshot`, `qa_note`, `qa_assert_visual`, `qa_visual`, `qa_visual_find_text`, `qa_locator_suggest`, `qa_input_capabilities`, `qa_wait`, `qa_idling_status` |
|
|
259
257
|
| State | `qa_seed`, `qa_state_prepare`, `qa_state_verify`, `qa_state_teardown` |
|
|
260
258
|
| Run | `qa_smoke`, `qa_explore`, `qa_report`, `qa_report_compare`, `qa_run_history` |
|
|
261
259
|
| App map | `qa_app_map_build`, `qa_app_map_read`, `qa_app_map_query`, `qa_app_map_feature_scope`, `qa_app_map_validate` |
|
|
260
|
+
| Feature | `qa_feature_scope`, `qa_feature_test_plan`, `qa_test_feature` |
|
|
262
261
|
| Flows and suites | `qa_flow_check`, `qa_flow_plan`, `qa_flow_run`, `qa_flow_generate`, `qa_flow_repair`, `qa_suite_generate`, `qa_suite_compile`, `qa_suite_lint`, `qa_pom_generate`, `qa_testcase_generate` |
|
|
263
262
|
| Test suite | `qa_test_suite_read`, `qa_test_suite_update`, `qa_test_suite_generate`, `qa_test_suite_export`, `qa_test_suite_lint` |
|
|
264
263
|
| Interop | `qa_maestro_import`, `qa_maestro_export` |
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// Code-aware feature index (SWIPIUM-REQ-03, foundation from SWIPIUM-REQ-01). A deterministic,
|
|
2
|
+
// LLM-free static index over the project's source so a natural-language feature request can be
|
|
3
|
+
// mapped to concrete code: screen/component/service/hook symbols, navigation route names, and
|
|
4
|
+
// per-file token sets. Pure logic lives in tokenize/extractSymbols/extractRoutes (unit-tested
|
|
5
|
+
// without a filesystem); buildFeatureIndex walks the tree and applies them.
|
|
6
|
+
//
|
|
7
|
+
// v1 is intentionally deterministic (Non-Goals): regex + name heuristics, not semantic analysis.
|
|
8
|
+
// The schema carries tokens so synonym/locale expansion can happen at query time later.
|
|
9
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
10
|
+
import { join, relative, extname, basename } from 'node:path';
|
|
11
|
+
const SOURCE_EXT = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.swift', '.kt', '.java', '.dart', '.vue']);
|
|
12
|
+
const SKIP_DIRS = new Set([
|
|
13
|
+
'node_modules', '.git', '.svn', '.hg', 'dist', 'build', 'out', 'coverage', '.next', '.expo',
|
|
14
|
+
'.gradle', 'Pods', 'DerivedData', 'vendor', '.swipium', '__snapshots__', '.cache', 'tmp', '.idea', '.vscode',
|
|
15
|
+
]);
|
|
16
|
+
/** Split an identifier (camelCase / PascalCase / snake_case / kebab-case) into lowercase word tokens. */
|
|
17
|
+
export function tokenize(name) {
|
|
18
|
+
return name
|
|
19
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2') // camelCase boundary
|
|
20
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') // ACRONYMWord boundary
|
|
21
|
+
.split(/[^A-Za-z0-9]+/)
|
|
22
|
+
.map((t) => t.trim().toLowerCase())
|
|
23
|
+
.filter((t) => t.length > 1 && !/^\d+$/.test(t));
|
|
24
|
+
}
|
|
25
|
+
/** Classify a symbol by conventional name suffixes/prefixes — deterministic, English-leaning v1. */
|
|
26
|
+
export function classifySymbol(name) {
|
|
27
|
+
if (/^use[A-Z]/.test(name))
|
|
28
|
+
return 'hook';
|
|
29
|
+
if (/(Screen|Page|View|Activity|Fragment|Route)$/.test(name))
|
|
30
|
+
return 'screen';
|
|
31
|
+
if (/(Service|Api|Client|Repository|Store|Manager|Provider|Controller|Analyzer|Engine)$/.test(name))
|
|
32
|
+
return 'service';
|
|
33
|
+
if (/(Component|Card|Button|List|Modal|Sheet|Header|Footer|Item|Row|Tile|Widget)$/.test(name))
|
|
34
|
+
return 'component';
|
|
35
|
+
if (/^[A-Z]/.test(name))
|
|
36
|
+
return 'component'; // PascalCase default → likely a component/class
|
|
37
|
+
return 'function';
|
|
38
|
+
}
|
|
39
|
+
/** Extract declared symbol names from a source file's text (JS/TS/Swift/Kotlin/Java/Dart). PURE. */
|
|
40
|
+
export function extractSymbols(text) {
|
|
41
|
+
const out = [];
|
|
42
|
+
const seen = new Set();
|
|
43
|
+
const lines = text.split('\n');
|
|
44
|
+
const patterns = [
|
|
45
|
+
// JS/TS declarations
|
|
46
|
+
/(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)/,
|
|
47
|
+
/(?:export\s+)?(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)/,
|
|
48
|
+
/(?:export\s+)?const\s+([A-Za-z_$][\w$]*)\s*(?::[^=]+)?=\s*(?:async\s*)?(?:\(|function|React\.|memo\(|forwardRef\(|styled)/,
|
|
49
|
+
/(?:export\s+)?(?:interface|type|enum)\s+([A-Za-z_$][\w$]*)/,
|
|
50
|
+
// Swift / Kotlin / Java
|
|
51
|
+
/(?:struct|protocol|extension)\s+([A-Z][\w]*)/,
|
|
52
|
+
/(?:fun|func)\s+([A-Za-z_][\w]*)/,
|
|
53
|
+
// Dart / Kotlin widgets
|
|
54
|
+
/(?:Widget|StatelessWidget|StatefulWidget)\s+([A-Z][\w]*)/,
|
|
55
|
+
];
|
|
56
|
+
for (let i = 0; i < lines.length; i++) {
|
|
57
|
+
const line = lines[i];
|
|
58
|
+
for (const re of patterns) {
|
|
59
|
+
const m = re.exec(line);
|
|
60
|
+
if (!m)
|
|
61
|
+
continue;
|
|
62
|
+
const name = m[1];
|
|
63
|
+
const key = `${name}@${i}`;
|
|
64
|
+
if (seen.has(key))
|
|
65
|
+
continue;
|
|
66
|
+
seen.add(key);
|
|
67
|
+
out.push({ name, kind: classifySymbol(name), line: i + 1 });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
/** Extract navigation route / deep-link names from a source file's text. PURE. */
|
|
73
|
+
export function extractRoutes(text) {
|
|
74
|
+
const out = [];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
const lines = text.split('\n');
|
|
77
|
+
const patterns = [
|
|
78
|
+
/\bpath\s*:\s*['"`]([^'"`]+)['"`]/g, // react-router / vue-router { path: '/weather' }
|
|
79
|
+
/\b(?:name|routeName|screen)\s*:\s*['"`]([A-Za-z][^'"`]*)['"`]/g, // { name: 'WeatherAnalysis' }
|
|
80
|
+
/<(?:Stack|Tab|Drawer|NativeStack)?\.?Screen\b[^>]*\bname\s*=\s*['"`]([^'"`]+)['"`]/g, // RN <Stack.Screen name="Weather" />
|
|
81
|
+
/navigation\.(?:navigate|push|replace)\(\s*['"`]([^'"`]+)['"`]/g, // navigation.navigate('Weather')
|
|
82
|
+
/(?:Link|NavLink|router\.push)\(?\s*[^'"`]*['"`](\/[A-Za-z][^'"`]*)['"`]/g, // router.push('/weather')
|
|
83
|
+
/['"`]([a-z][\w.+-]*:\/\/[^'"`\s]+)['"`]/g, // deep links myapp://weather
|
|
84
|
+
];
|
|
85
|
+
for (let i = 0; i < lines.length; i++) {
|
|
86
|
+
for (const re of patterns) {
|
|
87
|
+
re.lastIndex = 0;
|
|
88
|
+
let m;
|
|
89
|
+
while ((m = re.exec(lines[i]))) {
|
|
90
|
+
const route = m[1];
|
|
91
|
+
if (!route || route.length > 80)
|
|
92
|
+
continue;
|
|
93
|
+
const key = `${route}@${i}`;
|
|
94
|
+
if (seen.has(key))
|
|
95
|
+
continue;
|
|
96
|
+
seen.add(key);
|
|
97
|
+
out.push({ route, line: i + 1 });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
/** Tokenize a route string into searchable terms (drops scheme/slashes/params). */
|
|
104
|
+
function routeTokens(route) {
|
|
105
|
+
return tokenize(route.replace(/^[a-z]+:\/\//i, '').replace(/[/:?#&=]/g, ' '));
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Walk the project tree and build the static feature index. Reads files (best-effort) and applies
|
|
109
|
+
* the pure extractors. Skips build/vendor dirs and caps the file count to keep context bounded.
|
|
110
|
+
*/
|
|
111
|
+
export function buildFeatureIndex(root, opts = {}) {
|
|
112
|
+
const maxFiles = opts.maxFiles ?? 4000;
|
|
113
|
+
const maxBytes = opts.maxBytesPerFile ?? 512 * 1024;
|
|
114
|
+
const symbols = [];
|
|
115
|
+
const routes = [];
|
|
116
|
+
const files = [];
|
|
117
|
+
let scannedFiles = 0;
|
|
118
|
+
let truncated = false;
|
|
119
|
+
const walk = (dir) => {
|
|
120
|
+
if (scannedFiles >= maxFiles) {
|
|
121
|
+
truncated = true;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
let entries;
|
|
125
|
+
try {
|
|
126
|
+
entries = readdirSync(dir).sort();
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
for (const name of entries) {
|
|
132
|
+
if (scannedFiles >= maxFiles) {
|
|
133
|
+
truncated = true;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (name.startsWith('.') && name !== '.')
|
|
137
|
+
continue;
|
|
138
|
+
const full = join(dir, name);
|
|
139
|
+
let st;
|
|
140
|
+
try {
|
|
141
|
+
st = statSync(full);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (st.isDirectory()) {
|
|
147
|
+
if (SKIP_DIRS.has(name))
|
|
148
|
+
continue;
|
|
149
|
+
walk(full);
|
|
150
|
+
}
|
|
151
|
+
else if (st.isFile()) {
|
|
152
|
+
const ext = extname(name).toLowerCase();
|
|
153
|
+
if (!SOURCE_EXT.has(ext))
|
|
154
|
+
continue;
|
|
155
|
+
if (/\.(d\.ts|test\.[tj]sx?|spec\.[tj]sx?)$/.test(name))
|
|
156
|
+
continue; // skip declarations + tests
|
|
157
|
+
if (st.size > maxBytes)
|
|
158
|
+
continue;
|
|
159
|
+
const rel = relative(root, full);
|
|
160
|
+
const base = basename(name, ext);
|
|
161
|
+
files.push({ file: rel, base, tokens: tokenize(base) });
|
|
162
|
+
scannedFiles++;
|
|
163
|
+
let text;
|
|
164
|
+
try {
|
|
165
|
+
text = readFileSync(full, 'utf8');
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
for (const s of extractSymbols(text)) {
|
|
171
|
+
symbols.push({ name: s.name, kind: s.kind, file: rel, line: s.line, tokens: tokenize(s.name) });
|
|
172
|
+
}
|
|
173
|
+
for (const r of extractRoutes(text)) {
|
|
174
|
+
routes.push({ route: r.route, file: rel, line: r.line, tokens: routeTokens(r.route) });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
if (existsSync(root))
|
|
180
|
+
walk(root);
|
|
181
|
+
return { root, symbols, routes, files, scannedFiles, truncated };
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=featureIndex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"featureIndex.js","sourceRoot":"","sources":["../../src/appMap/featureIndex.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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"}
|