@xera-ai/core 0.1.5 → 0.1.6
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 +31 -31
- package/dist/bin-internal/exec.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/bin-internal/exec.ts +22 -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,29 @@ 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
|
+
log.log({ step: "exec.start", runId, env: envName, baseURL });
|
|
679
|
+
const r = await runPlaywright({
|
|
680
|
+
specPath: paths.specPath,
|
|
681
|
+
configPath: cfgPath,
|
|
682
|
+
outputDir: runDir,
|
|
683
|
+
env: { XERA_BASE_URL: baseURL, XERA_ENV: envName }
|
|
684
|
+
});
|
|
671
685
|
log.log({ step: "exec.done", runId, exit: r.exitCode, ms: Date.now() - t0 });
|
|
672
686
|
console.log(`[xera:exec] runId=${runId} outcome=${r.outcome}`);
|
|
673
687
|
return r.outcome === "PASS" ? 0 : 3;
|
|
@@ -675,20 +689,6 @@ async function execCmd(argv) {
|
|
|
675
689
|
releaseLock(paths.lockPath);
|
|
676
690
|
}
|
|
677
691
|
}
|
|
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
692
|
|
|
693
693
|
// src/bin-internal/normalize.ts
|
|
694
694
|
import { normalizeRun } from "@xera-ai/web";
|
|
@@ -718,7 +718,7 @@ async function normalizeCmd(argv) {
|
|
|
718
718
|
}
|
|
719
719
|
|
|
720
720
|
// src/bin-internal/report.ts
|
|
721
|
-
import { readFileSync as readFileSync9, writeFileSync as
|
|
721
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
722
722
|
import { join as join7 } from "path";
|
|
723
723
|
|
|
724
724
|
// src/classifier/aggregate.ts
|
|
@@ -753,7 +753,7 @@ function aggregateScenarios(scenarios) {
|
|
|
753
753
|
import { existsSync as existsSync12 } from "fs";
|
|
754
754
|
|
|
755
755
|
// src/artifact/status.ts
|
|
756
|
-
import { existsSync as existsSync11, readFileSync as readFileSync8, writeFileSync as
|
|
756
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync8 } from "fs";
|
|
757
757
|
import { dirname as dirname5 } from "path";
|
|
758
758
|
import { z as z4 } from "zod";
|
|
759
759
|
var ClassificationEnum = z4.enum(["PASS", "REAL_BUG", "SELECTOR_DRIFT", "FLAKY", "TEST_BUG"]);
|
|
@@ -787,7 +787,7 @@ function readStatus(path) {
|
|
|
787
787
|
}
|
|
788
788
|
function writeStatus(path, status) {
|
|
789
789
|
mkdirSync8(dirname5(path), { recursive: true });
|
|
790
|
-
|
|
790
|
+
writeFileSync6(path, JSON.stringify(status, null, 2));
|
|
791
791
|
}
|
|
792
792
|
function appendHistory(path, entry) {
|
|
793
793
|
const s = readStatus(path);
|
|
@@ -884,7 +884,7 @@ async function reportCmd(argv) {
|
|
|
884
884
|
promptsVersion: "1.0.0"
|
|
885
885
|
});
|
|
886
886
|
const draftPath = join7(paths.ticketDir, "jira-comment.draft.md");
|
|
887
|
-
|
|
887
|
+
writeFileSync7(draftPath, md);
|
|
888
888
|
console.log(`[xera:report] wrote status.json and ${draftPath}`);
|
|
889
889
|
return 0;
|
|
890
890
|
}
|
|
@@ -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,CA8F7D"}
|
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,40 @@ 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
|
+
log.log({ step: 'exec.start', runId, env: envName, baseURL });
|
|
92
|
+
const r = await runPlaywright({
|
|
93
|
+
specPath: paths.specPath,
|
|
94
|
+
configPath: cfgPath,
|
|
95
|
+
outputDir: runDir,
|
|
96
|
+
env: { XERA_BASE_URL: baseURL, XERA_ENV: envName },
|
|
97
|
+
});
|
|
88
98
|
log.log({ step: 'exec.done', runId, exit: r.exitCode, ms: Date.now() - t0 });
|
|
89
99
|
|
|
90
100
|
console.log(`[xera:exec] runId=${runId} outcome=${r.outcome}`);
|
|
@@ -95,17 +105,3 @@ export async function execCmd(argv: string[]): Promise<number> {
|
|
|
95
105
|
}
|
|
96
106
|
}
|
|
97
107
|
|
|
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
|
}
|