playwright-cucumber-ts-steps 1.0.1 → 1.0.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.
Files changed (132) hide show
  1. package/README.md +195 -256
  2. package/dist/backend/actions/index.js +4 -0
  3. package/dist/backend/actions/interactions.js +23 -0
  4. package/dist/backend/actions/navigation.js +19 -0
  5. package/dist/backend/api/assertions.js +26 -0
  6. package/dist/backend/api/index.js +4 -0
  7. package/dist/backend/api/requests.js +24 -0
  8. package/dist/backend/api/state.js +15 -0
  9. package/dist/backend/assertions/expectVisible.js +8 -0
  10. package/dist/backend/assertions/index.js +5 -0
  11. package/dist/backend/assertions/pageState.js +25 -0
  12. package/dist/backend/assertions/text.js +20 -0
  13. package/dist/backend/assertions/visibility.js +20 -0
  14. package/dist/backend/auth/index.js +71 -0
  15. package/dist/backend/elements/alerts.js +21 -0
  16. package/dist/backend/elements/forms.js +59 -0
  17. package/dist/backend/elements/frames.js +25 -0
  18. package/dist/backend/elements/index.js +5 -0
  19. package/dist/core/registry.js +20 -0
  20. package/dist/core/runner.js +136 -0
  21. package/dist/index.js +10 -0
  22. package/dist/reporting/index.js +43 -0
  23. package/package.json +19 -101
  24. package/LICENSE +0 -21
  25. package/lib/actions/clickSteps.d.ts +0 -251
  26. package/lib/actions/clickSteps.js +0 -415
  27. package/lib/actions/cookieSteps.d.ts +0 -18
  28. package/lib/actions/cookieSteps.js +0 -93
  29. package/lib/actions/debugSteps.d.ts +0 -14
  30. package/lib/actions/debugSteps.js +0 -23
  31. package/lib/actions/elementFindSteps.d.ts +0 -668
  32. package/lib/actions/elementFindSteps.js +0 -931
  33. package/lib/actions/fillFormSteps.d.ts +0 -69
  34. package/lib/actions/fillFormSteps.js +0 -237
  35. package/lib/actions/index.d.ts +0 -11
  36. package/lib/actions/index.js +0 -28
  37. package/lib/actions/inputSteps.d.ts +0 -218
  38. package/lib/actions/inputSteps.js +0 -343
  39. package/lib/actions/interceptionSteps.d.ts +0 -169
  40. package/lib/actions/interceptionSteps.js +0 -291
  41. package/lib/actions/miscSteps.d.ts +0 -645
  42. package/lib/actions/miscSteps.js +0 -1061
  43. package/lib/actions/mouseSteps.d.ts +0 -143
  44. package/lib/actions/mouseSteps.js +0 -234
  45. package/lib/actions/scrollSteps.d.ts +0 -82
  46. package/lib/actions/scrollSteps.js +0 -123
  47. package/lib/actions/storageSteps.d.ts +0 -174
  48. package/lib/actions/storageSteps.js +0 -292
  49. package/lib/assertions/buttonAndTextVisibilitySteps.d.ts +0 -245
  50. package/lib/assertions/buttonAndTextVisibilitySteps.js +0 -401
  51. package/lib/assertions/cookieSteps.d.ts +0 -75
  52. package/lib/assertions/cookieSteps.js +0 -113
  53. package/lib/assertions/elementSteps.d.ts +0 -264
  54. package/lib/assertions/elementSteps.js +0 -388
  55. package/lib/assertions/formInputSteps.d.ts +0 -248
  56. package/lib/assertions/formInputSteps.js +0 -350
  57. package/lib/assertions/index.d.ts +0 -10
  58. package/lib/assertions/index.js +0 -27
  59. package/lib/assertions/interceptionRequestsSteps.d.ts +0 -353
  60. package/lib/assertions/interceptionRequestsSteps.js +0 -593
  61. package/lib/assertions/locationSteps.d.ts +0 -217
  62. package/lib/assertions/locationSteps.js +0 -310
  63. package/lib/assertions/roleTestIdSteps.d.ts +0 -159
  64. package/lib/assertions/roleTestIdSteps.js +0 -221
  65. package/lib/assertions/semanticSteps.d.ts +0 -176
  66. package/lib/assertions/semanticSteps.js +0 -252
  67. package/lib/assertions/storageSteps.d.ts +0 -149
  68. package/lib/assertions/storageSteps.js +0 -210
  69. package/lib/assertions/visualSteps.d.ts +0 -74
  70. package/lib/assertions/visualSteps.js +0 -209
  71. package/lib/custom_setups/loginHooks.d.ts +0 -1
  72. package/lib/custom_setups/loginHooks.js +0 -130
  73. package/lib/helpers/checkPeerDeps.d.ts +0 -1
  74. package/lib/helpers/checkPeerDeps.js +0 -19
  75. package/lib/helpers/compareSnapshots.d.ts +0 -6
  76. package/lib/helpers/compareSnapshots.js +0 -20
  77. package/lib/helpers/hooks.d.ts +0 -1
  78. package/lib/helpers/hooks.js +0 -210
  79. package/lib/helpers/utils/fakerUtils.d.ts +0 -1
  80. package/lib/helpers/utils/fakerUtils.js +0 -60
  81. package/lib/helpers/utils/index.d.ts +0 -4
  82. package/lib/helpers/utils/index.js +0 -20
  83. package/lib/helpers/utils/optionsUtils.d.ts +0 -24
  84. package/lib/helpers/utils/optionsUtils.js +0 -88
  85. package/lib/helpers/utils/resolveUtils.d.ts +0 -6
  86. package/lib/helpers/utils/resolveUtils.js +0 -72
  87. package/lib/helpers/utils/sessionUtils.d.ts +0 -3
  88. package/lib/helpers/utils/sessionUtils.js +0 -40
  89. package/lib/helpers/world.d.ts +0 -34
  90. package/lib/helpers/world.js +0 -110
  91. package/lib/iframes/frames.d.ts +0 -1
  92. package/lib/iframes/frames.js +0 -11
  93. package/lib/index.d.ts +0 -10
  94. package/lib/index.js +0 -28
  95. package/lib/register.d.ts +0 -1
  96. package/lib/register.js +0 -6
  97. package/src/actions/clickSteps.ts +0 -429
  98. package/src/actions/cookieSteps.ts +0 -95
  99. package/src/actions/debugSteps.ts +0 -21
  100. package/src/actions/elementFindSteps.ts +0 -961
  101. package/src/actions/fillFormSteps.ts +0 -270
  102. package/src/actions/index.ts +0 -12
  103. package/src/actions/inputSteps.ts +0 -354
  104. package/src/actions/interceptionSteps.ts +0 -325
  105. package/src/actions/miscSteps.ts +0 -1144
  106. package/src/actions/mouseSteps.ts +0 -256
  107. package/src/actions/scrollSteps.ts +0 -122
  108. package/src/actions/storageSteps.ts +0 -308
  109. package/src/assertions/buttonAndTextVisibilitySteps.ts +0 -436
  110. package/src/assertions/cookieSteps.ts +0 -131
  111. package/src/assertions/elementSteps.ts +0 -432
  112. package/src/assertions/formInputSteps.ts +0 -377
  113. package/src/assertions/index.ts +0 -11
  114. package/src/assertions/interceptionRequestsSteps.ts +0 -640
  115. package/src/assertions/locationSteps.ts +0 -315
  116. package/src/assertions/roleTestIdSteps.ts +0 -254
  117. package/src/assertions/semanticSteps.ts +0 -267
  118. package/src/assertions/storageSteps.ts +0 -250
  119. package/src/assertions/visualSteps.ts +0 -275
  120. package/src/custom_setups/loginHooks.ts +0 -154
  121. package/src/helpers/checkPeerDeps.ts +0 -19
  122. package/src/helpers/compareSnapshots.ts +0 -35
  123. package/src/helpers/hooks.ts +0 -212
  124. package/src/helpers/utils/fakerUtils.ts +0 -64
  125. package/src/helpers/utils/index.ts +0 -4
  126. package/src/helpers/utils/optionsUtils.ts +0 -104
  127. package/src/helpers/utils/resolveUtils.ts +0 -74
  128. package/src/helpers/utils/sessionUtils.ts +0 -36
  129. package/src/helpers/world.ts +0 -119
  130. package/src/iframes/frames.ts +0 -15
  131. package/src/index.ts +0 -18
  132. package/src/register.ts +0 -4
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.apiState = void 0;
4
+ // A simple storage to hold the response between the "When" and "Then" steps
5
+ let lastResponse = null;
6
+ exports.apiState = {
7
+ setResponse: (response) => {
8
+ lastResponse = response;
9
+ },
10
+ getResponse: () => {
11
+ if (!lastResponse)
12
+ throw new Error("No API response found. Did you run a 'When I make a request' step first?");
13
+ return lastResponse;
14
+ },
15
+ };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.expectVisible = expectVisible;
4
+ async function expectVisible(page, selector, timeout = 2000) {
5
+ const el = await page.waitForSelector(selector, { state: 'visible', timeout });
6
+ if (!el)
7
+ throw new Error(`Element ${selector} not visible`);
8
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("./visibility");
4
+ require("./text");
5
+ require("./pageState");
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const test_1 = require("@playwright/test");
4
+ const registry_1 = require("../../core/registry");
5
+ // URL Check
6
+ (0, registry_1.Step)("I expect the url to be {string}", async (page, url) => {
7
+ await (0, test_1.expect)(page).toHaveURL(url);
8
+ });
9
+ // Partial URL Check
10
+ (0, registry_1.Step)("I expect the url to contain {string}", async (page, part) => {
11
+ // We use a RegExp to allow partial matching safely
12
+ await (0, test_1.expect)(page).toHaveURL(new RegExp(part));
13
+ });
14
+ // Title Check
15
+ (0, registry_1.Step)("I expect the title to be {string}", async (page, title) => {
16
+ await (0, test_1.expect)(page).toHaveTitle(title);
17
+ });
18
+ // Partial Title Check
19
+ (0, registry_1.Step)("I expect the title to contain {string}", async (page, titlePart) => {
20
+ await (0, test_1.expect)(page).toHaveTitle(new RegExp(titlePart));
21
+ });
22
+ // Screenshot Match (Visual Regression)
23
+ (0, registry_1.Step)("I expect the page screenshot to match {string}", async (page, filename) => {
24
+ await (0, test_1.expect)(page).toHaveScreenshot(filename);
25
+ });
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const test_1 = require("@playwright/test");
4
+ const registry_1 = require("../../core/registry");
5
+ // Exact text match
6
+ (0, registry_1.Step)("I expect {string} to have text {string}", async (page, selector, text) => {
7
+ await (0, test_1.expect)(page.locator(selector)).toHaveText(text);
8
+ });
9
+ // Partial text match (contains)
10
+ (0, registry_1.Step)("I expect {string} to contain text {string}", async (page, selector, text) => {
11
+ await (0, test_1.expect)(page.locator(selector)).toContainText(text);
12
+ });
13
+ // Check input value (for form fields)
14
+ (0, registry_1.Step)("I expect {string} to have value {string}", async (page, selector, value) => {
15
+ await (0, test_1.expect)(page.locator(selector)).toHaveValue(value);
16
+ });
17
+ // Check an attribute (e.g., class, href, src)
18
+ (0, registry_1.Step)("I expect {string} to have attribute {string} with value {string}", async (page, selector, attr, value) => {
19
+ await (0, test_1.expect)(page.locator(selector)).toHaveAttribute(attr, value);
20
+ });
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const test_1 = require("@playwright/test");
4
+ const registry_1 = require("../../core/registry");
5
+ // Check if element exists and is visible
6
+ (0, registry_1.Step)("I expect {string} to be visible", async (page, selector) => {
7
+ await (0, test_1.expect)(page.locator(selector)).toBeVisible();
8
+ });
9
+ // Check if element is hidden (or doesn't exist)
10
+ (0, registry_1.Step)("I expect {string} to be hidden", async (page, selector) => {
11
+ await (0, test_1.expect)(page.locator(selector)).toBeHidden();
12
+ });
13
+ // Check if element is enabled (clickable)
14
+ (0, registry_1.Step)("I expect {string} to be enabled", async (page, selector) => {
15
+ await (0, test_1.expect)(page.locator(selector)).toBeEnabled();
16
+ });
17
+ // Check if element is disabled
18
+ (0, registry_1.Step)("I expect {string} to be disabled", async (page, selector) => {
19
+ await (0, test_1.expect)(page.locator(selector)).toBeDisabled();
20
+ });
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const registry_1 = require("../../core/registry");
37
+ const path = __importStar(require("path"));
38
+ const fs = __importStar(require("fs"));
39
+ // 1. SAVE STATE (Use this after a successful login)
40
+ // Example: And I save the browser state to "admin.json"
41
+ (0, registry_1.Step)("I save the browser state to {string}", async (page, filename) => {
42
+ // Ensure the directory exists
43
+ const authDir = path.resolve(process.cwd(), "auth");
44
+ if (!fs.existsSync(authDir)) {
45
+ fs.mkdirSync(authDir);
46
+ }
47
+ const filePath = path.resolve(authDir, filename);
48
+ await page.context().storageState({ path: filePath });
49
+ console.log(`✅ State saved to: ${filePath}`);
50
+ });
51
+ // 2. LOAD STATE (Use this at the start of other scenarios)
52
+ // Example: Given I load the browser state from "admin.json"
53
+ (0, registry_1.Step)("I load the browser state from {string}", async (page, filename) => {
54
+ const filePath = path.resolve(process.cwd(), "auth", filename);
55
+ if (!fs.existsSync(filePath)) {
56
+ throw new Error(`❌ Auth file not found at: ${filePath}. Did you run the login scenario first?`);
57
+ }
58
+ // Playwright normally loads state at context creation.
59
+ // To load it dynamically mid-test, we need to add cookies/origins to the current context.
60
+ const state = JSON.parse(fs.readFileSync(filePath, "utf8"));
61
+ if (state.cookies) {
62
+ await page.context().addCookies(state.cookies);
63
+ }
64
+ if (state.origins) {
65
+ // LocalStorage requires a bit more work, usually handled by context creation,
66
+ // but adding cookies is often enough for 90% of apps.
67
+ // For full local storage support, users should configure this in playwright.config.ts
68
+ // but this step is a great dynamic helper.
69
+ }
70
+ console.log(`✅ Loaded session for: ${filename}`);
71
+ });
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const registry_1 = require("../../core/registry");
4
+ // Accept the next popup (Alert/Confirm/Prompt)
5
+ (0, registry_1.Step)("I accept the next dialog", async (page) => {
6
+ page.once("dialog", async (dialog) => {
7
+ await dialog.accept();
8
+ });
9
+ });
10
+ // Dismiss (Cancel) the next popup
11
+ (0, registry_1.Step)("I dismiss the next dialog", async (page) => {
12
+ page.once("dialog", async (dialog) => {
13
+ await dialog.dismiss();
14
+ });
15
+ });
16
+ // Type text into a prompt and accept
17
+ (0, registry_1.Step)("I type {string} into the next prompt and accept", async (page, text) => {
18
+ page.once("dialog", async (dialog) => {
19
+ await dialog.accept(text);
20
+ });
21
+ });
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const registry_1 = require("../../core/registry");
37
+ const path = __importStar(require("path"));
38
+ // Dropdown: Select by value or label
39
+ (0, registry_1.Step)("I select option {string} from {string}", async (page, option, selector) => {
40
+ await page.selectOption(selector, { label: option }).catch(() => {
41
+ // Fallback: try selecting by value if label fails
42
+ return page.selectOption(selector, { value: option });
43
+ });
44
+ });
45
+ // Checkboxes & Radio Buttons
46
+ (0, registry_1.Step)("I check {string}", async (page, selector) => {
47
+ await page.check(selector);
48
+ });
49
+ (0, registry_1.Step)("I uncheck {string}", async (page, selector) => {
50
+ await page.uncheck(selector);
51
+ });
52
+ // File Upload
53
+ // Usage: When I upload file "data.csv" to "#upload-input"
54
+ (0, registry_1.Step)("I upload file {string} to {string}", async (page, fileName, selector) => {
55
+ // We assume the user puts files in a 'fixtures' or root folder.
56
+ // You might want to customize this path resolution.
57
+ const filePath = path.resolve(process.cwd(), fileName);
58
+ await page.setInputFiles(selector, filePath);
59
+ });
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const registry_1 = require("../../core/registry");
4
+ // Click inside a specific frame
5
+ (0, registry_1.Step)("I click {string} inside frame {string}", async (page, elementSelector, frameSelector) => {
6
+ const frame = page.frameLocator(frameSelector);
7
+ await frame.locator(elementSelector).click();
8
+ });
9
+ // Fill input inside a specific frame
10
+ (0, registry_1.Step)("I fill {string} inside frame {string} with {string}", async (page, elementSelector, frameSelector, value) => {
11
+ const frame = page.frameLocator(frameSelector);
12
+ await frame.locator(elementSelector).fill(value);
13
+ });
14
+ // Assert text inside a specific frame
15
+ (0, registry_1.Step)("I expect {string} inside frame {string} to have text {string}", async (page, elementSelector, frameSelector, text) => {
16
+ const frame = page.frameLocator(frameSelector);
17
+ const locator = frame.locator(elementSelector);
18
+ // We need to import expect for this assertion, or use a custom check
19
+ // Since we are inside 'elements', let's just do a basic wait/check or re-export expect
20
+ await locator.waitFor();
21
+ const actualText = await locator.textContent();
22
+ if (!actualText?.includes(text)) {
23
+ throw new Error(`Expected text "${text}" but found "${actualText}" in frame`);
24
+ }
25
+ });
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("./forms");
4
+ require("./alerts");
5
+ require("./frames");
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stepRegistry = void 0;
4
+ exports.Step = Step;
5
+ // src/core/registry.ts
6
+ const cucumber_expressions_1 = require("@cucumber/cucumber-expressions");
7
+ // 2. The Global Registry
8
+ // This array stores every step you create in the backend folder
9
+ exports.stepRegistry = [];
10
+ const parameterTypeRegistry = new cucumber_expressions_1.ParameterTypeRegistry();
11
+ // 3. The Function to Register Steps
12
+ // You will use this inside src/backend/actions/...
13
+ function Step(pattern, fn) {
14
+ const expression = new cucumber_expressions_1.CucumberExpression(pattern, parameterTypeRegistry);
15
+ exports.stepRegistry.push({
16
+ expression,
17
+ fn,
18
+ pattern,
19
+ });
20
+ }
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runTests = runTests;
37
+ // src/core/runner.ts
38
+ const test_1 = require("@playwright/test");
39
+ const fs = __importStar(require("fs"));
40
+ const glob_1 = require("glob");
41
+ const registry_1 = require("./registry");
42
+ // LOAD BACKEND LIBRARIES
43
+ require("../backend/actions/index");
44
+ require("../backend/assertions/index");
45
+ require("../backend/elements/index");
46
+ require("../backend/api/index");
47
+ require("../backend/auth/index");
48
+ /**
49
+ * The main test runner. Parses feature files and executes them as Playwright tests.
50
+ * * @param featureGlob - Glob pattern to find feature files (e.g., 'features/*.feature')
51
+ * @param options - Configuration options for filtering tags
52
+ * * @example
53
+ * ```ts
54
+ * // Run all features
55
+ * runTests('features/*.feature');
56
+ * * // Run only @smoke tests
57
+ * runTests('features/*.feature', { tags: '@smoke' });
58
+ * ```
59
+ */
60
+ function runTests(featureGlob, options) {
61
+ const files = (0, glob_1.globSync)(featureGlob);
62
+ for (const file of files) {
63
+ const content = fs.readFileSync(file, "utf8");
64
+ const featureMatch = content.match(/Feature:\s*(.+)/);
65
+ const featureName = featureMatch
66
+ ? featureMatch[1].trim()
67
+ : "Unnamed Feature";
68
+ test_1.test.describe(featureName, () => {
69
+ const scenarioRegex = /(?:(@[\w\s@]+)\s+)?Scenario:\s*(.+)/g;
70
+ let match;
71
+ while ((match = scenarioRegex.exec(content)) !== null) {
72
+ // 1. CAPTURE DATA IMMEDIATELY
73
+ // We calculate everything here so we don't rely on 'match' later
74
+ const foundTags = match[1] || "";
75
+ const scenarioName = match[2].trim();
76
+ const startIndex = match.index + match[0].length;
77
+ const nextMatchIndex = content.slice(startIndex).search(/Scenario:/);
78
+ const blockEnd = nextMatchIndex === -1 ? content.length : startIndex + nextMatchIndex;
79
+ // This variable 'scenarioBlock' is now safe to use inside the test
80
+ const scenarioBlock = content.slice(startIndex, blockEnd);
81
+ if (options?.tags && !foundTags.includes(options.tags)) {
82
+ continue;
83
+ }
84
+ (0, test_1.test)(scenarioName, async ({ page }, testInfo) => {
85
+ // 1. Calculate the scenario block (Same as before)
86
+ const lines = scenarioBlock
87
+ .trim()
88
+ .split("\n")
89
+ .map((l) => l.trim())
90
+ .filter((l) => l);
91
+ for (const stepText of lines) {
92
+ if (stepText.startsWith("#") ||
93
+ stepText.startsWith("@") ||
94
+ stepText === "")
95
+ continue;
96
+ const cleanStep = stepText.replace(/^(Given|When|Then|And|But)\s+/i, "");
97
+ const stepDef = findMatchingStep(cleanStep);
98
+ if (!stepDef) {
99
+ throw new Error(`❌ Undefined Step: "${cleanStep}"`);
100
+ }
101
+ // 2. ERROR HANDLING WRAPPER
102
+ try {
103
+ // Execute the step
104
+ await stepDef.fn(page, ...stepDef.args);
105
+ }
106
+ catch (error) {
107
+ // 3. CAPTURE EVIDENCE ON FAILURE
108
+ console.error(`❌ Failed at step: "${stepText}"`);
109
+ // Take a screenshot immediately
110
+ const screenshot = await page.screenshot({
111
+ fullPage: true,
112
+ type: "png",
113
+ });
114
+ // Attach it to the report (HTML/JSON/Slack will pick this up)
115
+ await testInfo.attach("failure-screenshot", {
116
+ body: screenshot,
117
+ contentType: "image/png",
118
+ });
119
+ // Re-throw the error so the test is marked as Failed
120
+ throw error;
121
+ }
122
+ }
123
+ });
124
+ }
125
+ });
126
+ }
127
+ }
128
+ function findMatchingStep(text) {
129
+ for (const step of registry_1.stepRegistry) {
130
+ const match = step.expression.match(text);
131
+ if (match) {
132
+ return { fn: step.fn, args: match.map((arg) => arg.getValue(null)) };
133
+ }
134
+ }
135
+ return null;
136
+ }
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getReporters = exports.Step = exports.runTests = void 0;
4
+ // src/index.ts
5
+ var runner_1 = require("./core/runner");
6
+ Object.defineProperty(exports, "runTests", { enumerable: true, get: function () { return runner_1.runTests; } });
7
+ var registry_1 = require("./core/registry"); // Export this in case users want to add custom steps
8
+ Object.defineProperty(exports, "Step", { enumerable: true, get: function () { return registry_1.Step; } });
9
+ var index_1 = require("./reporting/index");
10
+ Object.defineProperty(exports, "getReporters", { enumerable: true, get: function () { return index_1.getReporters; } });
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ // src/reporting/index.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.getReporters = getReporters;
5
+ /**
6
+ * Helper to generate Playwright Reporter configuration.
7
+ * * @param options - Select which reporters to enable ('html', 'slack', etc.)
8
+ * @returns An array suitable for the 'reporter' field in playwright.config.ts
9
+ */
10
+ function getReporters(options) {
11
+ const reporters = [];
12
+ // 1. ALWAYS ADD LIST (So they see progress in terminal)
13
+ reporters.push(["list"]);
14
+ // 2. HTML REPORTER (Priority 1)
15
+ if (options.on.includes("html")) {
16
+ reporters.push([
17
+ "html",
18
+ { open: "on-failure", outputFolder: "playwright-report" },
19
+ ]);
20
+ }
21
+ // 3. JSON REPORTER (Useful for CI)
22
+ if (options.on.includes("json")) {
23
+ reporters.push(["json", { outputFile: "results.json" }]);
24
+ }
25
+ // 4. SLACK REPORTER (Requires external package)
26
+ if (options.on.includes("slack")) {
27
+ if (!options.slackWebhookUrl) {
28
+ console.warn("⚠️ Slack reporter requested but no Webhook URL provided.");
29
+ }
30
+ else {
31
+ // We assume the user has installed 'playwright-slack-report'
32
+ // We return the config string that Playwright expects
33
+ reporters.push([
34
+ "playwright-slack-report",
35
+ {
36
+ sendResults: "always", // 'always' | 'on-failure' | 'off'
37
+ slackWebHookUrl: options.slackWebhookUrl,
38
+ },
39
+ ]);
40
+ }
41
+ }
42
+ return reporters;
43
+ }
package/package.json CHANGED
@@ -1,113 +1,31 @@
1
1
  {
2
2
  "name": "playwright-cucumber-ts-steps",
3
- "version": "1.0.1",
4
- "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.",
5
- "type": "commonjs",
6
- "main": "lib/index.js",
7
- "types": "lib/index.d.ts",
8
- "publishConfig": {
9
- "access": "public"
10
- },
11
- "exports": {
12
- ".": {
13
- "import": "./lib/index.js"
14
- },
15
- "./register": {
16
- "import": "./lib/register.js"
17
- }
18
- },
19
- "sideEffects": false,
20
- "typesVersions": {
21
- "*": {
22
- "*": [
23
- "lib/*"
24
- ]
25
- }
26
- },
3
+ "version": "1.0.2",
4
+ "private": false,
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
27
10
  "scripts": {
28
11
  "build": "tsc",
29
12
  "docs": "typedoc",
30
- "docs:deploy": "gh-pages -d temp-docs",
31
- "prepare": "husky",
32
- "lint": "eslint . --ext .ts",
33
- "lint:fix": "eslint . --ext .ts --fix",
34
- "lint:tsc": "tsc --noEmit",
35
- "format": "prettier --write .",
36
- "clean": "rm -rf playwright/report",
37
- "test": "npm run clean && cucumber-js --config playwright/config/cucumber.js",
38
- "commitlint": "commitlint --edit"
39
- },
40
- "lint-staged": {
41
- "*.{js,ts,tsx,json,md}": [
42
- "eslint --fix",
43
- "prettier --write"
44
- ]
45
- },
46
- "commitlint": {
47
- "extends": [
48
- "@commitlint/config-conventional"
49
- ]
13
+ "prepublishOnly": "npm run build"
50
14
  },
51
- "repository": {
52
- "type": "git",
53
- "url": "git+https://github.com/qaPaschalE/playwright-cucumber-ts-steps.git"
54
- },
55
- "keywords": [
56
- "playwright",
57
- "cucumber",
58
- "typescript",
59
- "e2e",
60
- "steps",
61
- "step-definitions",
62
- "testing",
63
- "automation",
64
- "bdd",
65
- "api",
66
- "ui",
67
- "web",
68
- "end-to-end",
69
- "behavior-driven",
70
- "mobile",
71
- "visual-testing"
72
- ],
73
- "author": "qaPaschalE <paschal.enyimiri@gmail.com>",
74
- "license": "MIT",
75
- "bugs": {
76
- "url": "https://github.com/qaPaschalE/playwright-cucumber-ts-steps/issues"
15
+ "dependencies": {
16
+ "@cucumber/cucumber-expressions": "^17.1.0",
17
+ "@cucumber/gherkin": "^27.0.0",
18
+ "@cucumber/messages": "^22.0.0",
19
+ "glob": "^10.3.10"
77
20
  },
78
- "homepage": "https://github.com/qaPaschalE/playwright-cucumber-ts-steps#readme",
79
21
  "peerDependencies": {
80
- "@cucumber/cucumber": "*",
81
- "@playwright/test": "*"
82
- },
83
- "files": [
84
- "lib/",
85
- "src/"
86
- ],
87
- "dependencies": {
88
- "@cucumber/tag-expressions": "^6.2.0",
89
- "@faker-js/faker": "^9.8.0",
90
- "dayjs": "^1.11.13",
91
- "dotenv-cli": "^8.0.0",
92
- "glob": "^11.0.3",
93
- "parse": "^6.1.1",
94
- "pixelmatch": "^7.1.0",
95
- "pngjs": "^7.0.0"
22
+ "@playwright/test": "^1.0.0"
96
23
  },
97
24
  "devDependencies": {
98
- "@commitlint/cli": "^19.8.1",
99
- "@commitlint/config-conventional": "^19.8.1",
100
- "@types/pngjs": "^6.0.5",
101
- "eslint": "^9.29.0",
102
- "eslint-config-prettier": "^10.1.5",
103
- "eslint-plugin-import": "^2.32.0",
104
- "gh-pages": "^6.3.0",
105
- "husky": "^9.1.7",
106
- "lint-staged": "^16.1.2",
107
- "prettier": "^3.5.3",
108
- "ts-node": "^10.9.2",
109
- "typedoc": "^0.28.5",
110
- "typescript": "^5.8.3",
111
- "typescript-eslint": "^8.34.1"
25
+ "@playwright/test": "^1.41.0",
26
+ "@types/glob": "^8.1.0",
27
+ "@types/node": "^20.11.0",
28
+ "typedoc": "^0.28.15",
29
+ "typescript": "^5.3.3"
112
30
  }
113
31
  }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Chetachi (Paschal) Enyimiri
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.