playwright-cucumber-ts-steps 1.1.7 → 1.1.9

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 (89) hide show
  1. package/LICENSE +21 -0
  2. package/dist/backend/actions/click.d.ts +133 -0
  3. package/dist/backend/actions/click.d.ts.map +1 -0
  4. package/dist/backend/actions/click.js +248 -0
  5. package/dist/backend/actions/find.d.ts +244 -0
  6. package/dist/backend/actions/find.d.ts.map +1 -0
  7. package/dist/backend/actions/find.js +415 -0
  8. package/dist/backend/actions/form.d.ts +38 -0
  9. package/dist/backend/actions/form.d.ts.map +1 -0
  10. package/dist/backend/actions/form.js +237 -0
  11. package/dist/backend/actions/formTable.d.ts +25 -1
  12. package/dist/backend/actions/formTable.d.ts.map +1 -1
  13. package/dist/backend/actions/formTable.js +27 -5
  14. package/dist/backend/actions/frames.d.ts +54 -0
  15. package/dist/backend/actions/frames.d.ts.map +1 -0
  16. package/dist/backend/actions/frames.js +97 -0
  17. package/dist/backend/actions/index.d.ts +10 -0
  18. package/dist/backend/actions/index.d.ts.map +1 -1
  19. package/dist/backend/actions/index.js +10 -0
  20. package/dist/backend/actions/inputs.d.ts +140 -0
  21. package/dist/backend/actions/inputs.d.ts.map +1 -0
  22. package/dist/backend/actions/inputs.js +247 -0
  23. package/dist/backend/actions/interactions.d.ts +61 -1
  24. package/dist/backend/actions/interactions.d.ts.map +1 -1
  25. package/dist/backend/actions/interactions.js +65 -10
  26. package/dist/backend/actions/keyboard.d.ts +71 -0
  27. package/dist/backend/actions/keyboard.d.ts.map +1 -0
  28. package/dist/backend/actions/keyboard.js +98 -0
  29. package/dist/backend/actions/misc.d.ts +135 -0
  30. package/dist/backend/actions/misc.d.ts.map +1 -0
  31. package/dist/backend/actions/misc.js +208 -0
  32. package/dist/backend/actions/mobile.d.ts +75 -0
  33. package/dist/backend/actions/mobile.d.ts.map +1 -0
  34. package/dist/backend/actions/mobile.js +148 -0
  35. package/dist/backend/actions/mouse.d.ts +80 -0
  36. package/dist/backend/actions/mouse.d.ts.map +1 -0
  37. package/dist/backend/actions/mouse.js +150 -0
  38. package/dist/backend/actions/navigation.d.ts +48 -1
  39. package/dist/backend/actions/navigation.d.ts.map +1 -1
  40. package/dist/backend/actions/navigation.js +61 -10
  41. package/dist/backend/actions/waits.d.ts +56 -0
  42. package/dist/backend/actions/waits.d.ts.map +1 -0
  43. package/dist/backend/actions/waits.js +83 -0
  44. package/dist/backend/api/assertions.d.ts +32 -1
  45. package/dist/backend/api/assertions.d.ts.map +1 -1
  46. package/dist/backend/api/assertions.js +37 -9
  47. package/dist/backend/api/index.d.ts +1 -0
  48. package/dist/backend/api/index.d.ts.map +1 -1
  49. package/dist/backend/api/index.js +1 -0
  50. package/dist/backend/api/mock.d.ts +34 -1
  51. package/dist/backend/api/mock.d.ts.map +1 -1
  52. package/dist/backend/api/mock.js +37 -10
  53. package/dist/backend/api/network.d.ts +61 -0
  54. package/dist/backend/api/network.d.ts.map +1 -0
  55. package/dist/backend/api/network.js +177 -0
  56. package/dist/backend/api/requests.d.ts +43 -1
  57. package/dist/backend/api/requests.d.ts.map +1 -1
  58. package/dist/backend/api/requests.js +49 -17
  59. package/dist/backend/assertions/pageState.d.ts +40 -1
  60. package/dist/backend/assertions/pageState.d.ts.map +1 -1
  61. package/dist/backend/assertions/pageState.js +48 -16
  62. package/dist/backend/assertions/text.d.ts +46 -1
  63. package/dist/backend/assertions/text.d.ts.map +1 -1
  64. package/dist/backend/assertions/text.js +51 -8
  65. package/dist/backend/assertions/visibility.d.ts +102 -1
  66. package/dist/backend/assertions/visibility.d.ts.map +1 -1
  67. package/dist/backend/assertions/visibility.js +166 -12
  68. package/dist/backend/auth/index.js +2 -2
  69. package/dist/backend/db/steps.d.ts +35 -1
  70. package/dist/backend/db/steps.d.ts.map +1 -1
  71. package/dist/backend/db/steps.js +48 -15
  72. package/dist/backend/elements/alerts.d.ts +35 -1
  73. package/dist/backend/elements/alerts.d.ts.map +1 -1
  74. package/dist/backend/elements/alerts.js +39 -6
  75. package/dist/backend/elements/forms.d.ts +44 -1
  76. package/dist/backend/elements/forms.d.ts.map +1 -1
  77. package/dist/backend/elements/forms.js +50 -11
  78. package/dist/backend/elements/frames.d.ts +36 -1
  79. package/dist/backend/elements/frames.d.ts.map +1 -1
  80. package/dist/backend/elements/frames.js +43 -13
  81. package/dist/backend/utils/state.d.ts +18 -0
  82. package/dist/backend/utils/state.d.ts.map +1 -0
  83. package/dist/backend/utils/state.js +84 -0
  84. package/dist/core/registry.d.ts +14 -14
  85. package/dist/core/registry.d.ts.map +1 -1
  86. package/dist/core/registry.js +13 -4
  87. package/dist/core/runner.d.ts.map +1 -1
  88. package/dist/core/runner.js +91 -38
  89. package/package.json +52 -12
@@ -5,13 +5,22 @@ exports.Step = Step;
5
5
  // src/core/registry.ts
6
6
  const cucumber_expressions_1 = require("@cucumber/cucumber-expressions");
7
7
  // 2. The Global Registry
8
- // This array stores every step you create in the backend folder
9
8
  exports.stepRegistry = [];
10
9
  const parameterTypeRegistry = new cucumber_expressions_1.ParameterTypeRegistry();
11
- // 3. The Function to Register Steps
12
- // You will use this inside src/backend/actions/...
10
+ /**
11
+ * 3. The Function to Register Steps
12
+ * Supports passing a string (converted to CucumberExpression) OR a direct RegExp.
13
+ */
13
14
  function Step(pattern, fn) {
14
- const expression = new cucumber_expressions_1.CucumberExpression(pattern, parameterTypeRegistry);
15
+ let expression;
16
+ if (pattern instanceof RegExp) {
17
+ // ✅ If it's a Regex, use it directly (Faster, Standard)
18
+ expression = pattern;
19
+ }
20
+ else {
21
+ // ⚠️ If it's a String, convert to Cucumber Expression (Legacy)
22
+ expression = new cucumber_expressions_1.CucumberExpression(pattern, parameterTypeRegistry);
23
+ }
15
24
  exports.stepRegistry.push({
16
25
  expression,
17
26
  fn,
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"AAMA,OAAO,0BAA0B,CAAC;AAClC,OAAO,6BAA6B,CAAC;AACrC,OAAO,2BAA2B,CAAC;AACnC,OAAO,sBAAsB,CAAC;AAC9B,OAAO,uBAAuB,CAAC;AAE/B,OAAO,qBAAqB,CAAC;AAE7B,UAAU,aAAa;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CAC3C;AAED,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,QAyIpE"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"AAMA,OAAO,0BAA0B,CAAC;AAClC,OAAO,6BAA6B,CAAC;AACrC,OAAO,2BAA2B,CAAC;AACnC,OAAO,sBAAsB,CAAC;AAC9B,OAAO,uBAAuB,CAAC;AAE/B,OAAO,qBAAqB,CAAC;AAE7B,UAAU,aAAa;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CAC3C;AASD,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,QA4LpE"}
@@ -34,8 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.runTests = runTests;
37
- const test_1 = require("@playwright/test");
38
37
  const fs = __importStar(require("fs"));
38
+ const test_1 = require("@playwright/test");
39
39
  const glob_1 = require("glob");
40
40
  const registry_1 = require("./registry");
41
41
  // LOAD BACKEND LIBRARIES
@@ -58,7 +58,6 @@ function runTests(featureGlob, options) {
58
58
  for (const file of files) {
59
59
  const content = fs.readFileSync(file, "utf8");
60
60
  // 1. CAPTURE FEATURE TAGS
61
- // Loose Regex: Matches any lines starting with @ above "Feature:"
62
61
  const featureTagMatch = content.match(/((?:@\S+\s*)+)Feature:/);
63
62
  const rawFeatureTags = featureTagMatch ? featureTagMatch[1] : "";
64
63
  const featureTags = rawFeatureTags.replace(/[\r\n]+/g, " ").trim();
@@ -67,9 +66,8 @@ function runTests(featureGlob, options) {
67
66
  ? featureMatch[1].trim()
68
67
  : "Unnamed Feature";
69
68
  test_1.test.describe(featureName, () => {
70
- // 2. LOOSE SCENARIO REGEX
69
+ // 2. SCENARIO REGEX
71
70
  // Matches: Optional Tags -> (Scenario OR Scenario Outline) -> Name
72
- // Change: @\S+ allows dots/dashes. "Scenario|Scenario Outline" supports outlines.
73
71
  const scenarioRegex = /(?:((?:@\S+\s*)+))?(?:Scenario|Scenario Outline):\s*(.+)/g;
74
72
  let match;
75
73
  let foundCount = 0;
@@ -83,9 +81,7 @@ function runTests(featureGlob, options) {
83
81
  const fullName = combinedTags
84
82
  ? `${scenarioName} ${combinedTags}`
85
83
  : scenarioName;
86
- // DEBUG LOG: See what we found
87
- // console.log(` 👉 Found: "${scenarioName}" [Tags: ${combinedTags}]`);
88
- // 4. ENV FILTERING (Optional)
84
+ // 4. ENV FILTERING
89
85
  const activeFilter = options?.tags || envTag;
90
86
  if (activeFilter) {
91
87
  const targetGroups = activeFilter.split(",").map((t) => t.trim());
@@ -96,6 +92,7 @@ function runTests(featureGlob, options) {
96
92
  if (!isMatch)
97
93
  continue;
98
94
  }
95
+ // 5. EXTRACT SCENARIO BLOCK
99
96
  const startIndex = match.index + match[0].length;
100
97
  const nextMatchIndex = content
101
98
  .slice(startIndex)
@@ -103,43 +100,90 @@ function runTests(featureGlob, options) {
103
100
  const blockEnd = nextMatchIndex === -1 ? content.length : startIndex + nextMatchIndex;
104
101
  const scenarioBlock = content.slice(startIndex, blockEnd);
105
102
  (0, test_1.test)(fullName, async ({ page }, testInfo) => {
106
- const lines = scenarioBlock
107
- .trim()
108
- .split("\n")
109
- .map((l) => l.trim())
110
- .filter((l) => l);
111
- for (let i = 0; i < lines.length; i++) {
112
- const stepText = lines[i];
113
- if (stepText.startsWith("#") ||
114
- stepText.startsWith("@") ||
115
- stepText === "")
103
+ // ==================================================
104
+ // PHASE 1: PARSE GHERKIN (Preserve Formatting)
105
+ // ==================================================
106
+ const rawLines = scenarioBlock.split("\n");
107
+ const steps = [];
108
+ let currentStep = null;
109
+ let docStringBuffer = [];
110
+ let isDocStringOpen = false;
111
+ for (let line of rawLines) {
112
+ const trimmedLine = line.trim();
113
+ // A. Handle DocStrings (""")
114
+ if (trimmedLine.startsWith('"""')) {
115
+ isDocStringOpen = !isDocStringOpen;
116
+ if (!isDocStringOpen && currentStep) {
117
+ // Closing DocString: Save buffer to step
118
+ currentStep.docString = docStringBuffer.join("\n");
119
+ docStringBuffer = [];
120
+ }
121
+ continue;
122
+ }
123
+ // B. Inside DocString? Capture raw line (preserve indent)
124
+ if (isDocStringOpen) {
125
+ docStringBuffer.push(line); // Don't trim!
126
+ continue;
127
+ }
128
+ // C. Skip Empty Lines & Comments
129
+ if (!trimmedLine ||
130
+ trimmedLine.startsWith("#") ||
131
+ trimmedLine.startsWith("@")) {
116
132
  continue;
117
- // Handle Data Tables
118
- const tableData = [];
119
- while (i + 1 < lines.length && lines[i + 1].startsWith("|")) {
120
- i++;
121
- const row = lines[i]
122
- .split("|")
123
- .map((cell) => cell.trim())
124
- .filter((cell) => cell !== "");
125
- tableData.push(row);
126
133
  }
127
- const cleanStep = stepText
134
+ // D. Handle Data Tables (| col | col |)
135
+ if (trimmedLine.startsWith("|")) {
136
+ if (currentStep) {
137
+ if (!currentStep.dataTable)
138
+ currentStep.dataTable = [];
139
+ const row = trimmedLine;
140
+ const _row = trimmedLine.split("|").map((cell) => cell.trim())
141
+ .filter((cell, index, arr) => index > 0 && index < arr.length - 1);
142
+ // Simple split often leaves empty strings at start/end
143
+ // Better split logic:
144
+ const cleanRow = trimmedLine
145
+ .split("|")
146
+ .slice(1, -1) // Remove first and last empty splits from |...|
147
+ .map((c) => c.trim());
148
+ currentStep.dataTable.push(cleanRow);
149
+ }
150
+ continue;
151
+ }
152
+ // E. It is a New Step
153
+ const cleanText = trimmedLine
128
154
  .replace(/^(Given|When|Then|And|But)\s+/i, "")
129
- .replace(/:$/, "")
155
+ .replace(/:$/, "") // Remove trailing colon
130
156
  .trim();
131
- const matchResult = findMatchingStep(cleanStep);
157
+ currentStep = {
158
+ text: trimmedLine,
159
+ cleanText: cleanText,
160
+ };
161
+ steps.push(currentStep);
162
+ }
163
+ // ==================================================
164
+ // PHASE 2: EXECUTE STEPS
165
+ // ==================================================
166
+ console.log(`\n🔹 Scenario: ${scenarioName}`);
167
+ for (const step of steps) {
168
+ const matchResult = findMatchingStep(step.cleanText);
132
169
  if (!matchResult) {
133
- throw new Error(`❌ Undefined Step: "${cleanStep}"`);
170
+ throw new Error(`❌ Undefined Step: "${step.cleanText}"`);
134
171
  }
135
172
  try {
173
+ console.log(` executing: ${step.text.trim()}`);
136
174
  const args = [...matchResult.args];
137
- if (tableData.length > 0)
138
- args.push(tableData);
175
+ // Append Data Table if present
176
+ if (step.dataTable && step.dataTable.length > 0) {
177
+ args.push(step.dataTable);
178
+ }
179
+ // Append DocString if present
180
+ if (step.docString) {
181
+ args.push(step.docString);
182
+ }
139
183
  await matchResult.fn(page, ...args);
140
184
  }
141
185
  catch (error) {
142
- console.error(`❌ Failed at step: "${stepText}"`);
186
+ console.error(`❌ Failed at step: "${step.text.trim()}"`);
143
187
  const screenshot = await page.screenshot({
144
188
  fullPage: true,
145
189
  type: "png",
@@ -153,32 +197,41 @@ function runTests(featureGlob, options) {
153
197
  }
154
198
  });
155
199
  }
156
- // SAFETY CHECK
157
200
  if (foundCount === 0) {
158
201
  console.warn(`⚠️ File matched but 0 Scenarios found in: ${file}`);
159
- console.warn(` Check if you are using 'Scenario Outline' or strange spacing.`);
160
202
  }
161
203
  });
162
204
  }
163
205
  }
206
+ /**
207
+ * Finds the matching step definition from the registry.
208
+ * Supports: RegExp (with capture groups) and CucumberExpressions.
209
+ */
164
210
  function findMatchingStep(text) {
165
211
  for (const step of registry_1.stepRegistry) {
212
+ // 1. RegExp Match
166
213
  if (step.expression instanceof RegExp) {
167
214
  const match = step.expression.exec(text);
168
- if (match)
215
+ if (match) {
216
+ // match[0] is full string, slice(1) are capture groups
169
217
  return { fn: step.fn, args: match.slice(1) };
218
+ }
170
219
  }
220
+ // 2. Cucumber Expression Match (if strictly typed)
171
221
  else if (typeof step.expression.match === "function") {
172
222
  const match = step.expression.match(text);
173
- if (match)
223
+ if (match) {
174
224
  return {
175
225
  fn: step.fn,
176
226
  args: match.map((arg) => arg.getValue(null)),
177
227
  };
228
+ }
178
229
  }
230
+ // 3. String Match (Legacy/Simple)
179
231
  else if (typeof step.expression === "string") {
180
- if (step.expression === text)
232
+ if (step.expression === text) {
181
233
  return { fn: step.fn, args: [] };
234
+ }
182
235
  }
183
236
  }
184
237
  return null;
package/package.json CHANGED
@@ -1,23 +1,40 @@
1
1
  {
2
2
  "name": "playwright-cucumber-ts-steps",
3
3
  "description": "A collection of reusable Playwright step definitions for Cucumber in TypeScript, designed to streamline end-to-end testing across web, API, and mobile applications.",
4
- "version": "1.1.7",
4
+ "version": "1.1.9",
5
5
  "private": false,
6
6
  "main": "dist/index.js",
7
+ "type": "module",
7
8
  "types": "dist/index.d.ts",
8
- "files": [
9
- "dist"
10
- ],
11
9
  "scripts": {
12
10
  "build": "tsc",
13
11
  "docs": "typedoc",
14
- "prepublishOnly": "npm run build"
12
+ "docs:deploy": "gh-pages -d temp-docs",
13
+ "prepublishOnly": "npm run build",
14
+ "prepare": "husky",
15
+ "playwright:ui": "npx playwright test --headed",
16
+ "lint": "eslint .",
17
+ "lint:fix": "eslint . --ext .ts --fix",
18
+ "lint:tsc": "tsc --noEmit",
19
+ "format": "prettier --write .",
20
+ "clean": "rm -rf playwright/report",
21
+ "test": "npx playwright test demoTest.spec.ts",
22
+ "commitlint": "commitlint --edit"
15
23
  },
16
- "dependencies": {
17
- "@cucumber/cucumber-expressions": "^17.1.0",
18
- "@cucumber/gherkin": "^27.0.0",
19
- "@cucumber/messages": "^22.0.0",
20
- "glob": "^10.3.10"
24
+ "lint-staged": {
25
+ "*.{js,ts,tsx,json,md}": [
26
+ "eslint --fix",
27
+ "prettier --write"
28
+ ]
29
+ },
30
+ "commitlint": {
31
+ "extends": [
32
+ "@commitlint/config-conventional"
33
+ ]
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/qaPaschalE/playwright-cucumber-ts-steps.git"
21
38
  },
22
39
  "keywords": [
23
40
  "playwright",
@@ -44,13 +61,36 @@
44
61
  },
45
62
  "homepage": "https://github.com/qaPaschalE/playwright-cucumber-ts-steps#readme",
46
63
  "peerDependencies": {
47
- "@playwright/test": "^1.0.0"
64
+ "@playwright/test": "*"
48
65
  },
66
+ "files": [
67
+ "dist/"
68
+ ],
49
69
  "devDependencies": {
70
+ "@commitlint/cli": "^19.8.1",
71
+ "@commitlint/config-conventional": "^19.8.1",
50
72
  "@playwright/test": "^1.41.0",
51
73
  "@types/glob": "^8.1.0",
52
74
  "@types/node": "^20.11.0",
75
+ "@types/pngjs": "^6.0.5",
76
+ "@typescript-eslint/eslint-plugin": "^8.52.0",
77
+ "@typescript-eslint/parser": "^8.52.0",
78
+ "eslint": "^9.29.0",
79
+ "eslint-config-prettier": "^10.1.5",
80
+ "eslint-plugin-import": "^2.32.0",
81
+ "gh-pages": "^6.3.0",
82
+ "husky": "^9.1.7",
83
+ "lint-staged": "^16.1.2",
84
+ "prettier": "^3.5.3",
85
+ "ts-node": "^10.9.2",
53
86
  "typedoc": "^0.28.15",
54
- "typescript": "^5.3.3"
87
+ "typescript": "^5.3.3",
88
+ "typescript-eslint": "^8.34.1"
89
+ },
90
+ "dependencies": {
91
+ "@cucumber/cucumber-expressions": "^17.1.0",
92
+ "@cucumber/gherkin": "^27.0.0",
93
+ "@cucumber/messages": "^22.0.0",
94
+ "glob": "^10.3.10"
55
95
  }
56
96
  }