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.
- package/README.md +21 -11
- package/package.json +9 -2
- package/src/actions/clickSteps.ts +364 -142
- package/src/actions/cookieSteps.ts +66 -0
- package/src/actions/debugSteps.ts +17 -3
- package/src/actions/elementFindSteps.ts +822 -117
- package/src/actions/fillFormSteps.ts +234 -177
- package/src/actions/index.ts +12 -0
- package/src/actions/inputSteps.ts +318 -82
- package/src/actions/interceptionSteps.ts +295 -57
- package/src/actions/miscSteps.ts +984 -254
- package/src/actions/mouseSteps.ts +212 -55
- package/src/actions/scrollSteps.ts +114 -16
- package/src/actions/storageSteps.ts +267 -42
- package/src/assertions/buttonAndTextVisibilitySteps.ts +353 -95
- package/src/assertions/cookieSteps.ts +115 -36
- package/src/assertions/elementSteps.ts +414 -85
- package/src/assertions/formInputSteps.ts +375 -108
- package/src/assertions/index.ts +11 -0
- package/src/assertions/interceptionRequestsSteps.ts +619 -195
- package/src/assertions/locationSteps.ts +280 -64
- package/src/assertions/roleTestIdSteps.ts +244 -26
- package/src/assertions/semanticSteps.ts +257 -69
- package/src/assertions/storageSteps.ts +234 -73
- package/src/assertions/visualSteps.ts +245 -68
- package/src/custom_setups/loginHooks.ts +21 -2
- package/src/helpers/world.ts +30 -4
- package/src/index.ts +4 -25
- package/lib/actions/clickSteps.d.ts +0 -1
- package/lib/actions/clickSteps.js +0 -165
- package/lib/actions/cookieSteps.d.ts +0 -1
- package/lib/actions/cookieSteps.js +0 -28
- package/lib/actions/debugSteps.d.ts +0 -1
- package/lib/actions/debugSteps.js +0 -8
- package/lib/actions/elementFindSteps.d.ts +0 -1
- package/lib/actions/elementFindSteps.js +0 -217
- package/lib/actions/fillFormSteps.d.ts +0 -1
- package/lib/actions/fillFormSteps.js +0 -130
- package/lib/actions/inputSteps.d.ts +0 -1
- package/lib/actions/inputSteps.js +0 -97
- package/lib/actions/interceptionSteps.d.ts +0 -1
- package/lib/actions/interceptionSteps.js +0 -71
- package/lib/actions/miscSteps.d.ts +0 -1
- package/lib/actions/miscSteps.js +0 -320
- package/lib/actions/mouseSteps.d.ts +0 -1
- package/lib/actions/mouseSteps.js +0 -66
- package/lib/actions/scrollSteps.d.ts +0 -1
- package/lib/actions/scrollSteps.js +0 -23
- package/lib/actions/storageSteps.d.ts +0 -1
- package/lib/actions/storageSteps.js +0 -72
- package/lib/assertions/buttonAndTextVisibilitySteps.d.ts +0 -1
- package/lib/assertions/buttonAndTextVisibilitySteps.js +0 -150
- package/lib/assertions/cookieSteps.d.ts +0 -1
- package/lib/assertions/cookieSteps.js +0 -45
- package/lib/assertions/elementSteps.d.ts +0 -1
- package/lib/assertions/elementSteps.js +0 -90
- package/lib/assertions/formInputSteps.d.ts +0 -1
- package/lib/assertions/formInputSteps.js +0 -87
- package/lib/assertions/interceptionRequestsSteps.d.ts +0 -1
- package/lib/assertions/interceptionRequestsSteps.js +0 -201
- package/lib/assertions/locationSteps.d.ts +0 -1
- package/lib/assertions/locationSteps.js +0 -87
- package/lib/assertions/roleTestIdSteps.d.ts +0 -1
- package/lib/assertions/roleTestIdSteps.js +0 -26
- package/lib/assertions/semanticSteps.d.ts +0 -1
- package/lib/assertions/semanticSteps.js +0 -67
- package/lib/assertions/storageSteps.d.ts +0 -1
- package/lib/assertions/storageSteps.js +0 -74
- package/lib/assertions/visualSteps.d.ts +0 -1
- package/lib/assertions/visualSteps.js +0 -76
- package/lib/custom_setups/loginHooks.d.ts +0 -1
- package/lib/custom_setups/loginHooks.js +0 -113
- package/lib/helpers/checkPeerDeps.d.ts +0 -1
- package/lib/helpers/checkPeerDeps.js +0 -19
- package/lib/helpers/compareSnapshots.d.ts +0 -6
- package/lib/helpers/compareSnapshots.js +0 -20
- package/lib/helpers/hooks.d.ts +0 -1
- package/lib/helpers/hooks.js +0 -210
- package/lib/helpers/utils/fakerUtils.d.ts +0 -1
- package/lib/helpers/utils/fakerUtils.js +0 -60
- package/lib/helpers/utils/index.d.ts +0 -4
- package/lib/helpers/utils/index.js +0 -20
- package/lib/helpers/utils/optionsUtils.d.ts +0 -24
- package/lib/helpers/utils/optionsUtils.js +0 -88
- package/lib/helpers/utils/resolveUtils.d.ts +0 -6
- package/lib/helpers/utils/resolveUtils.js +0 -72
- package/lib/helpers/utils/sessionUtils.d.ts +0 -3
- package/lib/helpers/utils/sessionUtils.js +0 -40
- package/lib/helpers/world.d.ts +0 -31
- package/lib/helpers/world.js +0 -104
- package/lib/iframes/frames.d.ts +0 -1
- package/lib/iframes/frames.js +0 -11
- package/lib/index.d.ts +0 -28
- package/lib/index.js +0 -48
- package/lib/register.d.ts +0 -1
- 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,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
|
-
}
|
package/lib/helpers/hooks.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/lib/helpers/hooks.js
DELETED
|
@@ -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,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 {};
|