@vijaypjavvadi/bdd2pw 1.0.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 (83) hide show
  1. package/CHANGELOG.md +308 -0
  2. package/LICENSE +21 -0
  3. package/README.md +254 -0
  4. package/dist/cli.d.ts +6 -0
  5. package/dist/cli.js +176 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/discovery/mcpClient.d.ts +53 -0
  8. package/dist/discovery/mcpClient.js +326 -0
  9. package/dist/discovery/mcpClient.js.map +1 -0
  10. package/dist/discovery/snapshotParser.d.ts +13 -0
  11. package/dist/discovery/snapshotParser.js +51 -0
  12. package/dist/discovery/snapshotParser.js.map +1 -0
  13. package/dist/emitters/facade.d.ts +52 -0
  14. package/dist/emitters/facade.js +125 -0
  15. package/dist/emitters/facade.js.map +1 -0
  16. package/dist/http/artifacts.d.ts +14 -0
  17. package/dist/http/artifacts.js +36 -0
  18. package/dist/http/artifacts.js.map +1 -0
  19. package/dist/http/jobs.d.ts +26 -0
  20. package/dist/http/jobs.js +59 -0
  21. package/dist/http/jobs.js.map +1 -0
  22. package/dist/http/routes.d.ts +11 -0
  23. package/dist/http/routes.js +267 -0
  24. package/dist/http/routes.js.map +1 -0
  25. package/dist/http/schemas.d.ts +167 -0
  26. package/dist/http/schemas.js +57 -0
  27. package/dist/http/schemas.js.map +1 -0
  28. package/dist/index.d.ts +49 -0
  29. package/dist/index.js +507 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/llm/governanceClient.d.ts +24 -0
  32. package/dist/llm/governanceClient.js +22 -0
  33. package/dist/llm/governanceClient.js.map +1 -0
  34. package/dist/llm/providers/anthropic.d.ts +8 -0
  35. package/dist/llm/providers/anthropic.js +12 -0
  36. package/dist/llm/providers/anthropic.js.map +1 -0
  37. package/dist/llm/providers/gemini.d.ts +5 -0
  38. package/dist/llm/providers/gemini.js +9 -0
  39. package/dist/llm/providers/gemini.js.map +1 -0
  40. package/dist/llm/providers/openai.d.ts +5 -0
  41. package/dist/llm/providers/openai.js +9 -0
  42. package/dist/llm/providers/openai.js.map +1 -0
  43. package/dist/parser/gherkinParser.d.ts +17 -0
  44. package/dist/parser/gherkinParser.js +143 -0
  45. package/dist/parser/gherkinParser.js.map +1 -0
  46. package/dist/repo/projectScaffolder.d.ts +24 -0
  47. package/dist/repo/projectScaffolder.js +64 -0
  48. package/dist/repo/projectScaffolder.js.map +1 -0
  49. package/dist/repo/repoScanner.d.ts +21 -0
  50. package/dist/repo/repoScanner.js +132 -0
  51. package/dist/repo/repoScanner.js.map +1 -0
  52. package/dist/reports/reviewReport.d.ts +19 -0
  53. package/dist/reports/reviewReport.js +96 -0
  54. package/dist/reports/reviewReport.js.map +1 -0
  55. package/dist/server.d.ts +10 -0
  56. package/dist/server.js +40 -0
  57. package/dist/server.js.map +1 -0
  58. package/dist/transformers/locatorPicker.d.ts +40 -0
  59. package/dist/transformers/locatorPicker.js +283 -0
  60. package/dist/transformers/locatorPicker.js.map +1 -0
  61. package/dist/transformers/pomResolver.d.ts +28 -0
  62. package/dist/transformers/pomResolver.js +35 -0
  63. package/dist/transformers/pomResolver.js.map +1 -0
  64. package/dist/transformers/stepMatcher.d.ts +36 -0
  65. package/dist/transformers/stepMatcher.js +461 -0
  66. package/dist/transformers/stepMatcher.js.map +1 -0
  67. package/dist/types.d.ts +166 -0
  68. package/dist/types.js +7 -0
  69. package/dist/types.js.map +1 -0
  70. package/dist/utils/logger.d.ts +6 -0
  71. package/dist/utils/logger.js +25 -0
  72. package/dist/utils/logger.js.map +1 -0
  73. package/dist/utils/naming.d.ts +23 -0
  74. package/dist/utils/naming.js +58 -0
  75. package/dist/utils/naming.js.map +1 -0
  76. package/dist/validate/tscRunner.d.ts +17 -0
  77. package/dist/validate/tscRunner.js +98 -0
  78. package/dist/validate/tscRunner.js.map +1 -0
  79. package/package.json +69 -0
  80. package/templates/gitignore.tmpl +10 -0
  81. package/templates/package.json.tmpl +16 -0
  82. package/templates/playwright.config.ts.tmpl +24 -0
  83. package/templates/tsconfig.json.tmpl +15 -0
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Gherkin parser — wraps `@cucumber/gherkin` and produces a `FeatureIR`.
3
+ * See docs/ARCHITECTURE.md §2.
4
+ */
5
+ import type { FeatureIR } from "../types";
6
+ export declare class GherkinParseError extends Error {
7
+ readonly file: string;
8
+ readonly line?: number | undefined;
9
+ constructor(message: string, file: string, line?: number | undefined);
10
+ }
11
+ export declare function parseFeature(filePath: string): Promise<FeatureIR>;
12
+ export declare function parseFeatureSource(source: string, sourceFile: string): FeatureIR;
13
+ /**
14
+ * Substitute `<placeholder>` tokens in a step's text with values from an
15
+ * Examples row. Used by the orchestrator when expanding Scenario Outlines.
16
+ */
17
+ export declare function substituteOutlinePlaceholders(text: string, row: Record<string, string>): string;
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ /**
3
+ * Gherkin parser — wraps `@cucumber/gherkin` and produces a `FeatureIR`.
4
+ * See docs/ARCHITECTURE.md §2.
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.GherkinParseError = void 0;
41
+ exports.parseFeature = parseFeature;
42
+ exports.parseFeatureSource = parseFeatureSource;
43
+ exports.substituteOutlinePlaceholders = substituteOutlinePlaceholders;
44
+ const fs = __importStar(require("fs-extra"));
45
+ const gherkin_1 = require("@cucumber/gherkin");
46
+ const messages_1 = require("@cucumber/messages");
47
+ class GherkinParseError extends Error {
48
+ constructor(message, file, line) {
49
+ super(message);
50
+ this.file = file;
51
+ this.line = line;
52
+ this.name = "GherkinParseError";
53
+ }
54
+ }
55
+ exports.GherkinParseError = GherkinParseError;
56
+ async function parseFeature(filePath) {
57
+ if (!(await fs.pathExists(filePath))) {
58
+ throw new GherkinParseError(`Feature file not found: ${filePath}`, filePath);
59
+ }
60
+ const source = await fs.readFile(filePath, "utf8");
61
+ return parseFeatureSource(source, filePath);
62
+ }
63
+ function parseFeatureSource(source, sourceFile) {
64
+ const builder = new gherkin_1.AstBuilder(messages_1.IdGenerator.uuid());
65
+ const matcher = new gherkin_1.GherkinClassicTokenMatcher();
66
+ const parser = new gherkin_1.Parser(builder, matcher);
67
+ let document;
68
+ try {
69
+ document = parser.parse(source);
70
+ }
71
+ catch (err) {
72
+ throw new GherkinParseError(`Failed to parse Gherkin: ${err.message}`, sourceFile);
73
+ }
74
+ const feature = document.feature;
75
+ if (!feature) {
76
+ throw new GherkinParseError(`No Feature: declaration found in ${sourceFile}`, sourceFile);
77
+ }
78
+ const tags = (feature.tags ?? []).map((t) => t.name);
79
+ let background;
80
+ const scenarios = [];
81
+ for (const child of feature.children ?? []) {
82
+ if (child.background) {
83
+ background = child.background.steps.map(toStepIR);
84
+ }
85
+ else if (child.scenario) {
86
+ scenarios.push(toScenarioIR(child.scenario));
87
+ }
88
+ }
89
+ return {
90
+ name: feature.name ?? "(unnamed feature)",
91
+ description: feature.description?.trim() || undefined,
92
+ background,
93
+ scenarios,
94
+ tags,
95
+ sourceFile,
96
+ };
97
+ }
98
+ function toScenarioIR(scenario) {
99
+ const steps = (scenario.steps ?? []).map(toStepIR);
100
+ const tags = (scenario.tags ?? []).map((t) => t.name);
101
+ let examples;
102
+ // Scenario Outline → flatten Examples table into row records
103
+ if (scenario.examples && scenario.examples.length > 0) {
104
+ examples = [];
105
+ for (const block of scenario.examples) {
106
+ const headers = (block.tableHeader?.cells ?? []).map((c) => c.value);
107
+ for (const row of block.tableBody ?? []) {
108
+ const record = {};
109
+ const cells = row.cells ?? [];
110
+ headers.forEach((h, i) => {
111
+ record[h] = cells[i]?.value ?? "";
112
+ });
113
+ examples.push(record);
114
+ }
115
+ }
116
+ }
117
+ return {
118
+ name: scenario.name ?? "(unnamed scenario)",
119
+ steps,
120
+ examples,
121
+ tags,
122
+ };
123
+ }
124
+ function toStepIR(step) {
125
+ const keyword = String(step.keyword ?? "").trim();
126
+ const text = String(step.text ?? "").trim();
127
+ let argument;
128
+ if (step.docString) {
129
+ argument = String(step.docString.content ?? "");
130
+ }
131
+ else if (step.dataTable) {
132
+ argument = (step.dataTable.rows ?? []).map((r) => (r.cells ?? []).map((c) => String(c.value ?? "")));
133
+ }
134
+ return { keyword, text, argument };
135
+ }
136
+ /**
137
+ * Substitute `<placeholder>` tokens in a step's text with values from an
138
+ * Examples row. Used by the orchestrator when expanding Scenario Outlines.
139
+ */
140
+ function substituteOutlinePlaceholders(text, row) {
141
+ return text.replace(/<([^>]+)>/g, (_m, key) => row[key] ?? `<${key}>`);
142
+ }
143
+ //# sourceMappingURL=gherkinParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gherkinParser.js","sourceRoot":"","sources":["../../src/parser/gherkinParser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Project scaffolder — when the target repo lacks `playwright.config.ts`,
3
+ * delegates to `@vijaypjavvadi/pw-emit`'s `emitProject()` to copy templates
4
+ * into place so the emitted spec has a runnable home.
5
+ *
6
+ * `emitProject()` is itself idempotent — files that already exist are
7
+ * left untouched. So calling this on a repo that already has a config
8
+ * is a no-op + warnings.
9
+ */
10
+ import type { ReviewItem } from "../types";
11
+ export interface ScaffoldProjectOptions {
12
+ repoRoot: string;
13
+ templatesDir?: string;
14
+ /** Injected as `baseURL` in playwright.config.ts. */
15
+ baseUrl?: string;
16
+ /** Injected as `name` in package.json. */
17
+ projectName?: string;
18
+ }
19
+ export interface ScaffoldProjectResult {
20
+ filesWritten: string[];
21
+ alreadyExisted: boolean;
22
+ warnings: ReviewItem[];
23
+ }
24
+ export declare function scaffoldProject(opts: ScaffoldProjectOptions): Promise<ScaffoldProjectResult>;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * Project scaffolder — when the target repo lacks `playwright.config.ts`,
4
+ * delegates to `@vijaypjavvadi/pw-emit`'s `emitProject()` to copy templates
5
+ * into place so the emitted spec has a runnable home.
6
+ *
7
+ * `emitProject()` is itself idempotent — files that already exist are
8
+ * left untouched. So calling this on a repo that already has a config
9
+ * is a no-op + warnings.
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.scaffoldProject = scaffoldProject;
46
+ const fs = __importStar(require("fs-extra"));
47
+ const path = __importStar(require("path"));
48
+ const pw_emit_1 = require("@vijaypjavvadi/pw-emit");
49
+ async function scaffoldProject(opts) {
50
+ const configPath = path.join(opts.repoRoot, "playwright.config.ts");
51
+ const alreadyExisted = await fs.pathExists(configPath);
52
+ const result = await (0, pw_emit_1.emitProject)({
53
+ outDir: opts.repoRoot,
54
+ templatesDir: opts.templatesDir,
55
+ baseUrl: opts.baseUrl,
56
+ projectName: opts.projectName,
57
+ });
58
+ return {
59
+ filesWritten: result.filesWritten,
60
+ alreadyExisted,
61
+ warnings: result.warnings,
62
+ };
63
+ }
64
+ //# sourceMappingURL=projectScaffolder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectScaffolder.js","sourceRoot":"","sources":["../../src/repo/projectScaffolder.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBH,0CAkBC;AAtCD,6CAA+B;AAC/B,2CAA6B;AAC7B,oDAAqD;AAkB9C,KAAK,UAAU,eAAe,CACnC,IAA4B;IAE5B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC;IACpE,MAAM,cAAc,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IAEvD,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAW,EAAC;QAC/B,MAAM,EAAE,IAAI,CAAC,QAAQ;QACrB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,CAAC,CAAC;IAEH,OAAO;QACL,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,cAAc;QACd,QAAQ,EAAE,MAAM,CAAC,QAAQ;KAC1B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Repo scanner — reads `pages/*.page.ts` via `ts-morph` and builds a map of
3
+ * existing Page Object classes (fields + methods) so the resolver can
4
+ * decide reuse / augment / create.
5
+ *
6
+ * What it extracts per class:
7
+ * - className
8
+ * - field names + their initialiser locator expression (best-effort, so
9
+ * pomResolver can decide presence; not used for re-emission)
10
+ * - method names + parameter signatures + raw body
11
+ *
12
+ * Out of scope: detecting decorators, generic constraints, inherited fields.
13
+ * Augment-mode emission preserves whole methods verbatim (origin: 'existing').
14
+ */
15
+ import type { PageObjectIR } from "../types";
16
+ export interface RepoScanResult {
17
+ repoRoot: string;
18
+ hasPlaywrightConfig: boolean;
19
+ pageObjects: Map<string, PageObjectIR>;
20
+ }
21
+ export declare function scanRepo(repoRoot: string): Promise<RepoScanResult>;
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ /**
3
+ * Repo scanner — reads `pages/*.page.ts` via `ts-morph` and builds a map of
4
+ * existing Page Object classes (fields + methods) so the resolver can
5
+ * decide reuse / augment / create.
6
+ *
7
+ * What it extracts per class:
8
+ * - className
9
+ * - field names + their initialiser locator expression (best-effort, so
10
+ * pomResolver can decide presence; not used for re-emission)
11
+ * - method names + parameter signatures + raw body
12
+ *
13
+ * Out of scope: detecting decorators, generic constraints, inherited fields.
14
+ * Augment-mode emission preserves whole methods verbatim (origin: 'existing').
15
+ */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
28
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
29
+ }) : function(o, v) {
30
+ o["default"] = v;
31
+ });
32
+ var __importStar = (this && this.__importStar) || (function () {
33
+ var ownKeys = function(o) {
34
+ ownKeys = Object.getOwnPropertyNames || function (o) {
35
+ var ar = [];
36
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
37
+ return ar;
38
+ };
39
+ return ownKeys(o);
40
+ };
41
+ return function (mod) {
42
+ if (mod && mod.__esModule) return mod;
43
+ var result = {};
44
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
45
+ __setModuleDefault(result, mod);
46
+ return result;
47
+ };
48
+ })();
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.scanRepo = scanRepo;
51
+ const fs = __importStar(require("fs-extra"));
52
+ const path = __importStar(require("path"));
53
+ const ts_morph_1 = require("ts-morph");
54
+ async function scanRepo(repoRoot) {
55
+ const pageObjects = new Map();
56
+ const hasPlaywrightConfig = await fs.pathExists(path.join(repoRoot, "playwright.config.ts"));
57
+ const pagesDir = path.join(repoRoot, "pages");
58
+ if (!(await fs.pathExists(pagesDir))) {
59
+ return { repoRoot, hasPlaywrightConfig, pageObjects };
60
+ }
61
+ const project = new ts_morph_1.Project({
62
+ skipAddingFilesFromTsConfig: true,
63
+ compilerOptions: { allowJs: false, noEmit: true, target: 99 /* ESNext */ },
64
+ });
65
+ project.addSourceFilesAtPaths(path.join(pagesDir, "*.page.ts"));
66
+ for (const sourceFile of project.getSourceFiles()) {
67
+ const filePath = sourceFile.getFilePath().toString();
68
+ for (const cls of sourceFile.getClasses()) {
69
+ const className = cls.getName();
70
+ if (!className)
71
+ continue;
72
+ const fields = cls
73
+ .getProperties()
74
+ .filter((p) => {
75
+ const t = p.getType().getText();
76
+ // Heuristic: keep `Locator` typed fields; skip `Page`, `string`, etc.
77
+ return t.includes("Locator");
78
+ })
79
+ .map((p) => parseLocatorField(p.getName(), p.getInitializer()?.getText()));
80
+ const methods = cls.getMethods().map((m) => {
81
+ const params = m.getParameters().map((p) => ({
82
+ name: p.getName(),
83
+ type: p.getType().getText() || "unknown",
84
+ }));
85
+ const body = m.getBodyText() ?? "";
86
+ return {
87
+ name: m.getName(),
88
+ params,
89
+ body,
90
+ origin: "existing",
91
+ };
92
+ });
93
+ pageObjects.set(className, {
94
+ className,
95
+ filePath,
96
+ fields,
97
+ methods,
98
+ exists: true,
99
+ });
100
+ }
101
+ }
102
+ return { repoRoot, hasPlaywrightConfig, pageObjects };
103
+ }
104
+ /**
105
+ * Parse a class field initialiser like
106
+ * `page.getByRole('button', { name: 'Sign in' })`
107
+ * back into a `LocatorChoice`. Best-effort — we mainly need the
108
+ * `fieldName` for the resolver's "is this field present?" check; the
109
+ * `api`/`args` are advisory.
110
+ */
111
+ function parseLocatorField(fieldName, init) {
112
+ const fallback = {
113
+ api: "locator",
114
+ args: '""',
115
+ fieldName,
116
+ source: { tag: "" },
117
+ confidence: "fallback",
118
+ };
119
+ if (!init)
120
+ return fallback;
121
+ const m = init.match(/(?:this\.)?page\.(getByRole|getByLabel|getByPlaceholder|getByTestId|getByText|locator)\s*\(([\s\S]*)\)\s*;?\s*$/);
122
+ if (!m)
123
+ return fallback;
124
+ return {
125
+ api: m[1],
126
+ args: m[2].trim(),
127
+ fieldName,
128
+ source: { tag: "" },
129
+ confidence: "unique",
130
+ };
131
+ }
132
+ //# sourceMappingURL=repoScanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repoScanner.js","sourceRoot":"","sources":["../../src/repo/repoScanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Generate `BDD_REVIEW.md` — every warning, fallback, ambiguity, and
3
+ * `tsc --noEmit` diagnostic for human review. See docs/ARCHITECTURE.md §10.
4
+ *
5
+ * The report is the deliverable, not a clean compile (FR-12). Sections:
6
+ * 1. Summary (counts by severity)
7
+ * 2. Errors (block testing — fix first)
8
+ * 3. Warnings (likely correct but worth a glance)
9
+ * 4. Info (FYI, ambiguous locators, fallback steps, etc.)
10
+ * 5. Manual TODOs — every step that fell through to a TODO comment
11
+ */
12
+ import type { ReviewItem } from "../types";
13
+ export interface WriteReviewInput {
14
+ repoRoot: string;
15
+ items: ReviewItem[];
16
+ feature: string;
17
+ url: string;
18
+ }
19
+ export declare function writeReviewReport(input: WriteReviewInput): Promise<string>;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ /**
3
+ * Generate `BDD_REVIEW.md` — every warning, fallback, ambiguity, and
4
+ * `tsc --noEmit` diagnostic for human review. See docs/ARCHITECTURE.md §10.
5
+ *
6
+ * The report is the deliverable, not a clean compile (FR-12). Sections:
7
+ * 1. Summary (counts by severity)
8
+ * 2. Errors (block testing — fix first)
9
+ * 3. Warnings (likely correct but worth a glance)
10
+ * 4. Info (FYI, ambiguous locators, fallback steps, etc.)
11
+ * 5. Manual TODOs — every step that fell through to a TODO comment
12
+ */
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ exports.writeReviewReport = writeReviewReport;
48
+ const fs = __importStar(require("fs-extra"));
49
+ const path = __importStar(require("path"));
50
+ async function writeReviewReport(input) {
51
+ const lines = [];
52
+ const counts = {
53
+ error: input.items.filter((i) => i.severity === "error").length,
54
+ warn: input.items.filter((i) => i.severity === "warn").length,
55
+ info: input.items.filter((i) => i.severity === "info").length,
56
+ };
57
+ lines.push("# bdd2pw — Conversion Review");
58
+ lines.push("");
59
+ lines.push(`> **Source feature:** \`${input.feature}\``);
60
+ lines.push(`> **Target URL:** ${input.url}`);
61
+ lines.push(`> **Generated:** ${new Date().toISOString()}`);
62
+ lines.push("");
63
+ lines.push("## Summary");
64
+ lines.push("");
65
+ lines.push(`${counts.error} error${counts.error === 1 ? "" : "s"} · ${counts.warn} warning${counts.warn === 1 ? "" : "s"} · ${counts.info} info`);
66
+ lines.push("");
67
+ emitSection(lines, "Errors", "error", input.items);
68
+ emitSection(lines, "Warnings", "warn", input.items);
69
+ emitSection(lines, "Info", "info", input.items);
70
+ if (input.items.length === 0) {
71
+ lines.push("Nothing to review — everything mapped cleanly. Run `npx playwright test` to verify.");
72
+ lines.push("");
73
+ }
74
+ const outPath = path.join(input.repoRoot, "BDD_REVIEW.md");
75
+ await fs.ensureDir(input.repoRoot);
76
+ await fs.writeFile(outPath, lines.join("\n"), "utf8");
77
+ return outPath;
78
+ }
79
+ function emitSection(lines, heading, severity, items) {
80
+ const filtered = items.filter((i) => i.severity === severity);
81
+ if (filtered.length === 0)
82
+ return;
83
+ lines.push(`## ${heading}`);
84
+ lines.push("");
85
+ for (const item of filtered) {
86
+ const loc = item.file !== undefined
87
+ ? `\`${item.file}${item.line ? `:${item.line}` : ""}\` — `
88
+ : "";
89
+ lines.push(`- ${loc}${item.message}`);
90
+ if (item.suggestion) {
91
+ lines.push(` - Suggestion: ${item.suggestion}`);
92
+ }
93
+ }
94
+ lines.push("");
95
+ }
96
+ //# sourceMappingURL=reviewReport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewReport.js","sourceRoot":"","sources":["../../src/reports/reviewReport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * HTTP service entry. Express :4300. See docs/SCOPE.md §8b
3
+ * and docs/ARCHITECTURE.md §3 + §6.
4
+ */
5
+ import { Application } from "express";
6
+ export interface ServerOptions {
7
+ port?: number;
8
+ }
9
+ export declare function createApp(): Application;
10
+ export declare function startServer(opts?: ServerOptions): Promise<void>;
package/dist/server.js ADDED
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /**
3
+ * HTTP service entry. Express :4300. See docs/SCOPE.md §8b
4
+ * and docs/ARCHITECTURE.md §3 + §6.
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.createApp = createApp;
11
+ exports.startServer = startServer;
12
+ const express_1 = __importDefault(require("express"));
13
+ const logger_1 = require("./utils/logger");
14
+ const routes_1 = require("./http/routes");
15
+ function createApp() {
16
+ const app = (0, express_1.default)();
17
+ app.use(express_1.default.json({ limit: "1mb" }));
18
+ app.get("/healthz", (_req, res) => res.json({ ok: true }));
19
+ app.get("/version", (_req, res) => res.json({
20
+ name: "@vijaypjavvadi/bdd2pw",
21
+ version: require("../package.json").version,
22
+ commit: process.env.GIT_COMMIT ?? "unknown",
23
+ }));
24
+ (0, routes_1.mountRoutes)(app);
25
+ return app;
26
+ }
27
+ async function startServer(opts = {}) {
28
+ const port = opts.port ?? 4300;
29
+ const app = createApp();
30
+ app.listen(port, () => {
31
+ logger_1.logger.info({ port }, "bdd2pw HTTP service listening");
32
+ });
33
+ }
34
+ if (require.main === module) {
35
+ startServer({ port: Number(process.env.PORT ?? 4300) }).catch((err) => {
36
+ logger_1.logger.error({ err }, "server failed to start");
37
+ process.exit(1);
38
+ });
39
+ }
40
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;AAUH,8BAgBC;AAED,kCAMC;AAhCD,sDAA+C;AAC/C,2CAAwC;AACxC,0CAA4C;AAM5C,SAAgB,SAAS;IACvB,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAExC,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3D,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAChC,GAAG,CAAC,IAAI,CAAC;QACP,IAAI,EAAE,uBAAuB;QAC7B,OAAO,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO;QAC3C,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,SAAS;KAC5C,CAAC,CACH,CAAC;IAEF,IAAA,oBAAW,EAAC,GAAG,CAAC,CAAC;IAEjB,OAAO,GAAG,CAAC;AACb,CAAC;AAEM,KAAK,UAAU,WAAW,CAAC,OAAsB,EAAE;IACxD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;IAC/B,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;QACpB,eAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,+BAA+B,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACpE,eAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,wBAAwB,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Locator picker — given an `ElementIR` and the full snapshot context,
3
+ * choose the most stable Playwright locator API.
4
+ *
5
+ * Priority chain (FR-6):
6
+ * getByRole > getByLabel > getByPlaceholder > getByTestId > getByText
7
+ * > locator(css) > locator(xpath)
8
+ *
9
+ * The output `LocatorChoice` is then handed to `@vijaypjavvadi/pw-emit`
10
+ * which renders it into TS. We never render here.
11
+ */
12
+ import type { ElementIR, LocatorChoice } from "../types";
13
+ /**
14
+ * Pick the highest-priority unique locator for `element`. Uniqueness is
15
+ * checked against `allElements` — if multiple elements would match the
16
+ * candidate locator, we fall down the chain.
17
+ */
18
+ export declare function pickLocator(element: ElementIR, allElements: ElementIR[]): LocatorChoice;
19
+ /**
20
+ * Dedupe a list of locator choices.
21
+ *
22
+ * 1. **By locator identity (api + args).** When two `LocatorChoice`s
23
+ * resolve to the same Playwright call (e.g. a `<label>` and the
24
+ * `<input>` it labels both produce `getByLabel('Username')`), keep
25
+ * only the FIRST. Without this, the live scanner emits
26
+ * `username = page.getByLabel('Username')` AND
27
+ * `usernameInput = page.getByLabel('Username')` — two fields, one
28
+ * element, confusing for tests and noisy in the POM.
29
+ * The kept entry uses the LONGER fieldName when names differ —
30
+ * `usernameInput` is more useful than `username` because the
31
+ * step-matcher's preferredSuffix logic looks for `*Input`.
32
+ *
33
+ * 2. **By field name collision.** If two elements legitimately deserve
34
+ * different `LocatorChoice`s but happen to synthesise the same
35
+ * fieldName, suffix the later one with `2`, `3`, … so emitted TS
36
+ * stays valid.
37
+ *
38
+ * Stable-sorted by input order otherwise.
39
+ */
40
+ export declare function dedupeLocators(choices: LocatorChoice[]): LocatorChoice[];