playwright-cucumber-ts-steps 0.1.7 → 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 (96) hide show
  1. package/README.md +21 -11
  2. package/package.json +9 -2
  3. package/src/actions/clickSteps.ts +364 -142
  4. package/src/actions/cookieSteps.ts +66 -0
  5. package/src/actions/debugSteps.ts +17 -3
  6. package/src/actions/elementFindSteps.ts +822 -117
  7. package/src/actions/fillFormSteps.ts +234 -177
  8. package/src/actions/index.ts +12 -0
  9. package/src/actions/inputSteps.ts +318 -82
  10. package/src/actions/interceptionSteps.ts +295 -57
  11. package/src/actions/miscSteps.ts +984 -254
  12. package/src/actions/mouseSteps.ts +212 -55
  13. package/src/actions/scrollSteps.ts +114 -16
  14. package/src/actions/storageSteps.ts +267 -42
  15. package/src/assertions/buttonAndTextVisibilitySteps.ts +353 -95
  16. package/src/assertions/cookieSteps.ts +115 -36
  17. package/src/assertions/elementSteps.ts +414 -85
  18. package/src/assertions/formInputSteps.ts +375 -108
  19. package/src/assertions/index.ts +11 -0
  20. package/src/assertions/interceptionRequestsSteps.ts +619 -195
  21. package/src/assertions/locationSteps.ts +280 -64
  22. package/src/assertions/roleTestIdSteps.ts +244 -26
  23. package/src/assertions/semanticSteps.ts +257 -69
  24. package/src/assertions/storageSteps.ts +234 -73
  25. package/src/assertions/visualSteps.ts +245 -68
  26. package/src/custom_setups/loginHooks.ts +21 -2
  27. package/src/helpers/world.ts +30 -4
  28. package/src/index.ts +4 -25
  29. package/lib/actions/clickSteps.d.ts +0 -1
  30. package/lib/actions/clickSteps.js +0 -165
  31. package/lib/actions/cookieSteps.d.ts +0 -1
  32. package/lib/actions/cookieSteps.js +0 -28
  33. package/lib/actions/debugSteps.d.ts +0 -1
  34. package/lib/actions/debugSteps.js +0 -8
  35. package/lib/actions/elementFindSteps.d.ts +0 -1
  36. package/lib/actions/elementFindSteps.js +0 -217
  37. package/lib/actions/fillFormSteps.d.ts +0 -1
  38. package/lib/actions/fillFormSteps.js +0 -130
  39. package/lib/actions/inputSteps.d.ts +0 -1
  40. package/lib/actions/inputSteps.js +0 -97
  41. package/lib/actions/interceptionSteps.d.ts +0 -1
  42. package/lib/actions/interceptionSteps.js +0 -71
  43. package/lib/actions/miscSteps.d.ts +0 -1
  44. package/lib/actions/miscSteps.js +0 -320
  45. package/lib/actions/mouseSteps.d.ts +0 -1
  46. package/lib/actions/mouseSteps.js +0 -66
  47. package/lib/actions/scrollSteps.d.ts +0 -1
  48. package/lib/actions/scrollSteps.js +0 -23
  49. package/lib/actions/storageSteps.d.ts +0 -1
  50. package/lib/actions/storageSteps.js +0 -72
  51. package/lib/assertions/buttonAndTextVisibilitySteps.d.ts +0 -1
  52. package/lib/assertions/buttonAndTextVisibilitySteps.js +0 -150
  53. package/lib/assertions/cookieSteps.d.ts +0 -1
  54. package/lib/assertions/cookieSteps.js +0 -45
  55. package/lib/assertions/elementSteps.d.ts +0 -1
  56. package/lib/assertions/elementSteps.js +0 -90
  57. package/lib/assertions/formInputSteps.d.ts +0 -1
  58. package/lib/assertions/formInputSteps.js +0 -87
  59. package/lib/assertions/interceptionRequestsSteps.d.ts +0 -1
  60. package/lib/assertions/interceptionRequestsSteps.js +0 -201
  61. package/lib/assertions/locationSteps.d.ts +0 -1
  62. package/lib/assertions/locationSteps.js +0 -87
  63. package/lib/assertions/roleTestIdSteps.d.ts +0 -1
  64. package/lib/assertions/roleTestIdSteps.js +0 -26
  65. package/lib/assertions/semanticSteps.d.ts +0 -1
  66. package/lib/assertions/semanticSteps.js +0 -67
  67. package/lib/assertions/storageSteps.d.ts +0 -1
  68. package/lib/assertions/storageSteps.js +0 -74
  69. package/lib/assertions/visualSteps.d.ts +0 -1
  70. package/lib/assertions/visualSteps.js +0 -76
  71. package/lib/custom_setups/loginHooks.d.ts +0 -1
  72. package/lib/custom_setups/loginHooks.js +0 -113
  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 -31
  90. package/lib/helpers/world.js +0 -104
  91. package/lib/iframes/frames.d.ts +0 -1
  92. package/lib/iframes/frames.js +0 -11
  93. package/lib/index.d.ts +0 -28
  94. package/lib/index.js +0 -48
  95. package/lib/register.d.ts +0 -1
  96. package/lib/register.js +0 -6
@@ -1,74 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const fs_1 = __importDefault(require("fs"));
7
- const path_1 = __importDefault(require("path"));
8
- const cucumber_1 = require("@cucumber/cucumber");
9
- //
10
- // 🗃 LOCAL STORAGE
11
- //
12
- (0, cucumber_1.Then)("I see local storage item {string}", async function (key) {
13
- const value = await this.page.evaluate((k) => localStorage.getItem(k), key);
14
- if (value === null)
15
- throw new Error(`Local storage item "${key}" not found`);
16
- });
17
- (0, cucumber_1.Then)("I do not see local storage item {string}", async function (key) {
18
- const value = await this.page.evaluate((k) => localStorage.getItem(k), key);
19
- if (value !== null)
20
- throw new Error(`Expected localStorage["${key}"] to be null, but got "${value}"`);
21
- });
22
- (0, cucumber_1.Then)("I see local storage item {string} equals {string}", async function (key, expected) {
23
- const actual = await this.page.evaluate((k) => localStorage.getItem(k), key);
24
- if (actual !== expected) {
25
- throw new Error(`Expected localStorage["${key}"] to be "${expected}", but got "${actual}"`);
26
- }
27
- });
28
- (0, cucumber_1.Then)("I see local storage item {string} contains {string}", async function (key, part) {
29
- const value = await this.page.evaluate((k) => localStorage.getItem(k), key);
30
- if (!value || !value.includes(part)) {
31
- throw new Error(`localStorage["${key}"] does not contain "${part}". Got: "${value}"`);
32
- }
33
- });
34
- //
35
- // 🗂 SESSION STORAGE
36
- //
37
- (0, cucumber_1.Then)("I see session storage item {string}", async function (key) {
38
- const value = await this.page.evaluate((k) => sessionStorage.getItem(k), key);
39
- if (value === null)
40
- throw new Error(`Session storage item "${key}" not found`);
41
- });
42
- (0, cucumber_1.Then)("I do not see session storage item {string}", async function (key) {
43
- const value = await this.page.evaluate((k) => sessionStorage.getItem(k), key);
44
- if (value !== null)
45
- throw new Error(`Expected sessionStorage["${key}"] to be null, but got "${value}"`);
46
- });
47
- (0, cucumber_1.When)("I clear all saved session files", async function () {
48
- const authDir = path_1.default.resolve("e2e/support/helper/auth");
49
- if (fs_1.default.existsSync(authDir)) {
50
- const files = fs_1.default.readdirSync(authDir);
51
- for (const file of files) {
52
- const filePath = path_1.default.join(authDir, file);
53
- if (fs_1.default.lstatSync(filePath).isFile()) {
54
- fs_1.default.unlinkSync(filePath);
55
- this.log?.(`🧹 Deleted session file: ${file}`);
56
- }
57
- }
58
- }
59
- else {
60
- this.log?.(`⚠️ Auth directory not found at ${authDir}`);
61
- }
62
- });
63
- (0, cucumber_1.Then)("I see session storage item {string} equals {string}", async function (key, expected) {
64
- const actual = await this.page.evaluate((k) => sessionStorage.getItem(k), key);
65
- if (actual !== expected) {
66
- throw new Error(`Expected sessionStorage["${key}"] to be "${expected}", but got "${actual}"`);
67
- }
68
- });
69
- (0, cucumber_1.Then)("I see session storage item {string} contains {string}", async function (key, part) {
70
- const value = await this.page.evaluate((k) => sessionStorage.getItem(k), key);
71
- if (!value || !value.includes(part)) {
72
- throw new Error(`sessionStorage["${key}"] does not contain "${part}". Got: "${value}"`);
73
- }
74
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,76 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const fs_1 = __importDefault(require("fs"));
7
- const path_1 = __importDefault(require("path"));
8
- const cucumber_1 = require("@cucumber/cucumber");
9
- const test_1 = require("@playwright/test");
10
- const pixelmatch_1 = __importDefault(require("pixelmatch"));
11
- const pngjs_1 = require("pngjs");
12
- const BASELINE_DIR = path_1.default.resolve("e2e/snapshots/baseline");
13
- const CURRENT_DIR = path_1.default.resolve("e2e/snapshots/current");
14
- const DIFF_DIR = path_1.default.resolve("e2e/snapshots/diff");
15
- function getSnapshotPaths(name) {
16
- const safeName = name.replace(/[^a-z0-9]/gi, "_").toLowerCase();
17
- return {
18
- baseline: path_1.default.join(BASELINE_DIR, `${safeName}.png`),
19
- current: path_1.default.join(CURRENT_DIR, `${safeName}.png`),
20
- diff: path_1.default.join(DIFF_DIR, `${safeName}.diff.png`),
21
- };
22
- }
23
- (0, cucumber_1.Then)("I should see the page matches the snapshot {string}", async function (name) {
24
- const { page } = this;
25
- const paths = getSnapshotPaths(name);
26
- fs_1.default.mkdirSync(BASELINE_DIR, { recursive: true });
27
- fs_1.default.mkdirSync(CURRENT_DIR, { recursive: true });
28
- fs_1.default.mkdirSync(DIFF_DIR, { recursive: true });
29
- await page.screenshot({ path: paths.current, fullPage: true });
30
- if (!fs_1.default.existsSync(paths.baseline)) {
31
- fs_1.default.copyFileSync(paths.current, paths.baseline);
32
- this.log?.(`📸 Created baseline snapshot: ${paths.baseline}`);
33
- return;
34
- }
35
- const baseline = pngjs_1.PNG.sync.read(fs_1.default.readFileSync(paths.baseline));
36
- const current = pngjs_1.PNG.sync.read(fs_1.default.readFileSync(paths.current));
37
- const { width, height } = baseline;
38
- const diff = new pngjs_1.PNG({ width, height });
39
- const pixelDiff = (0, pixelmatch_1.default)(baseline.data, current.data, diff.data, width, height, {
40
- threshold: 0.1,
41
- });
42
- if (pixelDiff > 0) {
43
- fs_1.default.writeFileSync(paths.diff, pngjs_1.PNG.sync.write(diff));
44
- this.log?.(`❌ Visual mismatch detected, diff: ${paths.diff}`);
45
- }
46
- (0, test_1.expect)(pixelDiff, "Pixels that differ").toBe(0);
47
- });
48
- (0, cucumber_1.Then)("I capture a snapshot of the element {string} as {string}", async function (selector, alias) {
49
- const element = this.getScope().locator(selector);
50
- const pathCurrent = path_1.default.join(CURRENT_DIR, `${alias}.png`);
51
- fs_1.default.mkdirSync(CURRENT_DIR, { recursive: true });
52
- await element.screenshot({ path: pathCurrent });
53
- this.log?.(`📸 Snapshot for ${selector} saved as ${alias}`);
54
- });
55
- (0, cucumber_1.Then)("The snapshot {string} should match baseline", async function (alias) {
56
- const paths = getSnapshotPaths(alias);
57
- const current = pngjs_1.PNG.sync.read(fs_1.default.readFileSync(paths.current));
58
- const baseline = fs_1.default.existsSync(paths.baseline)
59
- ? pngjs_1.PNG.sync.read(fs_1.default.readFileSync(paths.baseline))
60
- : null;
61
- if (!baseline) {
62
- fs_1.default.copyFileSync(paths.current, paths.baseline);
63
- this.log?.(`📸 Created new baseline for ${alias}`);
64
- return;
65
- }
66
- const { width, height } = baseline;
67
- const diff = new pngjs_1.PNG({ width, height });
68
- const pixelDiff = (0, pixelmatch_1.default)(baseline.data, current.data, diff.data, width, height, {
69
- threshold: 0.1,
70
- });
71
- if (pixelDiff > 0) {
72
- fs_1.default.writeFileSync(paths.diff, pngjs_1.PNG.sync.write(diff));
73
- this.log?.(`⚠️ Snapshot mismatch: ${alias}`);
74
- }
75
- (0, test_1.expect)(pixelDiff).toBe(0);
76
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,113 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const fs_1 = __importDefault(require("fs"));
7
- const cucumber_1 = require("@cucumber/cucumber");
8
- const resolveUtils_1 = require("../helpers/utils/resolveUtils");
9
- // Step 1: Check and load existing session if valid
10
- (0, cucumber_1.When)("I login with a session data {string}", async function (sessionName) {
11
- const sessionPath = (0, resolveUtils_1.resolveSessionPath)(this, sessionName);
12
- this.data.sessionFile = sessionPath;
13
- if (fs_1.default.existsSync(sessionPath)) {
14
- try {
15
- await this.context?.addCookies(JSON.parse(fs_1.default.readFileSync(sessionPath, "utf-8")).cookies || []);
16
- this.log?.(`✅ Loaded session from ${sessionPath}`);
17
- }
18
- catch (err) {
19
- this.log?.(`⚠️ Failed to apply session: ${err.message}`);
20
- }
21
- }
22
- else {
23
- this.log?.(`⚠️ Session file not found: ${sessionPath}`);
24
- }
25
- });
26
- // Step 2: Save current context as session
27
- (0, cucumber_1.When)(/^I save session as "([^"]+)"$/, async function (sessionName) {
28
- const sessionPath = (0, resolveUtils_1.resolveSessionPath)(this, sessionName);
29
- const cookies = await this.context.cookies();
30
- const [localStorageData, sessionStorageData] = await this.page.evaluate(() => {
31
- const toPairs = (store) => Object.entries(store);
32
- return [
33
- [{ origin: location.origin, values: toPairs(localStorage) }],
34
- [{ origin: location.origin, values: toPairs(sessionStorage) }],
35
- ];
36
- });
37
- const sessionData = {
38
- cookies,
39
- localStorage: localStorageData,
40
- sessionStorage: sessionStorageData,
41
- };
42
- try {
43
- fs_1.default.writeFileSync(sessionPath, JSON.stringify(sessionData, null, 2));
44
- this.log?.(`💾 Saved session as "${sessionName}"`);
45
- }
46
- catch (err) {
47
- this.log?.(`❌ Failed to save session "${sessionName}": ${err.message}`);
48
- }
49
- });
50
- // Step 3: Remove a session
51
- (0, cucumber_1.When)("I clear session {string}", function (sessionName) {
52
- const sessionPath = (0, resolveUtils_1.resolveSessionPath)(this, sessionName);
53
- if (fs_1.default.existsSync(sessionPath)) {
54
- fs_1.default.unlinkSync(sessionPath);
55
- this.log?.(`🧹 Cleared session: ${sessionPath}`);
56
- }
57
- else {
58
- this.log?.(`⚠️ Session not found: ${sessionPath}`);
59
- }
60
- });
61
- (0, cucumber_1.When)(/^I restore session cookies "([^"]+)"(?: with reload "(true|false)")?$/, async function (sessionName, reload = "true") {
62
- const sessionPath = (0, resolveUtils_1.resolveSessionPath)(this, sessionName);
63
- if (!fs_1.default.existsSync(sessionPath)) {
64
- this.log?.(`❌ Session file not found: ${sessionPath}`);
65
- return;
66
- }
67
- const sessionData = JSON.parse(fs_1.default.readFileSync(sessionPath, "utf-8"));
68
- const { cookies = [], localStorage = [], sessionStorage = [] } = sessionData;
69
- try {
70
- // Clear & set cookies
71
- if (cookies.length) {
72
- const existing = await this.context.cookies();
73
- if (existing.length)
74
- await this.context.clearCookies();
75
- await this.context.addCookies(cookies);
76
- this.log?.(`🍪 Cookies restored from "${sessionName}"`);
77
- }
78
- // Apply storage into page context
79
- await this.page.goto("about:blank");
80
- if (localStorage.length > 0) {
81
- for (const entry of localStorage) {
82
- await this.page.addInitScript(([origin, values]) => {
83
- if (window.origin === origin) {
84
- for (const [key, val] of values) {
85
- localStorage.setItem(key, val);
86
- }
87
- }
88
- }, [entry.origin, entry.values]);
89
- }
90
- this.log?.("📦 localStorage restored");
91
- }
92
- if (sessionStorage.length > 0) {
93
- for (const entry of sessionStorage) {
94
- await this.page.addInitScript(([origin, values]) => {
95
- if (window.origin === origin) {
96
- for (const [key, val] of values) {
97
- sessionStorage.setItem(key, val);
98
- }
99
- }
100
- }, [entry.origin, entry.values]);
101
- }
102
- this.log?.("🗄️ sessionStorage restored");
103
- }
104
- // Final reload to apply context if requested
105
- if (reload !== "false") {
106
- await this.page.reload();
107
- this.log?.("🔄 Page reloaded to apply restored session");
108
- }
109
- }
110
- catch (err) {
111
- this.log?.(`❌ Error restoring session: ${err.message}`);
112
- }
113
- });
@@ -1 +0,0 @@
1
- export declare function checkPeerDependencies(dependencies: string[]): void;
@@ -1,19 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkPeerDependencies = checkPeerDependencies;
4
- function checkPeerDependencies(dependencies) {
5
- const missing = [];
6
- for (const dep of dependencies) {
7
- try {
8
- require.resolve(dep);
9
- }
10
- catch {
11
- missing.push(dep);
12
- }
13
- }
14
- if (missing.length) {
15
- console.warn(`\n❌ Missing peer dependencies: ${missing.join(", ")}` +
16
- `\nPlease install them in your project:\n\n` +
17
- `npm install --save-dev ${missing.join(" ")}\n`);
18
- }
19
- }
@@ -1,6 +0,0 @@
1
- export declare function compareSnapshots({ actualPath, baselinePath, diffPath, threshold, }: {
2
- actualPath: string;
3
- baselinePath: string;
4
- diffPath: string;
5
- threshold?: number;
6
- }): number;
@@ -1,20 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.compareSnapshots = compareSnapshots;
7
- const fs_1 = __importDefault(require("fs"));
8
- const pixelmatch_1 = __importDefault(require("pixelmatch"));
9
- const pngjs_1 = require("pngjs");
10
- function compareSnapshots({ actualPath, baselinePath, diffPath, threshold = 0.1, }) {
11
- const actual = pngjs_1.PNG.sync.read(fs_1.default.readFileSync(actualPath));
12
- const baseline = pngjs_1.PNG.sync.read(fs_1.default.readFileSync(baselinePath));
13
- if (actual.width !== baseline.width || actual.height !== baseline.height) {
14
- throw new Error("Snapshot size mismatch");
15
- }
16
- const diff = new pngjs_1.PNG({ width: actual.width, height: actual.height });
17
- const numDiffPixels = (0, pixelmatch_1.default)(actual.data, baseline.data, diff.data, actual.width, actual.height, { threshold });
18
- fs_1.default.writeFileSync(diffPath, pngjs_1.PNG.sync.write(diff));
19
- return numDiffPixels;
20
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,210 +0,0 @@
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
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- const fs_1 = __importDefault(require("fs"));
40
- const path_1 = __importDefault(require("path"));
41
- const cucumber_1 = require("@cucumber/cucumber");
42
- const dotenv = __importStar(require("dotenv"));
43
- const playwright_1 = require("playwright");
44
- const compareSnapshots_1 = require("./compareSnapshots");
45
- // Set to 30 seconds
46
- (0, cucumber_1.setDefaultTimeout)(30 * 1000);
47
- dotenv.config();
48
- let sharedBrowser;
49
- (0, cucumber_1.BeforeAll)(async () => {
50
- sharedBrowser = await playwright_1.chromium.launch({
51
- headless: process.env.HEADLESS !== "false",
52
- });
53
- console.log("🚀 Launched shared browser for all scenarios");
54
- });
55
- (0, cucumber_1.AfterAll)(async () => {
56
- await sharedBrowser?.close();
57
- console.log("🧹 Closed shared browser after all scenarios");
58
- });
59
- (0, cucumber_1.Before)(async function (scenario) {
60
- const params = this.parameters || {};
61
- const ARTIFACT_DIR = params.artifactDir || process.env.TEST_ARTIFACT_DIR || "test-artifacts";
62
- const SCREENSHOT_DIR = path_1.default.resolve(ARTIFACT_DIR, "screenshots");
63
- const VIDEO_DIR = path_1.default.resolve(ARTIFACT_DIR, "videos");
64
- const TRACE_DIR = path_1.default.resolve(ARTIFACT_DIR, "traces");
65
- const SESSION_FILE = path_1.default.resolve(ARTIFACT_DIR, "auth-cookies", "session.json");
66
- this.data.artifactDir = ARTIFACT_DIR;
67
- this.data.screenshotDir = SCREENSHOT_DIR;
68
- this.data.videoDir = VIDEO_DIR;
69
- this.data.traceDir = TRACE_DIR;
70
- this.data.sessionFile = SESSION_FILE;
71
- // Modes: "false" | "fail" | "all"
72
- const traceMode = (params.enableTrace || process.env.ENABLE_TRACE || "false").toLowerCase();
73
- const screenshotMode = (params.enableScreenshots ||
74
- process.env.ENABLE_SCREENSHOTS ||
75
- "false").toLowerCase();
76
- const videoMode = (params.enableVideos || process.env.ENABLE_VIDEOS || "false").toLowerCase();
77
- this.data.traceMode = traceMode;
78
- this.data.screenshotMode = screenshotMode;
79
- this.data.videoMode = videoMode;
80
- const isMobileTag = scenario.pickle.tags.some((t) => t.name === "@mobile");
81
- const deviceName = params.device || process.env.MOBILE_DEVICE || (isMobileTag ? "iPhone 13 Pro" : null);
82
- const deviceSettings = deviceName ? playwright_1.devices[deviceName] : undefined;
83
- if (deviceName && !deviceSettings) {
84
- throw new Error(`🚫 Invalid MOBILE_DEVICE: "${deviceName}" is not recognized by Playwright.`);
85
- }
86
- const isVisualTest = params.enableVisualTest ??
87
- (process.env.ENABLE_VISUAL_TEST === "true" ||
88
- scenario.pickle.tags.some((t) => t.name === "@visual"));
89
- this.data.enableVisualTest = isVisualTest;
90
- if (isVisualTest)
91
- process.env.VISUAL_TEST = "true";
92
- const contextOptions = {
93
- ...(videoMode !== "false" ? { recordVideo: { dir: VIDEO_DIR } } : {}),
94
- ...(deviceSettings || {}),
95
- };
96
- if (fs_1.default.existsSync(SESSION_FILE)) {
97
- contextOptions.storageState = SESSION_FILE;
98
- this.log?.("✅ Reusing session from saved file.");
99
- }
100
- const context = await sharedBrowser.newContext(contextOptions);
101
- const page = await context.newPage();
102
- this.browser = sharedBrowser;
103
- this.context = context;
104
- this.page = page;
105
- if (traceMode !== "false") {
106
- await context.tracing.start({
107
- screenshots: true,
108
- snapshots: true,
109
- sources: true,
110
- });
111
- this.data.tracingStarted = true;
112
- this.log?.(`🧪 Tracing started (${traceMode})`);
113
- }
114
- if (deviceName)
115
- this.log?.(`📱 Mobile emulation enabled (${deviceName})`);
116
- });
117
- (0, cucumber_1.After)(async function (scenario) {
118
- const name = scenario.pickle.name.replace(/[^a-z0-9]+/gi, "_").toLowerCase();
119
- const failed = scenario.result?.status === "FAILED";
120
- const mode = (value) => value?.toLowerCase();
121
- const screenshotMode = mode(this.parameters?.enableScreenshots || process.env.ENABLE_SCREENSHOTS);
122
- const videoMode = mode(this.parameters?.enableVideos || process.env.ENABLE_VIDEOS);
123
- const traceMode = mode(this.parameters?.enableTrace || process.env.ENABLE_TRACE);
124
- const shouldSaveScreenshot = screenshotMode === "all" || (screenshotMode === "fail" && failed);
125
- const shouldSaveVideo = videoMode === "all" || (videoMode === "fail" && failed);
126
- const shouldSaveTrace = traceMode === "all" || (traceMode === "fail" && failed);
127
- // 📸 Screenshot
128
- if (shouldSaveScreenshot && this.page) {
129
- const screenshotPath = path_1.default.join(this.data.screenshotDir, `${failed ? "failed-" : ""}${name}.png`);
130
- try {
131
- fs_1.default.mkdirSync(this.data.screenshotDir, { recursive: true });
132
- await this.page.screenshot({ path: screenshotPath, fullPage: true });
133
- console.log(`🖼️ Screenshot saved: ${screenshotPath}`);
134
- }
135
- catch (err) {
136
- console.warn("❌ Failed to save screenshot:", err);
137
- }
138
- }
139
- // 🎥 Video
140
- if (this.page && videoMode !== "false") {
141
- try {
142
- const video = this.page.video();
143
- if (video) {
144
- const rawPath = await video.path();
145
- if (fs_1.default.existsSync(rawPath)) {
146
- const finalPath = path_1.default.join(this.data.videoDir, `${failed ? "failed-" : ""}${name}.webm`);
147
- fs_1.default.mkdirSync(this.data.videoDir, { recursive: true });
148
- shouldSaveVideo ? fs_1.default.renameSync(rawPath, finalPath) : fs_1.default.unlinkSync(rawPath);
149
- console.log(`${shouldSaveVideo ? "🎥 Video saved" : "🧹 Deleted video"}: ${finalPath}`);
150
- }
151
- }
152
- }
153
- catch (err) {
154
- console.warn(`⚠️ Video error: ${err.message}`);
155
- }
156
- }
157
- // 🧪 Tracing
158
- if (this.context && this.data.tracingStarted) {
159
- const tracePath = path_1.default.join(this.data.artifactDir, "traces", `${name}.zip`);
160
- try {
161
- fs_1.default.mkdirSync(path_1.default.dirname(tracePath), { recursive: true });
162
- await this.context.tracing.stop({ path: tracePath });
163
- shouldSaveTrace
164
- ? console.log(`📦 Trace saved: ${tracePath}`)
165
- : (fs_1.default.existsSync(tracePath) && fs_1.default.unlinkSync(tracePath),
166
- console.log(`🧹 Trace discarded: ${tracePath}`));
167
- }
168
- catch (err) {
169
- console.warn("❌ Trace handling error:", err);
170
- }
171
- }
172
- // 🧪 Visual regression
173
- if (this.page && this.data.enableVisualTest) {
174
- const BASELINE_DIR = path_1.default.resolve(this.data.artifactDir, "snapshots/baseline");
175
- const DIFF_DIR = path_1.default.resolve(this.data.artifactDir, "snapshots/diff");
176
- fs_1.default.mkdirSync(BASELINE_DIR, { recursive: true });
177
- fs_1.default.mkdirSync(DIFF_DIR, { recursive: true });
178
- const baselinePath = path_1.default.join(BASELINE_DIR, `${name}.png`);
179
- const actualPath = path_1.default.join(DIFF_DIR, `${name}.actual.png`);
180
- const diffPath = path_1.default.join(DIFF_DIR, `${name}.diff.png`);
181
- await this.page.screenshot({ path: actualPath, fullPage: true });
182
- if (!fs_1.default.existsSync(baselinePath)) {
183
- fs_1.default.copyFileSync(actualPath, baselinePath);
184
- console.log(`📸 Created baseline image: ${baselinePath}`);
185
- }
186
- else {
187
- try {
188
- const diffPixels = (0, compareSnapshots_1.compareSnapshots)({
189
- actualPath,
190
- baselinePath,
191
- diffPath,
192
- threshold: 0.1,
193
- });
194
- console.log(diffPixels > 0
195
- ? `⚠️ Visual diff found (${diffPixels} pixels): ${diffPath}`
196
- : "✅ No visual changes detected");
197
- }
198
- catch (err) {
199
- console.warn("❌ Snapshot comparison failed:", err);
200
- }
201
- }
202
- }
203
- // Cleanup
204
- try {
205
- await this.cleanup(scenario);
206
- }
207
- catch (err) {
208
- this.log?.("❌ Error during cleanup: " + err.message);
209
- }
210
- });
@@ -1 +0,0 @@
1
- export declare function evaluateFaker(value: string): string;
@@ -1,60 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.evaluateFaker = evaluateFaker;
7
- const faker_1 = require("@faker-js/faker");
8
- const dayjs_1 = __importDefault(require("dayjs"));
9
- const fakerMapping = {
10
- "First Name": () => faker_1.faker.person.middleName(),
11
- Name: () => faker_1.faker.person.middleName(),
12
- "Last Name": () => faker_1.faker.person.middleName(),
13
- Email: () => faker_1.faker.internet.email(),
14
- "Phone Number": () => faker_1.faker.string.numeric(10),
15
- Number: () => faker_1.faker.string.numeric(11),
16
- "Complete Number": () => faker_1.faker.string.numeric(11),
17
- "App Colour": () => faker_1.faker.color.rgb(),
18
- "App Name": () => faker_1.faker.commerce.productName(),
19
- "Role Name": () => faker_1.faker.person.jobTitle(),
20
- "Company Name": () => faker_1.faker.company.name(),
21
- "Full Name": () => faker_1.faker.person.fullName(),
22
- "Disposable Email": () => faker_1.faker.internet.email({ provider: "inboxkitten.com" }),
23
- "ALpha Numeric": () => faker_1.faker.string.numeric(11) + "e",
24
- "Lorem Word": () => faker_1.faker.lorem.sentences({ min: 1, max: 3 }),
25
- Word: () => faker_1.faker.lorem.word({ length: { min: 5, max: 11 } }),
26
- "Current Date": () => (0, dayjs_1.default)().format("YYYY-MM-DD"),
27
- "Current Date2": () => new Date().toISOString().split("T")[0],
28
- MonthsFromNow: (months) => {
29
- const monthsToAdd = parseInt(months || "0", 10);
30
- return (0, dayjs_1.default)().add(monthsToAdd, "month").format("YYYY-MM-DD");
31
- },
32
- MonthsAgo: (months) => {
33
- const monthsToSubtract = parseInt(months || "0", 10);
34
- return (0, dayjs_1.default)().subtract(monthsToSubtract, "month").format("YYYY-MM-DD");
35
- },
36
- WeeksFromNow: (weeks) => {
37
- const weeksToAdd = parseInt(weeks || "0", 10);
38
- return (0, dayjs_1.default)().add(weeksToAdd, "week").format("YYYY-MM-DD");
39
- },
40
- WeeksAgo: (weeks) => {
41
- const weeksToSubtract = parseInt(weeks || "0", 10);
42
- return (0, dayjs_1.default)().subtract(weeksToSubtract, "week").format("YYYY-MM-DD");
43
- },
44
- DaysFromNow: (days) => {
45
- const daysToAdd = parseInt(days || "0", 10);
46
- return (0, dayjs_1.default)().add(daysToAdd, "day").format("YYYY-MM-DD");
47
- },
48
- DaysAgo: (days) => {
49
- const daysToSubtract = parseInt(days || "0", 10);
50
- return (0, dayjs_1.default)().subtract(daysToSubtract, "day").format("YYYY-MM-DD");
51
- },
52
- };
53
- function evaluateFaker(value) {
54
- const [key, param] = value.split(":");
55
- const fn = fakerMapping[key];
56
- if (typeof fn === "function") {
57
- return fn(param);
58
- }
59
- return value; // fallback to raw value if not mapped
60
- }
@@ -1,4 +0,0 @@
1
- export * from "./fakerUtils";
2
- export * from "./optionsUtils";
3
- export * from "./resolveUtils";
4
- export * from "./sessionUtils";
@@ -1,20 +0,0 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./fakerUtils"), exports);
18
- __exportStar(require("./optionsUtils"), exports);
19
- __exportStar(require("./resolveUtils"), exports);
20
- __exportStar(require("./sessionUtils"), exports);
@@ -1,24 +0,0 @@
1
- import type { DataTable } from "@cucumber/cucumber";
2
- import type { Locator } from "@playwright/test";
3
- type ClickOptions = Parameters<Locator["click"]>[0];
4
- type DblClickOptions = Parameters<Locator["dblclick"]>[0];
5
- type HoverOptions = Parameters<Locator["hover"]>[0];
6
- type FillOptions = Parameters<Locator["fill"]>[1];
7
- type TypeOptions = Parameters<Locator["type"]>[1];
8
- type CheckOptions = Parameters<Locator["check"]>[0];
9
- type UncheckOptions = Parameters<Locator["uncheck"]>[0];
10
- type SelectOptionOptions = Parameters<Locator["selectOption"]>[1];
11
- export declare function parseClickOptions(table?: DataTable): Partial<ClickOptions>;
12
- export declare function parseDblClickOptions(table?: DataTable): Partial<DblClickOptions>;
13
- export declare function parseHoverOptions(table?: DataTable): Partial<HoverOptions>;
14
- export declare function parseTypeOptions(table?: DataTable): Partial<TypeOptions>;
15
- export declare function parseFillOptions(table?: DataTable): Partial<FillOptions>;
16
- export declare function parseCheckOptions(table?: DataTable): Partial<CheckOptions>;
17
- export declare function parseUncheckOptions(table?: DataTable): Partial<UncheckOptions>;
18
- export declare function parseSelectOptions(table?: DataTable): Partial<SelectOptionOptions>;
19
- export declare function parseGenericOptions(table?: DataTable): Record<string, any>;
20
- export declare function parseExpectOptions(table?: DataTable): {
21
- timeout?: number;
22
- log?: boolean;
23
- };
24
- export {};