@esimplicity/stack-tests 0.1.4 → 0.1.7
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 +5 -5
- package/dist/{chunk-73J5FNGG.js → chunk-HSWPZQOM.js} +748 -9
- package/dist/index.d.ts +60 -2
- package/dist/index.js +29 -1
- package/dist/steps/index.d.ts +95 -2
- package/dist/steps/index.js +19 -3
- package/package.json +1 -1
- package/scripts/postinstall.cjs +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @esimplicity/stack-tests
|
|
2
2
|
|
|
3
3
|
Reusable Playwright-BDD fixtures, ports, adapters, and step registrations for API, UI, and hybrid testing. Designed to be consumed as a dev dependency across repos.
|
|
4
4
|
|
|
@@ -9,13 +9,13 @@ GitHub Packages (recommended for release builds):
|
|
|
9
9
|
```bash
|
|
10
10
|
npm config set @kata:registry https://npm.pkg.github.com
|
|
11
11
|
npm set //npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN
|
|
12
|
-
npm install -D @
|
|
12
|
+
npm install -D @esimplicity/stack-tests @playwright/test playwright-bdd
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Workspace/local development (from this monorepo):
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
bun add -d @
|
|
18
|
+
bun add -d @esimplicity/stack-tests@"file:../packages/stack-tests" @playwright/test playwright-bdd
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## What’s included
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
PlaywrightUiAdapter,
|
|
38
38
|
UniversalAuthAdapter,
|
|
39
39
|
DefaultCleanupAdapter,
|
|
40
|
-
} from '@
|
|
40
|
+
} from '@esimplicity/stack-tests';
|
|
41
41
|
|
|
42
42
|
export const test = createBddTest({
|
|
43
43
|
createApi: ({ apiRequest }) => new PlaywrightApiAdapter(apiRequest),
|
|
@@ -51,7 +51,7 @@ export const test = createBddTest({
|
|
|
51
51
|
```ts
|
|
52
52
|
// features/steps/steps_api/index.ts
|
|
53
53
|
import { test } from '../fixtures';
|
|
54
|
-
import { registerApiSteps } from '@
|
|
54
|
+
import { registerApiSteps } from '@esimplicity/stack-tests/steps';
|
|
55
55
|
registerApiSteps(test);
|
|
56
56
|
```
|
|
57
57
|
|
|
@@ -206,16 +206,116 @@ function registerSharedVarSteps(test) {
|
|
|
206
206
|
});
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
// src/steps/
|
|
209
|
+
// src/steps/shared.flags.ts
|
|
210
210
|
import { createBdd as createBdd7 } from "playwright-bdd";
|
|
211
|
+
var globalFlags = {};
|
|
212
|
+
function isFlagEnabled(world, flagName) {
|
|
213
|
+
const worldFlags = world._featureFlags || {};
|
|
214
|
+
if (flagName in worldFlags) {
|
|
215
|
+
return worldFlags[flagName];
|
|
216
|
+
}
|
|
217
|
+
return globalFlags[flagName] ?? false;
|
|
218
|
+
}
|
|
219
|
+
function setFlag(world, flagName, enabled) {
|
|
220
|
+
if (!world._featureFlags) {
|
|
221
|
+
world._featureFlags = {};
|
|
222
|
+
}
|
|
223
|
+
world._featureFlags[flagName] = enabled;
|
|
224
|
+
world.vars[`flag_${flagName}`] = String(enabled);
|
|
225
|
+
}
|
|
226
|
+
function registerFlagSteps(test) {
|
|
227
|
+
const { Given, Then } = createBdd7(test);
|
|
228
|
+
Given(
|
|
229
|
+
"the feature flag {string} is enabled",
|
|
230
|
+
async ({ world }, flagName) => {
|
|
231
|
+
setFlag(world, flagName, true);
|
|
232
|
+
console.log(`[BDD] Feature flag "${flagName}" enabled`);
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
Given(
|
|
236
|
+
"the feature flag {string} is disabled",
|
|
237
|
+
async ({ world }, flagName) => {
|
|
238
|
+
setFlag(world, flagName, false);
|
|
239
|
+
console.log(`[BDD] Feature flag "${flagName}" disabled`);
|
|
240
|
+
}
|
|
241
|
+
);
|
|
242
|
+
Given(
|
|
243
|
+
"the feature flag {string} is set to {string}",
|
|
244
|
+
async ({ world }, flagName, value) => {
|
|
245
|
+
const enabled = value.toLowerCase() === "true" || value === "1";
|
|
246
|
+
setFlag(world, flagName, enabled);
|
|
247
|
+
console.log(`[BDD] Feature flag "${flagName}" set to ${enabled}`);
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
Given(
|
|
251
|
+
"the following feature flags are enabled:",
|
|
252
|
+
async ({ world }, dataTable) => {
|
|
253
|
+
const rows = dataTable.hashes ? dataTable.hashes() : dataTable.rawTable?.slice(1) || [];
|
|
254
|
+
for (const row of rows) {
|
|
255
|
+
const flagName = row.flag || row[0];
|
|
256
|
+
setFlag(world, flagName, true);
|
|
257
|
+
console.log(`[BDD] Feature flag "${flagName}" enabled`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
Given(
|
|
262
|
+
"the following feature flags are disabled:",
|
|
263
|
+
async ({ world }, dataTable) => {
|
|
264
|
+
const rows = dataTable.hashes ? dataTable.hashes() : dataTable.rawTable?.slice(1) || [];
|
|
265
|
+
for (const row of rows) {
|
|
266
|
+
const flagName = row.flag || row[0];
|
|
267
|
+
setFlag(world, flagName, false);
|
|
268
|
+
console.log(`[BDD] Feature flag "${flagName}" disabled`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
Then(
|
|
273
|
+
"the feature flag {string} should be enabled",
|
|
274
|
+
async ({ world }, flagName) => {
|
|
275
|
+
const enabled = isFlagEnabled(world, flagName);
|
|
276
|
+
if (!enabled) {
|
|
277
|
+
throw new Error(`Expected feature flag "${flagName}" to be enabled, but it was disabled`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
Then(
|
|
282
|
+
"the feature flag {string} should be disabled",
|
|
283
|
+
async ({ world }, flagName) => {
|
|
284
|
+
const enabled = isFlagEnabled(world, flagName);
|
|
285
|
+
if (enabled) {
|
|
286
|
+
throw new Error(`Expected feature flag "${flagName}" to be disabled, but it was enabled`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
);
|
|
290
|
+
Then(
|
|
291
|
+
"I log all feature flags",
|
|
292
|
+
async ({ world }) => {
|
|
293
|
+
const worldFlags = world._featureFlags || {};
|
|
294
|
+
console.log("[DEBUG] Feature flags:");
|
|
295
|
+
for (const [name, enabled] of Object.entries(worldFlags)) {
|
|
296
|
+
console.log(` - ${name}: ${enabled ? "enabled" : "disabled"}`);
|
|
297
|
+
}
|
|
298
|
+
if (Object.keys(worldFlags).length === 0) {
|
|
299
|
+
console.log(" (no flags set)");
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/steps/ui.basic.ts
|
|
306
|
+
import { createBdd as createBdd8 } from "playwright-bdd";
|
|
307
|
+
import { expect as expect3 } from "@playwright/test";
|
|
211
308
|
function registerUiBasicSteps(test) {
|
|
212
|
-
const { Given, When, Then } =
|
|
309
|
+
const { Given, When, Then } = createBdd8(test);
|
|
213
310
|
Given("I navigate to {string}", { tags: "@ui or @hybrid" }, async ({ ui, world }, path) => {
|
|
214
311
|
await ui.goto(interpolate(path, world.vars));
|
|
215
312
|
});
|
|
216
313
|
When("I click the button {string}", { tags: "@ui or @hybrid" }, async ({ ui, world }, name) => {
|
|
217
314
|
await ui.clickButton(interpolate(name, world.vars));
|
|
218
315
|
});
|
|
316
|
+
When("I click the {string} button", { tags: "@ui or @hybrid" }, async ({ ui, world }, name) => {
|
|
317
|
+
await ui.clickButton(interpolate(name, world.vars));
|
|
318
|
+
});
|
|
219
319
|
When("I click the link {string}", { tags: "@ui or @hybrid" }, async ({ ui, world }, name) => {
|
|
220
320
|
await ui.clickLink(interpolate(name, world.vars));
|
|
221
321
|
});
|
|
@@ -225,22 +325,49 @@ function registerUiBasicSteps(test) {
|
|
|
225
325
|
When("I fill the field {string} with {string}", { tags: "@ui or @hybrid" }, async ({ ui, world }, label, value) => {
|
|
226
326
|
await ui.fillLabel(interpolate(label, world.vars), interpolate(value, world.vars));
|
|
227
327
|
});
|
|
328
|
+
When("I fill in {string} with {string}", { tags: "@ui or @hybrid" }, async ({ ui, world }, label, value) => {
|
|
329
|
+
await ui.fillLabel(interpolate(label, world.vars), interpolate(value, world.vars));
|
|
330
|
+
});
|
|
228
331
|
When("I log in as admin in UI", { tags: "@ui or @hybrid" }, async ({ auth, world }) => {
|
|
229
332
|
await auth.uiLoginAsAdmin(world);
|
|
230
333
|
});
|
|
231
334
|
When("I log in as user in UI", { tags: "@ui or @hybrid" }, async ({ auth, world }) => {
|
|
232
335
|
await auth.uiLoginAsUser(world);
|
|
233
336
|
});
|
|
337
|
+
When("I click the element {string}", { tags: "@ui or @hybrid" }, async ({ page, world }, selector) => {
|
|
338
|
+
await page.locator(interpolate(selector, world.vars)).click();
|
|
339
|
+
});
|
|
340
|
+
When("I select {string} from dropdown {string}", { tags: "@ui or @hybrid" }, async ({ page, world }, option, selector) => {
|
|
341
|
+
await page.locator(interpolate(selector, world.vars)).selectOption({ label: interpolate(option, world.vars) });
|
|
342
|
+
});
|
|
234
343
|
Then("I should see text {string}", { tags: "@ui or @hybrid" }, async ({ ui, world }, text) => {
|
|
235
344
|
await ui.expectText(interpolate(text, world.vars));
|
|
236
345
|
});
|
|
237
346
|
Then("the URL should contain {string}", { tags: "@ui or @hybrid" }, async ({ ui, world }, part) => {
|
|
238
347
|
await ui.expectUrlContains(interpolate(part, world.vars));
|
|
239
348
|
});
|
|
349
|
+
Then("I should be on page {string}", { tags: "@ui or @hybrid" }, async ({ ui, world }, path) => {
|
|
350
|
+
await ui.expectUrlContains(interpolate(path, world.vars));
|
|
351
|
+
});
|
|
352
|
+
Then("the element {string} should be visible", { tags: "@ui or @hybrid" }, async ({ page, world }, selector) => {
|
|
353
|
+
await expect3(page.locator(interpolate(selector, world.vars))).toBeVisible();
|
|
354
|
+
});
|
|
355
|
+
Then("the element {string} should not be visible", { tags: "@ui or @hybrid" }, async ({ page, world }, selector) => {
|
|
356
|
+
await expect3(page.locator(interpolate(selector, world.vars))).not.toBeVisible();
|
|
357
|
+
});
|
|
358
|
+
Then("the element {string} should have value {string}", { tags: "@ui or @hybrid" }, async ({ page, world }, selector, value) => {
|
|
359
|
+
await expect3(page.locator(interpolate(selector, world.vars))).toHaveValue(interpolate(value, world.vars));
|
|
360
|
+
});
|
|
361
|
+
Then("the element {string} should be checked", { tags: "@ui or @hybrid" }, async ({ page, world }, selector) => {
|
|
362
|
+
await expect3(page.locator(interpolate(selector, world.vars))).toBeChecked();
|
|
363
|
+
});
|
|
364
|
+
Then("the element {string} should not be checked", { tags: "@ui or @hybrid" }, async ({ page, world }, selector) => {
|
|
365
|
+
await expect3(page.locator(interpolate(selector, world.vars))).not.toBeChecked();
|
|
366
|
+
});
|
|
240
367
|
}
|
|
241
368
|
|
|
242
369
|
// src/steps/ui.wizard.ts
|
|
243
|
-
import { createBdd as
|
|
370
|
+
import { createBdd as createBdd9 } from "playwright-bdd";
|
|
244
371
|
function resolveValue(input, world) {
|
|
245
372
|
const interpolated = interpolate(input, world.vars);
|
|
246
373
|
if (interpolated === input && world.vars[input] !== void 0) {
|
|
@@ -338,7 +465,7 @@ function parseRegex(input) {
|
|
|
338
465
|
return new RegExp(raw);
|
|
339
466
|
}
|
|
340
467
|
function registerWizardSteps(test) {
|
|
341
|
-
const { Given, When, Then } =
|
|
468
|
+
const { Given, When, Then } = createBdd9(test);
|
|
342
469
|
Given("I open {string} page", { tags: "@ui or @hybrid" }, async ({ ui, world }, urlOrVar) => {
|
|
343
470
|
await ui.goto(resolveValue(urlOrVar, world));
|
|
344
471
|
});
|
|
@@ -510,10 +637,596 @@ function registerWizardSteps(test) {
|
|
|
510
637
|
);
|
|
511
638
|
}
|
|
512
639
|
|
|
640
|
+
// src/steps/ui.form.ts
|
|
641
|
+
import { createBdd as createBdd10 } from "playwright-bdd";
|
|
642
|
+
function registerFormSteps(test) {
|
|
643
|
+
const { Given, When } = createBdd10(test);
|
|
644
|
+
When(
|
|
645
|
+
"I fill the form:",
|
|
646
|
+
{ tags: "@ui or @hybrid" },
|
|
647
|
+
async ({ ui, world }, dataTable) => {
|
|
648
|
+
const rows = dataTable.hashes ? dataTable.hashes() : dataTable.rawTable?.slice(1).map((row) => ({
|
|
649
|
+
field: row[0],
|
|
650
|
+
value: row[1]
|
|
651
|
+
})) || [];
|
|
652
|
+
for (const row of rows) {
|
|
653
|
+
const field = interpolate(row.field, world.vars);
|
|
654
|
+
const value = interpolate(row.value, world.vars);
|
|
655
|
+
await ui.fillLabel(field, value);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
);
|
|
659
|
+
When(
|
|
660
|
+
"I fill the form with {string} locators:",
|
|
661
|
+
{ tags: "@ui or @hybrid" },
|
|
662
|
+
async ({ ui, world }, locatorMethod, dataTable) => {
|
|
663
|
+
const rows = dataTable.hashes ? dataTable.hashes() : dataTable.rawTable?.slice(1).map((row) => ({
|
|
664
|
+
field: row[0],
|
|
665
|
+
value: row[1]
|
|
666
|
+
})) || [];
|
|
667
|
+
for (const row of rows) {
|
|
668
|
+
const field = interpolate(row.field, world.vars);
|
|
669
|
+
const value = interpolate(row.value, world.vars);
|
|
670
|
+
await ui.inputInElement("fill", value, "1", field, locatorMethod);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
);
|
|
674
|
+
When(
|
|
675
|
+
"I fill and submit the form:",
|
|
676
|
+
{ tags: "@ui or @hybrid" },
|
|
677
|
+
async ({ ui, world }, dataTable) => {
|
|
678
|
+
const rows = dataTable.hashes ? dataTable.hashes() : dataTable.rawTable?.slice(1).map((row) => ({
|
|
679
|
+
field: row[0],
|
|
680
|
+
value: row[1]
|
|
681
|
+
})) || [];
|
|
682
|
+
for (const row of rows) {
|
|
683
|
+
const field = interpolate(row.field, world.vars);
|
|
684
|
+
const value = interpolate(row.value, world.vars);
|
|
685
|
+
await ui.fillLabel(field, value);
|
|
686
|
+
}
|
|
687
|
+
await ui.pressKey("Enter");
|
|
688
|
+
}
|
|
689
|
+
);
|
|
690
|
+
Given(
|
|
691
|
+
"I clear and fill the form:",
|
|
692
|
+
{ tags: "@ui or @hybrid" },
|
|
693
|
+
async ({ page, world }, dataTable) => {
|
|
694
|
+
const rows = dataTable.hashes ? dataTable.hashes() : dataTable.rawTable?.slice(1).map((row) => ({
|
|
695
|
+
field: row[0],
|
|
696
|
+
value: row[1]
|
|
697
|
+
})) || [];
|
|
698
|
+
for (const row of rows) {
|
|
699
|
+
const field = interpolate(row.field, world.vars);
|
|
700
|
+
const value = interpolate(row.value, world.vars);
|
|
701
|
+
const locator = page.getByLabel(field);
|
|
702
|
+
await locator.clear();
|
|
703
|
+
await locator.fill(value);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// src/steps/ui.debug.ts
|
|
710
|
+
import { createBdd as createBdd11 } from "playwright-bdd";
|
|
711
|
+
function registerDebugSteps(test) {
|
|
712
|
+
const { When, Then } = createBdd11(test);
|
|
713
|
+
When(
|
|
714
|
+
"I capture the page HTML as {string}",
|
|
715
|
+
{ tags: "@ui or @hybrid" },
|
|
716
|
+
async ({ page, world }, varName) => {
|
|
717
|
+
const content = await page.content();
|
|
718
|
+
world.vars[varName] = content;
|
|
719
|
+
}
|
|
720
|
+
);
|
|
721
|
+
Then(
|
|
722
|
+
"I log the current URL",
|
|
723
|
+
{ tags: "@ui or @hybrid" },
|
|
724
|
+
async ({ page }) => {
|
|
725
|
+
console.log(`[DEBUG] Current URL: ${page.url()}`);
|
|
726
|
+
}
|
|
727
|
+
);
|
|
728
|
+
Then(
|
|
729
|
+
"I log the page title",
|
|
730
|
+
{ tags: "@ui or @hybrid" },
|
|
731
|
+
async ({ page }) => {
|
|
732
|
+
const title = await page.title();
|
|
733
|
+
console.log(`[DEBUG] Page title: ${title}`);
|
|
734
|
+
}
|
|
735
|
+
);
|
|
736
|
+
When(
|
|
737
|
+
"I save a screenshot as {string}",
|
|
738
|
+
{ tags: "@ui or @hybrid" },
|
|
739
|
+
async ({ page, world }, filename) => {
|
|
740
|
+
const resolvedFilename = interpolate(filename, world.vars);
|
|
741
|
+
await page.screenshot({ path: resolvedFilename });
|
|
742
|
+
console.log(`[DEBUG] Screenshot saved: ${resolvedFilename}`);
|
|
743
|
+
}
|
|
744
|
+
);
|
|
745
|
+
When(
|
|
746
|
+
"I save a full page screenshot as {string}",
|
|
747
|
+
{ tags: "@ui or @hybrid" },
|
|
748
|
+
async ({ page, world }, filename) => {
|
|
749
|
+
const resolvedFilename = interpolate(filename, world.vars);
|
|
750
|
+
await page.screenshot({ path: resolvedFilename, fullPage: true });
|
|
751
|
+
console.log(`[DEBUG] Full page screenshot saved: ${resolvedFilename}`);
|
|
752
|
+
}
|
|
753
|
+
);
|
|
754
|
+
When(
|
|
755
|
+
"I pause for debugging",
|
|
756
|
+
{ tags: "@ui or @hybrid" },
|
|
757
|
+
async ({ page }) => {
|
|
758
|
+
console.log('[DEBUG] Pausing for debugging. Press "Resume" in Playwright Inspector to continue.');
|
|
759
|
+
await page.pause();
|
|
760
|
+
}
|
|
761
|
+
);
|
|
762
|
+
Then(
|
|
763
|
+
"I print visible text",
|
|
764
|
+
{ tags: "@ui or @hybrid" },
|
|
765
|
+
async ({ page }) => {
|
|
766
|
+
const text = await page.innerText("body");
|
|
767
|
+
const truncated = text.length > 2e3 ? `${text.substring(0, 2e3)}...(truncated)` : text;
|
|
768
|
+
console.log(`[DEBUG] Visible text:
|
|
769
|
+
${truncated}`);
|
|
770
|
+
}
|
|
771
|
+
);
|
|
772
|
+
Then(
|
|
773
|
+
"I count elements matching {string}",
|
|
774
|
+
{ tags: "@ui or @hybrid" },
|
|
775
|
+
async ({ page, world }, selector) => {
|
|
776
|
+
const resolvedSelector = interpolate(selector, world.vars);
|
|
777
|
+
const count = await page.locator(resolvedSelector).count();
|
|
778
|
+
console.log(`[DEBUG] Found ${count} elements matching "${resolvedSelector}"`);
|
|
779
|
+
}
|
|
780
|
+
);
|
|
781
|
+
Then(
|
|
782
|
+
"I print browser console messages",
|
|
783
|
+
{ tags: "@ui or @hybrid" },
|
|
784
|
+
async ({ page }) => {
|
|
785
|
+
console.log("[DEBUG] Browser console messages will be captured from this point.");
|
|
786
|
+
page.on("console", (msg) => {
|
|
787
|
+
console.log(`[BROWSER ${msg.type().toUpperCase()}] ${msg.text()}`);
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
);
|
|
791
|
+
When(
|
|
792
|
+
"I capture viewport size",
|
|
793
|
+
{ tags: "@ui or @hybrid" },
|
|
794
|
+
async ({ page, world }) => {
|
|
795
|
+
const viewport = page.viewportSize();
|
|
796
|
+
if (viewport) {
|
|
797
|
+
world.vars.viewportWidth = String(viewport.width);
|
|
798
|
+
world.vars.viewportHeight = String(viewport.height);
|
|
799
|
+
console.log(`[DEBUG] Viewport size: ${viewport.width}x${viewport.height}`);
|
|
800
|
+
} else {
|
|
801
|
+
console.log("[DEBUG] Viewport size not available");
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
);
|
|
805
|
+
Then(
|
|
806
|
+
"I log all cookies",
|
|
807
|
+
{ tags: "@ui or @hybrid" },
|
|
808
|
+
async ({ page }) => {
|
|
809
|
+
const context = page.context();
|
|
810
|
+
const cookies = await context.cookies();
|
|
811
|
+
console.log(`[DEBUG] Cookies (${cookies.length}):`);
|
|
812
|
+
for (const cookie of cookies) {
|
|
813
|
+
console.log(` - ${cookie.name}: ${cookie.value.substring(0, 50)}${cookie.value.length > 50 ? "..." : ""}`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
);
|
|
817
|
+
Then(
|
|
818
|
+
"I log localStorage",
|
|
819
|
+
{ tags: "@ui or @hybrid" },
|
|
820
|
+
async ({ page }) => {
|
|
821
|
+
const storage = await page.evaluate(() => {
|
|
822
|
+
const items = {};
|
|
823
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
824
|
+
const key = localStorage.key(i);
|
|
825
|
+
if (key) {
|
|
826
|
+
items[key] = localStorage.getItem(key) || "";
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return items;
|
|
830
|
+
});
|
|
831
|
+
console.log("[DEBUG] localStorage:");
|
|
832
|
+
for (const [key, value] of Object.entries(storage)) {
|
|
833
|
+
const truncated = value.length > 100 ? `${value.substring(0, 100)}...` : value;
|
|
834
|
+
console.log(` - ${key}: ${truncated}`);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
);
|
|
838
|
+
When(
|
|
839
|
+
"I highlight element {string}",
|
|
840
|
+
{ tags: "@ui or @hybrid" },
|
|
841
|
+
async ({ page, world }, selector) => {
|
|
842
|
+
const resolvedSelector = interpolate(selector, world.vars);
|
|
843
|
+
await page.evaluate((sel) => {
|
|
844
|
+
const elements = document.querySelectorAll(sel);
|
|
845
|
+
elements.forEach((el) => {
|
|
846
|
+
el.style.outline = "3px solid red";
|
|
847
|
+
el.style.outlineOffset = "2px";
|
|
848
|
+
});
|
|
849
|
+
}, resolvedSelector);
|
|
850
|
+
console.log(`[DEBUG] Highlighted ${await page.locator(resolvedSelector).count()} element(s) matching "${resolvedSelector}"`);
|
|
851
|
+
}
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// src/steps/ui.layout.ts
|
|
856
|
+
import { expect as expect4 } from "@playwright/test";
|
|
857
|
+
import { createBdd as createBdd12 } from "playwright-bdd";
|
|
858
|
+
function registerLayoutSteps(test) {
|
|
859
|
+
const { Given, When, Then } = createBdd12(test);
|
|
860
|
+
Then(
|
|
861
|
+
"I should see the {string} panel",
|
|
862
|
+
{ tags: "@ui or @hybrid" },
|
|
863
|
+
async ({ page, world }, panelName) => {
|
|
864
|
+
const resolvedName = interpolate(panelName, world.vars);
|
|
865
|
+
const panel = page.getByTestId(`${resolvedName}-panel`);
|
|
866
|
+
await expect4(panel).toBeVisible();
|
|
867
|
+
}
|
|
868
|
+
);
|
|
869
|
+
Then(
|
|
870
|
+
"I should not see the {string} panel",
|
|
871
|
+
{ tags: "@ui or @hybrid" },
|
|
872
|
+
async ({ page, world }, panelName) => {
|
|
873
|
+
const resolvedName = interpolate(panelName, world.vars);
|
|
874
|
+
const panel = page.getByTestId(`${resolvedName}-panel`);
|
|
875
|
+
await expect4(panel).toBeHidden();
|
|
876
|
+
}
|
|
877
|
+
);
|
|
878
|
+
Then(
|
|
879
|
+
"the {string} panel should be {string}",
|
|
880
|
+
{ tags: "@ui or @hybrid" },
|
|
881
|
+
async ({ page, world }, panelName, state) => {
|
|
882
|
+
const resolvedName = interpolate(panelName, world.vars);
|
|
883
|
+
const panel = page.getByTestId(`${resolvedName}-panel`);
|
|
884
|
+
if (state === "visible") {
|
|
885
|
+
await expect4(panel).toBeVisible();
|
|
886
|
+
} else if (state === "hidden") {
|
|
887
|
+
await expect4(panel).toBeHidden();
|
|
888
|
+
} else {
|
|
889
|
+
throw new Error(`Unknown panel state: ${state}. Expected 'visible' or 'hidden'.`);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
);
|
|
893
|
+
Then(
|
|
894
|
+
"the {string} panel should be full width",
|
|
895
|
+
{ tags: "@ui or @hybrid" },
|
|
896
|
+
async ({ page, world }, panelName) => {
|
|
897
|
+
const resolvedName = interpolate(panelName, world.vars);
|
|
898
|
+
const panel = page.getByTestId(`${resolvedName}-panel`);
|
|
899
|
+
const isFullWidth = await panel.evaluate((el) => {
|
|
900
|
+
return !el.classList.contains("split") && !el.classList.contains("narrow") && !el.classList.contains("split-view-list");
|
|
901
|
+
});
|
|
902
|
+
expect4(isFullWidth, `Expected "${resolvedName}" panel to be full width`).toBe(true);
|
|
903
|
+
}
|
|
904
|
+
);
|
|
905
|
+
Then(
|
|
906
|
+
"the {string} panel should be narrow",
|
|
907
|
+
{ tags: "@ui or @hybrid" },
|
|
908
|
+
async ({ page, world }, panelName) => {
|
|
909
|
+
const resolvedName = interpolate(panelName, world.vars);
|
|
910
|
+
const panel = page.getByTestId(`${resolvedName}-panel`);
|
|
911
|
+
const isNarrow = await panel.evaluate((el) => {
|
|
912
|
+
return el.classList.contains("split") || el.classList.contains("narrow") || el.classList.contains("split-view-list");
|
|
913
|
+
});
|
|
914
|
+
expect4(isNarrow, `Expected "${resolvedName}" panel to be narrow`).toBe(true);
|
|
915
|
+
}
|
|
916
|
+
);
|
|
917
|
+
Then(
|
|
918
|
+
"I should see a split view layout",
|
|
919
|
+
{ tags: "@ui or @hybrid" },
|
|
920
|
+
async ({ page }) => {
|
|
921
|
+
const splitContainer = page.locator("[data-testid='split-view'], .split-view, [data-split-view]");
|
|
922
|
+
await expect4(splitContainer.first()).toBeVisible();
|
|
923
|
+
}
|
|
924
|
+
);
|
|
925
|
+
Then(
|
|
926
|
+
"I should not see a split view layout",
|
|
927
|
+
{ tags: "@ui or @hybrid" },
|
|
928
|
+
async ({ page }) => {
|
|
929
|
+
const splitContainer = page.locator("[data-testid='split-view'], .split-view, [data-split-view]");
|
|
930
|
+
const count = await splitContainer.count();
|
|
931
|
+
expect4(count === 0 || !await splitContainer.first().isVisible()).toBe(true);
|
|
932
|
+
}
|
|
933
|
+
);
|
|
934
|
+
Then(
|
|
935
|
+
"the sidebar should be visible",
|
|
936
|
+
{ tags: "@ui or @hybrid" },
|
|
937
|
+
async ({ page }) => {
|
|
938
|
+
const sidebar = page.locator("[data-testid='sidebar'], aside, nav.sidebar, .sidebar");
|
|
939
|
+
await expect4(sidebar.first()).toBeVisible();
|
|
940
|
+
}
|
|
941
|
+
);
|
|
942
|
+
Then(
|
|
943
|
+
"the sidebar should be hidden",
|
|
944
|
+
{ tags: "@ui or @hybrid" },
|
|
945
|
+
async ({ page }) => {
|
|
946
|
+
const sidebar = page.locator("[data-testid='sidebar'], aside.sidebar, nav.sidebar");
|
|
947
|
+
const count = await sidebar.count();
|
|
948
|
+
if (count > 0) {
|
|
949
|
+
await expect4(sidebar.first()).toBeHidden();
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
);
|
|
953
|
+
Then(
|
|
954
|
+
"the sidebar should be collapsed",
|
|
955
|
+
{ tags: "@ui or @hybrid" },
|
|
956
|
+
async ({ page }) => {
|
|
957
|
+
const sidebar = page.locator("[data-testid='sidebar'], aside, nav.sidebar, .sidebar").first();
|
|
958
|
+
const isCollapsed = await sidebar.evaluate((el) => {
|
|
959
|
+
return el.classList.contains("collapsed") || el.classList.contains("minimized") || el.getAttribute("data-collapsed") === "true";
|
|
960
|
+
});
|
|
961
|
+
expect4(isCollapsed, "Expected sidebar to be collapsed").toBe(true);
|
|
962
|
+
}
|
|
963
|
+
);
|
|
964
|
+
Then(
|
|
965
|
+
"I should see a modal dialog",
|
|
966
|
+
{ tags: "@ui or @hybrid" },
|
|
967
|
+
async ({ page }) => {
|
|
968
|
+
const modal = page.locator("[role='dialog'], [data-testid='modal'], .modal, [aria-modal='true']");
|
|
969
|
+
await expect4(modal.first()).toBeVisible();
|
|
970
|
+
}
|
|
971
|
+
);
|
|
972
|
+
Then(
|
|
973
|
+
"I should not see a modal dialog",
|
|
974
|
+
{ tags: "@ui or @hybrid" },
|
|
975
|
+
async ({ page }) => {
|
|
976
|
+
const modal = page.locator("[role='dialog'], [data-testid='modal'], .modal, [aria-modal='true']");
|
|
977
|
+
const count = await modal.count();
|
|
978
|
+
if (count > 0) {
|
|
979
|
+
await expect4(modal.first()).toBeHidden();
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
);
|
|
983
|
+
Then(
|
|
984
|
+
"I should see the {string} modal",
|
|
985
|
+
{ tags: "@ui or @hybrid" },
|
|
986
|
+
async ({ page, world }, modalName) => {
|
|
987
|
+
const resolvedName = interpolate(modalName, world.vars);
|
|
988
|
+
const modal = page.getByTestId(`${resolvedName}-modal`);
|
|
989
|
+
await expect4(modal).toBeVisible();
|
|
990
|
+
}
|
|
991
|
+
);
|
|
992
|
+
Given(
|
|
993
|
+
"the viewport is {string} size",
|
|
994
|
+
{ tags: "@ui or @hybrid" },
|
|
995
|
+
async ({ page }, sizeName) => {
|
|
996
|
+
const sizes = {
|
|
997
|
+
mobile: { width: 375, height: 667 },
|
|
998
|
+
tablet: { width: 768, height: 1024 },
|
|
999
|
+
desktop: { width: 1280, height: 800 },
|
|
1000
|
+
"large-desktop": { width: 1920, height: 1080 }
|
|
1001
|
+
};
|
|
1002
|
+
const size = sizes[sizeName.toLowerCase()];
|
|
1003
|
+
if (!size) {
|
|
1004
|
+
throw new Error(`Unknown viewport size: ${sizeName}. Available: ${Object.keys(sizes).join(", ")}`);
|
|
1005
|
+
}
|
|
1006
|
+
await page.setViewportSize(size);
|
|
1007
|
+
}
|
|
1008
|
+
);
|
|
1009
|
+
Given(
|
|
1010
|
+
"the viewport is {int}x{int}",
|
|
1011
|
+
{ tags: "@ui or @hybrid" },
|
|
1012
|
+
async ({ page }, width, height) => {
|
|
1013
|
+
await page.setViewportSize({ width, height });
|
|
1014
|
+
}
|
|
1015
|
+
);
|
|
1016
|
+
Then(
|
|
1017
|
+
"the layout should be responsive",
|
|
1018
|
+
{ tags: "@ui or @hybrid" },
|
|
1019
|
+
async ({ page }) => {
|
|
1020
|
+
const original = page.viewportSize();
|
|
1021
|
+
await page.setViewportSize({ width: 375, height: 667 });
|
|
1022
|
+
await page.waitForTimeout(100);
|
|
1023
|
+
await page.setViewportSize({ width: 1280, height: 800 });
|
|
1024
|
+
await page.waitForTimeout(100);
|
|
1025
|
+
if (original) {
|
|
1026
|
+
await page.setViewportSize(original);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
);
|
|
1030
|
+
Then(
|
|
1031
|
+
"the {string} tab should be active",
|
|
1032
|
+
{ tags: "@ui or @hybrid" },
|
|
1033
|
+
async ({ page, world }, tabName) => {
|
|
1034
|
+
const resolvedName = interpolate(tabName, world.vars);
|
|
1035
|
+
const tab = page.getByRole("tab", { name: resolvedName });
|
|
1036
|
+
await expect4(tab).toHaveAttribute("aria-selected", "true");
|
|
1037
|
+
}
|
|
1038
|
+
);
|
|
1039
|
+
Then(
|
|
1040
|
+
"the {string} tab should not be active",
|
|
1041
|
+
{ tags: "@ui or @hybrid" },
|
|
1042
|
+
async ({ page, world }, tabName) => {
|
|
1043
|
+
const resolvedName = interpolate(tabName, world.vars);
|
|
1044
|
+
const tab = page.getByRole("tab", { name: resolvedName });
|
|
1045
|
+
await expect4(tab).toHaveAttribute("aria-selected", "false");
|
|
1046
|
+
}
|
|
1047
|
+
);
|
|
1048
|
+
When(
|
|
1049
|
+
"I click the {string} tab",
|
|
1050
|
+
{ tags: "@ui or @hybrid" },
|
|
1051
|
+
async ({ page, world }, tabName) => {
|
|
1052
|
+
const resolvedName = interpolate(tabName, world.vars);
|
|
1053
|
+
const tab = page.getByRole("tab", { name: resolvedName });
|
|
1054
|
+
await tab.click();
|
|
1055
|
+
}
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// src/steps/ui.auth.ts
|
|
1060
|
+
import { createBdd as createBdd13 } from "playwright-bdd";
|
|
1061
|
+
|
|
1062
|
+
// src/adapters/ui/fetch-intercept-auth.adapter.ts
|
|
1063
|
+
var defaultBypassConfig = {
|
|
1064
|
+
urlPattern: "/api/",
|
|
1065
|
+
headers: {
|
|
1066
|
+
"x-user-id": (data) => data.userId,
|
|
1067
|
+
"x-tenant-id": (data) => data.tenantId || "",
|
|
1068
|
+
"x-user-roles": (data) => data.roles.join(",")
|
|
1069
|
+
},
|
|
1070
|
+
localStorageKey: "mockAuth"
|
|
1071
|
+
};
|
|
1072
|
+
var defaultBearerConfig = {
|
|
1073
|
+
urlPattern: "/api/",
|
|
1074
|
+
headers: {
|
|
1075
|
+
Authorization: (data) => `Bearer ${data.token || ""}`
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
async function setupFetchIntercept(page, authData, config = defaultBypassConfig) {
|
|
1079
|
+
const staticHeaders = {};
|
|
1080
|
+
for (const [key, value] of Object.entries(config.headers)) {
|
|
1081
|
+
staticHeaders[key] = typeof value === "function" ? value(authData) : value;
|
|
1082
|
+
}
|
|
1083
|
+
const headers = {};
|
|
1084
|
+
for (const [key, value] of Object.entries(staticHeaders)) {
|
|
1085
|
+
if (value) {
|
|
1086
|
+
headers[key] = value;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
const urlPattern = config.urlPattern instanceof RegExp ? config.urlPattern.source : config.urlPattern || "";
|
|
1090
|
+
await page.addInitScript(
|
|
1091
|
+
(params) => {
|
|
1092
|
+
if (params.localStorageKey) {
|
|
1093
|
+
localStorage.setItem(params.localStorageKey, JSON.stringify(params.authData));
|
|
1094
|
+
}
|
|
1095
|
+
const originalFetch = window.fetch;
|
|
1096
|
+
window.fetch = function(input, init) {
|
|
1097
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
1098
|
+
const shouldIntercept = !params.urlPattern || url.includes(params.urlPattern);
|
|
1099
|
+
if (shouldIntercept) {
|
|
1100
|
+
const modifiedInit = init || {};
|
|
1101
|
+
modifiedInit.headers = {
|
|
1102
|
+
...modifiedInit.headers || {},
|
|
1103
|
+
...params.headers
|
|
1104
|
+
};
|
|
1105
|
+
return originalFetch.call(this, input, modifiedInit);
|
|
1106
|
+
}
|
|
1107
|
+
return originalFetch.call(this, input, init);
|
|
1108
|
+
};
|
|
1109
|
+
},
|
|
1110
|
+
{ authData, headers, urlPattern, localStorageKey: config.localStorageKey }
|
|
1111
|
+
);
|
|
1112
|
+
}
|
|
1113
|
+
async function clearFetchIntercept(page) {
|
|
1114
|
+
await page.reload();
|
|
1115
|
+
}
|
|
1116
|
+
async function setupBypassAuth(page, userId, roles, tenantId) {
|
|
1117
|
+
await setupFetchIntercept(page, { userId, roles, tenantId }, defaultBypassConfig);
|
|
1118
|
+
}
|
|
1119
|
+
async function setupBearerAuth(page, token) {
|
|
1120
|
+
await setupFetchIntercept(
|
|
1121
|
+
page,
|
|
1122
|
+
{ userId: "", roles: [], token },
|
|
1123
|
+
defaultBearerConfig
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
// src/steps/ui.auth.ts
|
|
1128
|
+
function registerUiAuthSteps(test) {
|
|
1129
|
+
const { Given } = createBdd13(test);
|
|
1130
|
+
Given(
|
|
1131
|
+
"I am authenticated in UI as {string}",
|
|
1132
|
+
{ tags: "@ui" },
|
|
1133
|
+
async ({ page, world }, rolesStr) => {
|
|
1134
|
+
const roles = rolesStr.split(",").map((r) => r.trim());
|
|
1135
|
+
const testRunId = world.testRunId || world.vars.test_run_id || Date.now();
|
|
1136
|
+
const userId = `test-user-${testRunId}`;
|
|
1137
|
+
const isAdmin = roles.includes("admin");
|
|
1138
|
+
const tenantId = isAdmin ? void 0 : "org-a";
|
|
1139
|
+
await setupBypassAuth(page, userId, roles, tenantId);
|
|
1140
|
+
world.vars.currentUserId = userId;
|
|
1141
|
+
world.vars.currentRoles = roles.join(",");
|
|
1142
|
+
if (tenantId) {
|
|
1143
|
+
world.vars.currentTenantId = tenantId;
|
|
1144
|
+
}
|
|
1145
|
+
console.log(`[BDD] UI authenticated as ${roles.join(", ")} (user: ${userId})`);
|
|
1146
|
+
}
|
|
1147
|
+
);
|
|
1148
|
+
Given(
|
|
1149
|
+
"I am authenticated in UI as {string} for tenant {string}",
|
|
1150
|
+
{ tags: "@ui" },
|
|
1151
|
+
async ({ page, world }, rolesStr, tenantId) => {
|
|
1152
|
+
const roles = rolesStr.split(",").map((r) => r.trim());
|
|
1153
|
+
const testRunId = world.testRunId || world.vars.test_run_id || Date.now();
|
|
1154
|
+
const userId = `test-user-${testRunId}`;
|
|
1155
|
+
const resolvedTenantId = interpolate(tenantId, world.vars);
|
|
1156
|
+
await setupBypassAuth(page, userId, roles, resolvedTenantId);
|
|
1157
|
+
world.vars.currentUserId = userId;
|
|
1158
|
+
world.vars.currentTenantId = resolvedTenantId;
|
|
1159
|
+
world.vars.currentRoles = roles.join(",");
|
|
1160
|
+
console.log(`[BDD] UI authenticated as ${roles.join(", ")} for tenant ${resolvedTenantId}`);
|
|
1161
|
+
}
|
|
1162
|
+
);
|
|
1163
|
+
Given(
|
|
1164
|
+
"I am authenticated in UI as {string} with id {string}",
|
|
1165
|
+
{ tags: "@ui" },
|
|
1166
|
+
async ({ page, world }, rolesStr, userId) => {
|
|
1167
|
+
const roles = rolesStr.split(",").map((r) => r.trim());
|
|
1168
|
+
const resolvedUserId = interpolate(userId, world.vars);
|
|
1169
|
+
const tenantId = "org-a";
|
|
1170
|
+
await setupBypassAuth(page, resolvedUserId, roles, tenantId);
|
|
1171
|
+
world.vars.currentUserId = resolvedUserId;
|
|
1172
|
+
world.vars.currentTenantId = tenantId;
|
|
1173
|
+
world.vars.currentRoles = roles.join(",");
|
|
1174
|
+
console.log(`[BDD] UI authenticated as ${roles.join(", ")} with id ${resolvedUserId}`);
|
|
1175
|
+
}
|
|
1176
|
+
);
|
|
1177
|
+
Given(
|
|
1178
|
+
"I am authenticated in UI with bearer token {string}",
|
|
1179
|
+
{ tags: "@ui" },
|
|
1180
|
+
async ({ page, world }, token) => {
|
|
1181
|
+
const resolvedToken = interpolate(token, world.vars);
|
|
1182
|
+
await setupBearerAuth(page, resolvedToken);
|
|
1183
|
+
console.log("[BDD] UI authenticated with bearer token");
|
|
1184
|
+
}
|
|
1185
|
+
);
|
|
1186
|
+
Given(
|
|
1187
|
+
"I am authenticated in UI with headers:",
|
|
1188
|
+
{ tags: "@ui" },
|
|
1189
|
+
async ({ page, world }, dataTable) => {
|
|
1190
|
+
const rows = dataTable.hashes ? dataTable.hashes() : dataTable.rawTable?.slice(1).map((row) => ({
|
|
1191
|
+
header: row[0],
|
|
1192
|
+
value: row[1]
|
|
1193
|
+
})) || [];
|
|
1194
|
+
const headers = {};
|
|
1195
|
+
for (const row of rows) {
|
|
1196
|
+
headers[row.header] = interpolate(row.value, world.vars);
|
|
1197
|
+
}
|
|
1198
|
+
await setupFetchIntercept(
|
|
1199
|
+
page,
|
|
1200
|
+
{ userId: headers["x-user-id"] || "", roles: (headers["x-user-roles"] || "").split(","), tenantId: headers["x-tenant-id"] },
|
|
1201
|
+
{
|
|
1202
|
+
urlPattern: "/api/",
|
|
1203
|
+
headers,
|
|
1204
|
+
localStorageKey: "mockAuth"
|
|
1205
|
+
}
|
|
1206
|
+
);
|
|
1207
|
+
console.log(`[BDD] UI authenticated with custom headers: ${Object.keys(headers).join(", ")}`);
|
|
1208
|
+
}
|
|
1209
|
+
);
|
|
1210
|
+
Given(
|
|
1211
|
+
"I switch UI user to {string} with id {string}",
|
|
1212
|
+
{ tags: "@ui" },
|
|
1213
|
+
async ({ page, world }, rolesStr, userId) => {
|
|
1214
|
+
const roles = rolesStr.split(",").map((r) => r.trim());
|
|
1215
|
+
const resolvedUserId = interpolate(userId, world.vars);
|
|
1216
|
+
const tenantId = world.vars.currentTenantId || "org-a";
|
|
1217
|
+
await page.reload();
|
|
1218
|
+
await setupBypassAuth(page, resolvedUserId, roles, tenantId);
|
|
1219
|
+
world.vars.currentUserId = resolvedUserId;
|
|
1220
|
+
world.vars.currentRoles = roles.join(",");
|
|
1221
|
+
console.log(`[BDD] UI switched to user ${resolvedUserId} with roles ${roles.join(", ")}`);
|
|
1222
|
+
}
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
513
1226
|
// src/steps/tui.basic.ts
|
|
514
|
-
import { createBdd as
|
|
1227
|
+
import { createBdd as createBdd14 } from "playwright-bdd";
|
|
515
1228
|
function registerTuiBasicSteps(test) {
|
|
516
|
-
const { Given, When, Then } =
|
|
1229
|
+
const { Given, When, Then } = createBdd14(test);
|
|
517
1230
|
Given("I start the TUI application", { tags: "@tui" }, async ({ tui }) => {
|
|
518
1231
|
await tui.start();
|
|
519
1232
|
await tui.waitForReady();
|
|
@@ -634,9 +1347,9 @@ ${result.diff || "No diff available"}`);
|
|
|
634
1347
|
}
|
|
635
1348
|
|
|
636
1349
|
// src/steps/tui.wizard.ts
|
|
637
|
-
import { createBdd as
|
|
1350
|
+
import { createBdd as createBdd15 } from "playwright-bdd";
|
|
638
1351
|
function registerTuiWizardSteps(test) {
|
|
639
|
-
const { Given, When, Then } =
|
|
1352
|
+
const { Given, When, Then } = createBdd15(test);
|
|
640
1353
|
When("I navigate down {int} times", { tags: "@tui" }, async ({ tui }, times) => {
|
|
641
1354
|
for (let i = 0; i < times; i++) {
|
|
642
1355
|
await tui.pressKey("down");
|
|
@@ -789,10 +1502,15 @@ function registerApiSteps(test) {
|
|
|
789
1502
|
function registerUiSteps(test) {
|
|
790
1503
|
registerUiBasicSteps(test);
|
|
791
1504
|
registerWizardSteps(test);
|
|
1505
|
+
registerFormSteps(test);
|
|
1506
|
+
registerDebugSteps(test);
|
|
1507
|
+
registerLayoutSteps(test);
|
|
1508
|
+
registerUiAuthSteps(test);
|
|
792
1509
|
}
|
|
793
1510
|
function registerSharedSteps(test) {
|
|
794
1511
|
registerSharedVarSteps(test);
|
|
795
1512
|
registerSharedCleanupSteps(test);
|
|
1513
|
+
registerFlagSteps(test);
|
|
796
1514
|
}
|
|
797
1515
|
function registerHybridSuite(test) {
|
|
798
1516
|
registerHybridSteps(test);
|
|
@@ -801,6 +1519,13 @@ function registerTuiSteps(test) {
|
|
|
801
1519
|
registerTuiBasicSteps(test);
|
|
802
1520
|
registerTuiWizardSteps(test);
|
|
803
1521
|
}
|
|
1522
|
+
function registerAllSteps(test) {
|
|
1523
|
+
registerApiSteps(test);
|
|
1524
|
+
registerUiSteps(test);
|
|
1525
|
+
registerTuiSteps(test);
|
|
1526
|
+
registerSharedSteps(test);
|
|
1527
|
+
registerHybridSuite(test);
|
|
1528
|
+
}
|
|
804
1529
|
|
|
805
1530
|
export {
|
|
806
1531
|
interpolate,
|
|
@@ -809,19 +1534,33 @@ export {
|
|
|
809
1534
|
parseExpected,
|
|
810
1535
|
assertMasked,
|
|
811
1536
|
registerCleanup,
|
|
1537
|
+
defaultBypassConfig,
|
|
1538
|
+
defaultBearerConfig,
|
|
1539
|
+
setupFetchIntercept,
|
|
1540
|
+
clearFetchIntercept,
|
|
1541
|
+
setupBypassAuth,
|
|
1542
|
+
setupBearerAuth,
|
|
812
1543
|
registerApiHttpSteps,
|
|
813
1544
|
registerApiAssertionSteps,
|
|
814
1545
|
registerApiAuthSteps,
|
|
815
1546
|
registerHybridSteps,
|
|
816
1547
|
registerSharedCleanupSteps,
|
|
817
1548
|
registerSharedVarSteps,
|
|
1549
|
+
isFlagEnabled,
|
|
1550
|
+
setFlag,
|
|
1551
|
+
registerFlagSteps,
|
|
818
1552
|
registerUiBasicSteps,
|
|
819
1553
|
registerWizardSteps,
|
|
1554
|
+
registerFormSteps,
|
|
1555
|
+
registerDebugSteps,
|
|
1556
|
+
registerLayoutSteps,
|
|
1557
|
+
registerUiAuthSteps,
|
|
820
1558
|
registerTuiBasicSteps,
|
|
821
1559
|
registerTuiWizardSteps,
|
|
822
1560
|
registerApiSteps,
|
|
823
1561
|
registerUiSteps,
|
|
824
1562
|
registerSharedSteps,
|
|
825
1563
|
registerHybridSuite,
|
|
826
|
-
registerTuiSteps
|
|
1564
|
+
registerTuiSteps,
|
|
1565
|
+
registerAllSteps
|
|
827
1566
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as playwright_bdd from 'playwright-bdd';
|
|
|
2
2
|
export { test as baseTest } from 'playwright-bdd';
|
|
3
3
|
import * as _playwright_test from '@playwright/test';
|
|
4
4
|
import { APIResponse, PlaywrightTestArgs, PlaywrightWorkerArgs, APIRequestContext, Page } from '@playwright/test';
|
|
5
|
-
export { registerApiAssertionSteps, registerApiAuthSteps, registerApiHttpSteps, registerApiSteps, registerHybridSteps, registerHybridSuite, registerSharedCleanupSteps, registerSharedSteps, registerSharedVarSteps, registerTuiBasicSteps, registerTuiSteps, registerTuiWizardSteps, registerUiBasicSteps, registerUiSteps, registerWizardSteps } from './steps/index.js';
|
|
5
|
+
export { isFlagEnabled, registerAllSteps, registerApiAssertionSteps, registerApiAuthSteps, registerApiHttpSteps, registerApiSteps, registerDebugSteps, registerFlagSteps, registerFormSteps, registerHybridSteps, registerHybridSuite, registerLayoutSteps, registerSharedCleanupSteps, registerSharedSteps, registerSharedVarSteps, registerTuiBasicSteps, registerTuiSteps, registerTuiWizardSteps, registerUiAuthSteps, registerUiBasicSteps, registerUiSteps, registerWizardSteps, setFlag } from './steps/index.js';
|
|
6
6
|
|
|
7
7
|
type CleanupItem = {
|
|
8
8
|
method: 'DELETE' | 'POST' | 'PATCH' | 'PUT';
|
|
@@ -529,6 +529,64 @@ declare class TuiTesterAdapter implements TuiPort {
|
|
|
529
529
|
getConfig(): TuiConfig;
|
|
530
530
|
}
|
|
531
531
|
|
|
532
|
+
/**
|
|
533
|
+
* Fetch Intercept Auth Adapter
|
|
534
|
+
*
|
|
535
|
+
* Provides UI authentication by intercepting fetch requests and injecting headers.
|
|
536
|
+
* Useful for testing UIs that communicate with APIs using fetch.
|
|
537
|
+
*/
|
|
538
|
+
|
|
539
|
+
interface FetchInterceptAuthData {
|
|
540
|
+
userId: string;
|
|
541
|
+
tenantId?: string;
|
|
542
|
+
roles: string[];
|
|
543
|
+
token?: string;
|
|
544
|
+
}
|
|
545
|
+
interface FetchInterceptConfig {
|
|
546
|
+
/**
|
|
547
|
+
* URL pattern to match for header injection.
|
|
548
|
+
* If not provided, headers are added to all fetch requests.
|
|
549
|
+
*/
|
|
550
|
+
urlPattern?: string | RegExp;
|
|
551
|
+
/**
|
|
552
|
+
* Headers to inject. Can be static values or functions that receive auth data.
|
|
553
|
+
*/
|
|
554
|
+
headers: Record<string, string | ((data: FetchInterceptAuthData) => string)>;
|
|
555
|
+
/**
|
|
556
|
+
* Key in localStorage to store auth data for UI components to read.
|
|
557
|
+
*/
|
|
558
|
+
localStorageKey?: string;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Default configuration for bypass auth mode.
|
|
562
|
+
*/
|
|
563
|
+
declare const defaultBypassConfig: FetchInterceptConfig;
|
|
564
|
+
/**
|
|
565
|
+
* Default configuration for bearer token auth.
|
|
566
|
+
*/
|
|
567
|
+
declare const defaultBearerConfig: FetchInterceptConfig;
|
|
568
|
+
/**
|
|
569
|
+
* Set up fetch interception on a page.
|
|
570
|
+
*
|
|
571
|
+
* @param page - Playwright page
|
|
572
|
+
* @param authData - Authentication data to inject
|
|
573
|
+
* @param config - Configuration for interception
|
|
574
|
+
*/
|
|
575
|
+
declare function setupFetchIntercept(page: Page, authData: FetchInterceptAuthData, config?: FetchInterceptConfig): Promise<void>;
|
|
576
|
+
/**
|
|
577
|
+
* Clear fetch interception setup (by reloading the page).
|
|
578
|
+
* Note: There's no clean way to remove addInitScript, so we reload.
|
|
579
|
+
*/
|
|
580
|
+
declare function clearFetchIntercept(page: Page): Promise<void>;
|
|
581
|
+
/**
|
|
582
|
+
* Helper to set up bypass auth for a page.
|
|
583
|
+
*/
|
|
584
|
+
declare function setupBypassAuth(page: Page, userId: string, roles: string[], tenantId?: string): Promise<void>;
|
|
585
|
+
/**
|
|
586
|
+
* Helper to set up bearer token auth for a page.
|
|
587
|
+
*/
|
|
588
|
+
declare function setupBearerAuth(page: Page, token: string): Promise<void>;
|
|
589
|
+
|
|
532
590
|
type TagsForProjectInput = {
|
|
533
591
|
projectTag: string;
|
|
534
592
|
extraTags?: string;
|
|
@@ -537,4 +595,4 @@ type TagsForProjectInput = {
|
|
|
537
595
|
declare function tagsForProject({ projectTag, extraTags, defaultExcludes }: TagsForProjectInput): string;
|
|
538
596
|
declare function resolveExtraTags(raw?: string | null): string | undefined;
|
|
539
597
|
|
|
540
|
-
export { type ApiMethod, type ApiPort, type ApiResult, type AuthPort, type CleanupItem, type CleanupPort, type CleanupRule, type CreateBddTestOptions, DefaultCleanupAdapter, PlaywrightApiAdapter, PlaywrightUiAdapter, type TuiConfig, type TuiFactory, type TuiKeyModifiers, type TuiMouseButton, type TuiMouseEvent, type TuiMouseEventType, type TuiPort, type TuiScreenCapture, type TuiSnapshotResult, TuiTesterAdapter, type TuiWaitOptions, type UiClickMode, type UiElementState, type UiInputMode, type UiLocatorMethod, type UiPort, type UiUrlAssertMode, UniversalAuthAdapter, type World, assertMasked, createBddTest, initWorld, interpolate, parseExpected, registerCleanup, resolveExtraTags, selectPath, tagsForProject, tryParseJson };
|
|
598
|
+
export { type ApiMethod, type ApiPort, type ApiResult, type AuthPort, type CleanupItem, type CleanupPort, type CleanupRule, type CreateBddTestOptions, DefaultCleanupAdapter, type FetchInterceptAuthData, type FetchInterceptConfig, PlaywrightApiAdapter, PlaywrightUiAdapter, type TuiConfig, type TuiFactory, type TuiKeyModifiers, type TuiMouseButton, type TuiMouseEvent, type TuiMouseEventType, type TuiPort, type TuiScreenCapture, type TuiSnapshotResult, TuiTesterAdapter, type TuiWaitOptions, type UiClickMode, type UiElementState, type UiInputMode, type UiLocatorMethod, type UiPort, type UiUrlAssertMode, UniversalAuthAdapter, type World, assertMasked, clearFetchIntercept, createBddTest, defaultBearerConfig, defaultBypassConfig, initWorld, interpolate, parseExpected, registerCleanup, resolveExtraTags, selectPath, setupBearerAuth, setupBypassAuth, setupFetchIntercept, tagsForProject, tryParseJson };
|
package/dist/index.js
CHANGED
|
@@ -1,26 +1,40 @@
|
|
|
1
1
|
import {
|
|
2
2
|
assertMasked,
|
|
3
|
+
clearFetchIntercept,
|
|
4
|
+
defaultBearerConfig,
|
|
5
|
+
defaultBypassConfig,
|
|
3
6
|
interpolate,
|
|
7
|
+
isFlagEnabled,
|
|
4
8
|
parseExpected,
|
|
9
|
+
registerAllSteps,
|
|
5
10
|
registerApiAssertionSteps,
|
|
6
11
|
registerApiAuthSteps,
|
|
7
12
|
registerApiHttpSteps,
|
|
8
13
|
registerApiSteps,
|
|
9
14
|
registerCleanup,
|
|
15
|
+
registerDebugSteps,
|
|
16
|
+
registerFlagSteps,
|
|
17
|
+
registerFormSteps,
|
|
10
18
|
registerHybridSteps,
|
|
11
19
|
registerHybridSuite,
|
|
20
|
+
registerLayoutSteps,
|
|
12
21
|
registerSharedCleanupSteps,
|
|
13
22
|
registerSharedSteps,
|
|
14
23
|
registerSharedVarSteps,
|
|
15
24
|
registerTuiBasicSteps,
|
|
16
25
|
registerTuiSteps,
|
|
17
26
|
registerTuiWizardSteps,
|
|
27
|
+
registerUiAuthSteps,
|
|
18
28
|
registerUiBasicSteps,
|
|
19
29
|
registerUiSteps,
|
|
20
30
|
registerWizardSteps,
|
|
21
31
|
selectPath,
|
|
32
|
+
setFlag,
|
|
33
|
+
setupBearerAuth,
|
|
34
|
+
setupBypassAuth,
|
|
35
|
+
setupFetchIntercept,
|
|
22
36
|
tryParseJson
|
|
23
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-HSWPZQOM.js";
|
|
24
38
|
|
|
25
39
|
// src/fixtures.ts
|
|
26
40
|
import { test as base } from "playwright-bdd";
|
|
@@ -854,28 +868,42 @@ export {
|
|
|
854
868
|
UniversalAuthAdapter,
|
|
855
869
|
assertMasked,
|
|
856
870
|
base as baseTest,
|
|
871
|
+
clearFetchIntercept,
|
|
857
872
|
createBddTest,
|
|
873
|
+
defaultBearerConfig,
|
|
874
|
+
defaultBypassConfig,
|
|
858
875
|
initWorld,
|
|
859
876
|
interpolate,
|
|
877
|
+
isFlagEnabled,
|
|
860
878
|
parseExpected,
|
|
879
|
+
registerAllSteps,
|
|
861
880
|
registerApiAssertionSteps,
|
|
862
881
|
registerApiAuthSteps,
|
|
863
882
|
registerApiHttpSteps,
|
|
864
883
|
registerApiSteps,
|
|
865
884
|
registerCleanup,
|
|
885
|
+
registerDebugSteps,
|
|
886
|
+
registerFlagSteps,
|
|
887
|
+
registerFormSteps,
|
|
866
888
|
registerHybridSteps,
|
|
867
889
|
registerHybridSuite,
|
|
890
|
+
registerLayoutSteps,
|
|
868
891
|
registerSharedCleanupSteps,
|
|
869
892
|
registerSharedSteps,
|
|
870
893
|
registerSharedVarSteps,
|
|
871
894
|
registerTuiBasicSteps,
|
|
872
895
|
registerTuiSteps,
|
|
873
896
|
registerTuiWizardSteps,
|
|
897
|
+
registerUiAuthSteps,
|
|
874
898
|
registerUiBasicSteps,
|
|
875
899
|
registerUiSteps,
|
|
876
900
|
registerWizardSteps,
|
|
877
901
|
resolveExtraTags,
|
|
878
902
|
selectPath,
|
|
903
|
+
setFlag,
|
|
904
|
+
setupBearerAuth,
|
|
905
|
+
setupBypassAuth,
|
|
906
|
+
setupFetchIntercept,
|
|
879
907
|
tagsForProject,
|
|
880
908
|
tryParseJson
|
|
881
909
|
};
|
package/dist/steps/index.d.ts
CHANGED
|
@@ -10,10 +10,60 @@ declare function registerSharedCleanupSteps(test: any): void;
|
|
|
10
10
|
|
|
11
11
|
declare function registerSharedVarSteps(test: any): void;
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Feature Flag Step Definitions
|
|
15
|
+
*
|
|
16
|
+
* Steps for managing feature flags in tests.
|
|
17
|
+
* These steps store flag state in the world object for use in other steps.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Check if a feature flag is enabled.
|
|
21
|
+
* Can be used by other steps for conditional logic.
|
|
22
|
+
*/
|
|
23
|
+
declare function isFlagEnabled(world: any, flagName: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Set a feature flag value.
|
|
26
|
+
* Can be used programmatically by other steps or adapters.
|
|
27
|
+
*/
|
|
28
|
+
declare function setFlag(world: any, flagName: string, enabled: boolean): void;
|
|
29
|
+
declare function registerFlagSteps(test: any): void;
|
|
30
|
+
|
|
13
31
|
declare function registerUiBasicSteps(test: any): void;
|
|
14
32
|
|
|
15
33
|
declare function registerWizardSteps(test: any): void;
|
|
16
34
|
|
|
35
|
+
/**
|
|
36
|
+
* UI Form Step Definitions
|
|
37
|
+
*
|
|
38
|
+
* Steps for bulk form filling using Gherkin data tables.
|
|
39
|
+
* Tagged with @ui or @hybrid for selective execution.
|
|
40
|
+
*/
|
|
41
|
+
declare function registerFormSteps(test: any): void;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* UI Debug Step Definitions
|
|
45
|
+
*
|
|
46
|
+
* Steps for debugging and troubleshooting UI tests.
|
|
47
|
+
* Tagged with @ui or @hybrid for selective execution.
|
|
48
|
+
*/
|
|
49
|
+
declare function registerDebugSteps(test: any): void;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* UI Layout Assertion Step Definitions
|
|
53
|
+
*
|
|
54
|
+
* Steps for asserting layout states like panels, split views, and responsive behavior.
|
|
55
|
+
* Tagged with @ui or @hybrid for selective execution.
|
|
56
|
+
*/
|
|
57
|
+
declare function registerLayoutSteps(test: any): void;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* UI Auth Step Definitions
|
|
61
|
+
*
|
|
62
|
+
* Steps for authenticating in UI tests using fetch interception.
|
|
63
|
+
* Tagged with @ui for selective execution.
|
|
64
|
+
*/
|
|
65
|
+
declare function registerUiAuthSteps(test: any): void;
|
|
66
|
+
|
|
17
67
|
/**
|
|
18
68
|
* TUI Basic Step Definitions
|
|
19
69
|
*
|
|
@@ -32,9 +82,37 @@ declare function registerTuiBasicSteps(test: any): void;
|
|
|
32
82
|
*/
|
|
33
83
|
declare function registerTuiWizardSteps(test: any): void;
|
|
34
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Register all API step definitions.
|
|
87
|
+
* Steps are tagged with @api or @hybrid for selective execution.
|
|
88
|
+
*/
|
|
35
89
|
declare function registerApiSteps(test: any): void;
|
|
90
|
+
/**
|
|
91
|
+
* Register all UI step definitions.
|
|
92
|
+
* Steps are tagged with @ui or @hybrid for selective execution.
|
|
93
|
+
*
|
|
94
|
+
* Includes:
|
|
95
|
+
* - Basic UI steps (navigation, clicks, fills)
|
|
96
|
+
* - Wizard steps (advanced interactions, locators, assertions)
|
|
97
|
+
* - Form steps (bulk form filling)
|
|
98
|
+
* - Debug steps (debugging utilities)
|
|
99
|
+
* - Layout steps (panel, split view, responsive assertions)
|
|
100
|
+
* - Auth steps (fetch interception auth)
|
|
101
|
+
*/
|
|
36
102
|
declare function registerUiSteps(test: any): void;
|
|
103
|
+
/**
|
|
104
|
+
* Register shared step definitions (used by all test types).
|
|
105
|
+
*
|
|
106
|
+
* Includes:
|
|
107
|
+
* - Variable management steps
|
|
108
|
+
* - Cleanup steps
|
|
109
|
+
* - Feature flag steps
|
|
110
|
+
*/
|
|
37
111
|
declare function registerSharedSteps(test: any): void;
|
|
112
|
+
/**
|
|
113
|
+
* Register hybrid step definitions.
|
|
114
|
+
* Steps that work with both API and UI in the same scenario.
|
|
115
|
+
*/
|
|
38
116
|
declare function registerHybridSuite(test: any): void;
|
|
39
117
|
/**
|
|
40
118
|
* Register all TUI (Terminal User Interface) step definitions.
|
|
@@ -42,7 +120,7 @@ declare function registerHybridSuite(test: any): void;
|
|
|
42
120
|
*
|
|
43
121
|
* @example
|
|
44
122
|
* ```typescript
|
|
45
|
-
* import { createBddTest, registerTuiSteps, TuiTesterAdapter } from '@
|
|
123
|
+
* import { createBddTest, registerTuiSteps, TuiTesterAdapter } from '@esimplicity/stack-tests';
|
|
46
124
|
*
|
|
47
125
|
* const test = createBddTest({
|
|
48
126
|
* createTui: () => new TuiTesterAdapter({
|
|
@@ -54,5 +132,20 @@ declare function registerHybridSuite(test: any): void;
|
|
|
54
132
|
* ```
|
|
55
133
|
*/
|
|
56
134
|
declare function registerTuiSteps(test: any): void;
|
|
135
|
+
/**
|
|
136
|
+
* Register all step definitions for a full-featured test suite.
|
|
137
|
+
* This is a convenience function that registers API, UI, TUI, Shared, and Hybrid steps.
|
|
138
|
+
*
|
|
139
|
+
* Note: TUI steps require the optional `tui-tester` peer dependency.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* import { createBddTest, registerAllSteps } from '@esimplicity/stack-tests';
|
|
144
|
+
*
|
|
145
|
+
* const test = createBddTest({ ... });
|
|
146
|
+
* registerAllSteps(test);
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
declare function registerAllSteps(test: any): void;
|
|
57
150
|
|
|
58
|
-
export { registerApiAssertionSteps, registerApiAuthSteps, registerApiHttpSteps, registerApiSteps, registerHybridSteps, registerHybridSuite, registerSharedCleanupSteps, registerSharedSteps, registerSharedVarSteps, registerTuiBasicSteps, registerTuiSteps, registerTuiWizardSteps, registerUiBasicSteps, registerUiSteps, registerWizardSteps };
|
|
151
|
+
export { isFlagEnabled, registerAllSteps, registerApiAssertionSteps, registerApiAuthSteps, registerApiHttpSteps, registerApiSteps, registerDebugSteps, registerFlagSteps, registerFormSteps, registerHybridSteps, registerHybridSuite, registerLayoutSteps, registerSharedCleanupSteps, registerSharedSteps, registerSharedVarSteps, registerTuiBasicSteps, registerTuiSteps, registerTuiWizardSteps, registerUiAuthSteps, registerUiBasicSteps, registerUiSteps, registerWizardSteps, setFlag };
|
package/dist/steps/index.js
CHANGED
|
@@ -1,34 +1,50 @@
|
|
|
1
1
|
import {
|
|
2
|
+
isFlagEnabled,
|
|
3
|
+
registerAllSteps,
|
|
2
4
|
registerApiAssertionSteps,
|
|
3
5
|
registerApiAuthSteps,
|
|
4
6
|
registerApiHttpSteps,
|
|
5
7
|
registerApiSteps,
|
|
8
|
+
registerDebugSteps,
|
|
9
|
+
registerFlagSteps,
|
|
10
|
+
registerFormSteps,
|
|
6
11
|
registerHybridSteps,
|
|
7
12
|
registerHybridSuite,
|
|
13
|
+
registerLayoutSteps,
|
|
8
14
|
registerSharedCleanupSteps,
|
|
9
15
|
registerSharedSteps,
|
|
10
16
|
registerSharedVarSteps,
|
|
11
17
|
registerTuiBasicSteps,
|
|
12
18
|
registerTuiSteps,
|
|
13
19
|
registerTuiWizardSteps,
|
|
20
|
+
registerUiAuthSteps,
|
|
14
21
|
registerUiBasicSteps,
|
|
15
22
|
registerUiSteps,
|
|
16
|
-
registerWizardSteps
|
|
17
|
-
|
|
23
|
+
registerWizardSteps,
|
|
24
|
+
setFlag
|
|
25
|
+
} from "../chunk-HSWPZQOM.js";
|
|
18
26
|
export {
|
|
27
|
+
isFlagEnabled,
|
|
28
|
+
registerAllSteps,
|
|
19
29
|
registerApiAssertionSteps,
|
|
20
30
|
registerApiAuthSteps,
|
|
21
31
|
registerApiHttpSteps,
|
|
22
32
|
registerApiSteps,
|
|
33
|
+
registerDebugSteps,
|
|
34
|
+
registerFlagSteps,
|
|
35
|
+
registerFormSteps,
|
|
23
36
|
registerHybridSteps,
|
|
24
37
|
registerHybridSuite,
|
|
38
|
+
registerLayoutSteps,
|
|
25
39
|
registerSharedCleanupSteps,
|
|
26
40
|
registerSharedSteps,
|
|
27
41
|
registerSharedVarSteps,
|
|
28
42
|
registerTuiBasicSteps,
|
|
29
43
|
registerTuiSteps,
|
|
30
44
|
registerTuiWizardSteps,
|
|
45
|
+
registerUiAuthSteps,
|
|
31
46
|
registerUiBasicSteps,
|
|
32
47
|
registerUiSteps,
|
|
33
|
-
registerWizardSteps
|
|
48
|
+
registerWizardSteps,
|
|
49
|
+
setFlag
|
|
34
50
|
};
|
package/package.json
CHANGED
package/scripts/postinstall.cjs
CHANGED
|
@@ -78,7 +78,7 @@ function main() {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// Always show success message
|
|
81
|
-
console.log(`${GREEN}${BOLD}@
|
|
81
|
+
console.log(`${GREEN}${BOLD}@esimplicity/stack-tests${RESET} installed successfully!`);
|
|
82
82
|
console.log('');
|
|
83
83
|
}
|
|
84
84
|
|