form-tester 0.4.3 → 0.5.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 +36 -0
- package/form-tester.js +37 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,6 +69,42 @@ If Copilot doesn't recognize the skill:
|
|
|
69
69
|
1. Make sure `.claude/skills/form-tester/` exists in your project (run `form-tester install`)
|
|
70
70
|
2. Run `/skills` in the Copilot CLI to reload skills, or restart the session
|
|
71
71
|
|
|
72
|
+
## Skip permission prompts
|
|
73
|
+
|
|
74
|
+
By default, AI agents will ask permission for every shell command. To run without interruptions, pre-allow the relevant tools.
|
|
75
|
+
|
|
76
|
+
### Claude Code
|
|
77
|
+
|
|
78
|
+
Add to your project's `.claude/settings.local.json` (or global `~/.claude/settings.json`):
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"permissions": {
|
|
83
|
+
"allow": [
|
|
84
|
+
"Skill(form-tester)",
|
|
85
|
+
"Bash(form-tester:*)",
|
|
86
|
+
"Bash(playwright-cli:*)"
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Or use the Claude Code CLI:
|
|
93
|
+
```bash
|
|
94
|
+
claude config add permissions.allow "Skill(form-tester)"
|
|
95
|
+
claude config add permissions.allow "Bash(form-tester:*)"
|
|
96
|
+
claude config add permissions.allow "Bash(playwright-cli:*)"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### GitHub Copilot
|
|
100
|
+
|
|
101
|
+
In Copilot CLI, use auto-approve mode:
|
|
102
|
+
```bash
|
|
103
|
+
copilot --auto-approve
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Or approve the tool categories when first prompted and select "Always allow".
|
|
107
|
+
|
|
72
108
|
## Test Output
|
|
73
109
|
|
|
74
110
|
Test runs are saved to `output/{form-id}/{timestamp}/` with:
|
package/form-tester.js
CHANGED
|
@@ -6,7 +6,7 @@ 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.5.0";
|
|
10
10
|
const RECOMMENDED_PERSON = "Uromantisk Direktør";
|
|
11
11
|
|
|
12
12
|
const PERSONAS = [
|
|
@@ -880,6 +880,10 @@ async function handleTest(url, config) {
|
|
|
880
880
|
}
|
|
881
881
|
|
|
882
882
|
async function handleTestAuto(url, config, flags) {
|
|
883
|
+
const v = flags.verbosity || "normal";
|
|
884
|
+
const log = (msg) => { if (v !== "silent") console.log(msg); };
|
|
885
|
+
const verbose = (msg) => { if (v === "verbose") console.log(msg); };
|
|
886
|
+
|
|
883
887
|
// Resolve PNR
|
|
884
888
|
const pnr = flags.pnr || config.pnr;
|
|
885
889
|
if (!pnr) {
|
|
@@ -889,6 +893,7 @@ async function handleTestAuto(url, config, flags) {
|
|
|
889
893
|
const fullUrl = extractPnrFromUrl(url) ? url : setPnrOnUrl(url, pnr);
|
|
890
894
|
config.pnr = pnr;
|
|
891
895
|
saveConfig(config);
|
|
896
|
+
verbose(`URL: ${fullUrl}`);
|
|
892
897
|
|
|
893
898
|
// Resolve persona
|
|
894
899
|
let personaChoice;
|
|
@@ -896,14 +901,14 @@ async function handleTestAuto(url, config, flags) {
|
|
|
896
901
|
if (personaId) {
|
|
897
902
|
const found = getPersonaById(personaId);
|
|
898
903
|
if (found) {
|
|
899
|
-
|
|
904
|
+
log(`Persona: ${found.name} — ${found.description}`);
|
|
900
905
|
personaChoice = { type: "preset", persona: found };
|
|
901
906
|
} else {
|
|
902
|
-
|
|
907
|
+
log(`Unknown persona "${personaId}", using Noen.`);
|
|
903
908
|
personaChoice = { type: "noen", persona: null };
|
|
904
909
|
}
|
|
905
910
|
} else {
|
|
906
|
-
|
|
911
|
+
log("Persona: Noen — nøytrale svar (auto)");
|
|
907
912
|
personaChoice = { type: "noen", persona: null };
|
|
908
913
|
}
|
|
909
914
|
|
|
@@ -911,7 +916,7 @@ async function handleTestAuto(url, config, flags) {
|
|
|
911
916
|
const scenarioChoice = flags.scenario
|
|
912
917
|
? { type: "custom", description: flags.scenario }
|
|
913
918
|
: { type: "default", description: "Standard test" };
|
|
914
|
-
|
|
919
|
+
log(`Scenario: ${scenarioChoice.description}`);
|
|
915
920
|
|
|
916
921
|
// Create output directory
|
|
917
922
|
const formId = sanitizeSegment(extractFormId(fullUrl));
|
|
@@ -934,16 +939,17 @@ async function handleTestAuto(url, config, flags) {
|
|
|
934
939
|
fs.writeFileSync(path.join(outputDir, "scenario.json"), JSON.stringify(scenarioChoice, null, 2));
|
|
935
940
|
|
|
936
941
|
// Open and take initial full-page screenshot
|
|
937
|
-
|
|
942
|
+
log("Opening form with Playwright CLI...");
|
|
938
943
|
await runPlaywrightCli(["open", fullUrl]);
|
|
939
944
|
await runPlaywrightCli(["snapshot", "--filename", path.join(outputDir, "page_open.yml")]);
|
|
940
945
|
await runPlaywrightCli(["screenshot", "--filename", path.join(outputDir, "page_open.png"), "--full-page"]);
|
|
941
|
-
|
|
946
|
+
log("Saved: page_open.yml + page_open.png (full-page)");
|
|
942
947
|
|
|
943
948
|
// Auto-select person (try recommended, then first available)
|
|
944
949
|
let options = extractPersonsFromSnapshotFile(path.join(outputDir, "page_open.yml"));
|
|
945
950
|
if (!options.length) {
|
|
946
951
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
952
|
+
verbose(`Scanning for person options (attempt ${attempt + 1})...`);
|
|
947
953
|
await sleep(1500);
|
|
948
954
|
options = await fetchPersonOptions();
|
|
949
955
|
if (options.length) break;
|
|
@@ -952,7 +958,7 @@ async function handleTestAuto(url, config, flags) {
|
|
|
952
958
|
if (options.length) {
|
|
953
959
|
options = prioritizeRecommended(options, RECOMMENDED_PERSON);
|
|
954
960
|
const chosen = options[0];
|
|
955
|
-
|
|
961
|
+
log(`Auto-selected person: ${chosen}`);
|
|
956
962
|
config.lastPerson = chosen;
|
|
957
963
|
saveConfig(config);
|
|
958
964
|
}
|
|
@@ -960,18 +966,23 @@ async function handleTestAuto(url, config, flags) {
|
|
|
960
966
|
// Take form loaded screenshot after person selection
|
|
961
967
|
await runPlaywrightCli(["snapshot", "--filename", path.join(outputDir, "form_loaded.yml")]);
|
|
962
968
|
await runPlaywrightCli(["screenshot", "--filename", path.join(outputDir, "form_loaded.png"), "--full-page"]);
|
|
963
|
-
|
|
969
|
+
log("Saved: form_loaded.yml + form_loaded.png (full-page)");
|
|
964
970
|
|
|
965
971
|
const dokumenterUrl = resolveDokumenterUrl(config);
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
972
|
+
|
|
973
|
+
// Always print output folder (even in silent mode)
|
|
974
|
+
console.log(`Output folder: ${outputDir}`);
|
|
975
|
+
|
|
976
|
+
if (v !== "silent") {
|
|
977
|
+
if (dokumenterUrl) {
|
|
978
|
+
console.log(`Dokumenter URL: ${dokumenterUrl}`);
|
|
979
|
+
}
|
|
980
|
+
console.log("");
|
|
981
|
+
console.log("IMPORTANT: All screenshots MUST use --full-page to capture the entire page.");
|
|
982
|
+
console.log("Example: playwright-cli screenshot --filename \"path/to/file.png\" --full-page");
|
|
983
|
+
console.log("");
|
|
984
|
+
printNextSteps(outputDir, dokumenterUrl || "/dokumenter?pnr={PNR}");
|
|
969
985
|
}
|
|
970
|
-
console.log("");
|
|
971
|
-
console.log("IMPORTANT: All screenshots MUST use --full-page to capture the entire page.");
|
|
972
|
-
console.log("Example: playwright-cli screenshot --filename \"path/to/file.png\" --full-page");
|
|
973
|
-
console.log("");
|
|
974
|
-
printNextSteps(outputDir, dokumenterUrl || "/dokumenter?pnr={PNR}");
|
|
975
986
|
}
|
|
976
987
|
|
|
977
988
|
async function handleCommand(line, config) {
|
|
@@ -1131,13 +1142,17 @@ async function main() {
|
|
|
1131
1142
|
const config = loadConfig();
|
|
1132
1143
|
const url = args.find((a) => a.startsWith("http"));
|
|
1133
1144
|
if (!url) {
|
|
1134
|
-
console.error("Usage: form-tester test <url> --auto [--pnr <pnr>] [--persona <id>] [--scenario <text>]");
|
|
1145
|
+
console.error("Usage: form-tester test <url> --auto [--pnr <pnr>] [--persona <id>] [--scenario <text>] [--silent|--verbose]");
|
|
1135
1146
|
process.exit(1);
|
|
1136
1147
|
}
|
|
1137
|
-
const
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1140
|
-
|
|
1148
|
+
const flagVal = (flag) => args.includes(flag) ? args[args.indexOf(flag) + 1] : undefined;
|
|
1149
|
+
const verbosity = args.includes("--silent") ? "silent" : args.includes("--verbose") ? "verbose" : "normal";
|
|
1150
|
+
await handleTestAuto(url, config, {
|
|
1151
|
+
pnr: flagVal("--pnr"),
|
|
1152
|
+
persona: flagVal("--persona"),
|
|
1153
|
+
scenario: flagVal("--scenario"),
|
|
1154
|
+
verbosity,
|
|
1155
|
+
});
|
|
1141
1156
|
process.exit(0);
|
|
1142
1157
|
}
|
|
1143
1158
|
|
package/package.json
CHANGED