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.
Files changed (3) hide show
  1. package/README.md +36 -0
  2. package/form-tester.js +37 -22
  3. 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.4.3";
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
- console.log(`Persona: ${found.name} — ${found.description}`);
904
+ log(`Persona: ${found.name} — ${found.description}`);
900
905
  personaChoice = { type: "preset", persona: found };
901
906
  } else {
902
- console.log(`Unknown persona "${personaId}", using Noen.`);
907
+ log(`Unknown persona "${personaId}", using Noen.`);
903
908
  personaChoice = { type: "noen", persona: null };
904
909
  }
905
910
  } else {
906
- console.log("Persona: Noen — nøytrale svar (auto)");
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
- console.log(`Scenario: ${scenarioChoice.description}`);
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
- console.log("Opening form with Playwright CLI...");
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
- console.log("Saved: page_open.yml + page_open.png (full-page)");
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
- console.log(`Auto-selected person: ${chosen}`);
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
- console.log("Saved: form_loaded.yml + form_loaded.png (full-page)");
969
+ log("Saved: form_loaded.yml + form_loaded.png (full-page)");
964
970
 
965
971
  const dokumenterUrl = resolveDokumenterUrl(config);
966
- console.log(`\nOutput folder: ${outputDir}`);
967
- if (dokumenterUrl) {
968
- console.log(`Dokumenter URL: ${dokumenterUrl}`);
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 pnrFlag = args[args.indexOf("--pnr") + 1];
1138
- const personaFlag = args[args.indexOf("--persona") + 1];
1139
- const scenarioFlag = args[args.indexOf("--scenario") + 1];
1140
- await handleTestAuto(url, config, { pnr: pnrFlag, persona: personaFlag, scenario: scenarioFlag });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-tester",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "description": "AI-powered form testing skill for /skjemautfyller forms using Playwright CLI. Works with Claude Code and GitHub Copilot.",
5
5
  "main": "form-tester.js",
6
6
  "bin": {