form-tester 0.5.0 → 0.6.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.
|
@@ -45,6 +45,7 @@ Notes:
|
|
|
45
45
|
- Use `--help` or `-h` to print the command list without starting the prompt.
|
|
46
46
|
- Playwright CLI commands are available from this skill when needed.
|
|
47
47
|
- IMPORTANT: All screenshots taken during a test run MUST use `--full-page` to capture the entire page, not just the viewport. This applies to every screenshot command: `playwright-cli screenshot --filename "..." --full-page`
|
|
48
|
+
- IMPORTANT: Take a full-page screenshot EVERY TIME the page changes. This includes: after clicking any action button (Neste, Forrige, Send inn, etc.), after a step/page transition, after form validation errors appear, after modals open, and after submission. Name screenshots descriptively (e.g., step1_filled.png, step2_before_submit.png, submit_result.png).
|
|
48
49
|
|
|
49
50
|
Test flow (when /test is triggered):
|
|
50
51
|
IMPORTANT: Each prompt below MUST be asked as a separate message to the user. Wait for the user's response before proceeding to the next step. Do NOT combine multiple prompts into one message.
|
|
@@ -63,7 +64,8 @@ Dokumenter verification (only when modal confirms storage):
|
|
|
63
64
|
1. Navigate to `/dokumenter?pnr={PNR}` and select the same person used during form fill.
|
|
64
65
|
2. The document list loads sorted newest first. The first entry should match the form title.
|
|
65
66
|
3. Click "Se detaljer" on the first document, then click "Åpne dokumentet".
|
|
66
|
-
4.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
4. IMPORTANT - document capture depends on format:
|
|
68
|
+
- PDF documents: DOWNLOAD the file. Use `playwright-cli pdf --filename "$OUTPUT_DIR/document.pdf"` or save via browser download. Do NOT screenshot PDFs.
|
|
69
|
+
- HTML documents: Take a FULL-PAGE screenshot of the ENTIRE document (`playwright-cli screenshot --filename "$OUTPUT_DIR/document_screenshot.png" --full-page`). HTML documents cannot be downloaded as files, so the full-page screenshot is the primary artifact. Also save the snapshot and raw HTML.
|
|
70
|
+
- XML/other formats: Note the document type in test_results.txt and skip capture.
|
|
71
|
+
5. Include the document verification results in test_results.txt (document title, whether it matched the form h1, document type: HTML/PDF/XML).
|
|
@@ -26,6 +26,14 @@ form-tester
|
|
|
26
26
|
|
|
27
27
|
Persona IDs: `ung-mann`, `gravid-kvinne`, `eldre-kvinne`, `kronisk-syk-mann`. Defaults to "noen" (neutral answers) if omitted.
|
|
28
28
|
|
|
29
|
+
## Recording & Replay
|
|
30
|
+
|
|
31
|
+
Every test run records all playwright-cli commands to `recording.json` in the output folder. Replay a previous run:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
form-tester replay output/form-id/timestamp/recording.json
|
|
35
|
+
```
|
|
36
|
+
|
|
29
37
|
## Commands
|
|
30
38
|
|
|
31
39
|
```bash
|
|
@@ -62,11 +62,17 @@ When `/test` is triggered:
|
|
|
62
62
|
|
|
63
63
|
- Provide a full /skjemautfyller URL. If `pnr` is missing, the CLI will prompt.
|
|
64
64
|
- All screenshots MUST use `--full-page` to capture the entire page.
|
|
65
|
+
- Take a full-page screenshot EVERY TIME the page changes: after clicking action buttons (Neste, Forrige, Send inn), after step/page transitions, after validation errors, after modals, and after submission.
|
|
65
66
|
- Use `/save {label}` to capture additional snapshots into the output folder.
|
|
66
67
|
- If an error modal appears on submit, open DevTools -> Network, retry once, and capture the Correlation ID header.
|
|
67
68
|
|
|
68
69
|
## Post-Submit Verification
|
|
69
70
|
|
|
70
71
|
After submission, read the modal text:
|
|
71
|
-
- If it mentions Dokumenter storage -> navigate to `/dokumenter?pnr={PNR}`, verify the document appears
|
|
72
|
+
- If it mentions Dokumenter storage -> navigate to `/dokumenter?pnr={PNR}`, verify the document appears.
|
|
72
73
|
- If it does NOT mention Dokumenter -> skip verification, note in test_results.txt.
|
|
74
|
+
|
|
75
|
+
Document capture depends on format:
|
|
76
|
+
- **PDF documents**: download the file (`playwright-cli pdf --filename "..." `). Do NOT screenshot PDFs.
|
|
77
|
+
- **HTML documents**: take a full-page screenshot of the ENTIRE document (`playwright-cli screenshot --filename "..." --full-page`). HTML documents cannot be downloaded, so the screenshot is the primary artifact.
|
|
78
|
+
- **XML/other**: note the type in test_results.txt and skip capture.
|
package/README.md
CHANGED
|
@@ -112,6 +112,13 @@ Test runs are saved to `output/{form-id}/{timestamp}/` with:
|
|
|
112
112
|
- Screenshots (PNG, full-page)
|
|
113
113
|
- `test_results.txt`
|
|
114
114
|
|
|
115
|
+
### Dokumenter verification
|
|
116
|
+
|
|
117
|
+
After form submission, some forms store a copy in Dokumenter. The document type determines how to capture it:
|
|
118
|
+
|
|
119
|
+
- **PDF documents** — download the file directly (`playwright-cli pdf` or via browser download)
|
|
120
|
+
- **HTML documents** — take a full-page screenshot of the entire document (`playwright-cli screenshot --filename "..." --full-page`). HTML documents cannot be downloaded as-is, so the screenshot is the primary artifact.
|
|
121
|
+
|
|
115
122
|
## Update
|
|
116
123
|
|
|
117
124
|
```bash
|
package/form-tester.js
CHANGED
|
@@ -6,9 +6,41 @@ const { spawn, execSync } = require("child_process");
|
|
|
6
6
|
|
|
7
7
|
const CONFIG_PATH = path.join(process.cwd(), "form-tester.config.json");
|
|
8
8
|
const OUTPUT_BASE = path.resolve(process.cwd(), "output");
|
|
9
|
-
const LOCAL_VERSION = "0.
|
|
9
|
+
const LOCAL_VERSION = "0.6.0";
|
|
10
10
|
const RECOMMENDED_PERSON = "Uromantisk Direktør";
|
|
11
11
|
|
|
12
|
+
// Recording state — when active, all playwright-cli commands are logged
|
|
13
|
+
let activeRecording = null;
|
|
14
|
+
|
|
15
|
+
function startRecording(outputDir) {
|
|
16
|
+
activeRecording = { commands: [], outputDir, startedAt: new Date().toISOString() };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function recordCommand(args) {
|
|
20
|
+
if (activeRecording) {
|
|
21
|
+
activeRecording.commands.push({ args, timestamp: new Date().toISOString() });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function saveRecording() {
|
|
26
|
+
if (!activeRecording || !activeRecording.commands.length) return null;
|
|
27
|
+
const filePath = path.join(activeRecording.outputDir, "recording.json");
|
|
28
|
+
const data = {
|
|
29
|
+
version: LOCAL_VERSION,
|
|
30
|
+
startedAt: activeRecording.startedAt,
|
|
31
|
+
completedAt: new Date().toISOString(),
|
|
32
|
+
commandCount: activeRecording.commands.length,
|
|
33
|
+
commands: activeRecording.commands,
|
|
34
|
+
};
|
|
35
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
36
|
+
activeRecording = null;
|
|
37
|
+
return filePath;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function stopRecording() {
|
|
41
|
+
activeRecording = null;
|
|
42
|
+
}
|
|
43
|
+
|
|
12
44
|
const PERSONAS = [
|
|
13
45
|
{
|
|
14
46
|
id: "ung-mann",
|
|
@@ -286,6 +318,7 @@ function runCommand(command, args, options = {}) {
|
|
|
286
318
|
}
|
|
287
319
|
|
|
288
320
|
function runPlaywrightCli(args) {
|
|
321
|
+
recordCommand(args);
|
|
289
322
|
return new Promise((resolve) => {
|
|
290
323
|
const spec = getPlaywrightCommandSpec();
|
|
291
324
|
const spawnOpts = { stdio: "inherit" };
|
|
@@ -821,6 +854,9 @@ async function handleTest(url, config) {
|
|
|
821
854
|
config.lastRunDir = outputDir;
|
|
822
855
|
saveConfig(config);
|
|
823
856
|
|
|
857
|
+
// Start recording
|
|
858
|
+
startRecording(outputDir);
|
|
859
|
+
|
|
824
860
|
if (personaChoice.type === "preset") {
|
|
825
861
|
fs.writeFileSync(
|
|
826
862
|
path.join(outputDir, "persona.json"),
|
|
@@ -877,6 +913,13 @@ async function handleTest(url, config) {
|
|
|
877
913
|
dokumenterUrl ||
|
|
878
914
|
"/dokumenter?pnr={PNR}",
|
|
879
915
|
);
|
|
916
|
+
|
|
917
|
+
// Save recording
|
|
918
|
+
const recordingPath = saveRecording();
|
|
919
|
+
if (recordingPath) {
|
|
920
|
+
console.log(`Recording saved: ${recordingPath}`);
|
|
921
|
+
console.log(`Replay with: form-tester replay "${recordingPath}"`);
|
|
922
|
+
}
|
|
880
923
|
}
|
|
881
924
|
|
|
882
925
|
async function handleTestAuto(url, config, flags) {
|
|
@@ -884,10 +927,10 @@ async function handleTestAuto(url, config, flags) {
|
|
|
884
927
|
const log = (msg) => { if (v !== "silent") console.log(msg); };
|
|
885
928
|
const verbose = (msg) => { if (v === "verbose") console.log(msg); };
|
|
886
929
|
|
|
887
|
-
// Resolve PNR
|
|
888
|
-
const pnr = flags.pnr || config.pnr;
|
|
930
|
+
// Resolve PNR — check URL first, then flag, then config
|
|
931
|
+
const pnr = extractPnrFromUrl(url) || flags.pnr || config.pnr;
|
|
889
932
|
if (!pnr) {
|
|
890
|
-
console.error("No PNR available. Pass --pnr <value
|
|
933
|
+
console.error("No PNR available. Pass --pnr <value>, include pnr= in the URL, or set it in form-tester.config.json");
|
|
891
934
|
process.exit(1);
|
|
892
935
|
}
|
|
893
936
|
const fullUrl = extractPnrFromUrl(url) ? url : setPnrOnUrl(url, pnr);
|
|
@@ -928,6 +971,9 @@ async function handleTestAuto(url, config, flags) {
|
|
|
928
971
|
config.lastRunDir = outputDir;
|
|
929
972
|
saveConfig(config);
|
|
930
973
|
|
|
974
|
+
// Start recording
|
|
975
|
+
startRecording(outputDir);
|
|
976
|
+
|
|
931
977
|
// Save persona and scenario
|
|
932
978
|
if (personaChoice.type === "preset") {
|
|
933
979
|
fs.writeFileSync(path.join(outputDir, "persona.json"), JSON.stringify(personaChoice.persona, null, 2));
|
|
@@ -983,6 +1029,35 @@ async function handleTestAuto(url, config, flags) {
|
|
|
983
1029
|
console.log("");
|
|
984
1030
|
printNextSteps(outputDir, dokumenterUrl || "/dokumenter?pnr={PNR}");
|
|
985
1031
|
}
|
|
1032
|
+
|
|
1033
|
+
// Save recording
|
|
1034
|
+
const recordingPath = saveRecording();
|
|
1035
|
+
if (recordingPath) {
|
|
1036
|
+
log(`Recording saved: ${recordingPath}`);
|
|
1037
|
+
log(`Replay with: form-tester replay "${recordingPath}"`);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
async function handleReplay(filePath) {
|
|
1042
|
+
if (!fs.existsSync(filePath)) {
|
|
1043
|
+
console.error(`Recording not found: ${filePath}`);
|
|
1044
|
+
process.exit(1);
|
|
1045
|
+
}
|
|
1046
|
+
const recording = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
1047
|
+
console.log(`Replaying ${recording.commandCount} commands from ${recording.startedAt}`);
|
|
1048
|
+
console.log("");
|
|
1049
|
+
|
|
1050
|
+
for (let i = 0; i < recording.commands.length; i++) {
|
|
1051
|
+
const cmd = recording.commands[i];
|
|
1052
|
+
console.log(`[${i + 1}/${recording.commandCount}] playwright-cli ${cmd.args.join(" ")}`);
|
|
1053
|
+
const code = await runPlaywrightCli(cmd.args);
|
|
1054
|
+
if (code !== 0) {
|
|
1055
|
+
console.error(`Command failed with exit code ${code}. Stopping replay.`);
|
|
1056
|
+
process.exit(1);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
console.log("\nReplay complete.");
|
|
986
1061
|
}
|
|
987
1062
|
|
|
988
1063
|
async function handleCommand(line, config) {
|
|
@@ -1138,6 +1213,16 @@ async function main() {
|
|
|
1138
1213
|
process.exit(0);
|
|
1139
1214
|
}
|
|
1140
1215
|
|
|
1216
|
+
if (args[0] === "replay") {
|
|
1217
|
+
const filePath = args[1];
|
|
1218
|
+
if (!filePath) {
|
|
1219
|
+
console.error("Usage: form-tester replay <recording.json>");
|
|
1220
|
+
process.exit(1);
|
|
1221
|
+
}
|
|
1222
|
+
await handleReplay(path.resolve(filePath));
|
|
1223
|
+
process.exit(0);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1141
1226
|
if (args[0] === "test" && args.includes("--auto")) {
|
|
1142
1227
|
const config = loadConfig();
|
|
1143
1228
|
const url = args.find((a) => a.startsWith("http"));
|
package/package.json
CHANGED