playwright-cucumber-ts-steps 1.1.1 → 1.1.3

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 CHANGED
@@ -95,30 +95,39 @@ Feature: User Authentication
95
95
  ### 4. Run Tests
96
96
 
97
97
  ```bash
98
- npx playwright test demo.spec.ts --headed
98
+ npx playwright test
99
99
  ```
100
100
 
101
101
  ---
102
102
 
103
- ## 🏷️ Tags & Filtering
103
+ ## 🏷️ Tag Filtering (New!)
104
104
 
105
- You can use tags to organize your tests and run specific subsets (e.g., only Smoke tests).
105
+ We support a **Friendly Syntax** for filtering tests via the `TAGS` environment variable.
106
106
 
107
- **In your Feature file:**
107
+ | Logic | Symbol | Example | Description |
108
+ | ------- | ------- | ---------------- | ----------------------------------------------------- |
109
+ | **OR** | `,` | `@login,@signup` | Run tests that have `@login` **OR** `@signup`. |
110
+ | **AND** | `+` | `@smoke+@api` | Run tests that have **BOTH** `@smoke` **AND** `@api`. |
111
+ | **MIX** | `,` `+` | `@a+@b, @c` | Run tests with (`@a` AND `@b`) **OR** just `@c`. |
108
112
 
109
- ```gherkin
110
- Feature: Checkout
113
+ **Usage:**
114
+
115
+ ```bash
116
+ # Run only smoke tests
117
+ TAGS='@smoke' npx playwright test
111
118
 
112
- @smoke @critical
113
- Scenario: Guest Checkout
114
- ...
119
+ # Run smoke tests that are also critical
120
+ TAGS='@smoke+@critical' npx playwright test
115
121
 
116
- @regression
117
- Scenario: Registered User Checkout
118
- ...
122
+ # Run login OR regression tests
123
+ TAGS='@login,@regression' npx playwright test
119
124
 
120
125
  ```
121
126
 
127
+ _(On Windows PowerShell, use `$env:TAGS="@smoke"; npx playwright test`)_
128
+
129
+ ````
130
+
122
131
  **In your Test Runner (`tests/bdd.spec.ts`):**
123
132
 
124
133
  ```typescript
@@ -129,7 +138,7 @@ import { runTests } from "playwright-cucumber-ts-steps";
129
138
 
130
139
  // OPTION 2: Run only Smoke tests
131
140
  runTests("features/*.feature", { tags: "@smoke" });
132
- ```
141
+ ````
133
142
 
134
143
  ---
135
144
 
@@ -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,QAmGpE"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"AAOA,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,QAqHpE"}
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.runTests = runTests;
37
+ // src/core/runner.ts
37
38
  const test_1 = require("@playwright/test");
38
39
  const fs = __importStar(require("fs"));
39
40
  const glob_1 = require("glob");
@@ -51,6 +52,7 @@ function runTests(featureGlob, options) {
51
52
  state_1.dbState.setAdapter(options.dbQuery);
52
53
  }
53
54
  const files = (0, glob_1.globSync)(featureGlob);
55
+ const envTag = process.env.TAGS;
54
56
  if (files.length === 0) {
55
57
  console.warn(`⚠️ No feature files found for pattern: ${featureGlob}`);
56
58
  }
@@ -61,20 +63,36 @@ function runTests(featureGlob, options) {
61
63
  ? featureMatch[1].trim()
62
64
  : "Unnamed Feature";
63
65
  test_1.test.describe(featureName, () => {
64
- // 1. SAFE SCENARIO REGEX
65
66
  const scenarioRegex = /(?:(@[^:\r\n]+)\s+)?Scenario:\s*(.+)/g;
66
67
  let match;
67
68
  while ((match = scenarioRegex.exec(content)) !== null) {
68
69
  const foundTags = match[1] || "";
69
70
  const scenarioName = match[2].trim();
71
+ // 1. APPEND TAGS TO TITLE (Keeps -g working if needed)
72
+ const fullName = foundTags
73
+ ? `${scenarioName} ${foundTags}`
74
+ : scenarioName;
75
+ // 2. 🔥 FRIENDLY TAG FILTERING 🔥
76
+ const activeFilter = options?.tags || envTag;
77
+ if (activeFilter) {
78
+ // Logic:
79
+ // ',' = OR (e.g., "@login, @smoke")
80
+ // '+' = AND (e.g., "@login+@smoke")
81
+ const orGroups = activeFilter.split(","); // Split into OR groups
82
+ const isMatch = orGroups.some((group) => {
83
+ // For each group, check if ALL tags (joined by +) are present
84
+ const andTags = group.split("+").map((t) => t.trim());
85
+ return andTags.every((t) => foundTags.includes(t));
86
+ });
87
+ if (!isMatch) {
88
+ continue; // Skip if no groups matched
89
+ }
90
+ }
70
91
  const startIndex = match.index + match[0].length;
71
92
  const nextMatchIndex = content.slice(startIndex).search(/Scenario:/);
72
93
  const blockEnd = nextMatchIndex === -1 ? content.length : startIndex + nextMatchIndex;
73
94
  const scenarioBlock = content.slice(startIndex, blockEnd);
74
- if (options?.tags && !foundTags.includes(options.tags)) {
75
- continue;
76
- }
77
- (0, test_1.test)(scenarioName, async ({ page }, testInfo) => {
95
+ (0, test_1.test)(fullName, async ({ page }, testInfo) => {
78
96
  const lines = scenarioBlock
79
97
  .trim()
80
98
  .split("\n")
@@ -86,7 +104,6 @@ function runTests(featureGlob, options) {
86
104
  stepText.startsWith("@") ||
87
105
  stepText === "")
88
106
  continue;
89
- // Handle Data Tables
90
107
  const tableData = [];
91
108
  while (i + 1 < lines.length && lines[i + 1].startsWith("|")) {
92
109
  i++;
@@ -100,7 +117,6 @@ function runTests(featureGlob, options) {
100
117
  .replace(/^(Given|When|Then|And|But)\s+/i, "")
101
118
  .replace(/:$/, "")
102
119
  .trim();
103
- // 2. SMART STEP MATCHING
104
120
  const matchResult = findMatchingStep(cleanStep);
105
121
  if (!matchResult) {
106
122
  throw new Error(`❌ Undefined Step: "${cleanStep}"`);
@@ -109,7 +125,6 @@ function runTests(featureGlob, options) {
109
125
  const args = [...matchResult.args];
110
126
  if (tableData.length > 0)
111
127
  args.push(tableData);
112
- console.log(`✅ Executing: ${cleanStep}`);
113
128
  await matchResult.fn(page, ...args);
114
129
  }
115
130
  catch (error) {
@@ -130,32 +145,24 @@ function runTests(featureGlob, options) {
130
145
  });
131
146
  }
132
147
  }
133
- // 3. ROBUST FINDER FUNCTION
134
148
  function findMatchingStep(text) {
135
149
  for (const step of registry_1.stepRegistry) {
136
- // A. Handle RegExp (If you defined steps with Regex)
137
150
  if (step.expression instanceof RegExp) {
138
151
  const match = step.expression.exec(text);
139
- if (match) {
152
+ if (match)
140
153
  return { fn: step.fn, args: match.slice(1) };
141
- }
142
154
  }
143
- // B. Handle Cucumber Expressions (The Objects we saw in your logs)
144
- // We check if it has a .match() method
145
155
  else if (typeof step.expression.match === "function") {
146
156
  const match = step.expression.match(text);
147
- if (match) {
157
+ if (match)
148
158
  return {
149
159
  fn: step.fn,
150
160
  args: match.map((arg) => arg.getValue(null)),
151
161
  };
152
- }
153
162
  }
154
- // C. Handle Simple Strings
155
163
  else if (typeof step.expression === "string") {
156
- if (step.expression === text) {
164
+ if (step.expression === text)
157
165
  return { fn: step.fn, args: [] };
158
- }
159
166
  }
160
167
  }
161
168
  return null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
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.1",
4
+ "version": "1.1.3",
5
5
  "private": false,
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",