muagqa 1.4.0 → 1.4.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muagqa",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "MuagQA CLI — Test case runner with one-time session token and Playwright recorder",
5
5
  "author": "Mary C.N. Enwezor",
6
6
  "license": "MIT",
package/src/index.js CHANGED
@@ -2,11 +2,9 @@
2
2
  import { exec } from "child_process";
3
3
  import { createRequire } from "module";
4
4
  import { promisify } from "util";
5
- import inquirer from "inquirer";
6
5
  import chalk from "chalk";
7
- import yargs from "yargs";
8
- import { hideBin } from "yargs/helpers";
9
- import run from "./runner.js";
6
+ import { startRecording } from "./recordMode.js";
7
+ import { generateTemplateTests } from "./templateMode.js";
10
8
 
11
9
  const execAsync = promisify(exec);
12
10
  const require = createRequire(import.meta.url);
@@ -36,62 +34,44 @@ async function checkForUpdate({ force = false } = {}) {
36
34
  }
37
35
  }
38
36
 
37
+ function extractToken(args) {
38
+ if (args[0] === "pull" && args[1]) {
39
+ return args[1];
40
+ }
41
+ return args.find(arg => !arg.startsWith("--"));
42
+ }
43
+
39
44
  async function startCLI() {
40
- let ranCommand = false;
45
+ const args = process.argv.slice(2);
46
+ const isTemplate = args.includes("--template");
47
+ const isRecord = args.includes("--record");
48
+ const wantsVersion = args.includes("--version") || args.includes("-v");
49
+ const wantsCheckUpdate = args.includes("--check-update");
50
+ const token = extractToken(args);
41
51
 
42
- const argv = await yargs(hideBin(process.argv))
43
- .scriptName("muagqa")
44
- .command(
45
- "pull <token>",
46
- "Pull test cases and generate tests",
47
- y => {
48
- return y
49
- .positional("token", {
50
- type: "string",
51
- describe: "One-time session token"
52
- })
53
- .option("mode", {
54
- choices: ["record", "template"],
55
- default: "record",
56
- describe: "Execution mode"
57
- });
58
- },
59
- async args => {
60
- ranCommand = true;
61
- checkForUpdate();
62
- await run(args.token, args.mode);
63
- }
64
- )
65
- .option("check-update", {
66
- type: "boolean",
67
- describe: "Check for updates"
68
- })
69
- .version(`muagqa v${pkg.version}`)
70
- .alias("version", "v")
71
- .help()
72
- .parseAsync();
52
+ if (wantsVersion) {
53
+ console.log(`muagqa v${pkg.version}`);
54
+ return;
55
+ }
73
56
 
74
- if (argv.checkUpdate) {
57
+ if (wantsCheckUpdate) {
75
58
  await checkForUpdate({ force: true });
76
59
  return;
77
60
  }
78
61
 
79
- if (ranCommand) {
62
+ if (isTemplate) {
63
+ checkForUpdate();
64
+ await generateTemplateTests(token);
80
65
  return;
81
66
  }
82
67
 
83
- checkForUpdate();
84
-
85
- const { token } = await inquirer.prompt([
86
- {
87
- type: "input",
88
- name: "token",
89
- message: "Enter your one-time session token:",
90
- validate: v => v.trim() !== "" || "Token is required."
91
- }
92
- ]);
68
+ if (isRecord) {
69
+ checkForUpdate();
70
+ await startRecording(token);
71
+ return;
72
+ }
93
73
 
94
- await run(token, "record");
74
+ console.log("Please specify --template or --record mode");
95
75
  }
96
76
 
97
77
  startCLI();
@@ -0,0 +1,20 @@
1
+ import inquirer from "inquirer";
2
+ import run from "./runner.js";
3
+
4
+ export async function startRecording(sessionToken) {
5
+ let token = sessionToken || process.env.MUAGQA_SESSION;
6
+
7
+ if (!token) {
8
+ const answer = await inquirer.prompt([
9
+ {
10
+ type: "input",
11
+ name: "token",
12
+ message: "Enter your one-time session token:",
13
+ validate: v => v.trim() !== "" || "Token is required."
14
+ }
15
+ ]);
16
+ token = answer.token;
17
+ }
18
+
19
+ await run(token, "record");
20
+ }
@@ -0,0 +1,66 @@
1
+ function formatArg(value) {
2
+ if (Array.isArray(value)) {
3
+ return value.map(formatArg).join(", ");
4
+ }
5
+ if (typeof value === "string") {
6
+ return JSON.stringify(value);
7
+ }
8
+ return JSON.stringify(value);
9
+ }
10
+
11
+ function formatArgs(args) {
12
+ if (Array.isArray(args)) {
13
+ return args.map(formatArg).join(", ");
14
+ }
15
+ if (args === undefined) {
16
+ return "";
17
+ }
18
+ return formatArg(args);
19
+ }
20
+
21
+ export function buildTemplate(testCase) {
22
+ const steps = Array.isArray(testCase.steps) ? testCase.steps : [];
23
+ const lines = steps.map(step => {
24
+ const action = step.action || "click";
25
+ const args = formatArgs(step.args);
26
+ const argsText = args ? `(${args})` : "()";
27
+ return ` await page.${action}${argsText};`;
28
+ });
29
+
30
+ const title = testCase.title || "Generated test";
31
+ const description = testCase.description || "";
32
+ const expected = testCase.expected || "";
33
+ const startUrl =
34
+ testCase.startUrl || testCase.url || testCase.start_url || "";
35
+
36
+ const headerLines = [];
37
+ if (description) {
38
+ headerLines.push(`// Description: ${description}`);
39
+ }
40
+ if (expected) {
41
+ headerLines.push(`// Expected: ${expected}`);
42
+ }
43
+ if (testCase.id) {
44
+ headerLines.push(`// Test case ID: ${testCase.id}`);
45
+ }
46
+
47
+ const header = headerLines.length ? `${headerLines.join("\n")}\n` : "";
48
+
49
+ if (lines.length === 0) {
50
+ lines.push(" // TODO: add steps for this test case.");
51
+ if (startUrl) {
52
+ lines.push(` await page.goto("${startUrl}");`);
53
+ } else {
54
+ lines.push(" // await page.goto(\"https://example.com\");");
55
+ }
56
+ lines.push(" // Example:");
57
+ lines.push(" // await page.getByRole(\"button\", { name: \"Sign in\" }).click();");
58
+ }
59
+
60
+ return `import { test } from "@playwright/test";
61
+
62
+ ${header}test("${title}", async ({ page }) => {
63
+ ${lines.join("\n")}
64
+ });
65
+ `;
66
+ }
@@ -0,0 +1,35 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import chalk from "chalk";
4
+ import { validateSessionToken } from "./api.js";
5
+ import { buildTemplate } from "./templateBuilder.js";
6
+
7
+ export async function generateTemplateTests(sessionToken) {
8
+ const token = sessionToken || process.env.MUAGQA_SESSION;
9
+
10
+ if (!token) {
11
+ console.error("Missing session token. Set MUAGQA_SESSION or pass a token.");
12
+ return;
13
+ }
14
+
15
+ const result = await validateSessionToken(token);
16
+ if (!result.success) {
17
+ console.error("Unable to fetch test flows — invalid token?");
18
+ return;
19
+ }
20
+
21
+ const testCases = result.testCases || [];
22
+
23
+ const outputDir = path.resolve(process.cwd(), "muagqa-tests");
24
+ if (!fs.existsSync(outputDir)) {
25
+ fs.mkdirSync(outputDir);
26
+ }
27
+
28
+ for (const tc of testCases) {
29
+ const template = buildTemplate(tc);
30
+ const slug = tc.slug || tc.id || `test-${Date.now()}`;
31
+ fs.writeFileSync(path.join(outputDir, `${slug}.spec.js`), template, "utf8");
32
+ }
33
+
34
+ console.log(chalk.green(`Generated ${testCases.length} test files in /muagqa-tests`));
35
+ }