@cementic/cementic-test 0.2.3 → 0.2.4
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 +273 -458
- package/dist/chunk-3EE7LWWT.js +353 -0
- package/dist/chunk-3EE7LWWT.js.map +1 -0
- package/dist/cli.js +337 -164
- package/dist/cli.js.map +1 -1
- package/dist/{gen-54KYT3RO.js → gen-IO4KKGYY.js} +2 -2
- package/dist/templates/student-framework/README.md +35 -5
- package/dist/templates/student-framework/package.json +3 -2
- package/dist/templates/student-framework/pages/BasePage.js +19 -0
- package/dist/templates/student-framework/pages/DashboardPage.js +16 -0
- package/dist/templates/student-framework/pages/FormPage.js +24 -0
- package/dist/templates/student-framework/pages/LoginPage.js +24 -0
- package/dist/templates/student-framework/tests/dashboard.spec.js +14 -0
- package/dist/templates/student-framework/tests/login.spec.js +14 -0
- package/dist/templates/student-framework-ts/README.md +20 -0
- package/dist/templates/student-framework-ts/package.json +24 -0
- package/dist/templates/student-framework-ts/pages/LandingPage.ts +15 -0
- package/dist/templates/student-framework-ts/playwright.config.ts +29 -0
- package/dist/templates/{student-framework/tests/landing.spec.js → student-framework-ts/tests/landing.spec.ts} +1 -1
- package/dist/templates/student-framework-ts/tsconfig.json +19 -0
- package/dist/templates/student-framework-ts/workflows/playwright.yml +20 -0
- package/package.json +2 -1
- package/dist/chunk-J63TUHIV.js +0 -80
- package/dist/chunk-J63TUHIV.js.map +0 -1
- package/dist/templates/student-framework/pages/LandingPage.js +0 -18
- /package/dist/{gen-54KYT3RO.js.map → gen-IO4KKGYY.js.map} +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"types": [
|
|
10
|
+
"node",
|
|
11
|
+
"@playwright/test"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"include": [
|
|
15
|
+
"pages/**/*.ts",
|
|
16
|
+
"tests/**/*.ts",
|
|
17
|
+
"playwright.config.ts"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Playwright Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-node@v4
|
|
15
|
+
with:
|
|
16
|
+
node-version: 20
|
|
17
|
+
cache: npm
|
|
18
|
+
- run: npm ci
|
|
19
|
+
- run: npx playwright install --with-deps
|
|
20
|
+
- run: npx playwright test
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cementic/cementic-test",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "CementicTest CLI (ct): normalize cases → generate Playwright tests",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "tsup && node scripts/copy-templates.cjs",
|
|
18
18
|
"dev": "tsup --watch",
|
|
19
|
+
"test": "npm run build && node --test test/*.test.mjs",
|
|
19
20
|
"lint": "echo \"(add linter later)\"",
|
|
20
21
|
"prepublishOnly": "npm run build"
|
|
21
22
|
},
|
package/dist/chunk-J63TUHIV.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/commands/gen.ts
|
|
4
|
-
import { Command } from "commander";
|
|
5
|
-
import fg from "fast-glob";
|
|
6
|
-
import { readFileSync, mkdirSync, writeFileSync } from "fs";
|
|
7
|
-
import { join, basename, relative, resolve } from "path";
|
|
8
|
-
function buildTestTitle(norm) {
|
|
9
|
-
const idPart = norm.id ? norm.id : "";
|
|
10
|
-
const cleanTitle = norm.title || "Untitled";
|
|
11
|
-
const tagSuffix = (norm.tags ?? []).map((t) => `@${t}`).join(" ");
|
|
12
|
-
return [idPart, cleanTitle, tagSuffix].filter(Boolean).join(" ").trim();
|
|
13
|
-
}
|
|
14
|
-
function buildTestBody(norm, relPomImport) {
|
|
15
|
-
const title = buildTestTitle(norm);
|
|
16
|
-
const stepsComment = (norm.steps ?? []).map((s) => `- ${s}`).join("\n // ");
|
|
17
|
-
const expectations = (norm.expected ?? []).map(
|
|
18
|
-
(e) => ` // expect: ${e}
|
|
19
|
-
// TODO: map this to a real assertion using LoginPage`
|
|
20
|
-
).join("\n\n");
|
|
21
|
-
let importPath = relPomImport.replace(/\\/g, "/");
|
|
22
|
-
if (!importPath.startsWith(".")) {
|
|
23
|
-
importPath = `./${importPath}`;
|
|
24
|
-
}
|
|
25
|
-
return `import { test, expect } from '@playwright/test';
|
|
26
|
-
import { LandingPage } from '${importPath}';
|
|
27
|
-
|
|
28
|
-
test('${title}', async ({ page }) => {
|
|
29
|
-
const pageObj = new LandingPage(page);
|
|
30
|
-
|
|
31
|
-
// Steps inferred from case:
|
|
32
|
-
// ${stepsComment || "TODO: no steps detected"}
|
|
33
|
-
|
|
34
|
-
// Example flow (replace with real mapping later):
|
|
35
|
-
await pageObj.goto();
|
|
36
|
-
// await pageObj.login('user@example.com', 'password');
|
|
37
|
-
|
|
38
|
-
${expectations || " // TODO: add assertions with LandingPage"}
|
|
39
|
-
});
|
|
40
|
-
`;
|
|
41
|
-
}
|
|
42
|
-
async function gen(opts) {
|
|
43
|
-
if (opts.lang !== "ts" && opts.lang !== "js") {
|
|
44
|
-
console.error("MVP supports --lang ts and --lang js only for now.");
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
const normalized = await fg([".cementic/normalized/*.json", "!.cementic/normalized/_index.json"]);
|
|
48
|
-
mkdirSync(opts.out, { recursive: true });
|
|
49
|
-
const projectRoot = process.cwd();
|
|
50
|
-
const pagesDir = join(projectRoot, "pages");
|
|
51
|
-
const outDir = resolve(projectRoot, opts.out);
|
|
52
|
-
const relPathToPages = relative(outDir, pagesDir);
|
|
53
|
-
const relPomImport = join(relPathToPages, "LandingPage");
|
|
54
|
-
for (const f of normalized) {
|
|
55
|
-
const norm = JSON.parse(readFileSync(f, "utf8"));
|
|
56
|
-
const stem = basename(f).replace(/\.json$/, "");
|
|
57
|
-
const fileStem = stem.replace(/[^\w-]+/g, "-");
|
|
58
|
-
const body = buildTestBody(norm, relPomImport);
|
|
59
|
-
const ext = opts.lang === "js" ? "spec.js" : "spec.ts";
|
|
60
|
-
const outFile = join(opts.out, `${fileStem}.${ext}`);
|
|
61
|
-
writeFileSync(outFile, body);
|
|
62
|
-
}
|
|
63
|
-
console.log(`\u2705 Generated ${normalized.length} POM-style test file(s) \u2192 ${opts.out}`);
|
|
64
|
-
}
|
|
65
|
-
function genCmd() {
|
|
66
|
-
const cmd = new Command("gen").description("Generate Playwright test files from normalized JSON cases").addHelpText("after", `
|
|
67
|
-
Examples:
|
|
68
|
-
$ ct gen --lang ts
|
|
69
|
-
$ ct gen --lang js --out tests/e2e
|
|
70
|
-
`).option("--lang <lang>", "Target language for test files (ts|js)", "ts").option("--out <dir>", "Output directory for generated tests", "tests/generated").action(async (opts) => {
|
|
71
|
-
await gen({ lang: opts.lang, out: opts.out });
|
|
72
|
-
});
|
|
73
|
-
return cmd;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export {
|
|
77
|
-
gen,
|
|
78
|
-
genCmd
|
|
79
|
-
};
|
|
80
|
-
//# sourceMappingURL=chunk-J63TUHIV.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/gen.ts"],"sourcesContent":["import { Command } from 'commander';\nimport fg from 'fast-glob';\nimport { readFileSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { join, basename, relative, resolve } from 'node:path';\n\ntype NormalizedCase = {\n id?: string;\n title: string;\n tags?: string[];\n steps?: string[];\n expected?: string[];\n needs_review?: boolean;\n source?: string;\n};\n\nfunction buildTestTitle(norm: NormalizedCase): string {\n const idPart = norm.id ? norm.id : '';\n const cleanTitle = norm.title || 'Untitled';\n const tagSuffix = (norm.tags ?? []).map(t => `@${t}`).join(' ');\n return [idPart, cleanTitle, tagSuffix].filter(Boolean).join(' ').trim();\n}\n\nfunction buildTestBody(norm: NormalizedCase, relPomImport: string): string {\n const title = buildTestTitle(norm);\n const stepsComment = (norm.steps ?? []).map(s => `- ${s}`).join('\\n // ');\n const expectations = (norm.expected ?? []).map(\n e => ` // expect: ${e}\\n // TODO: map this to a real assertion using LoginPage`\n ).join('\\n\\n');\n\n // Ensure import path starts with ./ or ../\n let importPath = relPomImport.replace(/\\\\/g, '/');\n if (!importPath.startsWith('.')) {\n importPath = `./${importPath}`;\n }\n\n return `import { test, expect } from '@playwright/test';\nimport { LandingPage } from '${importPath}';\n\ntest('${title}', async ({ page }) => {\n const pageObj = new LandingPage(page);\n\n // Steps inferred from case:\n // ${stepsComment || 'TODO: no steps detected'}\n\n // Example flow (replace with real mapping later):\n await pageObj.goto();\n // await pageObj.login('user@example.com', 'password');\n\n${expectations || ' // TODO: add assertions with LandingPage'}\n});\n`;\n}\n\nexport async function gen(opts: { lang: string; out: string }) {\n if (opts.lang !== 'ts' && opts.lang !== 'js') {\n console.error('MVP supports --lang ts and --lang js only for now.');\n process.exit(1);\n }\n\n const normalized = await fg(['.cementic/normalized/*.json', '!.cementic/normalized/_index.json']);\n mkdirSync(opts.out, { recursive: true });\n\n // Calculate relative path from output dir to 'pages/LandingPage'\n // Assumption: project root has 'pages/'\n const projectRoot = process.cwd();\n const pagesDir = join(projectRoot, 'pages');\n const outDir = resolve(projectRoot, opts.out);\n \n // path.relative(from, to)\n const relPathToPages = relative(outDir, pagesDir);\n const relPomImport = join(relPathToPages, 'LandingPage');\n\n for (const f of normalized) {\n const norm = JSON.parse(readFileSync(f, 'utf8')) as NormalizedCase;\n const stem = basename(f).replace(/\\.json$/, '');\n const fileStem = stem.replace(/[^\\w-]+/g, '-');\n\n const body = buildTestBody(norm, relPomImport);\n const ext = opts.lang === 'js' ? 'spec.js' : 'spec.ts';\n const outFile = join(opts.out, `${fileStem}.${ext}`);\n writeFileSync(outFile, body);\n }\n\n console.log(`✅ Generated ${normalized.length} POM-style test file(s) → ${opts.out}`);\n}\n\nexport function genCmd() {\n const cmd = new Command('gen')\n .description('Generate Playwright test files from normalized JSON cases')\n .addHelpText('after', `\nExamples:\n $ ct gen --lang ts\n $ ct gen --lang js --out tests/e2e\n`)\n .option('--lang <lang>', 'Target language for test files (ts|js)', 'ts')\n .option('--out <dir>', 'Output directory for generated tests', 'tests/generated')\n .action(async (opts) => {\n await gen({ lang: opts.lang, out: opts.out });\n });\n return cmd;\n}"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,OAAO,QAAQ;AACf,SAAS,cAAc,WAAW,qBAAqB;AACvD,SAAS,MAAM,UAAU,UAAU,eAAe;AAYlD,SAAS,eAAe,MAA8B;AACpD,QAAM,SAAS,KAAK,KAAK,KAAK,KAAK;AACnC,QAAM,aAAa,KAAK,SAAS;AACjC,QAAM,aAAa,KAAK,QAAQ,CAAC,GAAG,IAAI,OAAK,IAAI,CAAC,EAAE,EAAE,KAAK,GAAG;AAC9D,SAAO,CAAC,QAAQ,YAAY,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK;AACxE;AAEA,SAAS,cAAc,MAAsB,cAA8B;AACzE,QAAM,QAAQ,eAAe,IAAI;AACjC,QAAM,gBAAgB,KAAK,SAAS,CAAC,GAAG,IAAI,OAAK,KAAK,CAAC,EAAE,EAAE,KAAK,SAAS;AACzE,QAAM,gBAAgB,KAAK,YAAY,CAAC,GAAG;AAAA,IACzC,OAAK,gBAAgB,CAAC;AAAA;AAAA,EACxB,EAAE,KAAK,MAAM;AAGb,MAAI,aAAa,aAAa,QAAQ,OAAO,GAAG;AAChD,MAAI,CAAC,WAAW,WAAW,GAAG,GAAG;AAC/B,iBAAa,KAAK,UAAU;AAAA,EAC9B;AAEA,SAAO;AAAA,+BACsB,UAAU;AAAA;AAAA,QAEjC,KAAK;AAAA;AAAA;AAAA;AAAA,OAIN,gBAAgB,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM9C,gBAAgB,4CAA4C;AAAA;AAAA;AAG9D;AAEA,eAAsB,IAAI,MAAqC;AAC7D,MAAI,KAAK,SAAS,QAAQ,KAAK,SAAS,MAAM;AAC5C,YAAQ,MAAM,oDAAoD;AAClE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aAAa,MAAM,GAAG,CAAC,+BAA+B,mCAAmC,CAAC;AAChG,YAAU,KAAK,KAAK,EAAE,WAAW,KAAK,CAAC;AAIvC,QAAM,cAAc,QAAQ,IAAI;AAChC,QAAM,WAAW,KAAK,aAAa,OAAO;AAC1C,QAAM,SAAS,QAAQ,aAAa,KAAK,GAAG;AAG5C,QAAM,iBAAiB,SAAS,QAAQ,QAAQ;AAChD,QAAM,eAAe,KAAK,gBAAgB,aAAa;AAEvD,aAAW,KAAK,YAAY;AAC1B,UAAM,OAAO,KAAK,MAAM,aAAa,GAAG,MAAM,CAAC;AAC/C,UAAM,OAAO,SAAS,CAAC,EAAE,QAAQ,WAAW,EAAE;AAC9C,UAAM,WAAW,KAAK,QAAQ,YAAY,GAAG;AAE7C,UAAM,OAAO,cAAc,MAAM,YAAY;AAC7C,UAAM,MAAM,KAAK,SAAS,OAAO,YAAY;AAC7C,UAAM,UAAU,KAAK,KAAK,KAAK,GAAG,QAAQ,IAAI,GAAG,EAAE;AACnD,kBAAc,SAAS,IAAI;AAAA,EAC7B;AAEA,UAAQ,IAAI,oBAAe,WAAW,MAAM,kCAA6B,KAAK,GAAG,EAAE;AACrF;AAEO,SAAS,SAAS;AACvB,QAAM,MAAM,IAAI,QAAQ,KAAK,EAC1B,YAAY,2DAA2D,EACvE,YAAY,SAAS;AAAA;AAAA;AAAA;AAAA,CAIzB,EACI,OAAO,iBAAiB,0CAA0C,IAAI,EACtE,OAAO,eAAe,wCAAwC,iBAAiB,EAC/E,OAAO,OAAO,SAAS;AACtB,UAAM,IAAI,EAAE,MAAM,KAAK,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,EAC9C,CAAC;AACH,SAAO;AACT;","names":[]}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export class LandingPage {
|
|
2
|
-
/**
|
|
3
|
-
* @param {import('@playwright/test').Page} page
|
|
4
|
-
*/
|
|
5
|
-
constructor(page) {
|
|
6
|
-
this.page = page;
|
|
7
|
-
this.newArrivalsText = page.getByText('New Arrivals Just In!');
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async goto() {
|
|
11
|
-
await this.page.goto('/');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async verifyNewArrivals() {
|
|
15
|
-
await this.newArrivalsText.waitFor();
|
|
16
|
-
return await this.newArrivalsText.isVisible();
|
|
17
|
-
}
|
|
18
|
-
}
|
|
File without changes
|