@vardot/varbase-e2e 2.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/AGENTS.md +148 -0
- package/CLAUDE.md +544 -0
- package/README.md +22 -0
- package/bin/generate-reports.js +363 -0
- package/bin/init-varbase-e2e.js +330 -0
- package/bin/postinstall.js +26 -0
- package/cucumber.js +125 -0
- package/docs/00-quick-start.md +102 -0
- package/docs/01-getting-started.md +102 -0
- package/docs/02-bbr-smart-waits.md +77 -0
- package/docs/03-selector-registry.md +95 -0
- package/docs/04-step-reference.md +801 -0
- package/docs/05-web-first-assertions.md +91 -0
- package/docs/06-network-and-dialogs.md +83 -0
- package/docs/07-auth-state.md +75 -0
- package/docs/08-clock-mocking.md +59 -0
- package/docs/09-api-testing.md +114 -0
- package/docs/10-accessibility.md +167 -0
- package/docs/11-debugging.md +129 -0
- package/docs/12-ai-agent-guide.md +338 -0
- package/docs/13-faq.md +236 -0
- package/docs/14-recipes-cookbook.md +255 -0
- package/docs/15-tag-conventions.md +76 -0
- package/docs/16-ci-cd.md +90 -0
- package/docs/README.md +125 -0
- package/docs/advanced-screenshots/then-i-save-fullscreen-screenshot.md +36 -0
- package/docs/advanced-screenshots/then-i-save-width-x-height-screenshot.md +34 -0
- package/docs/advanced-screenshots/when-i-save-screenshot-name-filename.md +45 -0
- package/docs/advanced-screenshots.md +52 -0
- package/docs/advanced-selectors.md +18 -0
- package/docs/api-step-definitions.md +45 -0
- package/docs/global-settings.md +267 -0
- package/docs/install-varbase-e2e/ddev-varbase-e2e.md +52 -0
- package/docs/install-varbase-e2e.md +34 -0
- package/docs/overview.md +40 -0
- package/docs/step-definitions/given-i-am-anonymous-user.md +67 -0
- package/docs/step-definitions/given-i-am-homepage.md +68 -0
- package/docs/step-definitions/given-i-am-specific-page.md +72 -0
- package/docs/step-definitions/then-i-should-be.md +56 -0
- package/docs/step-definitions/then-i-should-see.md +62 -0
- package/docs/step-definitions/then-item-checkbox.md +54 -0
- package/docs/step-definitions/then-item-link-should-contain-url.md +84 -0
- package/docs/step-definitions/then-item-should-contain.md +67 -0
- package/docs/step-definitions/then-radio-button-value-should-be-selected.md +68 -0
- package/docs/step-definitions/then-response-status-code-should-be-number.md +69 -0
- package/docs/step-definitions/then-value-radio-button-selected.md +62 -0
- package/docs/step-definitions/when-i-attach-file-file-name-element.md +71 -0
- package/docs/step-definitions/when-i-check-checkbox.md +60 -0
- package/docs/step-definitions/when-i-click-link.md +51 -0
- package/docs/step-definitions/when-i-click-operation-text-row.md +71 -0
- package/docs/step-definitions/when-i-fill.md +59 -0
- package/docs/step-definitions/when-i-go-homepage.md +60 -0
- package/docs/step-definitions/when-i-go-specific-page.md +67 -0
- package/docs/step-definitions/when-i-move-backward-one-page.md +49 -0
- package/docs/step-definitions/when-i-move-forward-one-page.md +58 -0
- package/docs/step-definitions/when-i-press-button.md +58 -0
- package/docs/step-definitions/when-i-reload-page.md +51 -0
- package/docs/step-definitions/when-i-scroll.md +73 -0
- package/docs/step-definitions/when-i-select-option-select-list.md +53 -0
- package/docs/step-definitions/when-i-select-radio-button-value.md +62 -0
- package/docs/step-definitions/when-i-uncheck-checkbox.md +57 -0
- package/docs/step-definitions/when-i-wait-ajax-finish.md +64 -0
- package/docs/step-definitions/when-i-wait.md +62 -0
- package/docs/step-definitions.md +82 -0
- package/docs/steps/README.md +137 -0
- package/docs/steps/a11y.md +731 -0
- package/docs/steps/action.md +181 -0
- package/docs/steps/api.md +679 -0
- package/docs/steps/assertion.md +328 -0
- package/docs/steps/auth.md +108 -0
- package/docs/steps/clock.md +171 -0
- package/docs/steps/cookie.md +293 -0
- package/docs/steps/debug.md +52 -0
- package/docs/steps/dialog.md +226 -0
- package/docs/steps/drupal-canvas.md +414 -0
- package/docs/steps/drupal-ckeditor.md +118 -0
- package/docs/steps/drupal-core.md +688 -0
- package/docs/steps/drupal-layout-builder.md +437 -0
- package/docs/steps/drupal-media.md +107 -0
- package/docs/steps/drupal-moderation.md +81 -0
- package/docs/steps/drupal-paragraphs.md +36 -0
- package/docs/steps/element.md +455 -0
- package/docs/steps/field.md +648 -0
- package/docs/steps/file-download.md +235 -0
- package/docs/steps/form.md +344 -0
- package/docs/steps/iframe.md +248 -0
- package/docs/steps/input.md +220 -0
- package/docs/steps/javascript.md +111 -0
- package/docs/steps/keyboard.md +107 -0
- package/docs/steps/link.md +217 -0
- package/docs/steps/metatag.md +99 -0
- package/docs/steps/modal.md +232 -0
- package/docs/steps/navigation.md +275 -0
- package/docs/steps/network.md +284 -0
- package/docs/steps/path.md +203 -0
- package/docs/steps/response.md +102 -0
- package/docs/steps/responsive.md +139 -0
- package/docs/steps/rest.md +140 -0
- package/docs/steps/screenshot.md +211 -0
- package/docs/steps/scroll.md +266 -0
- package/docs/steps/selectors.md +848 -0
- package/docs/steps/storage.md +254 -0
- package/docs/steps/table.md +216 -0
- package/docs/steps/varbase.md +391 -0
- package/docs/steps/video.md +111 -0
- package/docs/steps/wait.md +553 -0
- package/docs/steps/web-first.md +289 -0
- package/docs/steps/xml.md +505 -0
- package/docs/steps/yaml.md +951 -0
- package/examples/a11y.html +31 -0
- package/examples/about-us.html +11 -0
- package/examples/account.html +32 -0
- package/examples/ajax-wait-examples.html +147 -0
- package/examples/contact-us.html +10 -0
- package/examples/cookies.html +10 -0
- package/examples/date.html +12 -0
- package/examples/dynamic-examples.html +498 -0
- package/examples/element.html +11 -0
- package/examples/example-api.json +28 -0
- package/examples/existing-page.html +11 -0
- package/examples/field.html +24 -0
- package/examples/index.html +37 -0
- package/examples/keyboard.html +11 -0
- package/examples/links.html +9 -0
- package/examples/modal.html +14 -0
- package/examples/path.html +3 -0
- package/examples/table.html +13 -0
- package/examples/test--modal-dialogs.html +252 -0
- package/examples/test--then--i-should--not--see-text-in-element.html +48 -0
- package/examples/test--then--i-should--see-text-in-element.html +48 -0
- package/examples/test--then--i-should--see-text-matching-pattern.html +31 -0
- package/examples/test--then--i-should-see-text-in-table-row.html +232 -0
- package/examples/test--then--the-checkbox-checked.html +68 -0
- package/examples/test--then--the-element-should_not-contain-cssproperty.html +47 -0
- package/examples/test--then--the-link-should-contain.html +21 -0
- package/examples/test--when--i-additionally-select.html +27 -0
- package/examples/test--when--i-attach-file-to-feild.html +50 -0
- package/examples/test--when--i-check-uncheck-checkbox.html +47 -0
- package/examples/test--when--i-click-link.html +18 -0
- package/examples/test--when--i-click-text-in-table-row.html +178 -0
- package/examples/test--when--i-fill-in.html +48 -0
- package/examples/test--when--i-press-button.html +31 -0
- package/examples/test--when--i-reload-page.html +27 -0
- package/examples/test--when--i-scroll-functionality.html +177 -0
- package/examples/test--when--i-scroll-left-right-functionality.html +149 -0
- package/examples/test--when--i-select-option-from-dropdownlist.html +35 -0
- package/examples/test--when--i-select-radio-button.html +95 -0
- package/examples/test--when-i-wait-max-of-time.html +39 -0
- package/examples/test--when-i-wait-page-until-is-loaded.html +47 -0
- package/examples/test--when-i-wait-specific-time.html +39 -0
- package/examples/test-acceptable-url-path.html +11 -0
- package/examples/video-demo.html +43 -0
- package/package.json +56 -0
- package/playwright.config.ts +39 -0
- package/tests/assets/test-document.pdf +14 -0
- package/tests/assets/vardot.png +0 -0
- package/tests/features/a11y.feature +39 -0
- package/tests/features/ajax-wait-examples.feature +24 -0
- package/tests/features/api-step-definitions-individual.feature +185 -0
- package/tests/features/complete-api-testing-examples.feature +205 -0
- package/tests/features/cookie.feature +13 -0
- package/tests/features/date.feature +11 -0
- package/tests/features/dynamic-content-examples.feature +156 -0
- package/tests/features/element.feature +16 -0
- package/tests/features/field.feature +25 -0
- package/tests/features/keyboard.feature +10 -0
- package/tests/features/link.feature +11 -0
- package/tests/features/modal-advanced-example.feature +54 -0
- package/tests/features/modal-basic-example.feature +42 -0
- package/tests/features/modal-error-handling-example.feature +37 -0
- package/tests/features/modal.feature +11 -0
- package/tests/features/path.feature +16 -0
- package/tests/features/response.feature +8 -0
- package/tests/features/responsive.feature +10 -0
- package/tests/features/table.feature +18 -0
- package/tests/features/test--given--i-am-an-anonymous-user.feature +10 -0
- package/tests/features/test--given--i-am-on-homepage.feature +16 -0
- package/tests/features/test--then--i-save-screenshot.feature +33 -0
- package/tests/features/test--then--i-see-locator.feature +320 -0
- package/tests/features/test--then--i-should--not--see-text-in-element.feature +13 -0
- package/tests/features/test--then--i-should--see-text-in-element-by-attr.feature +10 -0
- package/tests/features/test--then--i-should--see-text-in-element.feature +10 -0
- package/tests/features/test--then--i-should--see-text-matching-pattern-in-element.feature +13 -0
- package/tests/features/test--then--i-should--see-text-matching-pattern.feature +18 -0
- package/tests/features/test--then--i-should-be-on-page.feature +10 -0
- package/tests/features/test--then--i-should-be-on-the-homepage.feature +10 -0
- package/tests/features/test--then--i-should-not-see-an-element-by-attr.feature +8 -0
- package/tests/features/test--then--i-should-not-see-an-element.feature +8 -0
- package/tests/features/test--then--i-should-see-an-element-by-attr.feature +8 -0
- package/tests/features/test--then--i-should-see-an-element.feature +8 -0
- package/tests/features/test--then--i-should-see-n-elements.feature +24 -0
- package/tests/features/test--then--i-should-see-text-in-table-row.feature +96 -0
- package/tests/features/test--then--print-current-url-and-response.feature +21 -0
- package/tests/features/test--then--the-checkbox-is-checked.feature +18 -0
- package/tests/features/test--then--the-checkbox-should-be-checked.feature +16 -0
- package/tests/features/test--then--the-element-should-contain-cssproperty.feature +9 -0
- package/tests/features/test--then--the-element-should-not-contain-cssproperty.feature +10 -0
- package/tests/features/test--then--the-field-should-contain-value.feature +11 -0
- package/tests/features/test--then--the-response-should-contain.feature +11 -0
- package/tests/features/test--then--the-response-status-code-should-be.feature +31 -0
- package/tests/features/test--then--the-url-should-match.feature +7 -0
- package/tests/features/test--then-the-link-should-contain-by-attribute.feature +18 -0
- package/tests/features/test--then-the-link-should-contain.feature +7 -0
- package/tests/features/test--when--i-additionally-select.feature +30 -0
- package/tests/features/test--when--i-attach-file-to-feild.feature +9 -0
- package/tests/features/test--when--i-check-checkbox.feature +21 -0
- package/tests/features/test--when--i-click-link-by-attribute.feature +21 -0
- package/tests/features/test--when--i-click-link.feature +9 -0
- package/tests/features/test--when--i-click-text-in-table-row.feature +37 -0
- package/tests/features/test--when--i-fill-in-by-attr.feature +31 -0
- package/tests/features/test--when--i-fill-in-table-by-attr.feature +19 -0
- package/tests/features/test--when--i-fill-in.feature +32 -0
- package/tests/features/test--when--i-follow.feature +22 -0
- package/tests/features/test--when--i-go-to-homepage.feature +9 -0
- package/tests/features/test--when--i-move-forward-backward-one-page.feature +14 -0
- package/tests/features/test--when--i-press-button-by-attribute.feature +18 -0
- package/tests/features/test--when--i-press-button.feature +13 -0
- package/tests/features/test--when--i-reload-page.feature +11 -0
- package/tests/features/test--when--i-scroll-left-right.feature +48 -0
- package/tests/features/test--when--i-scroll.feature +48 -0
- package/tests/features/test--when--i-select-option-from-dropdownlist.feature +21 -0
- package/tests/features/test--when--i-select-radio-button.feature +37 -0
- package/tests/features/test--when--i-wait-page-is-loaded.feature +8 -0
- package/tests/features/test--when--i-wait-time.feature +21 -0
- package/tests/features/video.feature +18 -0
- package/tests/features/wait.feature +7 -0
- package/tests/features/yaml.feature +102 -0
- package/tests/selectors/_canonical-keys.json +113 -0
- package/tests/selectors/back-end-selectors.json +19 -0
- package/tests/selectors/cms-contentful-admin.json +35 -0
- package/tests/selectors/cms-craft-admin.json +39 -0
- package/tests/selectors/cms-drupal-cms-gin.json +56 -0
- package/tests/selectors/cms-drupal-core-claro.json +50 -0
- package/tests/selectors/cms-generic-admin.json +34 -0
- package/tests/selectors/cms-ghost-admin.json +34 -0
- package/tests/selectors/cms-joomla-admin.json +41 -0
- package/tests/selectors/cms-magento2-admin.json +32 -0
- package/tests/selectors/cms-prestashop-admin.json +38 -0
- package/tests/selectors/cms-shopify-admin.json +25 -0
- package/tests/selectors/cms-strapi-admin.json +42 -0
- package/tests/selectors/cms-typo3-admin.json +41 -0
- package/tests/selectors/cms-woocommerce-front.json +50 -0
- package/tests/selectors/cms-wordpress-admin.json +31 -0
- package/tests/selectors/framework-ant-design.json +80 -0
- package/tests/selectors/framework-bootstrap.json +86 -0
- package/tests/selectors/framework-bulma.json +74 -0
- package/tests/selectors/framework-chakra.json +58 -0
- package/tests/selectors/framework-foundation.json +51 -0
- package/tests/selectors/framework-material-ui.json +72 -0
- package/tests/selectors/framework-shadcn.json +51 -0
- package/tests/selectors/framework-tailwind.json +40 -0
- package/tests/selectors/framework-vuetify.json +61 -0
- package/tests/selectors/front-end-selectors.json +16 -0
- package/tests/selectors/homepage-selectors.json +13 -0
- package/tests/step-definitions/a11y.steps.js +800 -0
- package/tests/step-definitions/action.steps.js +167 -0
- package/tests/step-definitions/api.steps.js +732 -0
- package/tests/step-definitions/assertion.steps.js +352 -0
- package/tests/step-definitions/auth.steps.js +126 -0
- package/tests/step-definitions/clock.steps.js +128 -0
- package/tests/step-definitions/cookie.steps.js +216 -0
- package/tests/step-definitions/debug.steps.js +39 -0
- package/tests/step-definitions/dialog.steps.js +193 -0
- package/tests/step-definitions/drupal-canvas.steps.js +764 -0
- package/tests/step-definitions/drupal-ckeditor.steps.js +108 -0
- package/tests/step-definitions/drupal-core.steps.js +665 -0
- package/tests/step-definitions/drupal-helpers.js +371 -0
- package/tests/step-definitions/drupal-layout-builder.steps.js +466 -0
- package/tests/step-definitions/drupal-media.steps.js +99 -0
- package/tests/step-definitions/drupal-moderation.steps.js +71 -0
- package/tests/step-definitions/drupal-paragraphs.steps.js +50 -0
- package/tests/step-definitions/element.steps.js +356 -0
- package/tests/step-definitions/field.steps.js +653 -0
- package/tests/step-definitions/file-download.steps.js +211 -0
- package/tests/step-definitions/form.steps.js +333 -0
- package/tests/step-definitions/iframe.steps.js +228 -0
- package/tests/step-definitions/input.steps.js +202 -0
- package/tests/step-definitions/javascript.steps.js +232 -0
- package/tests/step-definitions/keyboard.steps.js +112 -0
- package/tests/step-definitions/link.steps.js +142 -0
- package/tests/step-definitions/metatag.steps.js +91 -0
- package/tests/step-definitions/modal.steps.js +244 -0
- package/tests/step-definitions/navigation.steps.js +218 -0
- package/tests/step-definitions/network.steps.js +280 -0
- package/tests/step-definitions/path.steps.js +141 -0
- package/tests/step-definitions/response.steps.js +98 -0
- package/tests/step-definitions/responsive.steps.js +125 -0
- package/tests/step-definitions/rest.steps.js +111 -0
- package/tests/step-definitions/screenshot.steps.js +423 -0
- package/tests/step-definitions/scroll.steps.js +189 -0
- package/tests/step-definitions/selectors.steps.js +1003 -0
- package/tests/step-definitions/storage.steps.js +194 -0
- package/tests/step-definitions/table.steps.js +181 -0
- package/tests/step-definitions/varbase-e2e.js +897 -0
- package/tests/step-definitions/varbase.steps.js +458 -0
- package/tests/step-definitions/video.steps.js +178 -0
- package/tests/step-definitions/wait.steps.js +400 -0
- package/tests/step-definitions/web-first.steps.js +298 -0
- package/tests/step-definitions/xml.steps.js +411 -0
- package/tests/step-definitions/yaml.steps.js +913 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Cookie / local storage / session storage manipulation steps.
|
|
4
|
+
//
|
|
5
|
+
// Pre-seed per-scenario state without going through the UI. Useful for
|
|
6
|
+
// onboarding flags, A/B-test bucket assignments, feature toggles, language
|
|
7
|
+
// preferences, and any cookie-driven server behaviour.
|
|
8
|
+
//
|
|
9
|
+
// Cookies are scoped to the current page's domain (or `localhost` if no page
|
|
10
|
+
// is loaded yet). Local storage and session storage are scoped to the current
|
|
11
|
+
// page's origin and require a navigated page first.
|
|
12
|
+
|
|
13
|
+
const { Given } = require('@cucumber/cucumber');
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Cookies — context-level
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Set a cookie on the current browser context.
|
|
21
|
+
*
|
|
22
|
+
* Example #1: Given the cookie "session" is set to "abc123"
|
|
23
|
+
* Example #2: Given the cookie "lang" is set to "en"
|
|
24
|
+
* Example #3: Given cookie "ab_bucket" is set to "variant-b"
|
|
25
|
+
* Example #4: Given the cookie "consent" is set to "accepted"
|
|
26
|
+
* And I am on the homepage
|
|
27
|
+
* Example #5: Given the cookie "feature_flag" is set to "enabled"
|
|
28
|
+
* And I am on "/dashboard"
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
31
|
+
Given(/^(?:the cookie|cookie) "([^"]*)" is set to "([^"]*)"$/, async function (name, value) {
|
|
32
|
+
const url = this.page.url() && this.page.url() !== 'about:blank' ? this.page.url() : (this.launchUrl || 'http://localhost');
|
|
33
|
+
const u = new URL(url);
|
|
34
|
+
await this.context.addCookies([{
|
|
35
|
+
name,
|
|
36
|
+
value,
|
|
37
|
+
domain: u.hostname,
|
|
38
|
+
path: '/',
|
|
39
|
+
}]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Remove a single cookie by name (other cookies preserved).
|
|
44
|
+
*
|
|
45
|
+
* Example #1: Given the cookie "session" is removed
|
|
46
|
+
* Example #2: Given the cookie "consent" is removed
|
|
47
|
+
* When I reload the page
|
|
48
|
+
* Then I should see "Accept cookies"
|
|
49
|
+
* Example #3: Given the cookie "ab_bucket" is removed
|
|
50
|
+
* Example #4: Given the cookie "lang" is removed
|
|
51
|
+
* And I am on the homepage
|
|
52
|
+
* Example #5: Given the cookie "feature_flag" is removed
|
|
53
|
+
* When I follow "Pricing"
|
|
54
|
+
*
|
|
55
|
+
*/
|
|
56
|
+
Given(/^the cookie "([^"]*)" is removed$/, async function (name) {
|
|
57
|
+
const cookies = await this.context.cookies();
|
|
58
|
+
const remaining = cookies.filter((c) => c.name !== name);
|
|
59
|
+
await this.context.clearCookies();
|
|
60
|
+
if (remaining.length) await this.context.addCookies(remaining);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Wipe every cookie on the browser context.
|
|
65
|
+
*
|
|
66
|
+
* Example #1: Given all cookies are cleared
|
|
67
|
+
* Example #2: Given all cookies are cleared
|
|
68
|
+
* When I am on the homepage
|
|
69
|
+
* Then I should see "Sign in"
|
|
70
|
+
* Example #3: Given all cookies are cleared
|
|
71
|
+
* And I am an anonymous user
|
|
72
|
+
* Example #4: Given all cookies are cleared
|
|
73
|
+
* And localStorage is cleared
|
|
74
|
+
* And sessionStorage is cleared
|
|
75
|
+
* Example #5: Given all cookies are cleared
|
|
76
|
+
* When I follow "Login"
|
|
77
|
+
*
|
|
78
|
+
*/
|
|
79
|
+
Given(/^all cookies are cleared$/, async function () {
|
|
80
|
+
await this.context.clearCookies();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// localStorage / sessionStorage — page-level
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Set a local storage entry on the current page.
|
|
89
|
+
*
|
|
90
|
+
* The page must be loaded first (local storage is per-origin). Stored value
|
|
91
|
+
* is always a string — JSON-encode complex objects manually.
|
|
92
|
+
*
|
|
93
|
+
* Example #1: Given the local storage "user" is set to "Alice"
|
|
94
|
+
* Example #2: Given I am on the homepage
|
|
95
|
+
* And the local storage "theme" is set to "dark"
|
|
96
|
+
* When I reload the page
|
|
97
|
+
* Then "<body>" should have class "theme-dark"
|
|
98
|
+
* Example #3: Given local storage "lang" is set to "fr"
|
|
99
|
+
* Example #4: Given the local storage "onboarding_complete" is set to "true"
|
|
100
|
+
* And I am on "/dashboard"
|
|
101
|
+
* Then I should not see "Welcome tour"
|
|
102
|
+
* Example #5: Given the local storage "feature_flag" is set to "enabled"
|
|
103
|
+
*
|
|
104
|
+
*/
|
|
105
|
+
Given(/^(?:the )?local storage "([^"]*)" is set to "([^"]*)"$/, async function (key, value) {
|
|
106
|
+
await this.page.evaluate(({ k, v }) => localStorage.setItem(k, v), { k: key, v: value });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Remove a single local storage entry.
|
|
111
|
+
*
|
|
112
|
+
* Example #1: Given the local storage "user" is removed
|
|
113
|
+
* Example #2: Given local storage "theme" is removed
|
|
114
|
+
* When I reload the page
|
|
115
|
+
* Then "<body>" should not have class "theme-dark"
|
|
116
|
+
* Example #3: Given the local storage "onboarding_complete" is removed
|
|
117
|
+
* Example #4: Given the local storage "cart" is removed
|
|
118
|
+
* And I am on "/cart"
|
|
119
|
+
* Then I should see "Your cart is empty"
|
|
120
|
+
* Example #5: Given the local storage "auth_token" is removed
|
|
121
|
+
*
|
|
122
|
+
*/
|
|
123
|
+
Given(/^(?:the )?local storage "([^"]*)" is removed$/, async function (key) {
|
|
124
|
+
await this.page.evaluate((k) => localStorage.removeItem(k), key);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Wipe local storage for the current origin.
|
|
129
|
+
*
|
|
130
|
+
* Example #1: Given local storage is cleared
|
|
131
|
+
* Example #2: Given I am on the homepage
|
|
132
|
+
* And local storage is cleared
|
|
133
|
+
* When I reload the page
|
|
134
|
+
* Example #3: Given local storage is cleared
|
|
135
|
+
* And all cookies are cleared
|
|
136
|
+
* Example #4: Given local storage is cleared
|
|
137
|
+
* And session storage is cleared
|
|
138
|
+
* Example #5: Given local storage is cleared
|
|
139
|
+
* Then I should see "First-time visitor"
|
|
140
|
+
*
|
|
141
|
+
*/
|
|
142
|
+
Given(/^local storage is cleared$/, async function () {
|
|
143
|
+
await this.page.evaluate(() => localStorage.clear());
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Set a session storage entry on the current page.
|
|
148
|
+
*
|
|
149
|
+
* Example #1: Given the session storage "checkout step" is set to "2"
|
|
150
|
+
* Example #2: Given the session storage "temp form" is set to "draft"
|
|
151
|
+
* Example #3: Given session storage "tab id" is set to "tab-1"
|
|
152
|
+
* Example #4: Given the session storage "wizard progress" is set to "3/5"
|
|
153
|
+
* And I am on "/wizard"
|
|
154
|
+
* Example #5: Given the session storage "from checkout" is set to "true"
|
|
155
|
+
* And I am on "/cart"
|
|
156
|
+
*
|
|
157
|
+
*/
|
|
158
|
+
Given(/^(?:the )?session storage "([^"]*)" is set to "([^"]*)"$/, async function (key, value) {
|
|
159
|
+
await this.page.evaluate(({ k, v }) => sessionStorage.setItem(k, v), { k: key, v: value });
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Remove a single session storage entry.
|
|
164
|
+
*
|
|
165
|
+
* Example #1: Given the session storage "checkout step" is removed
|
|
166
|
+
* Example #2: Given session storage "temp form" is removed
|
|
167
|
+
* Example #3: Given the session storage "wizard progress" is removed
|
|
168
|
+
* When I reload the page
|
|
169
|
+
* Then I should see "Step 1"
|
|
170
|
+
* Example #4: Given the session storage "tab id" is removed
|
|
171
|
+
* Example #5: Given the session storage "from checkout" is removed
|
|
172
|
+
*
|
|
173
|
+
*/
|
|
174
|
+
Given(/^(?:the )?session storage "([^"]*)" is removed$/, async function (key) {
|
|
175
|
+
await this.page.evaluate((k) => sessionStorage.removeItem(k), key);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Wipe session storage for the current origin.
|
|
180
|
+
*
|
|
181
|
+
* Example #1: Given session storage is cleared
|
|
182
|
+
* Example #2: Given session storage is cleared
|
|
183
|
+
* And local storage is cleared
|
|
184
|
+
* Example #3: Given session storage is cleared
|
|
185
|
+
* And all cookies are cleared
|
|
186
|
+
* Example #4: Given session storage is cleared
|
|
187
|
+
* When I reload the page
|
|
188
|
+
* Example #5: Given session storage is cleared
|
|
189
|
+
* And I am an anonymous user
|
|
190
|
+
*
|
|
191
|
+
*/
|
|
192
|
+
Given(/^session storage is cleared$/, async function () {
|
|
193
|
+
await this.page.evaluate(() => sessionStorage.clear());
|
|
194
|
+
});
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Inspect and assert HTML table contents.
|
|
4
|
+
|
|
5
|
+
const { Then } = require('@cucumber/cucumber');
|
|
6
|
+
const assert = require('assert');
|
|
7
|
+
|
|
8
|
+
async function getTable(page, sel) {
|
|
9
|
+
return await page.locator(sel).first().evaluate((tbl) => {
|
|
10
|
+
const rows = Array.from(tbl.querySelectorAll('tr'));
|
|
11
|
+
const head = Array.from((tbl.querySelector('thead') || tbl).querySelectorAll('th')).map((c) => c.textContent.trim());
|
|
12
|
+
const body = rows
|
|
13
|
+
.filter((r) => r.querySelector('td'))
|
|
14
|
+
.map((r) => Array.from(r.querySelectorAll('td')).map((c) => c.textContent.trim()));
|
|
15
|
+
return { head, body, rowCount: body.length, colCount: (body[0] || head).length };
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Assert a table has exactly N body rows.
|
|
21
|
+
*
|
|
22
|
+
* Example #1: Then the table "#users" should have 5 rows
|
|
23
|
+
* Example #2: Then the table "table.orders" should have 1 row
|
|
24
|
+
* Example #3: And the table "[data-testid=products]" should have 12 rows
|
|
25
|
+
* Example #4: Then the table "#audit-log" should have 0 rows
|
|
26
|
+
* Example #5: Then the table "table.responsive" should have 20 rows
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
Then('the table {string} should have {int} row(s)', async function (sel, count) {
|
|
30
|
+
const t = await getTable(this.page, sel);
|
|
31
|
+
assert.strictEqual(t.rowCount, count, `Table "${sel}" has ${t.rowCount} rows, expected ${count}.`);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Assert a table has exactly N columns.
|
|
36
|
+
*
|
|
37
|
+
* Example #1: Then the table "#users" should have 4 columns
|
|
38
|
+
* Example #2: Then the table "table.orders" should have 6 columns
|
|
39
|
+
* Example #3: And the table "[data-testid=products]" should have 5 columns
|
|
40
|
+
* Example #4: Then the table "#summary" should have 2 columns
|
|
41
|
+
* Example #5: Then the table "table.responsive" should have 8 columns
|
|
42
|
+
*
|
|
43
|
+
*/
|
|
44
|
+
Then('the table {string} should have {int} column(s)', async function (sel, count) {
|
|
45
|
+
const t = await getTable(this.page, sel);
|
|
46
|
+
assert.strictEqual(t.colCount, count, `Table "${sel}" has ${t.colCount} columns, expected ${count}.`);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Assert the table header contains every column listed in the table.
|
|
51
|
+
*
|
|
52
|
+
* Example #1: Then the table "#users" should contain the following columns:
|
|
53
|
+
* | Name |
|
|
54
|
+
* | Email |
|
|
55
|
+
* Example #2: Then the table "table.orders" should contain the following columns:
|
|
56
|
+
* | Order ID | Status | Total |
|
|
57
|
+
* Example #3: And the table "[data-testid=products]" should contain the following columns:
|
|
58
|
+
* | Title | Price | Stock |
|
|
59
|
+
* Example #4: Then the table "#audit-log" should contain the following columns:
|
|
60
|
+
* | Timestamp | Actor | Action |
|
|
61
|
+
* Example #5: Then the table ".grid" should contain the following columns:
|
|
62
|
+
* | Country | Population | Capital |
|
|
63
|
+
*
|
|
64
|
+
*/
|
|
65
|
+
Then('the table {string} should contain the following columns:', async function (sel, table) {
|
|
66
|
+
const t = await getTable(this.page, sel);
|
|
67
|
+
const want = table.raw().flat();
|
|
68
|
+
for (const col of want) {
|
|
69
|
+
assert.ok(t.head.includes(col), `Column "${col}" not found in [${t.head.join(', ')}].`);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Assert a table has zero body rows.
|
|
75
|
+
*
|
|
76
|
+
* Example #1: Then the table "#users" should be empty
|
|
77
|
+
* Example #2: Then the table "table.orders" should be empty
|
|
78
|
+
* Example #3: And the table "[data-testid=products]" should be empty
|
|
79
|
+
* Example #4: Then the table "#audit-log" should be empty
|
|
80
|
+
* Example #5: Then the table ".grid" should be empty
|
|
81
|
+
*
|
|
82
|
+
*/
|
|
83
|
+
Then('the table {string} should be empty', async function (sel) {
|
|
84
|
+
const t = await getTable(this.page, sel);
|
|
85
|
+
assert.strictEqual(t.rowCount, 0, `Table "${sel}" should be empty.`);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Assert a table has at least one body row.
|
|
90
|
+
*
|
|
91
|
+
* Example #1: Then the table "#users" should not be empty
|
|
92
|
+
* Example #2: Then the table "table.orders" should not be empty
|
|
93
|
+
* Example #3: And the table "[data-testid=products]" should not be empty
|
|
94
|
+
* Example #4: Then the table "#audit-log" should not be empty
|
|
95
|
+
* Example #5: Then the table ".grid" should not be empty
|
|
96
|
+
*
|
|
97
|
+
*/
|
|
98
|
+
Then('the table {string} should not be empty', async function (sel) {
|
|
99
|
+
const t = await getTable(this.page, sel);
|
|
100
|
+
assert.ok(t.rowCount > 0, `Table "${sel}" should not be empty.`);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Assert a table is sorted ascending or descending by a header.
|
|
105
|
+
*
|
|
106
|
+
* Direction accepts `ascending` / `asc` / `descending` / `desc`.
|
|
107
|
+
*
|
|
108
|
+
* Example #1: Then the table "#users" should be sorted by "Name" in "ascending" order
|
|
109
|
+
* Example #2: Then the table "table.orders" should be sorted by "Total" in "descending" order
|
|
110
|
+
* Example #3: And the table "[data-testid=products]" should be sorted by "Price" in "asc" order
|
|
111
|
+
* Example #4: Then the table "#audit-log" should be sorted by "Timestamp" in "desc" order
|
|
112
|
+
* Example #5: Then the table ".grid" should be sorted by "Country" in "ascending" order
|
|
113
|
+
*
|
|
114
|
+
*/
|
|
115
|
+
Then('the table {string} should be sorted by {string} in {string} order', async function (sel, column, direction) {
|
|
116
|
+
const t = await getTable(this.page, sel);
|
|
117
|
+
const idx = t.head.findIndex((h) => h === column);
|
|
118
|
+
assert.ok(idx !== -1, `Column "${column}" not found.`);
|
|
119
|
+
const values = t.body.map((r) => r[idx]);
|
|
120
|
+
const sorted = [...values].sort();
|
|
121
|
+
if (direction.toLowerCase() === 'descending' || direction.toLowerCase() === 'desc') sorted.reverse();
|
|
122
|
+
assert.deepStrictEqual(values, sorted, `Table "${sel}" not sorted by "${column}" ${direction}.`);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Assert a table contains every row described by the data table (substring match per cell).
|
|
127
|
+
*
|
|
128
|
+
* Example #1: Then the table "#users" should contain the following rows:
|
|
129
|
+
* | Alice | Editor |
|
|
130
|
+
* | Bob | Admin |
|
|
131
|
+
* Example #2: Then the table "table.orders" should contain the following rows:
|
|
132
|
+
* | 1234 | Shipped |
|
|
133
|
+
* | 5678 | Cancelled |
|
|
134
|
+
* Example #3: And the table "[data-testid=products]" should contain the following rows:
|
|
135
|
+
* | Laptop | $999.00 |
|
|
136
|
+
* Example #4: Then the table "#audit-log" should contain the following rows:
|
|
137
|
+
* | login |
|
|
138
|
+
* Example #5: Then the table ".grid" should contain the following rows:
|
|
139
|
+
* | Jordan | Amman |
|
|
140
|
+
*
|
|
141
|
+
*/
|
|
142
|
+
Then('the table {string} should contain the following rows:', async function (sel, table) {
|
|
143
|
+
const t = await getTable(this.page, sel);
|
|
144
|
+
for (const row of table.raw()) {
|
|
145
|
+
const found = t.body.some((br) => row.every((cell) => br.some((bc) => bc.indexOf(cell) !== -1)));
|
|
146
|
+
assert.ok(found, `Row ${JSON.stringify(row)} not found in table.`);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Assert a row containing a given text fragment also contains every listed cell value.
|
|
152
|
+
*
|
|
153
|
+
* Example #1: Then the "Alice" row should contain the following:
|
|
154
|
+
* | Editor |
|
|
155
|
+
* | Active |
|
|
156
|
+
* Example #2: Then the "Order #1234" row should contain the following:
|
|
157
|
+
* | Shipped |
|
|
158
|
+
* Example #3: And the "Project Alpha" row should contain the following:
|
|
159
|
+
* | Open |
|
|
160
|
+
* | High |
|
|
161
|
+
* Example #4: Then the "alice@example.com" row should contain the following:
|
|
162
|
+
* | Admin |
|
|
163
|
+
* Example #5: Then the "Premium" row should contain the following:
|
|
164
|
+
* | $99 |
|
|
165
|
+
*
|
|
166
|
+
*/
|
|
167
|
+
Then('the {string} row should contain the following:', async function (rowText, table) {
|
|
168
|
+
const cells = table.raw().flat();
|
|
169
|
+
// Find a tr whose text contains rowText.
|
|
170
|
+
const trs = await this.page.locator('tr').all();
|
|
171
|
+
let match = null;
|
|
172
|
+
for (const tr of trs) {
|
|
173
|
+
const txt = await tr.innerText();
|
|
174
|
+
if (txt.indexOf(rowText) !== -1) { match = tr; break; }
|
|
175
|
+
}
|
|
176
|
+
assert.ok(match, `No row matching "${rowText}" was found.`);
|
|
177
|
+
const text = await match.innerText();
|
|
178
|
+
for (const c of cells) {
|
|
179
|
+
assert.ok(text.indexOf(c) !== -1, `Row "${rowText}" does not contain "${c}".`);
|
|
180
|
+
}
|
|
181
|
+
});
|