@sun-asterisk/sungen 3.2.3 → 3.2.5
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/dist/cli/commands/dashboard.js +2 -2
- package/dist/cli/commands/dashboard.js.map +1 -1
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +22 -5
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/feedback.d.ts +11 -0
- package/dist/cli/commands/feedback.d.ts.map +1 -1
- package/dist/cli/commands/feedback.js +154 -57
- package/dist/cli/commands/feedback.js.map +1 -1
- package/dist/cli/commands/login.d.ts +19 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/login.js +76 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/qa-feedback.d.ts +14 -0
- package/dist/cli/commands/qa-feedback.d.ts.map +1 -0
- package/dist/cli/commands/qa-feedback.js +83 -0
- package/dist/cli/commands/qa-feedback.js.map +1 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/snapshot-builder.d.ts +3 -0
- package/dist/dashboard/snapshot-builder.d.ts.map +1 -1
- package/dist/dashboard/snapshot-builder.js +155 -52
- package/dist/dashboard/snapshot-builder.js.map +1 -1
- package/dist/dashboard/templates/index.html +23 -23
- package/dist/dashboard/types.d.ts +8 -0
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/exporters/api-testcase-formatter.d.ts +55 -0
- package/dist/exporters/api-testcase-formatter.d.ts.map +1 -0
- package/dist/exporters/api-testcase-formatter.js +304 -0
- package/dist/exporters/api-testcase-formatter.js.map +1 -0
- package/dist/exporters/csv-exporter.d.ts +13 -1
- package/dist/exporters/csv-exporter.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.js +35 -6
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/feature-parser.d.ts +4 -0
- package/dist/exporters/feature-parser.d.ts.map +1 -1
- package/dist/exporters/feature-parser.js +7 -2
- package/dist/exporters/feature-parser.js.map +1 -1
- package/dist/exporters/json-exporter.d.ts.map +1 -1
- package/dist/exporters/json-exporter.js +11 -3
- package/dist/exporters/json-exporter.js.map +1 -1
- package/dist/exporters/playwright-report-parser.d.ts +21 -2
- package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
- package/dist/exporters/playwright-report-parser.js +52 -12
- package/dist/exporters/playwright-report-parser.js.map +1 -1
- package/dist/exporters/scenario-merger.d.ts.map +1 -1
- package/dist/exporters/scenario-merger.js +50 -2
- package/dist/exporters/scenario-merger.js.map +1 -1
- package/dist/exporters/spec-parser.js +1 -1
- package/dist/exporters/spec-parser.js.map +1 -1
- package/dist/exporters/test-data-resolver.d.ts +7 -0
- package/dist/exporters/test-data-resolver.d.ts.map +1 -1
- package/dist/exporters/test-data-resolver.js +13 -0
- package/dist/exporters/test-data-resolver.js.map +1 -1
- package/dist/exporters/types.d.ts +13 -0
- package/dist/exporters/types.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.js +131 -42
- package/dist/exporters/xlsx-exporter.js.map +1 -1
- package/dist/generators/gherkin-parser/index.d.ts +15 -0
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +33 -1
- package/dist/generators/gherkin-parser/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/check-action.hbs +29 -8
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/scroll-action.hbs +21 -17
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/toggle-action.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/uncheck-action.hbs +26 -6
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/is-hidden-assertion.hbs +3 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-assertion.hbs +14 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/tap-top-action.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/partials/locator.hbs +9 -2
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +115 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +12 -0
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +21 -1
- package/dist/generators/test-generator/patterns/index.js.map +1 -1
- package/dist/harness/script-check.d.ts.map +1 -1
- package/dist/harness/script-check.js +10 -5
- package/dist/harness/script-check.js.map +1 -1
- package/dist/orchestrator/ai-skills/writer.d.ts.map +1 -1
- package/dist/orchestrator/ai-skills/writer.js +46 -4
- package/dist/orchestrator/ai-skills/writer.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +14 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +58 -0
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/commands/add-flow.md +52 -5
- package/dist/orchestrator/templates/ai-src/commands/create-test.md +7 -0
- package/dist/orchestrator/templates/ai-src/commands/feedback.md +6 -6
- package/dist/orchestrator/templates/ai-src/commands/locale.md +26 -4
- package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +5 -4
- package/dist/orchestrator/templates/wdio.conf.ts +39 -3
- package/dist/tools/sungen-api/edit-in-editor.d.ts +15 -0
- package/dist/tools/sungen-api/edit-in-editor.d.ts.map +1 -0
- package/dist/tools/sungen-api/edit-in-editor.js +85 -0
- package/dist/tools/sungen-api/edit-in-editor.js.map +1 -0
- package/dist/tools/sungen-api/feedback-client.d.ts +24 -0
- package/dist/tools/sungen-api/feedback-client.d.ts.map +1 -0
- package/dist/tools/sungen-api/feedback-client.js +53 -0
- package/dist/tools/sungen-api/feedback-client.js.map +1 -0
- package/dist/tools/sungen-api/read-stdin.d.ts +6 -0
- package/dist/tools/sungen-api/read-stdin.d.ts.map +1 -0
- package/dist/tools/sungen-api/read-stdin.js +19 -0
- package/dist/tools/sungen-api/read-stdin.js.map +1 -0
- package/dist/tools/sungen-auth/api-base-url.d.ts +14 -0
- package/dist/tools/sungen-auth/api-base-url.d.ts.map +1 -0
- package/dist/tools/sungen-auth/api-base-url.js +28 -0
- package/dist/tools/sungen-auth/api-base-url.js.map +1 -0
- package/dist/tools/sungen-auth/credentials-store.d.ts +19 -0
- package/dist/tools/sungen-auth/credentials-store.d.ts.map +1 -0
- package/dist/tools/sungen-auth/credentials-store.js +94 -0
- package/dist/tools/sungen-auth/credentials-store.js.map +1 -0
- package/dist/tools/sungen-auth/login-flow.d.ts +33 -0
- package/dist/tools/sungen-auth/login-flow.d.ts.map +1 -0
- package/dist/tools/sungen-auth/login-flow.js +107 -0
- package/dist/tools/sungen-auth/login-flow.js.map +1 -0
- package/dist/tools/sungen-auth/loopback-server.d.ts +20 -0
- package/dist/tools/sungen-auth/loopback-server.d.ts.map +1 -0
- package/dist/tools/sungen-auth/loopback-server.js +132 -0
- package/dist/tools/sungen-auth/loopback-server.js.map +1 -0
- package/dist/tools/sungen-auth/open-browser.d.ts +10 -0
- package/dist/tools/sungen-auth/open-browser.d.ts.map +1 -0
- package/dist/tools/sungen-auth/open-browser.js +43 -0
- package/dist/tools/sungen-auth/open-browser.js.map +1 -0
- package/dist/tools/sungen-auth/pkce.d.ts +13 -0
- package/dist/tools/sungen-auth/pkce.d.ts.map +1 -0
- package/dist/tools/sungen-auth/pkce.js +27 -0
- package/dist/tools/sungen-auth/pkce.js.map +1 -0
- package/dist/tools/sungen-auth/sungen-auth-types.d.ts +41 -0
- package/dist/tools/sungen-auth/sungen-auth-types.d.ts.map +1 -0
- package/dist/tools/sungen-auth/sungen-auth-types.js +7 -0
- package/dist/tools/sungen-auth/sungen-auth-types.js.map +1 -0
- package/package.json +4 -3
- package/src/cli/commands/dashboard.ts +2 -2
- package/src/cli/commands/delivery.ts +24 -6
- package/src/cli/commands/feedback.ts +123 -51
- package/src/cli/commands/login.ts +75 -0
- package/src/cli/commands/qa-feedback.ts +77 -0
- package/src/cli/index.ts +4 -0
- package/src/dashboard/snapshot-builder.ts +146 -46
- package/src/dashboard/templates/index.html +23 -23
- package/src/dashboard/types.ts +10 -2
- package/src/exporters/api-testcase-formatter.ts +325 -0
- package/src/exporters/csv-exporter.ts +46 -7
- package/src/exporters/feature-parser.ts +7 -2
- package/src/exporters/json-exporter.ts +11 -3
- package/src/exporters/playwright-report-parser.ts +50 -11
- package/src/exporters/scenario-merger.ts +49 -3
- package/src/exporters/spec-parser.ts +1 -1
- package/src/exporters/test-data-resolver.ts +13 -0
- package/src/exporters/types.ts +13 -0
- package/src/exporters/xlsx-exporter.ts +155 -49
- package/src/generators/gherkin-parser/index.ts +44 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/check-action.hbs +29 -8
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/scroll-action.hbs +21 -17
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/toggle-action.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/uncheck-action.hbs +26 -6
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/is-hidden-assertion.hbs +3 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-assertion.hbs +14 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/tap-top-action.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/partials/locator.hbs +9 -2
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +115 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +1 -1
- package/src/generators/test-generator/patterns/index.ts +21 -1
- package/src/harness/script-check.ts +10 -5
- package/src/orchestrator/ai-skills/writer.ts +39 -4
- package/src/orchestrator/project-initializer.ts +59 -0
- package/src/orchestrator/templates/ai-src/commands/add-flow.md +52 -5
- package/src/orchestrator/templates/ai-src/commands/create-test.md +7 -0
- package/src/orchestrator/templates/ai-src/commands/feedback.md +6 -6
- package/src/orchestrator/templates/ai-src/commands/locale.md +26 -4
- package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +5 -4
- package/src/orchestrator/templates/wdio.conf.ts +39 -3
- package/src/tools/sungen-api/edit-in-editor.ts +48 -0
- package/src/tools/sungen-api/feedback-client.ts +62 -0
- package/src/tools/sungen-api/read-stdin.ts +15 -0
- package/src/tools/sungen-auth/api-base-url.ts +23 -0
- package/src/tools/sungen-auth/credentials-store.ts +57 -0
- package/src/tools/sungen-auth/login-flow.ts +135 -0
- package/src/tools/sungen-auth/loopback-server.ts +113 -0
- package/src/tools/sungen-auth/open-browser.ts +42 -0
- package/src/tools/sungen-auth/pkce.ts +27 -0
- package/src/tools/sungen-auth/sungen-auth-types.ts +45 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `sungen login` / `sungen logout`.
|
|
4
|
+
*
|
|
5
|
+
* login — RFC 8252 native-app login (PKCE-S256 + loopback + one-time code)
|
|
6
|
+
* against the FastSchema backend; caches the token at
|
|
7
|
+
* ~/.sungen/credentials.json (0600).
|
|
8
|
+
* logout — clears that file.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors the structure of `commands/figma.ts` (commander + scrubbed errors).
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.performLogin = performLogin;
|
|
14
|
+
exports.registerLoginCommand = registerLoginCommand;
|
|
15
|
+
const pkce_1 = require("../../tools/sungen-auth/pkce");
|
|
16
|
+
const loopback_server_1 = require("../../tools/sungen-auth/loopback-server");
|
|
17
|
+
const login_flow_1 = require("../../tools/sungen-auth/login-flow");
|
|
18
|
+
const credentials_store_1 = require("../../tools/sungen-auth/credentials-store");
|
|
19
|
+
const open_browser_1 = require("../../tools/sungen-auth/open-browser");
|
|
20
|
+
const api_base_url_1 = require("../../tools/sungen-auth/api-base-url");
|
|
21
|
+
/**
|
|
22
|
+
* Run the full login flow against `apiBaseUrl`, persist the token, and return
|
|
23
|
+
* the stored credentials. Reused by `sungen feedback` for on-demand re-auth.
|
|
24
|
+
*/
|
|
25
|
+
async function performLogin(apiBaseUrl) {
|
|
26
|
+
const pkce = (0, pkce_1.generatePkce)();
|
|
27
|
+
const correlation = (0, pkce_1.randomCorrelation)();
|
|
28
|
+
const loopback = await (0, loopback_server_1.startLoopback)();
|
|
29
|
+
try {
|
|
30
|
+
const result = await (0, login_flow_1.runLoginFlow)({
|
|
31
|
+
apiBaseUrl,
|
|
32
|
+
pkce,
|
|
33
|
+
correlation,
|
|
34
|
+
redirectUri: loopback.redirectUri,
|
|
35
|
+
openBrowser: open_browser_1.openBrowser,
|
|
36
|
+
waitForCode: (corr, timeoutMs) => loopback.waitForCode(corr, timeoutMs),
|
|
37
|
+
});
|
|
38
|
+
const creds = {
|
|
39
|
+
token: result.token,
|
|
40
|
+
email: result.email,
|
|
41
|
+
api_base_url: apiBaseUrl,
|
|
42
|
+
expires_at: result.expires_at,
|
|
43
|
+
};
|
|
44
|
+
(0, credentials_store_1.saveCredentials)(creds);
|
|
45
|
+
return creds;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
loopback.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function registerLoginCommand(program) {
|
|
52
|
+
program
|
|
53
|
+
.command('login')
|
|
54
|
+
.description('Log in to the sungen feedback backend (opens your browser)')
|
|
55
|
+
.option('--api-base-url <url>', 'Backend base URL (overrides SUNGEN_API_BASE_URL + stored value)')
|
|
56
|
+
.action(async (options) => {
|
|
57
|
+
const apiBaseUrl = (0, api_base_url_1.resolveApiBaseUrl)(options.apiBaseUrl);
|
|
58
|
+
try {
|
|
59
|
+
const creds = await performLogin(apiBaseUrl);
|
|
60
|
+
console.log(`Logged in as ${creds.email}.`);
|
|
61
|
+
console.log(`Token saved to ${(0, credentials_store_1.credentialsPath)()} (mode 0600).`);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
console.error(`Login failed: ${err?.message ?? err}`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
program
|
|
69
|
+
.command('logout')
|
|
70
|
+
.description('Remove the cached sungen login token')
|
|
71
|
+
.action(() => {
|
|
72
|
+
(0, credentials_store_1.clearCredentials)();
|
|
73
|
+
console.log(`Logged out. Removed ${(0, credentials_store_1.credentialsPath)()} (if present).`);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/cli/commands/login.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;AAeH,oCAwBC;AAED,oDAwBC;AA9DD,uDAA+E;AAC/E,6EAAwE;AACxE,mEAAkE;AAClE,iFAA+G;AAC/G,uEAAmE;AACnE,uEAAyE;AAGzE;;;GAGG;AACI,KAAK,UAAU,YAAY,CAAC,UAAkB;IACnD,MAAM,IAAI,GAAG,IAAA,mBAAY,GAAE,CAAC;IAC5B,MAAM,WAAW,GAAG,IAAA,wBAAiB,GAAE,CAAC;IACxC,MAAM,QAAQ,GAAG,MAAM,IAAA,+BAAa,GAAE,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,yBAAY,EAAC;YAChC,UAAU;YACV,IAAI;YACJ,WAAW;YACX,WAAW,EAAE,QAAQ,CAAC,WAAW;YACjC,WAAW,EAAX,0BAAW;YACX,WAAW,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC;SACxE,CAAC,CAAC;QACH,MAAM,KAAK,GAAgB;YACzB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,YAAY,EAAE,UAAU;YACxB,UAAU,EAAE,MAAM,CAAC,UAAU;SAC9B,CAAC;QACF,IAAA,mCAAe,EAAC,KAAK,CAAC,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;YAAS,CAAC;QACT,QAAQ,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAgB,oBAAoB,CAAC,OAAgB;IACnD,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,4DAA4D,CAAC;SACzE,MAAM,CAAC,sBAAsB,EAAE,iEAAiE,CAAC;SACjG,MAAM,CAAC,KAAK,EAAE,OAAgC,EAAE,EAAE;QACjD,MAAM,UAAU,GAAG,IAAA,gCAAiB,EAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACzD,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,CAAC;YAC7C,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;YAC5C,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAA,mCAAe,GAAE,eAAe,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,iBAAiB,GAAG,EAAE,OAAO,IAAI,GAAG,EAAE,CAAC,CAAC;YACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,sCAAsC,CAAC;SACnD,MAAM,CAAC,GAAG,EAAE;QACX,IAAA,oCAAgB,GAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAA,mCAAe,GAAE,gBAAgB,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sungen qa-feedback` — the LOCAL QA-feedback log (test-design knowledge +
|
|
3
|
+
* product telemetry), persisted to .sungen/feedback/feedback.jsonl.
|
|
4
|
+
*
|
|
5
|
+
* This is the persistence primitive behind the `/sungen:feedback` slash command
|
|
6
|
+
* (which auto-attaches screen/viewpoint/decision context and then calls
|
|
7
|
+
* `sungen qa-feedback record`). It is rarely typed by hand.
|
|
8
|
+
*
|
|
9
|
+
* NOTE: distinct from `sungen feedback` (see commands/feedback.ts) — that command
|
|
10
|
+
* submits PRODUCT feedback to the remote backend. This one only writes locally.
|
|
11
|
+
*/
|
|
12
|
+
import { Command } from 'commander';
|
|
13
|
+
export declare function registerQaFeedbackCommand(program: Command): void;
|
|
14
|
+
//# sourceMappingURL=qa-feedback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-feedback.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/qa-feedback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0DhE"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `sungen qa-feedback` — the LOCAL QA-feedback log (test-design knowledge +
|
|
4
|
+
* product telemetry), persisted to .sungen/feedback/feedback.jsonl.
|
|
5
|
+
*
|
|
6
|
+
* This is the persistence primitive behind the `/sungen:feedback` slash command
|
|
7
|
+
* (which auto-attaches screen/viewpoint/decision context and then calls
|
|
8
|
+
* `sungen qa-feedback record`). It is rarely typed by hand.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: distinct from `sungen feedback` (see commands/feedback.ts) — that command
|
|
11
|
+
* submits PRODUCT feedback to the remote backend. This one only writes locally.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.registerQaFeedbackCommand = registerQaFeedbackCommand;
|
|
15
|
+
const feedback_1 = require("../../harness/feedback");
|
|
16
|
+
const TYPES = ['test-design', 'product', 'other'];
|
|
17
|
+
const DECISIONS = ['accept', 'reject', 'edit', 'add', 'none'];
|
|
18
|
+
function registerQaFeedbackCommand(program) {
|
|
19
|
+
const fb = program
|
|
20
|
+
.command('qa-feedback')
|
|
21
|
+
.description('Local-first QA feedback log (test-design knowledge + product telemetry). Primarily invoked by the /sungen:feedback slash command.');
|
|
22
|
+
fb
|
|
23
|
+
.command('record')
|
|
24
|
+
.description('Record a feedback entry')
|
|
25
|
+
.requiredOption('--message <text>', 'The feedback')
|
|
26
|
+
.option('--type <t>', `Feedback type (${TYPES.join('|')})`, 'test-design')
|
|
27
|
+
.option('-s, --screen <name>', 'Screen/flow the feedback relates to')
|
|
28
|
+
.option('--target <ref>', 'What it is about (viewpoint id / scenario / command / artifact)')
|
|
29
|
+
.option('--decision <d>', `QA decision (${DECISIONS.join('|')})`, 'none')
|
|
30
|
+
.option('--reason <text>', 'Why')
|
|
31
|
+
.option('--source <who>', 'Who gave the feedback', 'qa')
|
|
32
|
+
.action((o) => {
|
|
33
|
+
try {
|
|
34
|
+
if (!TYPES.includes(o.type))
|
|
35
|
+
throw new Error(`--type must be one of ${TYPES.join(', ')}`);
|
|
36
|
+
if (!DECISIONS.includes(o.decision))
|
|
37
|
+
throw new Error(`--decision must be one of ${DECISIONS.join(', ')}`);
|
|
38
|
+
const p = (0, feedback_1.recordFeedback)({
|
|
39
|
+
type: o.type,
|
|
40
|
+
screen: o.screen,
|
|
41
|
+
target: o.target,
|
|
42
|
+
decision: o.decision,
|
|
43
|
+
message: o.message,
|
|
44
|
+
reason: o.reason,
|
|
45
|
+
source: o.source,
|
|
46
|
+
});
|
|
47
|
+
console.log(`✓ feedback recorded (${o.type}${o.screen ? ' · ' + o.screen : ''}) → ${p}`);
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
console.error('Error:', e instanceof Error ? e.message : e);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
fb
|
|
55
|
+
.command('list')
|
|
56
|
+
.description('List / summarise recorded feedback')
|
|
57
|
+
.option('-s, --screen <name>', 'Filter by screen')
|
|
58
|
+
.option('--type <t>', 'Filter by type')
|
|
59
|
+
.option('--json', 'Output raw JSON')
|
|
60
|
+
.action((o) => {
|
|
61
|
+
try {
|
|
62
|
+
const s = (0, feedback_1.summarize)({ screen: o.screen, type: o.type });
|
|
63
|
+
if (o.json) {
|
|
64
|
+
console.log(JSON.stringify(s, null, 2));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
console.log(`\n━━━ Feedback (${s.total}) ━━━`);
|
|
68
|
+
console.log(` by type: ${Object.entries(s.byType).map(([k, v]) => `${k}=${v}`).join(' ') || '—'}`);
|
|
69
|
+
console.log(` by decision: ${Object.entries(s.byDecision).map(([k, v]) => `${k}=${v}`).join(' ') || '—'}\n`);
|
|
70
|
+
for (const e of s.entries.slice(-20)) {
|
|
71
|
+
console.log(` [${e.type}${e.decision && e.decision !== 'none' ? '/' + e.decision : ''}] ${e.screen ? e.screen + ' · ' : ''}${e.target ? e.target + ' — ' : ''}${e.message}`);
|
|
72
|
+
if (e.reason)
|
|
73
|
+
console.log(` reason: ${e.reason}`);
|
|
74
|
+
}
|
|
75
|
+
console.log('');
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
console.error('Error:', e instanceof Error ? e.message : e);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=qa-feedback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-feedback.js","sourceRoot":"","sources":["../../../src/cli/commands/qa-feedback.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -24,6 +24,7 @@ const eval_1 = require("./commands/eval");
|
|
|
24
24
|
const manifest_1 = require("./commands/manifest");
|
|
25
25
|
const ledger_1 = require("./commands/ledger");
|
|
26
26
|
const feedback_1 = require("./commands/feedback");
|
|
27
|
+
const qa_feedback_1 = require("./commands/qa-feedback");
|
|
27
28
|
const script_check_1 = require("./commands/script-check");
|
|
28
29
|
const trace_1 = require("./commands/trace");
|
|
29
30
|
const challenge_1 = require("./commands/challenge");
|
|
@@ -32,6 +33,7 @@ const capability_1 = require("./commands/capability");
|
|
|
32
33
|
const flow_check_1 = require("./commands/flow-check");
|
|
33
34
|
const context_1 = require("./commands/context");
|
|
34
35
|
const repair_1 = require("./commands/repair");
|
|
36
|
+
const login_1 = require("./commands/login");
|
|
35
37
|
const registry_1 = require("../capabilities/registry");
|
|
36
38
|
const discover_1 = require("../capabilities/discover");
|
|
37
39
|
// Read version from package.json so `--version` never drifts from the released version.
|
|
@@ -62,6 +64,7 @@ async function main() {
|
|
|
62
64
|
(0, manifest_1.registerManifestCommand)(program);
|
|
63
65
|
(0, ledger_1.registerLedgerCommand)(program);
|
|
64
66
|
(0, feedback_1.registerFeedbackCommand)(program);
|
|
67
|
+
(0, qa_feedback_1.registerQaFeedbackCommand)(program);
|
|
65
68
|
(0, script_check_1.registerScriptCheckCommand)(program);
|
|
66
69
|
(0, trace_1.registerTraceCommand)(program);
|
|
67
70
|
(0, challenge_1.registerChallengeCommand)(program);
|
|
@@ -72,6 +75,7 @@ async function main() {
|
|
|
72
75
|
(0, repair_1.registerRepairCommand)(program);
|
|
73
76
|
(0, ingest_1.registerIngestCommand)(program);
|
|
74
77
|
(0, eval_1.registerEvalCommand)(program);
|
|
78
|
+
(0, login_1.registerLoginCommand)(program);
|
|
75
79
|
// Capability-contributed CLI commands (Capability SPI): drivers own their authoring commands
|
|
76
80
|
// (e.g. @sungen/driver-api → `sungen api import`). Discover, then register each.
|
|
77
81
|
(0, discover_1.discoverAndRegisterCapabilities)();
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;AACA;;;GAGG;;AAEH,yCAAoC;AACpC,0CAAsD;AACtD,wCAAoD;AACpD,kDAA8D;AAC9D,kDAA8D;AAC9D,8CAA0D;AAC1D,kDAA8D;AAC9D,4CAAwD;AACxD,kDAA6D;AAC7D,oDAAgE;AAChE,4CAAwD;AACxD,sDAAiE;AACjE,gDAA4D;AAC5D,0CAAsD;AACtD,8CAA0D;AAC1D,0CAAsD;AACtD,kDAA8D;AAC9D,8CAA0D;AAC1D,kDAA8D;AAC9D,0DAAqE;AACrE,4CAAwD;AACxD,oDAAgE;AAChE,oDAAgE;AAChE,sDAAkE;AAClE,sDAAiE;AACjE,gDAA4D;AAC5D,8CAA0D;AAC1D,uDAA8D;AAC9D,uDAA2E;AAE3E,wFAAwF;AACxF,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAwB,CAAC;AAEzE,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,QAAQ,CAAC;SACd,WAAW,CAAC,oEAAoE,CAAC;SACjF,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,iBAAiB;IACjB,OAAO;SACJ,MAAM,CAAC,eAAe,EAAE,wBAAwB,CAAC,CAAC;IAErD,oBAAoB;IACpB,IAAA,0BAAmB,EAAC,OAAO,CAAC,CAAC;IAC7B,IAAA,wBAAkB,EAAC,OAAO,CAAC,CAAC;IAC5B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,4BAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,IAAA,iCAAsB,EAAC,OAAO,CAAC,CAAC;IAChC,IAAA,oCAAwB,EAAC,OAAO,CAAC,CAAC;IAClC,IAAA,4BAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,IAAA,qCAAwB,EAAC,OAAO,CAAC,CAAC;IAClC,IAAA,gCAAsB,EAAC,OAAO,CAAC,CAAC;IAChC,IAAA,0BAAmB,EAAC,OAAO,CAAC,CAAC;IAC7B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,yCAA0B,EAAC,OAAO,CAAC,CAAC;IACpC,IAAA,4BAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,IAAA,oCAAwB,EAAC,OAAO,CAAC,CAAC;IAClC,IAAA,oCAAwB,EAAC,OAAO,CAAC,CAAC;IAClC,IAAA,sCAAyB,EAAC,OAAO,CAAC,CAAC;IACnC,IAAA,qCAAwB,EAAC,OAAO,CAAC,CAAC;IAClC,IAAA,gCAAsB,EAAC,OAAO,CAAC,CAAC;IAChC,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,0BAAmB,EAAC,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,9 @@ import { EnvironmentInfo } from '../exporters/types';
|
|
|
14
14
|
import { DashboardSnapshot, ScenarioSnapshot } from './types';
|
|
15
15
|
export interface DashboardTarget {
|
|
16
16
|
name: string;
|
|
17
|
+
kind: 'screen' | 'flow' | 'api';
|
|
18
|
+
/** Derived convenience (`kind === 'flow'`) — kept for the flow-specific
|
|
19
|
+
* branches that predate the api tier. */
|
|
17
20
|
isFlow: boolean;
|
|
18
21
|
}
|
|
19
22
|
export interface BuildSnapshotOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot-builder.d.ts","sourceRoot":"","sources":["../../src/dashboard/snapshot-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAeH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAEL,iBAAiB,EAGjB,gBAAgB,EAIjB,MAAM,SAAS,CAAC;AAQjB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,GAAG,EAAE,eAAe,CAAC;IACrB,oEAAoE;IACpE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,iBAAiB,CAwBvF;
|
|
1
|
+
{"version":3,"file":"snapshot-builder.d.ts","sourceRoot":"","sources":["../../src/dashboard/snapshot-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAeH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAEL,iBAAiB,EAGjB,gBAAgB,EAIjB,MAAM,SAAS,CAAC;AAQjB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;IAChC;8CAC0C;IAC1C,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,GAAG,EAAE,eAAe,CAAC;IACrB,oEAAoE;IACpE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,iBAAiB,CAwBvF;AA8XD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,CAwBnE;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CAU5E;AAwFD,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -96,6 +96,24 @@ function buildDashboardSnapshot(options) {
|
|
|
96
96
|
// ----------------------------------------------------------------------------
|
|
97
97
|
// Per-target build
|
|
98
98
|
// ----------------------------------------------------------------------------
|
|
99
|
+
/**
|
|
100
|
+
* True when this is a mobile project (`platform: mobile` in qa/capabilities.yaml).
|
|
101
|
+
* Mobile projects run per-OS (android/ios) — the dashboard surfaces those on a
|
|
102
|
+
* distinct platform axis (one FeatureSnapshot per OS) rather than as locale tabs.
|
|
103
|
+
* Mirrors `isMobileProject` in src/cli/commands/delivery.ts.
|
|
104
|
+
*/
|
|
105
|
+
function isMobileProject(cwd) {
|
|
106
|
+
try {
|
|
107
|
+
const p = path.join(cwd, 'qa', 'capabilities.yaml');
|
|
108
|
+
if (!fs.existsSync(p))
|
|
109
|
+
return false;
|
|
110
|
+
const parsed = (0, yaml_1.parse)(fs.readFileSync(p, 'utf-8'));
|
|
111
|
+
return parsed?.platform === 'mobile';
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
99
117
|
function buildOneScreen(cwd, target, env) {
|
|
100
118
|
const base = qaDir(cwd, target);
|
|
101
119
|
const featuresDir = path.join(base, 'features');
|
|
@@ -111,28 +129,49 @@ function buildOneScreen(cwd, target, env) {
|
|
|
111
129
|
return null;
|
|
112
130
|
const specMdFile = path.join(base, 'requirements', 'spec.md');
|
|
113
131
|
const specLink = fs.existsSync(specMdFile) ? path.relative(cwd, specMdFile) : undefined;
|
|
132
|
+
const mobile = isMobileProject(cwd);
|
|
114
133
|
const features = [];
|
|
115
134
|
for (const featureBaseName of featureBaseNames) {
|
|
116
|
-
|
|
117
|
-
if (fs2)
|
|
118
|
-
features.push(fs2);
|
|
135
|
+
features.push(...buildOneFeature(cwd, target, featureBaseName, env, specLink, mobile));
|
|
119
136
|
}
|
|
120
137
|
if (features.length === 0)
|
|
121
138
|
return null;
|
|
122
139
|
// Flat scenarios across all features (each scenario already carries its
|
|
123
140
|
// own `featureBaseName`, so consumers that want to group can do so cheaply).
|
|
141
|
+
// For mobile, per-OS features flatten here so the Compare view can key each
|
|
142
|
+
// scenario by (tcId, platform) via its OS-precise testEnvironment.
|
|
124
143
|
const scenarios = features.flatMap((f) => f.scenarios);
|
|
125
|
-
const label = target.
|
|
126
|
-
|
|
144
|
+
const label = target.kind === 'flow' ? `flow/${target.name}`
|
|
145
|
+
: target.kind === 'api' ? `api/${target.name}`
|
|
146
|
+
: target.name;
|
|
147
|
+
// Screen-level platform axis: 'mobile' when any feature is OS-tagged; the
|
|
148
|
+
// union of OSes that actually produced a result file this run.
|
|
149
|
+
const execSet = new Set();
|
|
150
|
+
let anyPlatform = false;
|
|
151
|
+
for (const f of features) {
|
|
152
|
+
if (f.platform)
|
|
153
|
+
anyPlatform = true;
|
|
154
|
+
for (const p of f.executedPlatforms ?? [])
|
|
155
|
+
execSet.add(p);
|
|
156
|
+
}
|
|
157
|
+
const executedPlatforms = execSet.size > 0
|
|
158
|
+
? ['android', 'ios'].filter((p) => execSet.has(p))
|
|
159
|
+
: undefined;
|
|
160
|
+
// Representative feature for the back-compat top-level fields: prefer Android
|
|
161
|
+
// (deterministic, matches delivery's summary-row preference).
|
|
162
|
+
const primary = features.find((f) => f.platform === 'android') ?? features[0];
|
|
127
163
|
return {
|
|
128
164
|
name: label,
|
|
129
165
|
isFlow: target.isFlow,
|
|
166
|
+
kind: target.kind,
|
|
167
|
+
platform: anyPlatform ? 'mobile' : undefined,
|
|
130
168
|
featureName: primary.featureName,
|
|
131
169
|
featurePath: primary.featurePath,
|
|
132
170
|
specLink,
|
|
133
171
|
summary: rollupScreenSummary(features),
|
|
134
172
|
scenarios,
|
|
135
173
|
features,
|
|
174
|
+
executedPlatforms,
|
|
136
175
|
};
|
|
137
176
|
}
|
|
138
177
|
/**
|
|
@@ -140,14 +179,14 @@ function buildOneScreen(cwd, target, env) {
|
|
|
140
179
|
* TC IDs are generated using `featureBaseName` as the prefix so they match
|
|
141
180
|
* the delivery CSV (e.g. `HOME-MODAL-UI-001`, not `HOME-UI-001`).
|
|
142
181
|
*/
|
|
143
|
-
function buildOneFeature(cwd, target, featureBaseName, env, specLink) {
|
|
182
|
+
function buildOneFeature(cwd, target, featureBaseName, env, specLink, mobile) {
|
|
144
183
|
const base = qaDir(cwd, target);
|
|
145
184
|
const genBase = generatedDir(cwd, target);
|
|
146
185
|
const featureFile = path.join(base, 'features', `${featureBaseName}.feature`);
|
|
147
186
|
const testDataFile = path.join(base, 'test-data', `${featureBaseName}.yaml`);
|
|
148
187
|
const specFile = path.join(genBase, `${featureBaseName}.spec.ts`);
|
|
149
188
|
if (!fs.existsSync(featureFile))
|
|
150
|
-
return
|
|
189
|
+
return [];
|
|
151
190
|
const feature = (0, feature_parser_1.parseFeatureMetadata)(featureFile);
|
|
152
191
|
const spec = fs.existsSync(specFile)
|
|
153
192
|
? (0, spec_parser_1.parseSpecFile)(specFile)
|
|
@@ -164,24 +203,15 @@ function buildOneFeature(cwd, target, featureBaseName, env, specLink) {
|
|
|
164
203
|
// Discover locale variants by scanning <feature>-test-result*.json files.
|
|
165
204
|
// Always includes base ('') as the first entry, even if its results file
|
|
166
205
|
// is missing — UI still needs to render the feature.
|
|
167
|
-
// NOTE (mobile): for a `platform: mobile` project the "locale" variants are
|
|
168
|
-
// actually the per-OS runs (android / ios) — the dashboard reuses the locale
|
|
169
|
-
// switcher to surface them as tabs. The env column per tab is OS-precise (see
|
|
170
|
-
// resolvePlatformAppId below), but the top-level featurePath (base variant)
|
|
171
|
-
// defaults to the Android id. Delivery has a dedicated per-OS path; aligning the
|
|
172
|
-
// dashboard onto a distinct "platform" axis is a possible follow-up.
|
|
173
206
|
const variants = discoverLocaleVariants(genBase, featureBaseName);
|
|
174
|
-
// Build per-
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
const locales = [];
|
|
178
|
-
let baseBuilt = null;
|
|
179
|
-
for (const variant of variants) {
|
|
207
|
+
// Build one per-variant ScreenSnapshot (test-data overlay + results + OS-precise
|
|
208
|
+
// env column). Reused by both the web/i18n (locale-axis) and mobile (platform-axis) paths.
|
|
209
|
+
const buildVariant = (variant) => {
|
|
180
210
|
const testData = fs.existsSync(testDataFile)
|
|
181
211
|
? (0, test_data_resolver_1.loadTestData)(testDataFile, variant.locale || null)
|
|
182
212
|
: {};
|
|
183
213
|
const results = variant.resultsPath ? (0, playwright_report_parser_1.loadPlaywrightReport)(variant.resultsPath) : null;
|
|
184
|
-
|
|
214
|
+
return (0, json_exporter_1.buildScreenSnapshot)({
|
|
185
215
|
screen: featureBaseName,
|
|
186
216
|
isFlow: target.isFlow,
|
|
187
217
|
featureName: feature.featureName,
|
|
@@ -196,15 +226,63 @@ function buildOneFeature(cwd, target, featureBaseName, env, specLink) {
|
|
|
196
226
|
featureBaseName,
|
|
197
227
|
selectorKeyMap,
|
|
198
228
|
});
|
|
229
|
+
};
|
|
230
|
+
// -- Mobile (platform axis): the per-OS runs (android / ios) are their own
|
|
231
|
+
// FeatureSnapshot entries — one per OS, `platform` tagged — NOT locale tabs.
|
|
232
|
+
// Locale overlays for a given OS (e.g. `android.ja`) nest inside that OS's
|
|
233
|
+
// entry as `locales[]`. This mirrors the delivery per-OS deliverables. --
|
|
234
|
+
if (mobile) {
|
|
235
|
+
const osOf = (loc) => /^android(\.|$)/.test(loc) ? 'android' : /^ios(\.|$)/.test(loc) ? 'ios' : null;
|
|
236
|
+
const osVariants = variants.filter((v) => osOf(v.locale));
|
|
237
|
+
if (osVariants.length > 0) {
|
|
238
|
+
const executedPlatforms = ['android', 'ios'].filter((os) => osVariants.some((v) => osOf(v.locale) === os && v.resultsPath));
|
|
239
|
+
const feats = [];
|
|
240
|
+
for (const os of ['android', 'ios']) {
|
|
241
|
+
const group = osVariants.filter((v) => osOf(v.locale) === os);
|
|
242
|
+
if (group.length === 0)
|
|
243
|
+
continue;
|
|
244
|
+
const localeSnaps = [];
|
|
245
|
+
let primaryBuilt = null;
|
|
246
|
+
for (const v of group) {
|
|
247
|
+
const built = buildVariant(v);
|
|
248
|
+
localeSnaps.push({ locale: v.locale, displayCode: v.displayCode, summary: built.summary, scenarios: built.scenarios });
|
|
249
|
+
// Representative = the bare OS run (locale === os); else the first overlay.
|
|
250
|
+
if (v.locale === os || !primaryBuilt)
|
|
251
|
+
primaryBuilt = built;
|
|
252
|
+
}
|
|
253
|
+
if (!primaryBuilt)
|
|
254
|
+
continue;
|
|
255
|
+
feats.push({
|
|
256
|
+
featureBaseName,
|
|
257
|
+
featureName: primaryBuilt.featureName,
|
|
258
|
+
featurePath: primaryBuilt.featurePath,
|
|
259
|
+
specLink: primaryBuilt.specLink,
|
|
260
|
+
summary: primaryBuilt.summary,
|
|
261
|
+
scenarios: primaryBuilt.scenarios,
|
|
262
|
+
platform: os,
|
|
263
|
+
executedPlatforms,
|
|
264
|
+
locales: localeSnaps.length >= 2 ? localeSnaps : undefined,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
if (feats.length > 0)
|
|
268
|
+
return feats;
|
|
269
|
+
}
|
|
270
|
+
// Mobile project but no per-OS result files yet → fall through to the base
|
|
271
|
+
// (locale-axis) path so the test CASES still render.
|
|
272
|
+
}
|
|
273
|
+
// -- Web / i18n (locale axis): one FeatureSnapshot; variants become locales[]. --
|
|
274
|
+
const locales = [];
|
|
275
|
+
let baseBuilt = null;
|
|
276
|
+
for (const variant of variants) {
|
|
277
|
+
const built = buildVariant(variant);
|
|
199
278
|
locales.push({
|
|
200
279
|
locale: variant.locale,
|
|
201
280
|
displayCode: variant.displayCode,
|
|
202
281
|
summary: built.summary,
|
|
203
282
|
scenarios: built.scenarios,
|
|
204
283
|
});
|
|
205
|
-
if (variant.locale === '')
|
|
284
|
+
if (variant.locale === '')
|
|
206
285
|
baseBuilt = built;
|
|
207
|
-
}
|
|
208
286
|
}
|
|
209
287
|
// Guarantee a base entry — every feature in the dashboard must have
|
|
210
288
|
// something to fall back to even if base results file is missing.
|
|
@@ -218,18 +296,18 @@ function buildOneFeature(cwd, target, featureBaseName, env, specLink) {
|
|
|
218
296
|
};
|
|
219
297
|
}
|
|
220
298
|
if (!baseBuilt)
|
|
221
|
-
return
|
|
222
|
-
return {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
299
|
+
return [];
|
|
300
|
+
return [{
|
|
301
|
+
featureBaseName,
|
|
302
|
+
featureName: baseBuilt.featureName,
|
|
303
|
+
featurePath: baseBuilt.featurePath,
|
|
304
|
+
specLink: baseBuilt.specLink,
|
|
305
|
+
summary: baseBuilt.summary,
|
|
306
|
+
scenarios: baseBuilt.scenarios,
|
|
307
|
+
// Only expose `locales` array when 2+ variants exist — single-locale
|
|
308
|
+
// features stay slimmer and UI can skip the locale tab UI entirely.
|
|
309
|
+
locales: locales.length >= 2 ? locales : undefined,
|
|
310
|
+
}];
|
|
233
311
|
}
|
|
234
312
|
/**
|
|
235
313
|
* Scan the generated directory for `<basename>-test-result*.json` files
|
|
@@ -327,40 +405,65 @@ function bumpKey(record, key) {
|
|
|
327
405
|
// ----------------------------------------------------------------------------
|
|
328
406
|
// Path helpers (mirror delivery.ts to stay consistent)
|
|
329
407
|
// ----------------------------------------------------------------------------
|
|
408
|
+
/** qa/ subfolder for a unit kind (mirrors delivery.ts's qaParent). */
|
|
409
|
+
function qaParent(kind) {
|
|
410
|
+
return kind === 'flow' ? 'flows' : kind === 'api' ? 'api' : 'screens';
|
|
411
|
+
}
|
|
330
412
|
function qaDir(cwd, target) {
|
|
331
|
-
return path.join(cwd, 'qa', target.
|
|
413
|
+
return path.join(cwd, 'qa', qaParent(target.kind), target.name);
|
|
332
414
|
}
|
|
333
415
|
function generatedDir(cwd, target) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
416
|
+
// Mirrors delivery.ts: screen → specs/generated/<name>, flow → …/flows/<name>,
|
|
417
|
+
// api → …/api/<name> (api flows carry name 'flows/<flow>', composing correctly).
|
|
418
|
+
const sub = target.kind === 'flow'
|
|
419
|
+
? path.join('flows', target.name)
|
|
420
|
+
: target.kind === 'api'
|
|
421
|
+
? path.join('api', target.name)
|
|
422
|
+
: target.name;
|
|
423
|
+
return path.join(cwd, 'specs', 'generated', sub);
|
|
337
424
|
}
|
|
338
425
|
// ----------------------------------------------------------------------------
|
|
339
426
|
// Discovery (also used by CLI)
|
|
340
427
|
// ----------------------------------------------------------------------------
|
|
341
428
|
function listDashboardTargets(cwd) {
|
|
342
429
|
const targets = [];
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
430
|
+
const scan = (kind, skip = () => false) => {
|
|
431
|
+
const dir = path.join(cwd, 'qa', qaParent(kind));
|
|
432
|
+
if (!fs.existsSync(dir))
|
|
433
|
+
return;
|
|
434
|
+
for (const d of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
435
|
+
if (d.isDirectory() && !skip(d.name))
|
|
436
|
+
targets.push({ name: d.name, kind, isFlow: kind === 'flow' });
|
|
348
437
|
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
438
|
+
};
|
|
439
|
+
scan('screen');
|
|
440
|
+
scan('flow');
|
|
441
|
+
scan('api', (n) => n === 'flows'); // api areas: qa/api/<area> (the flows/ container is a nested tier)
|
|
442
|
+
// api flows: qa/api/flows/<flow> → name 'flows/<flow>', kind 'api'
|
|
443
|
+
// (paths compose to qa/api/flows/… + specs/generated/api/flows/…), mirroring delivery.ts.
|
|
444
|
+
const apiFlowsRoot = path.join(cwd, 'qa', 'api', 'flows');
|
|
445
|
+
if (fs.existsSync(apiFlowsRoot)) {
|
|
446
|
+
for (const d of fs.readdirSync(apiFlowsRoot, { withFileTypes: true })) {
|
|
353
447
|
if (d.isDirectory())
|
|
354
|
-
targets.push({ name: d.name, isFlow:
|
|
448
|
+
targets.push({ name: path.posix.join('flows', d.name), kind: 'api', isFlow: false });
|
|
355
449
|
}
|
|
356
450
|
}
|
|
357
451
|
return targets.sort((a, b) => a.name.localeCompare(b.name));
|
|
358
452
|
}
|
|
359
453
|
function resolveTargetType(cwd, name) {
|
|
360
|
-
if (fs.existsSync(path.join(cwd, 'qa', 'flows', name)))
|
|
361
|
-
return { name, isFlow: true };
|
|
362
|
-
|
|
363
|
-
|
|
454
|
+
if (fs.existsSync(path.join(cwd, 'qa', 'flows', name)))
|
|
455
|
+
return { name, kind: 'flow', isFlow: true };
|
|
456
|
+
// Screen before api so `dashboard <name>` resolves the same unit as `delivery <name>`
|
|
457
|
+
// when a repo has both qa/screens/<name> and qa/api/<name> (delivery.ts precedence).
|
|
458
|
+
if (fs.existsSync(path.join(cwd, 'qa', 'screens', name)))
|
|
459
|
+
return { name, kind: 'screen', isFlow: false };
|
|
460
|
+
if (fs.existsSync(path.join(cwd, 'qa', 'api', name)))
|
|
461
|
+
return { name, kind: 'api', isFlow: false };
|
|
462
|
+
// `flows/<flow>` or a bare api-flow name → qa/api/flows/<flow>
|
|
463
|
+
const apiFlow = name.startsWith('flows/') ? name : path.posix.join('flows', name);
|
|
464
|
+
if (fs.existsSync(path.join(cwd, 'qa', 'api', apiFlow)))
|
|
465
|
+
return { name: apiFlow, kind: 'api', isFlow: false };
|
|
466
|
+
return { name, kind: 'screen', isFlow: false };
|
|
364
467
|
}
|
|
365
468
|
// ----------------------------------------------------------------------------
|
|
366
469
|
// Run ID + environment metadata (Vietnam Indochina Time, UTC+7, no DST)
|