@brunosps00/dev-workflow 0.0.3 → 0.0.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/README.md +42 -42
- package/bin/dev-workflow.js +6 -4
- package/lib/constants.js +42 -40
- package/lib/init.js +66 -19
- package/package.json +1 -1
- package/scaffold/en/commands/{analyze-project.md → dw-analyze-project.md} +69 -40
- package/scaffold/en/commands/{brainstorm.md → dw-brainstorm.md} +31 -4
- package/scaffold/en/commands/{bugfix.md → dw-bugfix.md} +63 -19
- package/scaffold/en/commands/{code-review.md → dw-code-review.md} +38 -15
- package/scaffold/en/commands/{commit.md → dw-commit.md} +25 -0
- package/scaffold/en/commands/{create-prd.md → dw-create-prd.md} +24 -10
- package/scaffold/en/commands/{create-tasks.md → dw-create-tasks.md} +11 -4
- package/scaffold/en/commands/{create-techspec.md → dw-create-techspec.md} +38 -11
- package/scaffold/en/commands/{deep-research.md → dw-deep-research.md} +18 -17
- package/scaffold/en/commands/{fix-qa.md → dw-fix-qa.md} +20 -3
- package/scaffold/en/commands/dw-functional-doc.md +276 -0
- package/scaffold/en/commands/{generate-pr.md → dw-generate-pr.md} +20 -5
- package/scaffold/en/commands/dw-help.md +309 -0
- package/scaffold/en/commands/{refactoring-analysis.md → dw-refactoring-analysis.md} +50 -26
- package/scaffold/en/commands/{review-implementation.md → dw-review-implementation.md} +25 -6
- package/scaffold/en/commands/{run-plan.md → dw-run-plan.md} +21 -6
- package/scaffold/en/commands/{run-qa.md → dw-run-qa.md} +32 -13
- package/scaffold/en/commands/{run-task.md → dw-run-task.md} +17 -7
- package/scaffold/en/references/playwright-patterns.md +136 -0
- package/scaffold/en/references/refactoring-catalog.md +167 -0
- package/scaffold/en/templates/brainstorm-matrix.md +44 -0
- package/scaffold/en/templates/functional-doc/case-matrix.md +5 -0
- package/scaffold/en/templates/functional-doc/e2e-runbook.md +3 -0
- package/scaffold/en/templates/functional-doc/features.md +3 -0
- package/scaffold/en/templates/functional-doc/overview.md +21 -0
- package/scaffold/en/templates/functional-doc/playwright.spec.ts.tpl +19 -0
- package/scaffold/en/templates/pr-bugfix-template.md +28 -0
- package/scaffold/en/templates/qa-test-credentials.md +37 -0
- package/scaffold/en/templates/tasks-template.md +1 -1
- package/scaffold/en/templates/techspec-template.md +1 -1
- package/scaffold/pt-br/commands/{analyze-project.md → dw-analyze-project.md} +94 -44
- package/scaffold/pt-br/commands/{brainstorm.md → dw-brainstorm.md} +32 -5
- package/scaffold/pt-br/commands/{bugfix.md → dw-bugfix.md} +73 -16
- package/scaffold/pt-br/commands/{code-review.md → dw-code-review.md} +80 -17
- package/scaffold/pt-br/commands/{commit.md → dw-commit.md} +45 -1
- package/scaffold/pt-br/commands/{create-prd.md → dw-create-prd.md} +25 -10
- package/scaffold/pt-br/commands/{create-tasks.md → dw-create-tasks.md} +24 -17
- package/scaffold/pt-br/commands/{create-techspec.md → dw-create-techspec.md} +40 -13
- package/scaffold/pt-br/commands/{deep-research.md → dw-deep-research.md} +19 -11
- package/scaffold/pt-br/commands/{fix-qa.md → dw-fix-qa.md} +30 -1
- package/scaffold/pt-br/commands/dw-functional-doc.md +276 -0
- package/scaffold/pt-br/commands/{generate-pr.md → dw-generate-pr.md} +61 -6
- package/scaffold/pt-br/commands/dw-help.md +248 -0
- package/scaffold/pt-br/commands/{refactoring-analysis.md → dw-refactoring-analysis.md} +49 -25
- package/scaffold/pt-br/commands/{review-implementation.md → dw-review-implementation.md} +53 -5
- package/scaffold/pt-br/commands/{run-plan.md → dw-run-plan.md} +100 -12
- package/scaffold/pt-br/commands/{run-qa.md → dw-run-qa.md} +93 -18
- package/scaffold/pt-br/commands/{run-task.md → dw-run-task.md} +35 -10
- package/scaffold/pt-br/references/playwright-patterns.md +133 -0
- package/scaffold/pt-br/references/refactoring-catalog.md +166 -0
- package/scaffold/pt-br/templates/brainstorm-matrix.md +44 -0
- package/scaffold/pt-br/templates/functional-doc/case-matrix.md +5 -0
- package/scaffold/pt-br/templates/functional-doc/e2e-runbook.md +3 -0
- package/scaffold/pt-br/templates/functional-doc/features.md +3 -0
- package/scaffold/pt-br/templates/functional-doc/overview.md +21 -0
- package/scaffold/pt-br/templates/functional-doc/playwright.spec.ts.tpl +19 -0
- package/scaffold/pt-br/templates/pr-bugfix-template.md +28 -0
- package/scaffold/pt-br/templates/qa-test-credentials.md +37 -0
- package/scaffold/pt-br/templates/tasks-template.md +2 -2
- package/scaffold/pt-br/templates/techspec-template.md +1 -1
- package/scaffold/rules-readme.md +3 -3
- package/scaffold/scripts/functional-doc/generate-dossier.mjs +821 -0
- package/scaffold/scripts/functional-doc/run-playwright-flow.mjs +275 -0
- package/scaffold/skills/agent-browser/SKILL.md +750 -0
- package/scaffold/skills/agent-browser/references/authentication.md +303 -0
- package/scaffold/skills/agent-browser/references/commands.md +295 -0
- package/scaffold/skills/agent-browser/references/profiling.md +120 -0
- package/scaffold/skills/agent-browser/references/proxy-support.md +194 -0
- package/scaffold/skills/agent-browser/references/session-management.md +193 -0
- package/scaffold/skills/agent-browser/references/snapshot-refs.md +219 -0
- package/scaffold/skills/agent-browser/references/video-recording.md +173 -0
- package/scaffold/skills/agent-browser/templates/authenticated-session.sh +105 -0
- package/scaffold/skills/agent-browser/templates/capture-workflow.sh +69 -0
- package/scaffold/skills/agent-browser/templates/form-automation.sh +62 -0
- package/scaffold/skills/humanizer/README.md +143 -0
- package/scaffold/skills/humanizer/SKILL.md +488 -0
- package/scaffold/skills/humanizer/WARP.md +53 -0
- package/scaffold/skills/remotion-best-practices/SKILL.md +61 -0
- package/scaffold/skills/remotion-best-practices/rules/3d.md +86 -0
- package/scaffold/skills/remotion-best-practices/rules/animations.md +27 -0
- package/scaffold/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/scaffold/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/scaffold/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +103 -0
- package/scaffold/skills/remotion-best-practices/rules/assets.md +78 -0
- package/scaffold/skills/remotion-best-practices/rules/audio-visualization.md +198 -0
- package/scaffold/skills/remotion-best-practices/rules/audio.md +169 -0
- package/scaffold/skills/remotion-best-practices/rules/calculate-metadata.md +134 -0
- package/scaffold/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/scaffold/skills/remotion-best-practices/rules/charts.md +120 -0
- package/scaffold/skills/remotion-best-practices/rules/compositions.md +154 -0
- package/scaffold/skills/remotion-best-practices/rules/display-captions.md +184 -0
- package/scaffold/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/scaffold/skills/remotion-best-practices/rules/ffmpeg.md +38 -0
- package/scaffold/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/scaffold/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/scaffold/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/scaffold/skills/remotion-best-practices/rules/get-video-duration.md +60 -0
- package/scaffold/skills/remotion-best-practices/rules/gifs.md +141 -0
- package/scaffold/skills/remotion-best-practices/rules/images.md +134 -0
- package/scaffold/skills/remotion-best-practices/rules/import-srt-captions.md +69 -0
- package/scaffold/skills/remotion-best-practices/rules/light-leaks.md +73 -0
- package/scaffold/skills/remotion-best-practices/rules/lottie.md +70 -0
- package/scaffold/skills/remotion-best-practices/rules/maps.md +412 -0
- package/scaffold/skills/remotion-best-practices/rules/measuring-dom-nodes.md +34 -0
- package/scaffold/skills/remotion-best-practices/rules/measuring-text.md +140 -0
- package/scaffold/skills/remotion-best-practices/rules/parameters.md +109 -0
- package/scaffold/skills/remotion-best-practices/rules/sequencing.md +118 -0
- package/scaffold/skills/remotion-best-practices/rules/sfx.md +26 -0
- package/scaffold/skills/remotion-best-practices/rules/subtitles.md +36 -0
- package/scaffold/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/scaffold/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/scaffold/skills/remotion-best-practices/rules/timing.md +179 -0
- package/scaffold/skills/remotion-best-practices/rules/transcribe-captions.md +70 -0
- package/scaffold/skills/remotion-best-practices/rules/transitions.md +197 -0
- package/scaffold/skills/remotion-best-practices/rules/transparent-videos.md +106 -0
- package/scaffold/skills/remotion-best-practices/rules/trimming.md +51 -0
- package/scaffold/skills/remotion-best-practices/rules/videos.md +171 -0
- package/scaffold/skills/remotion-best-practices/rules/voiceover.md +99 -0
- package/scaffold/skills/security-review/LICENSE +22 -0
- package/scaffold/skills/security-review/SKILL.md +312 -0
- package/scaffold/skills/security-review/infrastructure/docker.md +432 -0
- package/scaffold/skills/security-review/languages/javascript.md +388 -0
- package/scaffold/skills/security-review/languages/python.md +363 -0
- package/scaffold/skills/security-review/references/api-security.md +519 -0
- package/scaffold/skills/security-review/references/authentication.md +353 -0
- package/scaffold/skills/security-review/references/authorization.md +372 -0
- package/scaffold/skills/security-review/references/business-logic.md +443 -0
- package/scaffold/skills/security-review/references/cryptography.md +329 -0
- package/scaffold/skills/security-review/references/csrf.md +398 -0
- package/scaffold/skills/security-review/references/data-protection.md +378 -0
- package/scaffold/skills/security-review/references/deserialization.md +410 -0
- package/scaffold/skills/security-review/references/error-handling.md +436 -0
- package/scaffold/skills/security-review/references/file-security.md +457 -0
- package/scaffold/skills/security-review/references/injection.md +259 -0
- package/scaffold/skills/security-review/references/logging.md +433 -0
- package/scaffold/skills/security-review/references/misconfiguration.md +435 -0
- package/scaffold/skills/security-review/references/modern-threats.md +475 -0
- package/scaffold/skills/security-review/references/ssrf.md +415 -0
- package/scaffold/skills/security-review/references/supply-chain.md +405 -0
- package/scaffold/skills/security-review/references/xss.md +336 -0
- package/scaffold/skills/vercel-react-best-practices/AGENTS.md +3648 -0
- package/scaffold/skills/vercel-react-best-practices/README.md +123 -0
- package/scaffold/skills/vercel-react-best-practices/SKILL.md +146 -0
- package/scaffold/skills/vercel-react-best-practices/rules/_sections.md +46 -0
- package/scaffold/skills/vercel-react-best-practices/rules/_template.md +28 -0
- package/scaffold/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/scaffold/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/scaffold/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/scaffold/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/scaffold/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/scaffold/skills/vercel-react-best-practices/rules/async-defer-await.md +82 -0
- package/scaffold/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/scaffold/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/scaffold/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/scaffold/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +60 -0
- package/scaffold/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/scaffold/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/scaffold/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/scaffold/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/scaffold/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/scaffold/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/scaffold/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/scaffold/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-request-idle-callback.md +105 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/scaffold/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/scaffold/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/scaffold/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/scaffold/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/scaffold/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/scaffold/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/scaffold/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/scaffold/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +149 -0
- package/scaffold/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/scaffold/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
- package/scaffold/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/scaffold/skills/webapp-testing/SKILL.md +133 -0
- package/scaffold/skills/webapp-testing/assets/test-helper.js +56 -0
- package/scaffold/en/commands/help.md +0 -289
- package/scaffold/pt-br/commands/help.md +0 -226
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
|
|
7
|
+
function parseArgs(argv) {
|
|
8
|
+
const result = {};
|
|
9
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
10
|
+
const token = argv[index];
|
|
11
|
+
if (!token.startsWith("--")) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const [rawKey, inlineValue] = token.slice(2).split("=", 2);
|
|
16
|
+
const key = rawKey.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
17
|
+
if (inlineValue !== undefined) {
|
|
18
|
+
result[key] = inlineValue;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const next = argv[index + 1];
|
|
23
|
+
if (!next || next.startsWith("--")) {
|
|
24
|
+
result[key] = true;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
result[key] = next;
|
|
29
|
+
index += 1;
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function detectPackageManager(projectRoot) {
|
|
35
|
+
if (fs.existsSync(path.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
|
|
36
|
+
if (fs.existsSync(path.join(projectRoot, "package-lock.json"))) return "npm";
|
|
37
|
+
if (fs.existsSync(path.join(projectRoot, "yarn.lock"))) return "yarn";
|
|
38
|
+
return "npm";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function updateManifest(manifestPath, patch) {
|
|
42
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
43
|
+
const next = {
|
|
44
|
+
...manifest,
|
|
45
|
+
execution: {
|
|
46
|
+
...(manifest.execution ?? {}),
|
|
47
|
+
...patch,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(next, null, 2)}\n`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function detectTestDir(playwrightConfig) {
|
|
54
|
+
const content = fs.readFileSync(playwrightConfig, "utf8");
|
|
55
|
+
const match = content.match(/testDir:\s*"([^"]+)"/);
|
|
56
|
+
return match?.[1] ?? "e2e";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function ensureDir(dirPath) {
|
|
60
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function escapeForTemplate(value) {
|
|
64
|
+
return JSON.stringify(value);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parseResolution(rawValue) {
|
|
68
|
+
const normalized = String(rawValue ?? "").trim().toLowerCase();
|
|
69
|
+
if (!normalized || normalized === "fullhd") {
|
|
70
|
+
return { width: 1920, height: 1080, label: "1920x1080" };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const match = normalized.match(/^(\d{3,5})x(\d{3,5})$/);
|
|
74
|
+
if (!match) {
|
|
75
|
+
return { width: 1920, height: 1080, label: "1920x1080" };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
width: Number(match[1]),
|
|
80
|
+
height: Number(match[2]),
|
|
81
|
+
label: `${Number(match[1])}x${Number(match[2])}`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function createTemporaryPlaywrightConfig({
|
|
86
|
+
generatedDir,
|
|
87
|
+
flowDir,
|
|
88
|
+
baseUrl,
|
|
89
|
+
browserName,
|
|
90
|
+
resolution,
|
|
91
|
+
}) {
|
|
92
|
+
const rawArtifactsDir = path.join(flowDir, "evidence", "playwright-artifacts");
|
|
93
|
+
ensureDir(rawArtifactsDir);
|
|
94
|
+
|
|
95
|
+
const configPath = path.join(generatedDir, "playwright.functional-doc.config.mjs");
|
|
96
|
+
const content = `import { defineConfig } from "@playwright/test";
|
|
97
|
+
|
|
98
|
+
const rawOutputDir = ${escapeForTemplate(rawArtifactsDir)};
|
|
99
|
+
const baseURL = process.env.BASE_URL ?? ${escapeForTemplate(baseUrl)};
|
|
100
|
+
const generatedDir = ${escapeForTemplate(generatedDir)};
|
|
101
|
+
|
|
102
|
+
export default defineConfig({
|
|
103
|
+
testDir: generatedDir,
|
|
104
|
+
outputDir: rawOutputDir,
|
|
105
|
+
fullyParallel: false,
|
|
106
|
+
retries: 0,
|
|
107
|
+
workers: 1,
|
|
108
|
+
reporter: "line",
|
|
109
|
+
use: {
|
|
110
|
+
baseURL,
|
|
111
|
+
video: {
|
|
112
|
+
mode: "on",
|
|
113
|
+
size: { width: ${resolution.width}, height: ${resolution.height} },
|
|
114
|
+
},
|
|
115
|
+
screenshot: "on",
|
|
116
|
+
trace: "retain-on-failure",
|
|
117
|
+
viewport: { width: ${resolution.width}, height: ${resolution.height} },
|
|
118
|
+
},
|
|
119
|
+
projects: [
|
|
120
|
+
{
|
|
121
|
+
name: ${escapeForTemplate(browserName)},
|
|
122
|
+
use: { browserName: ${escapeForTemplate(browserName)} },
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
`;
|
|
127
|
+
fs.writeFileSync(configPath, content);
|
|
128
|
+
return { configPath, rawArtifactsDir };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function collectFilesByExtension(rootDir, extension) {
|
|
132
|
+
if (!fs.existsSync(rootDir)) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const found = [];
|
|
137
|
+
const queue = [rootDir];
|
|
138
|
+
while (queue.length > 0) {
|
|
139
|
+
const current = queue.pop();
|
|
140
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
141
|
+
const absolutePath = path.join(current, entry.name);
|
|
142
|
+
if (entry.isDirectory()) {
|
|
143
|
+
queue.push(absolutePath);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (absolutePath.toLowerCase().endsWith(extension.toLowerCase())) {
|
|
147
|
+
found.push(absolutePath);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return found.sort();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function copyArtifacts(flowDir, slug, rawArtifactsDir) {
|
|
155
|
+
const videoDir = path.join(flowDir, "evidence", "videos");
|
|
156
|
+
const screenshotDir = path.join(flowDir, "evidence", "screenshots");
|
|
157
|
+
ensureDir(videoDir);
|
|
158
|
+
ensureDir(screenshotDir);
|
|
159
|
+
|
|
160
|
+
const videos = collectFilesByExtension(rawArtifactsDir, ".webm");
|
|
161
|
+
const screenshots = collectFilesByExtension(rawArtifactsDir, ".png");
|
|
162
|
+
|
|
163
|
+
const copiedVideos = videos.map((sourcePath, index) => {
|
|
164
|
+
const destinationPath = path.join(videoDir, `${slug}${videos.length > 1 ? `-${index + 1}` : ""}.webm`);
|
|
165
|
+
fs.copyFileSync(sourcePath, destinationPath);
|
|
166
|
+
return destinationPath;
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const copiedScreenshots = screenshots.map((sourcePath, index) => {
|
|
170
|
+
const destinationPath = path.join(screenshotDir, `${slug}${screenshots.length > 1 ? `-${index + 1}` : ""}.png`);
|
|
171
|
+
fs.copyFileSync(sourcePath, destinationPath);
|
|
172
|
+
return destinationPath;
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
videos: copiedVideos,
|
|
177
|
+
screenshots: copiedScreenshots,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function main() {
|
|
182
|
+
const args = parseArgs(process.argv.slice(2));
|
|
183
|
+
if (!args.flowDir) {
|
|
184
|
+
throw new Error("Missing required argument: --flow-dir");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const flowDir = path.resolve(args.flowDir);
|
|
188
|
+
const manifestPath = path.join(flowDir, "manifest.json");
|
|
189
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
190
|
+
const projectRoot = path.resolve(manifest.project);
|
|
191
|
+
const scriptPath = path.resolve(manifest.outputs.script);
|
|
192
|
+
const packageManager = detectPackageManager(projectRoot);
|
|
193
|
+
const browserName = args.browserName || "chromium";
|
|
194
|
+
const resolution = parseResolution(
|
|
195
|
+
args.videoResolution
|
|
196
|
+
?? manifest.artifacts?.human_final?.resolution
|
|
197
|
+
?? manifest.videoResolution
|
|
198
|
+
?? "fullhd"
|
|
199
|
+
);
|
|
200
|
+
const playwrightConfig = ["playwright.config.ts", "playwright.config.js"]
|
|
201
|
+
.map((file) => path.join(projectRoot, file))
|
|
202
|
+
.find((file) => fs.existsSync(file));
|
|
203
|
+
|
|
204
|
+
if (!playwrightConfig) {
|
|
205
|
+
updateManifest(manifestPath, {
|
|
206
|
+
status: "blocked",
|
|
207
|
+
reason: "Playwright configuration not found",
|
|
208
|
+
});
|
|
209
|
+
process.stdout.write("Playwright configuration not found.\n");
|
|
210
|
+
process.exit(0);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const testDir = detectTestDir(playwrightConfig);
|
|
214
|
+
const generatedDir = path.join(projectRoot, testDir, ".functional-doc-generated");
|
|
215
|
+
ensureDir(generatedDir);
|
|
216
|
+
const materializedScriptPath = path.join(generatedDir, path.basename(scriptPath));
|
|
217
|
+
fs.copyFileSync(scriptPath, materializedScriptPath);
|
|
218
|
+
const slug = path.basename(scriptPath, path.extname(scriptPath));
|
|
219
|
+
const { configPath: temporaryConfigPath, rawArtifactsDir } = createTemporaryPlaywrightConfig({
|
|
220
|
+
generatedDir,
|
|
221
|
+
flowDir,
|
|
222
|
+
baseUrl: manifest.baseUrl,
|
|
223
|
+
browserName,
|
|
224
|
+
resolution,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const commandArgs = ["exec", "playwright", "test", materializedScriptPath, "--config", temporaryConfigPath];
|
|
228
|
+
if (args.listOnly) {
|
|
229
|
+
commandArgs.push("--list");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const result = spawnSync(
|
|
233
|
+
packageManager,
|
|
234
|
+
commandArgs,
|
|
235
|
+
{
|
|
236
|
+
cwd: projectRoot,
|
|
237
|
+
env: {
|
|
238
|
+
...process.env,
|
|
239
|
+
BASE_URL: manifest.baseUrl,
|
|
240
|
+
},
|
|
241
|
+
encoding: "utf8",
|
|
242
|
+
},
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
const logDir = path.join(flowDir, "evidence", "logs");
|
|
246
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
247
|
+
fs.writeFileSync(path.join(logDir, "playwright.stdout.log"), result.stdout ?? "");
|
|
248
|
+
fs.writeFileSync(path.join(logDir, "playwright.stderr.log"), result.stderr ?? "");
|
|
249
|
+
|
|
250
|
+
const copiedArtifacts = args.listOnly
|
|
251
|
+
? { videos: [], screenshots: [] }
|
|
252
|
+
: copyArtifacts(flowDir, slug, rawArtifactsDir);
|
|
253
|
+
|
|
254
|
+
updateManifest(manifestPath, {
|
|
255
|
+
status: args.listOnly ? (result.status === 0 ? "listed" : "list_failed") : (result.status === 0 ? "passed" : "failed"),
|
|
256
|
+
command: `${packageManager} ${commandArgs.join(" ")}`,
|
|
257
|
+
exitCode: result.status ?? 1,
|
|
258
|
+
videoResolution: resolution.label,
|
|
259
|
+
materializedScript: materializedScriptPath,
|
|
260
|
+
temporaryConfig: temporaryConfigPath,
|
|
261
|
+
video: copiedArtifacts.videos[0] ?? null,
|
|
262
|
+
videos: copiedArtifacts.videos,
|
|
263
|
+
screenshots: copiedArtifacts.screenshots,
|
|
264
|
+
logs: [
|
|
265
|
+
path.join(logDir, "playwright.stdout.log"),
|
|
266
|
+
path.join(logDir, "playwright.stderr.log"),
|
|
267
|
+
],
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
process.stdout.write(result.stdout ?? "");
|
|
271
|
+
process.stderr.write(result.stderr ?? "");
|
|
272
|
+
process.exit(result.status ?? 1);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
main();
|