@xera-ai/core 0.1.5 → 0.1.7
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/bin/internal.js +36 -31
- package/dist/bin-internal/exec.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/bin-internal/exec.ts +31 -26
- package/src/bin-internal/fetch.ts +16 -3
package/dist/bin/internal.js
CHANGED
|
@@ -335,12 +335,22 @@ async function fetchCmd(argv, opts = {}) {
|
|
|
335
335
|
function renderStory(t) {
|
|
336
336
|
const lines = [];
|
|
337
337
|
lines.push(`# ${t.key}: ${t.summary}`, "");
|
|
338
|
-
|
|
338
|
+
const story = t.story.trim();
|
|
339
|
+
if (/^##\s+story\b/i.test(story)) {
|
|
340
|
+
lines.push(story, "");
|
|
341
|
+
} else {
|
|
342
|
+
lines.push("## Story", "", story, "");
|
|
343
|
+
}
|
|
339
344
|
if (t.acceptanceCriteria && t.acceptanceCriteria.trim()) {
|
|
340
|
-
|
|
345
|
+
const ac = t.acceptanceCriteria.trim();
|
|
346
|
+
if (/^##\s+acceptance\s+criteria\b/i.test(ac)) {
|
|
347
|
+
lines.push(ac, "");
|
|
348
|
+
} else {
|
|
349
|
+
lines.push("## Acceptance Criteria", "", ac, "");
|
|
350
|
+
}
|
|
341
351
|
}
|
|
342
352
|
if (t.attachments.length > 0) {
|
|
343
|
-
lines.push(
|
|
353
|
+
lines.push("## Attachments", "", ...t.attachments.map((a) => `- [${a.filename}](${a.url})`), "");
|
|
344
354
|
}
|
|
345
355
|
return lines.join(`
|
|
346
356
|
`);
|
|
@@ -597,7 +607,7 @@ function needsRefresh(entry, policy, now = new Date) {
|
|
|
597
607
|
// src/bin-internal/exec.ts
|
|
598
608
|
import { stagePlaywrightState, runAuthSetup, runPlaywright } from "@xera-ai/web";
|
|
599
609
|
import { chromium } from "@playwright/test";
|
|
600
|
-
import {
|
|
610
|
+
import { mkdirSync as mkdirSync7, existsSync as existsSync9 } from "fs";
|
|
601
611
|
import { join as join5 } from "path";
|
|
602
612
|
async function execCmd(argv) {
|
|
603
613
|
const ticket = argv[0];
|
|
@@ -649,25 +659,34 @@ async function execCmd(argv) {
|
|
|
649
659
|
await browser.close();
|
|
650
660
|
}
|
|
651
661
|
}
|
|
652
|
-
const stagedRoles = {};
|
|
653
662
|
if (config.web.auth.strategy === "storageState") {
|
|
654
663
|
for (const roleName of Object.keys(config.web.auth.roles)) {
|
|
655
664
|
if (readAuthState(paths.authDir, roleName)) {
|
|
656
|
-
|
|
665
|
+
stagePlaywrightState(paths.authDir, roleName);
|
|
657
666
|
}
|
|
658
667
|
}
|
|
659
668
|
}
|
|
660
|
-
const cfgPath = join5(
|
|
669
|
+
const cfgPath = join5(cwd, "playwright.config.ts");
|
|
661
670
|
if (!existsSync9(cfgPath)) {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
storageStatePathPerRole: stagedRoles
|
|
665
|
-
}));
|
|
671
|
+
console.error(`[xera:exec] missing ${cfgPath}. Run \`xera init\` to scaffold it, then re-run.`);
|
|
672
|
+
return 1;
|
|
666
673
|
}
|
|
667
674
|
const runDir = paths.runPath(runId).runDir;
|
|
668
675
|
mkdirSync7(runDir, { recursive: true });
|
|
669
|
-
|
|
670
|
-
const
|
|
676
|
+
const envName = process.env.XERA_ENV ?? config.web.defaultEnv;
|
|
677
|
+
const baseURL = config.web.baseUrl[envName] ?? config.web.baseUrl[config.web.defaultEnv];
|
|
678
|
+
const reportJsonPath = join5(runDir, "report.json");
|
|
679
|
+
log.log({ step: "exec.start", runId, env: envName, baseURL });
|
|
680
|
+
const r = await runPlaywright({
|
|
681
|
+
specPath: paths.specPath,
|
|
682
|
+
configPath: cfgPath,
|
|
683
|
+
outputDir: runDir,
|
|
684
|
+
env: {
|
|
685
|
+
XERA_BASE_URL: baseURL,
|
|
686
|
+
XERA_ENV: envName,
|
|
687
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME: reportJsonPath
|
|
688
|
+
}
|
|
689
|
+
});
|
|
671
690
|
log.log({ step: "exec.done", runId, exit: r.exitCode, ms: Date.now() - t0 });
|
|
672
691
|
console.log(`[xera:exec] runId=${runId} outcome=${r.outcome}`);
|
|
673
692
|
return r.outcome === "PASS" ? 0 : 3;
|
|
@@ -675,20 +694,6 @@ async function execCmd(argv) {
|
|
|
675
694
|
releaseLock(paths.lockPath);
|
|
676
695
|
}
|
|
677
696
|
}
|
|
678
|
-
function renderPlaywrightConfig(opts) {
|
|
679
|
-
const projects = Object.entries(opts.storageStatePathPerRole).map(([role, path]) => ` { name: '${role}', use: { ...devices['Desktop Chromium'], storageState: '${path}' } }`);
|
|
680
|
-
if (projects.length === 0)
|
|
681
|
-
projects.push(` { name: 'default', use: { ...devices['Desktop Chromium'] } }`);
|
|
682
|
-
return `import { defineConfig, devices } from '@playwright/test';
|
|
683
|
-
export default defineConfig({
|
|
684
|
-
use: { baseURL: '${opts.baseUrl}', trace: 'on' },
|
|
685
|
-
projects: [
|
|
686
|
-
${projects.join(`,
|
|
687
|
-
`)}
|
|
688
|
-
],
|
|
689
|
-
});
|
|
690
|
-
`;
|
|
691
|
-
}
|
|
692
697
|
|
|
693
698
|
// src/bin-internal/normalize.ts
|
|
694
699
|
import { normalizeRun } from "@xera-ai/web";
|
|
@@ -718,7 +723,7 @@ async function normalizeCmd(argv) {
|
|
|
718
723
|
}
|
|
719
724
|
|
|
720
725
|
// src/bin-internal/report.ts
|
|
721
|
-
import { readFileSync as readFileSync9, writeFileSync as
|
|
726
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
722
727
|
import { join as join7 } from "path";
|
|
723
728
|
|
|
724
729
|
// src/classifier/aggregate.ts
|
|
@@ -753,7 +758,7 @@ function aggregateScenarios(scenarios) {
|
|
|
753
758
|
import { existsSync as existsSync12 } from "fs";
|
|
754
759
|
|
|
755
760
|
// src/artifact/status.ts
|
|
756
|
-
import { existsSync as existsSync11, readFileSync as readFileSync8, writeFileSync as
|
|
761
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync8 } from "fs";
|
|
757
762
|
import { dirname as dirname5 } from "path";
|
|
758
763
|
import { z as z4 } from "zod";
|
|
759
764
|
var ClassificationEnum = z4.enum(["PASS", "REAL_BUG", "SELECTOR_DRIFT", "FLAKY", "TEST_BUG"]);
|
|
@@ -787,7 +792,7 @@ function readStatus(path) {
|
|
|
787
792
|
}
|
|
788
793
|
function writeStatus(path, status) {
|
|
789
794
|
mkdirSync8(dirname5(path), { recursive: true });
|
|
790
|
-
|
|
795
|
+
writeFileSync6(path, JSON.stringify(status, null, 2));
|
|
791
796
|
}
|
|
792
797
|
function appendHistory(path, entry) {
|
|
793
798
|
const s = readStatus(path);
|
|
@@ -884,7 +889,7 @@ async function reportCmd(argv) {
|
|
|
884
889
|
promptsVersion: "1.0.0"
|
|
885
890
|
});
|
|
886
891
|
const draftPath = join7(paths.ticketDir, "jira-comment.draft.md");
|
|
887
|
-
|
|
892
|
+
writeFileSync7(draftPath, md);
|
|
888
893
|
console.log(`[xera:report] wrote status.json and ${draftPath}`);
|
|
889
894
|
return 0;
|
|
890
895
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/bin-internal/exec.ts"],"names":[],"mappings":"AAWA,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/bin-internal/exec.ts"],"names":[],"mappings":"AAWA,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAuG7D"}
|
package/package.json
CHANGED
package/src/bin-internal/exec.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { readAuthState } from '../auth/state';
|
|
|
6
6
|
import { needsRefresh } from '../auth/refresh';
|
|
7
7
|
import { stagePlaywrightState, runAuthSetup, runPlaywright } from '@xera-ai/web';
|
|
8
8
|
import { chromium } from '@playwright/test';
|
|
9
|
-
import {
|
|
9
|
+
import { mkdirSync, existsSync } from 'node:fs';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
|
|
12
12
|
export async function execCmd(argv: string[]): Promise<number> {
|
|
@@ -61,30 +61,49 @@ export async function execCmd(argv: string[]): Promise<number> {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
// Stage Playwright storageState files
|
|
65
|
-
|
|
64
|
+
// Stage Playwright storageState files at predictable paths
|
|
65
|
+
// (.xera/.auth/.cache/<role>.json) — generated spec.ts references these
|
|
66
|
+
// via test.use({ storageState }) when an authenticated session is needed.
|
|
66
67
|
if (config.web.auth.strategy === 'storageState') {
|
|
67
68
|
for (const roleName of Object.keys(config.web.auth.roles)) {
|
|
68
69
|
if (readAuthState(paths.authDir, roleName)) {
|
|
69
|
-
|
|
70
|
+
stagePlaywrightState(paths.authDir, roleName);
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
//
|
|
75
|
-
|
|
75
|
+
// Use the root playwright.config.ts (generated by `xera init`). We never
|
|
76
|
+
// emit a per-ticket config — that path duplicates the root and bit-rots.
|
|
77
|
+
const cfgPath = join(cwd, 'playwright.config.ts');
|
|
76
78
|
if (!existsSync(cfgPath)) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
console.error(
|
|
80
|
+
`[xera:exec] missing ${cfgPath}. Run \`xera init\` to scaffold it, then re-run.`,
|
|
81
|
+
);
|
|
82
|
+
return 1;
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
const runDir = paths.runPath(runId).runDir;
|
|
84
86
|
mkdirSync(runDir, { recursive: true });
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
+
const envName = process.env.XERA_ENV ?? config.web.defaultEnv;
|
|
89
|
+
const baseURL = config.web.baseUrl[envName] ?? config.web.baseUrl[config.web.defaultEnv]!;
|
|
90
|
+
|
|
91
|
+
const reportJsonPath = join(runDir, 'report.json');
|
|
92
|
+
|
|
93
|
+
log.log({ step: 'exec.start', runId, env: envName, baseURL });
|
|
94
|
+
const r = await runPlaywright({
|
|
95
|
+
specPath: paths.specPath,
|
|
96
|
+
configPath: cfgPath,
|
|
97
|
+
outputDir: runDir,
|
|
98
|
+
env: {
|
|
99
|
+
XERA_BASE_URL: baseURL,
|
|
100
|
+
XERA_ENV: envName,
|
|
101
|
+
// Playwright's JSON reporter prints to stdout by default. Redirect it
|
|
102
|
+
// to a file inside the run dir so xera:normalize has a deterministic
|
|
103
|
+
// path to read.
|
|
104
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME: reportJsonPath,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
88
107
|
log.log({ step: 'exec.done', runId, exit: r.exitCode, ms: Date.now() - t0 });
|
|
89
108
|
|
|
90
109
|
console.log(`[xera:exec] runId=${runId} outcome=${r.outcome}`);
|
|
@@ -95,17 +114,3 @@ export async function execCmd(argv: string[]): Promise<number> {
|
|
|
95
114
|
}
|
|
96
115
|
}
|
|
97
116
|
|
|
98
|
-
function renderPlaywrightConfig(opts: { baseUrl: string; storageStatePathPerRole: Record<string, string> }): string {
|
|
99
|
-
const projects = Object.entries(opts.storageStatePathPerRole).map(
|
|
100
|
-
([role, path]) => ` { name: '${role}', use: { ...devices['Desktop Chromium'], storageState: '${path}' } }`,
|
|
101
|
-
);
|
|
102
|
-
if (projects.length === 0) projects.push(` { name: 'default', use: { ...devices['Desktop Chromium'] } }`);
|
|
103
|
-
return `import { defineConfig, devices } from '@playwright/test';
|
|
104
|
-
export default defineConfig({
|
|
105
|
-
use: { baseURL: '${opts.baseUrl}', trace: 'on' },
|
|
106
|
-
projects: [
|
|
107
|
-
${projects.join(',\n')}
|
|
108
|
-
],
|
|
109
|
-
});
|
|
110
|
-
`;
|
|
111
|
-
}
|
|
@@ -60,12 +60,25 @@ export async function fetchCmd(argv: string[], opts: FetchCmdOpts = {}): Promise
|
|
|
60
60
|
function renderStory(t: JiraTicket): string {
|
|
61
61
|
const lines: string[] = [];
|
|
62
62
|
lines.push(`# ${t.key}: ${t.summary}`, '');
|
|
63
|
-
|
|
63
|
+
|
|
64
|
+
const story = t.story.trim();
|
|
65
|
+
// Avoid double "## Story" heading when Jira description already starts with it.
|
|
66
|
+
if (/^##\s+story\b/i.test(story)) {
|
|
67
|
+
lines.push(story, '');
|
|
68
|
+
} else {
|
|
69
|
+
lines.push('## Story', '', story, '');
|
|
70
|
+
}
|
|
71
|
+
|
|
64
72
|
if (t.acceptanceCriteria && t.acceptanceCriteria.trim()) {
|
|
65
|
-
|
|
73
|
+
const ac = t.acceptanceCriteria.trim();
|
|
74
|
+
if (/^##\s+acceptance\s+criteria\b/i.test(ac)) {
|
|
75
|
+
lines.push(ac, '');
|
|
76
|
+
} else {
|
|
77
|
+
lines.push('## Acceptance Criteria', '', ac, '');
|
|
78
|
+
}
|
|
66
79
|
}
|
|
67
80
|
if (t.attachments.length > 0) {
|
|
68
|
-
lines.push(
|
|
81
|
+
lines.push('## Attachments', '', ...t.attachments.map((a) => `- [${a.filename}](${a.url})`), '');
|
|
69
82
|
}
|
|
70
83
|
return lines.join('\n');
|
|
71
84
|
}
|