playwright-checkpoint 0.1.0-beta.0 → 0.4.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 +57 -0
- package/dist/{chunk-KG37WSYS.js → chunk-M3BRR3LT.js} +9 -3
- package/dist/{chunk-KG37WSYS.js.map → chunk-M3BRR3LT.js.map} +1 -1
- package/dist/{chunk-X5IPL32H.js → chunk-WXZOP7XI.js} +153 -35
- package/dist/chunk-WXZOP7XI.js.map +1 -0
- package/dist/{chunk-K5DX32TO.js → chunk-YUFXGGZM.js} +2 -2
- package/dist/cli/bin.cjs +2502 -2387
- package/dist/cli/bin.cjs.map +1 -1
- package/dist/cli/bin.js +3 -2
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/index.cjs +1405 -68
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +2 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +3 -2
- package/dist/{core-CD4jHGgI.d.cts → core-6gyzs35M.d.ts} +2 -1
- package/dist/{core-CZvnc0rE.d.ts → core-Dd3WLuTs.d.cts} +2 -1
- package/dist/core.cjs +8 -2
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1 -1
- package/dist/{index-BjYQX_hK.d.ts → index-CvcgBzvl.d.ts} +1 -1
- package/dist/{index-Cabk31qi.d.cts → index-OQx9qcVO.d.cts} +1 -1
- package/dist/index.cjs +216 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +73 -19
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.cjs +149 -35
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +5 -5
- package/dist/mcp/index.js.map +1 -1
- package/dist/teardown.cjs +1409 -72
- package/dist/teardown.cjs.map +1 -1
- package/dist/teardown.js +3 -2
- package/dist/teardown.js.map +1 -1
- package/dist/{types-G7w4n8kR.d.cts → types-wX4eB9mb.d.cts} +16 -1
- package/dist/{types-G7w4n8kR.d.ts → types-wX4eB9mb.d.ts} +16 -1
- package/package.json +2 -1
- package/dist/chunk-X5IPL32H.js.map +0 -1
- /package/dist/{chunk-K5DX32TO.js.map → chunk-YUFXGGZM.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -1358,6 +1358,12 @@ function createManifest(sessionMetadata) {
|
|
|
1358
1358
|
checkpoints: []
|
|
1359
1359
|
};
|
|
1360
1360
|
}
|
|
1361
|
+
function resolveTestConfig(testConfig) {
|
|
1362
|
+
if (typeof testConfig === "function") {
|
|
1363
|
+
return testConfig();
|
|
1364
|
+
}
|
|
1365
|
+
return testConfig ?? null;
|
|
1366
|
+
}
|
|
1361
1367
|
async function writeManifestFile(manifestPath, manifest) {
|
|
1362
1368
|
await import_promises12.default.mkdir(import_node_path13.default.dirname(manifestPath), { recursive: true });
|
|
1363
1369
|
await import_promises12.default.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
@@ -1563,7 +1569,7 @@ async function createCheckpointSession(page, options) {
|
|
|
1563
1569
|
}
|
|
1564
1570
|
}
|
|
1565
1571
|
await import_promises12.default.mkdir(outputDir, { recursive: true });
|
|
1566
|
-
await ensureCollectorsSetup(resolveCollectors(sessionConfig));
|
|
1572
|
+
await ensureCollectorsSetup(resolveCollectors(sessionConfig, resolveTestConfig(options.testConfig)));
|
|
1567
1573
|
return {
|
|
1568
1574
|
outputDir,
|
|
1569
1575
|
manifest,
|
|
@@ -1571,7 +1577,7 @@ async function createCheckpointSession(page, options) {
|
|
|
1571
1577
|
if (finalizePromise) {
|
|
1572
1578
|
throw new Error("Checkpoint session has already been finalized.");
|
|
1573
1579
|
}
|
|
1574
|
-
const resolvedCollectors = resolveCollectors(sessionConfig,
|
|
1580
|
+
const resolvedCollectors = resolveCollectors(sessionConfig, resolveTestConfig(options.testConfig), checkpointOptions);
|
|
1575
1581
|
await ensureCollectorsSetup(resolvedCollectors);
|
|
1576
1582
|
return runCollectorPipeline({
|
|
1577
1583
|
page,
|
|
@@ -1648,11 +1654,54 @@ function mergeCollectorOverrides(current, updates) {
|
|
|
1648
1654
|
}
|
|
1649
1655
|
function mergeTestConfig(current, update) {
|
|
1650
1656
|
const collectors = mergeCollectorOverrides(current?.collectors, update.collectors);
|
|
1657
|
+
const article = mergeArticleMetadata(current?.article, update.article);
|
|
1658
|
+
const articles = update.articles ? cloneArticleDefinitions(update.articles) : current?.articles ? cloneArticleDefinitions(current.articles) : void 0;
|
|
1651
1659
|
return {
|
|
1652
1660
|
description: update.description ?? current?.description,
|
|
1661
|
+
...article ? { article } : {},
|
|
1662
|
+
...articles ? { articles } : {},
|
|
1653
1663
|
...collectors ? { collectors } : {}
|
|
1654
1664
|
};
|
|
1655
1665
|
}
|
|
1666
|
+
function mergeArticleMetadata(current, update) {
|
|
1667
|
+
if (!current && !update) {
|
|
1668
|
+
return void 0;
|
|
1669
|
+
}
|
|
1670
|
+
const merged = {
|
|
1671
|
+
...current ?? {},
|
|
1672
|
+
...update ?? {}
|
|
1673
|
+
};
|
|
1674
|
+
if (current?.frontmatter || update?.frontmatter) {
|
|
1675
|
+
merged.frontmatter = {
|
|
1676
|
+
...current?.frontmatter ?? {},
|
|
1677
|
+
...update?.frontmatter ?? {}
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
return merged;
|
|
1681
|
+
}
|
|
1682
|
+
function cloneArticleMetadata(article) {
|
|
1683
|
+
return {
|
|
1684
|
+
...article,
|
|
1685
|
+
...article.frontmatter ? { frontmatter: { ...article.frontmatter } } : {}
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
function cloneArticleDefinition(article) {
|
|
1689
|
+
return {
|
|
1690
|
+
...cloneArticleMetadata(article),
|
|
1691
|
+
steps: [...article.steps]
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
function cloneArticleDefinitions(articles) {
|
|
1695
|
+
return articles.map((article) => cloneArticleDefinition(article));
|
|
1696
|
+
}
|
|
1697
|
+
function syncManifestArticle(manifest, testConfig) {
|
|
1698
|
+
if (testConfig?.article) {
|
|
1699
|
+
manifest.article = cloneArticleMetadata(testConfig.article);
|
|
1700
|
+
}
|
|
1701
|
+
if (testConfig?.articles) {
|
|
1702
|
+
manifest.articles = cloneArticleDefinitions(testConfig.articles);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1656
1705
|
function manifestEnvironment() {
|
|
1657
1706
|
return process.env.PLAYWRIGHT_CHECKPOINT_ENV || process.env.NODE_ENV || "test";
|
|
1658
1707
|
}
|
|
@@ -1729,7 +1778,9 @@ function createCheckpoint(globalConfig = {}) {
|
|
|
1729
1778
|
const base = playwright.test;
|
|
1730
1779
|
const test2 = base.extend({
|
|
1731
1780
|
checkpointManifest: [
|
|
1732
|
-
|
|
1781
|
+
// Playwright fixture callbacks must use object destructuring for the first arg.
|
|
1782
|
+
// eslint-disable-next-line no-empty-pattern
|
|
1783
|
+
async ({}, use, testInfo) => {
|
|
1733
1784
|
const manifest = createCheckpointManifestRecord(testInfo);
|
|
1734
1785
|
try {
|
|
1735
1786
|
await use(manifest);
|
|
@@ -1743,7 +1794,9 @@ function createCheckpoint(globalConfig = {}) {
|
|
|
1743
1794
|
},
|
|
1744
1795
|
{ auto: true }
|
|
1745
1796
|
],
|
|
1746
|
-
|
|
1797
|
+
// Playwright fixture callbacks must use object destructuring for the first arg.
|
|
1798
|
+
// eslint-disable-next-line no-empty-pattern
|
|
1799
|
+
testCheckpointConfig: async ({}, use) => {
|
|
1747
1800
|
let current = null;
|
|
1748
1801
|
const controller = {
|
|
1749
1802
|
set(config) {
|
|
@@ -1758,7 +1811,9 @@ function createCheckpoint(globalConfig = {}) {
|
|
|
1758
1811
|
};
|
|
1759
1812
|
await use(controller);
|
|
1760
1813
|
},
|
|
1761
|
-
|
|
1814
|
+
// Playwright fixture callbacks must use object destructuring for the first arg.
|
|
1815
|
+
// eslint-disable-next-line no-empty-pattern
|
|
1816
|
+
deviceProfile: async ({}, use, testInfo) => {
|
|
1762
1817
|
await use(createDeviceProfile(testInfo));
|
|
1763
1818
|
},
|
|
1764
1819
|
checkpoint: async ({ page, checkpointManifest, testCheckpointConfig }, use, testInfo) => {
|
|
@@ -1766,15 +1821,20 @@ function createCheckpoint(globalConfig = {}) {
|
|
|
1766
1821
|
outputDir: testInfo.outputPath("checkpoints"),
|
|
1767
1822
|
manifestPath: testInfo.outputPath("checkpoint-manifest.json"),
|
|
1768
1823
|
manifest: checkpointManifest,
|
|
1769
|
-
collectors:
|
|
1824
|
+
collectors: globalConfig.collectors,
|
|
1825
|
+
testConfig: () => testCheckpointConfig.get(),
|
|
1770
1826
|
custom: globalConfig.custom,
|
|
1771
1827
|
redact: globalConfig.redact,
|
|
1772
1828
|
testInfo,
|
|
1773
1829
|
adjustTimeout: createAdjustTimeout(testInfo)
|
|
1774
1830
|
});
|
|
1775
1831
|
try {
|
|
1776
|
-
await use((name, options = {}) =>
|
|
1832
|
+
await use((name, options = {}) => {
|
|
1833
|
+
syncManifestArticle(checkpointManifest, testCheckpointConfig.get());
|
|
1834
|
+
return session.checkpoint(name, options);
|
|
1835
|
+
});
|
|
1777
1836
|
} finally {
|
|
1837
|
+
syncManifestArticle(checkpointManifest, testCheckpointConfig.get());
|
|
1778
1838
|
await session.finalize();
|
|
1779
1839
|
}
|
|
1780
1840
|
}
|
|
@@ -2425,6 +2485,28 @@ function stripTags(value) {
|
|
|
2425
2485
|
const stripped = value.replace(/\s+@[a-z0-9-]+/gi, " ").replace(/\s+/g, " ").trim();
|
|
2426
2486
|
return stripped || value.trim() || "Untitled story";
|
|
2427
2487
|
}
|
|
2488
|
+
function articleTitle(run) {
|
|
2489
|
+
const override = run.article?.title?.trim();
|
|
2490
|
+
return override || stripTags(run.title);
|
|
2491
|
+
}
|
|
2492
|
+
function articleDescription(run) {
|
|
2493
|
+
const description = run.article?.description?.trim();
|
|
2494
|
+
return description ? description : null;
|
|
2495
|
+
}
|
|
2496
|
+
function articleSlug(run) {
|
|
2497
|
+
const override = run.article?.slug?.trim();
|
|
2498
|
+
return slugify2(override || stripTags(run.title));
|
|
2499
|
+
}
|
|
2500
|
+
function uniqueArticleSlug(baseSlug, usedSlugs) {
|
|
2501
|
+
if (!usedSlugs.has(baseSlug)) {
|
|
2502
|
+
return baseSlug;
|
|
2503
|
+
}
|
|
2504
|
+
let index = 1;
|
|
2505
|
+
while (usedSlugs.has(`${baseSlug}-${index}`)) {
|
|
2506
|
+
index += 1;
|
|
2507
|
+
}
|
|
2508
|
+
return `${baseSlug}-${index}`;
|
|
2509
|
+
}
|
|
2428
2510
|
function normalizeConfig(config) {
|
|
2429
2511
|
return {
|
|
2430
2512
|
storiesDir: typeof config.storiesDir === "string" ? config.storiesDir : ".",
|
|
@@ -2435,7 +2517,8 @@ function normalizeConfig(config) {
|
|
|
2435
2517
|
footer: typeof config.footer === "string" ? config.footer : void 0,
|
|
2436
2518
|
frontmatter: config.frontmatter === true || config.frontmatter === false || config.frontmatter != null && typeof config.frontmatter === "object" && !Array.isArray(config.frontmatter) ? config.frontmatter : false,
|
|
2437
2519
|
imagePathPrefix: typeof config.imagePathPrefix === "string" ? config.imagePathPrefix : void 0,
|
|
2438
|
-
copyScreenshots: typeof config.copyScreenshots === "boolean" ? config.copyScreenshots : true
|
|
2520
|
+
copyScreenshots: typeof config.copyScreenshots === "boolean" ? config.copyScreenshots : true,
|
|
2521
|
+
requireExplicitStep: typeof config.requireExplicitStep === "boolean" ? config.requireExplicitStep : false
|
|
2439
2522
|
};
|
|
2440
2523
|
}
|
|
2441
2524
|
function normalizeTags(tags) {
|
|
@@ -2447,6 +2530,9 @@ function shouldIncludeRun(run, config) {
|
|
|
2447
2530
|
const runTags = new Set(normalizeTags(run.tags));
|
|
2448
2531
|
return includeTags.some((tag) => runTags.has(tag));
|
|
2449
2532
|
}
|
|
2533
|
+
if (run.articles) {
|
|
2534
|
+
return true;
|
|
2535
|
+
}
|
|
2450
2536
|
return run.checkpoints.some((checkpoint) => {
|
|
2451
2537
|
const hasDescription = typeof checkpoint.description === "string" && checkpoint.description.trim().length > 0;
|
|
2452
2538
|
return hasDescription || typeof checkpoint.step === "number";
|
|
@@ -2565,17 +2651,22 @@ async function materializeScreenshot(args) {
|
|
|
2565
2651
|
if (!sourcePath) {
|
|
2566
2652
|
return null;
|
|
2567
2653
|
}
|
|
2654
|
+
const cachedTargetPath = args.screenshotCopies?.get(sourcePath);
|
|
2655
|
+
if (cachedTargetPath) {
|
|
2656
|
+
return rewriteImagePath(args.markdownFile, cachedTargetPath, args.outputDir, args.config.imagePathPrefix);
|
|
2657
|
+
}
|
|
2568
2658
|
const extension = import_node_path16.default.extname(sourcePath) || ".png";
|
|
2569
2659
|
const targetPath = import_node_path16.default.join(
|
|
2570
2660
|
args.outputDir,
|
|
2571
2661
|
args.config.screenshotsDir ?? "screenshots",
|
|
2572
|
-
args.
|
|
2573
|
-
`${
|
|
2662
|
+
args.screenshotDirSlug,
|
|
2663
|
+
`${args.screenshotFileSlug}${extension}`
|
|
2574
2664
|
);
|
|
2575
2665
|
try {
|
|
2576
2666
|
if (args.config.copyScreenshots !== false) {
|
|
2577
2667
|
await import_promises15.default.mkdir(import_node_path16.default.dirname(targetPath), { recursive: true });
|
|
2578
2668
|
await import_promises15.default.copyFile(sourcePath, targetPath);
|
|
2669
|
+
args.screenshotCopies?.set(sourcePath, targetPath);
|
|
2579
2670
|
args.writtenFiles.add(targetPath);
|
|
2580
2671
|
return rewriteImagePath(args.markdownFile, targetPath, args.outputDir, args.config.imagePathPrefix);
|
|
2581
2672
|
}
|
|
@@ -2595,10 +2686,31 @@ function orderedCheckpoints(checkpoints) {
|
|
|
2595
2686
|
});
|
|
2596
2687
|
}
|
|
2597
2688
|
async function buildSteps(args) {
|
|
2598
|
-
|
|
2689
|
+
let checkpoints;
|
|
2690
|
+
if (args.stepNames && args.stepNames.length > 0) {
|
|
2691
|
+
const byName = /* @__PURE__ */ new Map();
|
|
2692
|
+
for (const checkpoint of args.run.checkpoints) {
|
|
2693
|
+
if (byName.has(checkpoint.name)) {
|
|
2694
|
+
warn(`Duplicate checkpoint name "${checkpoint.name}" in "${args.run.title}". Using the latest capture for article generation.`);
|
|
2695
|
+
}
|
|
2696
|
+
byName.set(checkpoint.name, checkpoint);
|
|
2697
|
+
}
|
|
2698
|
+
checkpoints = args.stepNames.map((stepName) => {
|
|
2699
|
+
const checkpoint = byName.get(stepName);
|
|
2700
|
+
if (!checkpoint) {
|
|
2701
|
+
warn(`Markdown article step "${stepName}" was not captured in "${args.run.title}". Skipping step.`);
|
|
2702
|
+
return null;
|
|
2703
|
+
}
|
|
2704
|
+
return checkpoint;
|
|
2705
|
+
}).filter((checkpoint) => checkpoint !== null);
|
|
2706
|
+
} else {
|
|
2707
|
+
checkpoints = orderedCheckpoints(args.run.checkpoints).filter(
|
|
2708
|
+
(checkpoint) => !args.config.requireExplicitStep || typeof checkpoint.step === "number"
|
|
2709
|
+
);
|
|
2710
|
+
}
|
|
2599
2711
|
const steps = [];
|
|
2600
2712
|
for (const [index, checkpoint] of checkpoints.entries()) {
|
|
2601
|
-
const order = typeof checkpoint.step === "number" ? checkpoint.step : index + 1;
|
|
2713
|
+
const order = args.stepNames ? index + 1 : typeof checkpoint.step === "number" ? checkpoint.step : index + 1;
|
|
2602
2714
|
steps.push({
|
|
2603
2715
|
checkpoint,
|
|
2604
2716
|
order,
|
|
@@ -2607,12 +2719,13 @@ async function buildSteps(args) {
|
|
|
2607
2719
|
imagePath: await materializeScreenshot({
|
|
2608
2720
|
run: args.run,
|
|
2609
2721
|
checkpoint,
|
|
2610
|
-
|
|
2611
|
-
|
|
2722
|
+
screenshotDirSlug: args.screenshotDirSlug,
|
|
2723
|
+
screenshotFileSlug: args.stepNames ? checkpoint.slug : `${String(order).padStart(2, "0")}-${slugify2(checkpoint.name)}`,
|
|
2612
2724
|
outputDir: args.outputDir,
|
|
2613
2725
|
markdownFile: args.markdownFile,
|
|
2614
2726
|
config: args.config,
|
|
2615
|
-
writtenFiles: args.writtenFiles
|
|
2727
|
+
writtenFiles: args.writtenFiles,
|
|
2728
|
+
screenshotCopies: args.screenshotCopies
|
|
2616
2729
|
}),
|
|
2617
2730
|
urlLabel: urlLabel(checkpoint.url),
|
|
2618
2731
|
breadcrumbLabel: breadcrumbLabel(checkpoint.url),
|
|
@@ -2623,13 +2736,14 @@ async function buildSteps(args) {
|
|
|
2623
2736
|
}
|
|
2624
2737
|
function renderMarkdown(args) {
|
|
2625
2738
|
const frontmatterFields = args.config.frontmatter === true || typeof args.config.frontmatter === "object" ? {
|
|
2626
|
-
title: args.title,
|
|
2627
2739
|
project: args.run.project,
|
|
2628
|
-
testId: args.run.testId,
|
|
2629
2740
|
tags: args.run.tags,
|
|
2741
|
+
...args.config.frontmatter && typeof args.config.frontmatter === "object" ? args.config.frontmatter : {},
|
|
2742
|
+
...args.article?.frontmatter ?? {},
|
|
2743
|
+
testId: args.run.testId,
|
|
2630
2744
|
startedAt: args.run.startedAt,
|
|
2631
2745
|
generatedAt: args.generatedAt,
|
|
2632
|
-
|
|
2746
|
+
title: args.title
|
|
2633
2747
|
} : null;
|
|
2634
2748
|
const sections = args.steps.map((step) => {
|
|
2635
2749
|
const lines = [`## Step ${step.order}: ${step.heading}`, ""];
|
|
@@ -2649,6 +2763,7 @@ function renderMarkdown(args) {
|
|
|
2649
2763
|
const parts = [
|
|
2650
2764
|
frontmatterFields ? serializeFrontmatter(frontmatterFields) : "",
|
|
2651
2765
|
`# ${args.title}`,
|
|
2766
|
+
args.description?.trim() ?? "",
|
|
2652
2767
|
args.config.header ? args.config.header.trim() : "",
|
|
2653
2768
|
sections,
|
|
2654
2769
|
args.config.footer ? args.config.footer.trim() : ""
|
|
@@ -2656,6 +2771,42 @@ function renderMarkdown(args) {
|
|
|
2656
2771
|
return `${parts.join("\n\n")}
|
|
2657
2772
|
`;
|
|
2658
2773
|
}
|
|
2774
|
+
function resolveArticles(run) {
|
|
2775
|
+
const multiArticles = (run.articles ?? []).filter((article) => Array.isArray(article.steps));
|
|
2776
|
+
if (run.articles && multiArticles.length === 0) {
|
|
2777
|
+
warn(`Markdown reporter received an empty articles array for "${run.title}". Falling back to the default single-article output.`);
|
|
2778
|
+
}
|
|
2779
|
+
if (multiArticles.length === 0) {
|
|
2780
|
+
return [
|
|
2781
|
+
{
|
|
2782
|
+
title: articleTitle(run),
|
|
2783
|
+
description: articleDescription(run),
|
|
2784
|
+
slug: articleSlug(run),
|
|
2785
|
+
metadata: run.article,
|
|
2786
|
+
screenshotDirSlug: articleSlug(run)
|
|
2787
|
+
}
|
|
2788
|
+
];
|
|
2789
|
+
}
|
|
2790
|
+
const usedSlugs = /* @__PURE__ */ new Set();
|
|
2791
|
+
const screenshotDirSlug = slugify2(stripTags(run.title));
|
|
2792
|
+
return multiArticles.map((article, index) => {
|
|
2793
|
+
const fallbackSlug = `${screenshotDirSlug}-${index + 1}`;
|
|
2794
|
+
const baseSlug = slugify2(article.slug?.trim() || fallbackSlug);
|
|
2795
|
+
const uniqueSlug = uniqueArticleSlug(baseSlug, usedSlugs);
|
|
2796
|
+
if (uniqueSlug !== baseSlug) {
|
|
2797
|
+
warn(`Markdown article slug collision for "${article.title ?? run.title}" resolved as "${uniqueSlug}".`);
|
|
2798
|
+
}
|
|
2799
|
+
usedSlugs.add(uniqueSlug);
|
|
2800
|
+
return {
|
|
2801
|
+
title: article.title?.trim() || stripTags(run.title),
|
|
2802
|
+
description: article.description?.trim() || null,
|
|
2803
|
+
slug: uniqueSlug,
|
|
2804
|
+
metadata: article,
|
|
2805
|
+
stepNames: [...article.steps],
|
|
2806
|
+
screenshotDirSlug
|
|
2807
|
+
};
|
|
2808
|
+
});
|
|
2809
|
+
}
|
|
2659
2810
|
var markdownReporter = {
|
|
2660
2811
|
name: "markdown",
|
|
2661
2812
|
description: "Generates one Markdown help article per captured story.",
|
|
@@ -2667,37 +2818,56 @@ var markdownReporter = {
|
|
|
2667
2818
|
const stories = groupByStory(context.runs);
|
|
2668
2819
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2669
2820
|
const writtenFiles = /* @__PURE__ */ new Set();
|
|
2821
|
+
const usedStorySlugs = /* @__PURE__ */ new Set();
|
|
2822
|
+
const screenshotCopies = /* @__PURE__ */ new Map();
|
|
2670
2823
|
let articleCount = 0;
|
|
2671
2824
|
for (const [storyTitle, runs] of stories) {
|
|
2672
2825
|
const primaryRun = choosePrimaryRun(runs, config.preferredProject);
|
|
2673
2826
|
if (!primaryRun || !shouldIncludeRun(primaryRun, config)) {
|
|
2674
2827
|
continue;
|
|
2675
2828
|
}
|
|
2676
|
-
const
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
markdownFile,
|
|
2690
|
-
renderMarkdown({
|
|
2691
|
-
title,
|
|
2692
|
-
steps,
|
|
2829
|
+
for (const article of resolveArticles(primaryRun)) {
|
|
2830
|
+
let storySlug = article.slug;
|
|
2831
|
+
if (usedStorySlugs.has(storySlug)) {
|
|
2832
|
+
let index = 2;
|
|
2833
|
+
while (usedStorySlugs.has(`${article.slug}-${index}`)) {
|
|
2834
|
+
index += 1;
|
|
2835
|
+
}
|
|
2836
|
+
storySlug = `${article.slug}-${index}`;
|
|
2837
|
+
warn(`Markdown article slug collision for "${article.title || storyTitle}" resolved as "${storySlug}".`);
|
|
2838
|
+
}
|
|
2839
|
+
usedStorySlugs.add(storySlug);
|
|
2840
|
+
const markdownFile = import_node_path16.default.join(context.outputDir, config.storiesDir ?? ".", `${storySlug}.md`);
|
|
2841
|
+
const steps = await buildSteps({
|
|
2693
2842
|
run: primaryRun,
|
|
2843
|
+
stepNames: article.stepNames,
|
|
2844
|
+
screenshotDirSlug: article.screenshotDirSlug,
|
|
2845
|
+
outputDir: context.outputDir,
|
|
2846
|
+
markdownFile,
|
|
2694
2847
|
config,
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2848
|
+
writtenFiles,
|
|
2849
|
+
screenshotCopies
|
|
2850
|
+
});
|
|
2851
|
+
if (steps.length === 0) {
|
|
2852
|
+
continue;
|
|
2853
|
+
}
|
|
2854
|
+
await import_promises15.default.mkdir(import_node_path16.default.dirname(markdownFile), { recursive: true });
|
|
2855
|
+
await import_promises15.default.writeFile(
|
|
2856
|
+
markdownFile,
|
|
2857
|
+
renderMarkdown({
|
|
2858
|
+
title: article.title,
|
|
2859
|
+
description: article.description,
|
|
2860
|
+
steps,
|
|
2861
|
+
run: primaryRun,
|
|
2862
|
+
article: article.metadata,
|
|
2863
|
+
config,
|
|
2864
|
+
generatedAt
|
|
2865
|
+
}),
|
|
2866
|
+
"utf8"
|
|
2867
|
+
);
|
|
2868
|
+
writtenFiles.add(markdownFile);
|
|
2869
|
+
articleCount += 1;
|
|
2870
|
+
}
|
|
2701
2871
|
}
|
|
2702
2872
|
return {
|
|
2703
2873
|
files: [...writtenFiles],
|
|
@@ -3163,6 +3333,8 @@ function toRunRecord(manifest, sourceManifestPath) {
|
|
|
3163
3333
|
project: manifest.project,
|
|
3164
3334
|
testId: manifest.testId,
|
|
3165
3335
|
title: manifest.title,
|
|
3336
|
+
...manifest.article ? { article: manifest.article } : {},
|
|
3337
|
+
...manifest.articles ? { articles: manifest.articles } : {},
|
|
3166
3338
|
tags: manifest.tags,
|
|
3167
3339
|
startedAt: manifest.startedAt,
|
|
3168
3340
|
checkpoints: manifest.checkpoints
|
|
@@ -3174,6 +3346,8 @@ function toManifest(run) {
|
|
|
3174
3346
|
project: run.project,
|
|
3175
3347
|
testId: run.testId,
|
|
3176
3348
|
title: run.title,
|
|
3349
|
+
...run.article ? { article: run.article } : {},
|
|
3350
|
+
...run.articles ? { articles: run.articles } : {},
|
|
3177
3351
|
tags: run.tags,
|
|
3178
3352
|
startedAt: run.startedAt,
|
|
3179
3353
|
checkpoints: run.checkpoints
|
|
@@ -3263,7 +3437,7 @@ registerBuiltinReporter(markdownReporter);
|
|
|
3263
3437
|
registerBuiltinReporter(mdxReporter);
|
|
3264
3438
|
|
|
3265
3439
|
// src/index.ts
|
|
3266
|
-
var VERSION = "0.1.0
|
|
3440
|
+
var VERSION = "0.1.0";
|
|
3267
3441
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3268
3442
|
0 && (module.exports = {
|
|
3269
3443
|
VERSION,
|