@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,653 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Form field assertions and interactions.
|
|
4
|
+
|
|
5
|
+
const { Given, When, Then } = require('@cucumber/cucumber');
|
|
6
|
+
const assert = require('assert');
|
|
7
|
+
|
|
8
|
+
// Resolve a field by label, name, id, or selector.
|
|
9
|
+
function fieldLocator(page, field) {
|
|
10
|
+
if (field.startsWith('#') || field.startsWith('.') || field.startsWith('[')) {
|
|
11
|
+
return page.locator(field).first();
|
|
12
|
+
}
|
|
13
|
+
// Try label, then by name/id.
|
|
14
|
+
return page.locator(
|
|
15
|
+
`label:has-text("${field}") >> xpath=..//input | label:has-text("${field}") >> xpath=..//textarea | label:has-text("${field}") >> xpath=..//select, [name="${field}"], #${field}`
|
|
16
|
+
).first().or(page.getByLabel(field).first());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function getField(page, field) {
|
|
20
|
+
// Prefer Playwright's accessible getByLabel; fallback to name/id/CSS.
|
|
21
|
+
if (field.startsWith('#') || field.startsWith('.') || field.startsWith('[')) {
|
|
22
|
+
return page.locator(field).first();
|
|
23
|
+
}
|
|
24
|
+
let loc = page.getByLabel(field).first();
|
|
25
|
+
if (await loc.count() > 0) return loc;
|
|
26
|
+
loc = page.locator(`[name="${field}"], #${field}`).first();
|
|
27
|
+
return loc;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Assert a field's current value is the empty string.
|
|
32
|
+
*
|
|
33
|
+
* Example #1: Then the field "username" should be empty
|
|
34
|
+
* Example #2: Then the field "Email" should be empty
|
|
35
|
+
* Example #3: And the field "#search" should be empty
|
|
36
|
+
* Example #4: Then the field "[name=phone]" should be empty
|
|
37
|
+
* Example #5: When I fill in "" for "Username"
|
|
38
|
+
* Then the field "username" should be empty
|
|
39
|
+
*
|
|
40
|
+
*/
|
|
41
|
+
Then('the field {string} should be empty', async function (field) {
|
|
42
|
+
const loc = await getField(this.page, field);
|
|
43
|
+
const v = await loc.inputValue();
|
|
44
|
+
assert.strictEqual(v, '', `Field "${field}" should be empty, got "${v}".`);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Assert a field has a non-empty value.
|
|
49
|
+
*
|
|
50
|
+
* Example #1: Then the field "username" should not be empty
|
|
51
|
+
* Example #2: Then the field "Email" should not be empty
|
|
52
|
+
* Example #3: And the field "#search" should not be empty
|
|
53
|
+
* Example #4: When I fill in "alice" for "Username"
|
|
54
|
+
* Then the field "username" should not be empty
|
|
55
|
+
* Example #5: Then the field "[name=phone]" should not be empty
|
|
56
|
+
*
|
|
57
|
+
*/
|
|
58
|
+
Then('the field {string} should not be empty', async function (field) {
|
|
59
|
+
const loc = await getField(this.page, field);
|
|
60
|
+
const v = await loc.inputValue();
|
|
61
|
+
assert.notStrictEqual(v, '', `Field "${field}" should not be empty.`);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Assert a field exists in the DOM (any state).
|
|
66
|
+
*
|
|
67
|
+
* Example #1: Then the field "username" should exist
|
|
68
|
+
* Example #2: Then the field "Email" should exist
|
|
69
|
+
* Example #3: And the field "#search" should exist
|
|
70
|
+
* Example #4: Then the field "Password" should exist
|
|
71
|
+
* Example #5: Then the field "[name=phone]" should exist
|
|
72
|
+
*
|
|
73
|
+
*/
|
|
74
|
+
Then('the field {string} should exist', async function (field) {
|
|
75
|
+
const loc = await getField(this.page, field);
|
|
76
|
+
assert.ok(await loc.count() > 0, `Field "${field}" not found.`);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Assert a field does NOT exist in the DOM.
|
|
81
|
+
*
|
|
82
|
+
* Example #1: Then the field "missing-field" should not exist
|
|
83
|
+
* Example #2: Then the field "Legacy field" should not exist
|
|
84
|
+
* Example #3: And the field "#deprecated" should not exist
|
|
85
|
+
* Example #4: Then the field "Old name" should not exist
|
|
86
|
+
* Example #5: Then the field "[name=internal_only]" should not exist
|
|
87
|
+
*
|
|
88
|
+
*/
|
|
89
|
+
Then('the field {string} should not exist', async function (field) {
|
|
90
|
+
const loc = await getField(this.page, field);
|
|
91
|
+
assert.strictEqual(await loc.count(), 0, `Field "${field}" should not exist.`);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Assert a field's enabled / disabled state.
|
|
96
|
+
*
|
|
97
|
+
* Example #1: Then the field "username" should have "enabled" state
|
|
98
|
+
* Example #2: Then the field "Email" should have "disabled" state
|
|
99
|
+
* Example #3: And the field "#search" should have "enabled" state
|
|
100
|
+
* Example #4: Then the field "Password" should have "disabled" state
|
|
101
|
+
* Example #5: Then the field "Country" should have "enabled" state
|
|
102
|
+
*
|
|
103
|
+
*/
|
|
104
|
+
Then('the field {string} should have {string} state', async function (field, state) {
|
|
105
|
+
const loc = await getField(this.page, field);
|
|
106
|
+
const disabled = await loc.isDisabled();
|
|
107
|
+
if (state === 'disabled') assert.ok(disabled, `Field "${field}" should be disabled.`);
|
|
108
|
+
else assert.ok(!disabled, `Field "${field}" should be enabled.`);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Assert a field has the `required` or `aria-required="true"` attribute.
|
|
113
|
+
*
|
|
114
|
+
* Example #1: Then the field "username" should be required
|
|
115
|
+
* Example #2: Then the field "Email" should be required
|
|
116
|
+
* Example #3: And the field "Password" should be required
|
|
117
|
+
* Example #4: Then the field "Country" should be required
|
|
118
|
+
* Example #5: Then the field "Card number" should be required
|
|
119
|
+
*
|
|
120
|
+
*/
|
|
121
|
+
Then('the field {string} should be required', async function (field) {
|
|
122
|
+
const loc = await getField(this.page, field);
|
|
123
|
+
const req = await loc.getAttribute('required');
|
|
124
|
+
const aria = await loc.getAttribute('aria-required');
|
|
125
|
+
assert.ok(req !== null || aria === 'true', `Field "${field}" should be required.`);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Assert a field is NOT required.
|
|
130
|
+
*
|
|
131
|
+
* Example #1: Then the field "bio" should not be required
|
|
132
|
+
* Example #2: Then the field "Nickname" should not be required
|
|
133
|
+
* Example #3: And the field "Phone" should not be required
|
|
134
|
+
* Example #4: Then the field "Comments" should not be required
|
|
135
|
+
* Example #5: Then the field "Twitter handle" should not be required
|
|
136
|
+
*
|
|
137
|
+
*/
|
|
138
|
+
Then('the field {string} should not be required', async function (field) {
|
|
139
|
+
const loc = await getField(this.page, field);
|
|
140
|
+
const req = await loc.getAttribute('required');
|
|
141
|
+
const aria = await loc.getAttribute('aria-required');
|
|
142
|
+
assert.ok(req === null && aria !== 'true', `Field "${field}" should not be required.`);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Fill a multi-value form field (e.g. `name[]` array inputs) from a table.
|
|
147
|
+
*
|
|
148
|
+
* Example #1: When I fill in the multi-value field "tags" with the following values:
|
|
149
|
+
* | bdd |
|
|
150
|
+
* | testing |
|
|
151
|
+
* Example #2: When I fill in the multi-value field "skills" with the following values:
|
|
152
|
+
* | js |
|
|
153
|
+
* | css |
|
|
154
|
+
* | sql |
|
|
155
|
+
* Example #3: And we fill in the multi-value field "phones" with the following values:
|
|
156
|
+
* | 0790000000 |
|
|
157
|
+
* | 0791111111 |
|
|
158
|
+
* Example #4: When I fill in the multi-value field "ids" with the following values:
|
|
159
|
+
* | 1 |
|
|
160
|
+
* | 2 |
|
|
161
|
+
* Example #5: When I fill in the multi-value field "categories" with the following values:
|
|
162
|
+
* | tech |
|
|
163
|
+
* | devops |
|
|
164
|
+
* | testing |
|
|
165
|
+
*
|
|
166
|
+
*/
|
|
167
|
+
When(/^(I |we )*fill in the multi-value field "([^"]*)" with the following values:$/, async function (pronoun, field, table) {
|
|
168
|
+
const values = table.raw().flat();
|
|
169
|
+
for (let i = 0; i < values.length; i++) {
|
|
170
|
+
const inputs = this.page.locator(`[name="${field}[${i}]"], [name="${field}[]"]`);
|
|
171
|
+
const count = await inputs.count();
|
|
172
|
+
const target = count > i ? inputs.nth(i) : inputs.first();
|
|
173
|
+
await target.fill(values[i]);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Fill an `<input type="color">` field with a hex value.
|
|
179
|
+
*
|
|
180
|
+
* Example #1: When I fill in the color field "favorite" with the value "#ff0000"
|
|
181
|
+
* Example #2: When I fill in the color field "primary" with the value "#0066cc"
|
|
182
|
+
* Example #3: And we fill in the color field "Accent" with the value "#00aa66"
|
|
183
|
+
* Example #4: When I fill in the color field "#bg-color" with the value "#000000"
|
|
184
|
+
* Example #5: When I fill in the color field "Theme" with the value "#ffaa00"
|
|
185
|
+
*
|
|
186
|
+
*/
|
|
187
|
+
When(/^(I |we )*fill in the color field "([^"]*)" with the value "([^"]*)"$/, async function (pronoun, field, value) {
|
|
188
|
+
const loc = await getField(this.page, field);
|
|
189
|
+
await loc.fill(value);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Assert an `<input type="color">` field's current value (case-insensitive).
|
|
194
|
+
*
|
|
195
|
+
* Example #1: Then the color field "favorite" should have the value "#ff0000"
|
|
196
|
+
* Example #2: Then the color field "primary" should have the value "#0066cc"
|
|
197
|
+
* Example #3: And the color field "Accent" should have the value "#00aa66"
|
|
198
|
+
* Example #4: Then the color field "#bg-color" should have the value "#000000"
|
|
199
|
+
* Example #5: Then the color field "Theme" should have the value "#ffaa00"
|
|
200
|
+
*
|
|
201
|
+
*/
|
|
202
|
+
Then('the color field {string} should have the value {string}', async function (field, value) {
|
|
203
|
+
const loc = await getField(this.page, field);
|
|
204
|
+
assert.strictEqual((await loc.inputValue()).toLowerCase(), value.toLowerCase());
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Fill a CKEditor / contenteditable rich-text field with HTML.
|
|
209
|
+
*
|
|
210
|
+
* Example #1: When I fill in the WYSIWYG field "body" with the "Hello world"
|
|
211
|
+
* Example #2: When I fill in the WYSIWYG field "Description" with the "<strong>Bold</strong>"
|
|
212
|
+
* Example #3: And we fill in the WYSIWYG field "content" with the "Plain paragraph"
|
|
213
|
+
* Example #4: When I fill in the WYSIWYG field "post" with the "Multi line\ntext"
|
|
214
|
+
* Example #5: When I fill in the WYSIWYG field "Notes" with the "Final notes here"
|
|
215
|
+
*
|
|
216
|
+
*/
|
|
217
|
+
When(/^(I |we )*fill in the WYSIWYG field "([^"]*)" with the "([^"]*)"$/, async function (pronoun, field, value) {
|
|
218
|
+
// CKEditor5/4 fallback: try contenteditable.
|
|
219
|
+
await this.page.evaluate(({ field, value }) => {
|
|
220
|
+
const editor = document.querySelector(`[name="${field}"], #${field}`);
|
|
221
|
+
if (editor && 'value' in editor) editor.value = value;
|
|
222
|
+
const ce = document.querySelector('.ck-editor__editable, [contenteditable="true"]');
|
|
223
|
+
if (ce) ce.innerHTML = `<p>${value}</p>`;
|
|
224
|
+
}, { field, value });
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Assert a `<select>` contains an option with the given visible text.
|
|
229
|
+
*
|
|
230
|
+
* Example #1: Then the option "Mercedes" should exist within the select element "#cars"
|
|
231
|
+
* Example #2: Then the option "Editor" should exist within the select element "select[name=role]"
|
|
232
|
+
* Example #3: And the option "English" should exist within the select element "#language"
|
|
233
|
+
* Example #4: Then the option "Premium" should exist within the select element "#plan"
|
|
234
|
+
* Example #5: Then the option "Jordan" should exist within the select element "#country"
|
|
235
|
+
*
|
|
236
|
+
*/
|
|
237
|
+
Then('the option {string} should exist within the select element {string}', async function (option, sel) {
|
|
238
|
+
const count = await this.page.locator(`${sel} option:has-text("${option}")`).count();
|
|
239
|
+
assert.ok(count > 0, `Option "${option}" not found in "${sel}".`);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Assert a `<select>` does NOT contain an option with the given text.
|
|
244
|
+
*
|
|
245
|
+
* Example #1: Then the option "Manager" should not exist within the select element "#role"
|
|
246
|
+
* Example #2: Then the option "Trial" should not exist within the select element "#plan"
|
|
247
|
+
* Example #3: And the option "Old country" should not exist within the select element "#country"
|
|
248
|
+
* Example #4: Then the option "Hidden" should not exist within the select element "#status"
|
|
249
|
+
* Example #5: Then the option "Legacy" should not exist within the select element "#mode"
|
|
250
|
+
*
|
|
251
|
+
*/
|
|
252
|
+
Then('the option {string} should not exist within the select element {string}', async function (option, sel) {
|
|
253
|
+
const count = await this.page.locator(`${sel} option:has-text("${option}")`).count();
|
|
254
|
+
assert.strictEqual(count, 0, `Option "${option}" should not exist in "${sel}".`);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Assert an option is currently selected in a `<select>`.
|
|
259
|
+
*
|
|
260
|
+
* Example #1: Then the option "Mercedes" should be selected within the select element "#cars"
|
|
261
|
+
* Example #2: Then the option "Editor" should be selected within the select element "#role"
|
|
262
|
+
* Example #3: And the option "English" should be selected within the select element "#language"
|
|
263
|
+
* Example #4: Then the option "Premium" should be selected within the select element "#plan"
|
|
264
|
+
* Example #5: Then the option "Jordan" should be selected within the select element "#country"
|
|
265
|
+
*
|
|
266
|
+
*/
|
|
267
|
+
Then('the option {string} should be selected within the select element {string}', async function (option, sel) {
|
|
268
|
+
const selected = await this.page.locator(sel).first().evaluate((el) => Array.from(el.selectedOptions || []).map((o) => o.text));
|
|
269
|
+
assert.ok(selected.includes(option), `Option "${option}" not selected in "${sel}".`);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Assert an option is NOT currently selected in a `<select>`.
|
|
274
|
+
*
|
|
275
|
+
* Example #1: Then the option "Admin" should not be selected within the select element "#role"
|
|
276
|
+
* Example #2: Then the option "Pro" should not be selected within the select element "#plan"
|
|
277
|
+
* Example #3: And the option "Other" should not be selected within the select element "#country"
|
|
278
|
+
* Example #4: Then the option "Inactive" should not be selected within the select element "#status"
|
|
279
|
+
* Example #5: Then the option "Trial" should not be selected within the select element "#mode"
|
|
280
|
+
*
|
|
281
|
+
*/
|
|
282
|
+
Then('the option {string} should not be selected within the select element {string}', async function (option, sel) {
|
|
283
|
+
const selected = await this.page.locator(sel).first().evaluate((el) => Array.from(el.selectedOptions || []).map((o) => o.text));
|
|
284
|
+
assert.ok(!selected.includes(option), `Option "${option}" should not be selected in "${sel}".`);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Unselect a single option from a `<select multiple>`.
|
|
289
|
+
*
|
|
290
|
+
* Example #1: When I unselect "Red" from "#colors"
|
|
291
|
+
* Example #2: When I unselect "Editor" from "#role"
|
|
292
|
+
* Example #3: And we unselect "tag-a" from "select[name=tags]"
|
|
293
|
+
* Example #4: When I unselect "EN" from "#languages"
|
|
294
|
+
* Example #5: When I unselect "Beta" from "#features"
|
|
295
|
+
*
|
|
296
|
+
*/
|
|
297
|
+
When(/^(I |we )*unselect "([^"]*)" from "([^"]*)"$/, async function (pronoun, option, sel) {
|
|
298
|
+
await this.page.locator(sel).first().evaluate((el, opt) => {
|
|
299
|
+
Array.from(el.options).forEach((o) => { if (o.text === opt || o.value === opt) o.selected = false; });
|
|
300
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
301
|
+
}, option);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Clear all selections in a `<select multiple>`.
|
|
306
|
+
*
|
|
307
|
+
* Example #1: When I clear the select "#colors"
|
|
308
|
+
* Example #2: When I clear the select "#tags"
|
|
309
|
+
* Example #3: And I clear the select "select[name=roles]"
|
|
310
|
+
* Example #4: When I clear the select "#languages"
|
|
311
|
+
* Example #5: When I clear the select "#features"
|
|
312
|
+
*
|
|
313
|
+
*/
|
|
314
|
+
When(/^(I |we )*clear the select "([^"]*)"$/, async function (pronoun, sel) {
|
|
315
|
+
await this.page.locator(sel).first().evaluate((el) => {
|
|
316
|
+
Array.from(el.options).forEach((o) => (o.selected = false));
|
|
317
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Check a CSS-addressed checkbox.
|
|
323
|
+
*
|
|
324
|
+
* Example #1: When I check the checkbox "#agree"
|
|
325
|
+
* Example #2: When I check the checkbox "input[name=newsletter]"
|
|
326
|
+
* Example #3: And we check the checkbox ".terms"
|
|
327
|
+
* Example #4: When I check the checkbox "#remember-me"
|
|
328
|
+
* Example #5: When I check the checkbox "[data-testid=privacy-checkbox]"
|
|
329
|
+
*
|
|
330
|
+
*/
|
|
331
|
+
When(/^(I |we )*check the checkbox "([^"]*)"$/, async function (pronoun, sel) {
|
|
332
|
+
await this.page.locator(sel).first().check();
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Uncheck a CSS-addressed checkbox.
|
|
337
|
+
*
|
|
338
|
+
* Example #1: When I uncheck the checkbox "#newsletter"
|
|
339
|
+
* Example #2: When I uncheck the checkbox "input[name=marketing]"
|
|
340
|
+
* Example #3: And we uncheck the checkbox ".analytics-opt-in"
|
|
341
|
+
* Example #4: When I uncheck the checkbox "#remember-me"
|
|
342
|
+
* Example #5: When I uncheck the checkbox "[data-testid=privacy-checkbox]"
|
|
343
|
+
*
|
|
344
|
+
*/
|
|
345
|
+
When(/^(I |we )*uncheck the checkbox "([^"]*)"$/, async function (pronoun, sel) {
|
|
346
|
+
await this.page.locator(sel).first().uncheck();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Select a CSS-addressed radio button.
|
|
351
|
+
*
|
|
352
|
+
* Example #1: When I choose the radio button "#gender-male"
|
|
353
|
+
* Example #2: When I choose the radio button "input[value=premium]"
|
|
354
|
+
* Example #3: And we choose the radio button ".plan-monthly"
|
|
355
|
+
* Example #4: When I choose the radio button "#yes"
|
|
356
|
+
* Example #5: When I choose the radio button "[data-testid=annual]"
|
|
357
|
+
*
|
|
358
|
+
*/
|
|
359
|
+
When(/^(I |we )*choose the radio button "([^"]*)"$/, async function (pronoun, sel) {
|
|
360
|
+
await this.page.locator(sel).first().check();
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// Note: "the radio button :sel should be selected" is already provided by
|
|
364
|
+
// varbase-e2e.js core via a regex with the same phrasing. Skipped to avoid
|
|
365
|
+
// ambiguous step matches.
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Fill a CSS-addressed input/textarea with a value.
|
|
369
|
+
*
|
|
370
|
+
* Example #1: When I fill in the field "#username" with "alice"
|
|
371
|
+
* Example #2: When I fill in the field "input[name=email]" with "alice@example.com"
|
|
372
|
+
* Example #3: And we fill in the field "#search" with "laptops"
|
|
373
|
+
* Example #4: When I fill in the field "[data-testid=phone]" with "0790000000"
|
|
374
|
+
* Example #5: When I fill in the field "textarea#message" with "Hello"
|
|
375
|
+
*
|
|
376
|
+
*/
|
|
377
|
+
When(/^(I |we )*fill in the field "([^"]*)" with "([^"]*)"$/, async function (pronoun, sel, value) {
|
|
378
|
+
await this.page.locator(sel).first().fill(value);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Disable native browser validation on a form (sets `novalidate`).
|
|
383
|
+
*
|
|
384
|
+
* Useful when a test needs to submit an intentionally invalid form to
|
|
385
|
+
* exercise server-side error rendering.
|
|
386
|
+
*
|
|
387
|
+
* Example #1: Given browser validation for the form "#signup" is disabled
|
|
388
|
+
* Example #2: Given browser validation for the form "form[name=login]" is disabled
|
|
389
|
+
* Example #3: And browser validation for the form ".checkout-form" is disabled
|
|
390
|
+
* Example #4: Given browser validation for the form "#contact" is disabled
|
|
391
|
+
* Example #5: Given browser validation for the form "[data-testid=newsletter]" is disabled
|
|
392
|
+
*
|
|
393
|
+
*/
|
|
394
|
+
Given('browser validation for the form {string} is disabled', async function (sel) {
|
|
395
|
+
await this.page.evaluate((s) => {
|
|
396
|
+
const f = document.querySelector(s);
|
|
397
|
+
if (f) f.setAttribute('novalidate', 'novalidate');
|
|
398
|
+
}, sel);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Fill paired `<input type=date>` + `<input type=time>` controls for a label.
|
|
403
|
+
*
|
|
404
|
+
* Example #1: When I fill in the datetime field "Start" with date "2026-05-08" and time "10:00"
|
|
405
|
+
* Example #2: When I fill in the datetime field "Departure" with date "2026-12-31" and time "23:59"
|
|
406
|
+
* Example #3: And we fill in the datetime field "Pickup" with date "2026-07-04" and time "08:30"
|
|
407
|
+
* Example #4: When I fill in the datetime field "Booking" with date "2026-01-01" and time "00:00"
|
|
408
|
+
* Example #5: When I fill in the datetime field "Reminder" with date "2026-09-15" and time "14:00"
|
|
409
|
+
*
|
|
410
|
+
*/
|
|
411
|
+
When(/^(I |we )*fill in the datetime field "([^"]*)" with date "([^"]*)" and time "([^"]*)"$/, async function (pronoun, label, date, time) {
|
|
412
|
+
const root = this.page.locator(`label:has-text("${label}")`).first();
|
|
413
|
+
await this.page.locator(`input[type="date"][name*="${label}"], label:has-text("${label}") ~ * input[type="date"]`).first().fill(date).catch(() => {});
|
|
414
|
+
await this.page.locator(`input[type="time"][name*="${label}"], label:has-text("${label}") ~ * input[type="time"]`).first().fill(time).catch(() => {});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Fill only the date component of a paired datetime field.
|
|
419
|
+
*
|
|
420
|
+
* Example #1: When I fill in the date part of the datetime field "Start" with "2026-05-08"
|
|
421
|
+
* Example #2: When I fill in the date part of the datetime field "Birth date" with "1990-01-15"
|
|
422
|
+
* Example #3: And we fill in the date part of the datetime field "Booking" with "2026-12-31"
|
|
423
|
+
* Example #4: When I fill in the date part of the datetime field "Pickup" with "2026-07-04"
|
|
424
|
+
* Example #5: When I fill in the date part of the datetime field "Reminder" with "2026-09-15"
|
|
425
|
+
*
|
|
426
|
+
*/
|
|
427
|
+
When(/^(I |we )*fill in the date part of the datetime field "([^"]*)" with "([^"]*)"$/, async function (pronoun, label, date) {
|
|
428
|
+
await this.page.locator(`label:has-text("${label}") ~ * input[type="date"], input[type="date"][name*="${label}"]`).first().fill(date);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Fill only the time component of a paired datetime field.
|
|
433
|
+
*
|
|
434
|
+
* Example #1: When I fill in the time part of the datetime field "Start" with "10:00"
|
|
435
|
+
* Example #2: When I fill in the time part of the datetime field "Pickup" with "08:30"
|
|
436
|
+
* Example #3: And we fill in the time part of the datetime field "Booking" with "23:59"
|
|
437
|
+
* Example #4: When I fill in the time part of the datetime field "Reminder" with "14:00"
|
|
438
|
+
* Example #5: When I fill in the time part of the datetime field "Departure" with "06:15"
|
|
439
|
+
*
|
|
440
|
+
*/
|
|
441
|
+
When(/^(I |we )*fill in the time part of the datetime field "([^"]*)" with "([^"]*)"$/, async function (pronoun, label, time) {
|
|
442
|
+
await this.page.locator(`label:has-text("${label}") ~ * input[type="time"], input[type="time"][name*="${label}"]`).first().fill(time);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Fill the `start` half of a date-range pair.
|
|
447
|
+
*
|
|
448
|
+
* Example #1: When I fill in the start datetime field "Event" with date "2026-05-08" and time "10:00"
|
|
449
|
+
* Example #2: When I fill in the start datetime field "Trip" with date "2026-12-31" and time "08:00"
|
|
450
|
+
* Example #3: And we fill in the start datetime field "Window" with date "2026-07-04" and time "12:00"
|
|
451
|
+
* Example #4: When I fill in the start datetime field "Promo" with date "2026-01-01" and time "00:00"
|
|
452
|
+
* Example #5: When I fill in the start datetime field "Booking" with date "2026-09-15" and time "14:00"
|
|
453
|
+
*
|
|
454
|
+
*/
|
|
455
|
+
When(/^(I |we )*fill in the start datetime field "([^"]*)" with date "([^"]*)" and time "([^"]*)"$/, async function (pronoun, label, date, time) {
|
|
456
|
+
await this.page.locator(`input[name*="${label}"][name*="start"][type="date"]`).first().fill(date).catch(() => {});
|
|
457
|
+
await this.page.locator(`input[name*="${label}"][name*="start"][type="time"]`).first().fill(time).catch(() => {});
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Fill the `end` half of a date-range pair.
|
|
462
|
+
*
|
|
463
|
+
* Example #1: When I fill in the end datetime field "Event" with date "2026-05-09" and time "18:00"
|
|
464
|
+
* Example #2: When I fill in the end datetime field "Trip" with date "2027-01-07" and time "20:00"
|
|
465
|
+
* Example #3: And we fill in the end datetime field "Window" with date "2026-07-04" and time "18:00"
|
|
466
|
+
* Example #4: When I fill in the end datetime field "Promo" with date "2026-01-31" and time "23:59"
|
|
467
|
+
* Example #5: When I fill in the end datetime field "Booking" with date "2026-09-15" and time "16:00"
|
|
468
|
+
*
|
|
469
|
+
*/
|
|
470
|
+
When(/^(I |we )*fill in the end datetime field "([^"]*)" with date "([^"]*)" and time "([^"]*)"$/, async function (pronoun, label, date, time) {
|
|
471
|
+
await this.page.locator(`input[name*="${label}"][name*="end"][type="date"]`).first().fill(date).catch(() => {});
|
|
472
|
+
await this.page.locator(`input[name*="${label}"][name*="end"][type="time"]`).first().fill(time).catch(() => {});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// ---------------------------------------------------------------------------
|
|
476
|
+
// Field text content + checkbox / radio state assertions
|
|
477
|
+
// (relocated from varbase-e2e.js; field-related assertions live in this file).
|
|
478
|
+
// ---------------------------------------------------------------------------
|
|
479
|
+
|
|
480
|
+
const { Then: ThenF } = require('@cucumber/cucumber');
|
|
481
|
+
const assertF = require('assert');
|
|
482
|
+
const { getLocatorText: getLocatorTextF } = require('./varbase-e2e');
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Assert that a form field contains or does not contain specific text.
|
|
486
|
+
*
|
|
487
|
+
* Example #1: Then the "Username" field should contain "John Smith"
|
|
488
|
+
* Example #2: Then the "#username" field should not contain "John Smith"
|
|
489
|
+
* Example #3: And the "Email" field should contain "jon@example.com"
|
|
490
|
+
*
|
|
491
|
+
*/
|
|
492
|
+
ThenF(/^(the )*"([^"]*)?" field should( not)* contain "([^"]*)?"$/, async function (theCase, field, notCase, expectedText) {
|
|
493
|
+
let selector = field;
|
|
494
|
+
if (!field.startsWith('#') && !field.startsWith('.')) {
|
|
495
|
+
const forAttr = await this.page.getByText(field, { exact: true }).getAttribute('for').catch(() => null);
|
|
496
|
+
if (forAttr) selector = '#' + forAttr;
|
|
497
|
+
}
|
|
498
|
+
const loc = this.page.locator(selector).first();
|
|
499
|
+
await loc.waitFor({ timeout: 5000 });
|
|
500
|
+
const content = await getLocatorTextF(loc);
|
|
501
|
+
if (notCase) {
|
|
502
|
+
assertF.ok(!content.includes(expectedText), `Field should NOT contain "${expectedText}" but it does.`);
|
|
503
|
+
} else {
|
|
504
|
+
assertF.ok(content.includes(expectedText), `Field should contain "${expectedText}" but it does not.`);
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Assert that a checkbox should or should not be checked.
|
|
510
|
+
*
|
|
511
|
+
* Example #1: Then the "#privacy-policy" checkbox should be checked
|
|
512
|
+
* Example #2: Then the "#newsletter" checkbox should not be checked
|
|
513
|
+
* Example #3: And the "#agree" checkbox should be checked
|
|
514
|
+
* Example #4: Then the ".terms" checkbox should not be checked
|
|
515
|
+
* Example #5: When I check "Remember me"
|
|
516
|
+
* Then the "#remember" checkbox should be checked
|
|
517
|
+
*
|
|
518
|
+
*/
|
|
519
|
+
ThenF(/^(the )*"([^"]*)?" checkbox should( not)* be checked$/, async function (theCase, checkbox, notCase) {
|
|
520
|
+
const isChecked = await this.page.locator(checkbox).isChecked();
|
|
521
|
+
if (notCase) {
|
|
522
|
+
assertF.ok(!isChecked, `Checkbox "${checkbox}" should NOT be checked but it is.`);
|
|
523
|
+
} else {
|
|
524
|
+
assertF.ok(isChecked, `Checkbox "${checkbox}" should be checked but it is not.`);
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Assert that a checkbox is or is not checked.
|
|
530
|
+
*
|
|
531
|
+
* Alternate phrasing of "should be / should not be checked".
|
|
532
|
+
*
|
|
533
|
+
* Example #1: Then the "#remember-me" checkbox is checked
|
|
534
|
+
* Example #2: Then the "#newsletter" checkbox is not checked
|
|
535
|
+
* Example #3: And the "#agree" checkbox is checked
|
|
536
|
+
* Example #4: Then the ".terms" checkbox is not checked
|
|
537
|
+
* Example #5: When I check "Subscribe"
|
|
538
|
+
* Then the "#subscribe" checkbox is checked
|
|
539
|
+
*
|
|
540
|
+
*/
|
|
541
|
+
ThenF(/^(the )*"([^"]*)?" checkbox is( not)* checked$/, async function (theCase, checkbox, notCase) {
|
|
542
|
+
const isChecked = await this.page.locator(checkbox).isChecked();
|
|
543
|
+
if (notCase) {
|
|
544
|
+
assertF.ok(!isChecked, `Checkbox "${checkbox}" should NOT be checked but it is.`);
|
|
545
|
+
} else {
|
|
546
|
+
assertF.ok(isChecked, `Checkbox "${checkbox}" should be checked but it is not.`);
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Assert that a named checkbox should or should not be checked.
|
|
552
|
+
*
|
|
553
|
+
* Reverse phrasing — "checkbox <selector>" instead of "<selector> checkbox".
|
|
554
|
+
*
|
|
555
|
+
* Example #1: Then the checkbox "#privacy-policy" should be checked
|
|
556
|
+
* Example #2: Then the checkbox "#newsletter" should not be checked
|
|
557
|
+
* Example #3: And the checkbox "#agree" should be checked
|
|
558
|
+
* Example #4: Then the checkbox ".terms" should not be checked
|
|
559
|
+
* Example #5: When I check "Subscribe"
|
|
560
|
+
* Then the checkbox "#subscribe" should be checked
|
|
561
|
+
*
|
|
562
|
+
*/
|
|
563
|
+
ThenF(/^(the )*checkbox "([^"]*)?" should( not)* be checked$/, async function (theCase, checkbox, notCase) {
|
|
564
|
+
const isChecked = await this.page.locator(checkbox).isChecked();
|
|
565
|
+
if (notCase) {
|
|
566
|
+
assertF.ok(!isChecked, `Checkbox "${checkbox}" should NOT be checked but it is.`);
|
|
567
|
+
} else {
|
|
568
|
+
assertF.ok(isChecked, `Checkbox "${checkbox}" should be checked but it is not.`);
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Assert that a named checkbox is or is not checked.
|
|
574
|
+
*
|
|
575
|
+
* Reverse phrasing using "is / is not".
|
|
576
|
+
*
|
|
577
|
+
* Example #1: Then the checkbox "#remember-me" is checked
|
|
578
|
+
* Example #2: Then the checkbox "#newsletter" is not checked
|
|
579
|
+
* Example #3: And the checkbox "#agree" is checked
|
|
580
|
+
* Example #4: Then the checkbox ".terms" is not checked
|
|
581
|
+
* Example #5: When I check "Subscribe"
|
|
582
|
+
* Then the checkbox "#subscribe" is checked
|
|
583
|
+
*
|
|
584
|
+
*/
|
|
585
|
+
ThenF(/^(the )*checkbox "([^"]*)?" is( not)* checked$/, async function (theCase, checkbox, notCase) {
|
|
586
|
+
const isChecked = await this.page.locator(checkbox).isChecked();
|
|
587
|
+
if (notCase) {
|
|
588
|
+
assertF.ok(!isChecked, `Checkbox "${checkbox}" should NOT be checked but it is.`);
|
|
589
|
+
} else {
|
|
590
|
+
assertF.ok(isChecked, `Checkbox "${checkbox}" should be checked but it is not.`);
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Assert that a radio button should or should not be selected.
|
|
596
|
+
*
|
|
597
|
+
* Example #1: Then the radio button "#gender-male" should be selected
|
|
598
|
+
* Example #2: Then the radio button "#gender-female" should not be selected
|
|
599
|
+
* Example #3: Then the radio button ".option-1" should be selected
|
|
600
|
+
*
|
|
601
|
+
*/
|
|
602
|
+
ThenF(/^(the )*radio button "([^"]*)?" should( not)* be selected$/, async function (theCase, radioButton, notCase) {
|
|
603
|
+
const isChecked = await this.page.locator(radioButton).isChecked();
|
|
604
|
+
if (notCase) {
|
|
605
|
+
assertF.ok(!isChecked, `Radio button "${radioButton}" should NOT be selected but it is.`);
|
|
606
|
+
} else {
|
|
607
|
+
assertF.ok(isChecked, `Radio button "${radioButton}" should be selected but it is not.`);
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Assert that a radio button with a given value should or should not be selected.
|
|
613
|
+
*
|
|
614
|
+
* Looks up the radio by `[value="..."]`.
|
|
615
|
+
*
|
|
616
|
+
* Example #1: Then the radio button with value "male" should be selected
|
|
617
|
+
* Example #2: Then the radio button with value "female" should not be selected
|
|
618
|
+
* Example #3: And the radio button with value "premium" should be selected
|
|
619
|
+
* Example #4: Then the radio button with value "monthly" should not be selected
|
|
620
|
+
* Example #5: When I select radio button "Premium"
|
|
621
|
+
* Then the radio button with value "premium" should be selected
|
|
622
|
+
*
|
|
623
|
+
*/
|
|
624
|
+
ThenF(/^(the )*radio button with value "([^"]*)?" should( not)* be selected$/, async function (theCase, radioValue, notCase) {
|
|
625
|
+
const isChecked = await this.page.locator(`input[type="radio"][value="${radioValue}"]`).first().isChecked();
|
|
626
|
+
if (notCase) {
|
|
627
|
+
assertF.ok(!isChecked, `Radio button with value "${radioValue}" should NOT be selected but it is.`);
|
|
628
|
+
} else {
|
|
629
|
+
assertF.ok(isChecked, `Radio button with value "${radioValue}" should be selected but it is not.`);
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Assert that the radio button is or is not selected.
|
|
635
|
+
*
|
|
636
|
+
* Alternate phrasing using "is / is not selected".
|
|
637
|
+
*
|
|
638
|
+
* Example #1: Then the "#gender-male" radio button is selected
|
|
639
|
+
* Example #2: Then the "#gender-female" radio button is not selected
|
|
640
|
+
* Example #3: And the "#plan-premium" radio button is selected
|
|
641
|
+
* Example #4: Then the ".option-1" radio button is not selected
|
|
642
|
+
* Example #5: When I select radio button "Female"
|
|
643
|
+
* Then the "#gender-female" radio button is selected
|
|
644
|
+
*
|
|
645
|
+
*/
|
|
646
|
+
ThenF(/^(the )*"([^"]*)?" radio button is( not)* selected$/, async function (theCase, radioButton, notCase) {
|
|
647
|
+
const isChecked = await this.page.locator(radioButton).isChecked();
|
|
648
|
+
if (notCase) {
|
|
649
|
+
assertF.ok(!isChecked, `Radio button "${radioButton}" should NOT be selected but it is.`);
|
|
650
|
+
} else {
|
|
651
|
+
assertF.ok(isChecked, `Radio button "${radioButton}" should be selected but it is not.`);
|
|
652
|
+
}
|
|
653
|
+
});
|