@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.
Files changed (301) hide show
  1. package/AGENTS.md +148 -0
  2. package/CLAUDE.md +544 -0
  3. package/README.md +22 -0
  4. package/bin/generate-reports.js +363 -0
  5. package/bin/init-varbase-e2e.js +330 -0
  6. package/bin/postinstall.js +26 -0
  7. package/cucumber.js +125 -0
  8. package/docs/00-quick-start.md +102 -0
  9. package/docs/01-getting-started.md +102 -0
  10. package/docs/02-bbr-smart-waits.md +77 -0
  11. package/docs/03-selector-registry.md +95 -0
  12. package/docs/04-step-reference.md +801 -0
  13. package/docs/05-web-first-assertions.md +91 -0
  14. package/docs/06-network-and-dialogs.md +83 -0
  15. package/docs/07-auth-state.md +75 -0
  16. package/docs/08-clock-mocking.md +59 -0
  17. package/docs/09-api-testing.md +114 -0
  18. package/docs/10-accessibility.md +167 -0
  19. package/docs/11-debugging.md +129 -0
  20. package/docs/12-ai-agent-guide.md +338 -0
  21. package/docs/13-faq.md +236 -0
  22. package/docs/14-recipes-cookbook.md +255 -0
  23. package/docs/15-tag-conventions.md +76 -0
  24. package/docs/16-ci-cd.md +90 -0
  25. package/docs/README.md +125 -0
  26. package/docs/advanced-screenshots/then-i-save-fullscreen-screenshot.md +36 -0
  27. package/docs/advanced-screenshots/then-i-save-width-x-height-screenshot.md +34 -0
  28. package/docs/advanced-screenshots/when-i-save-screenshot-name-filename.md +45 -0
  29. package/docs/advanced-screenshots.md +52 -0
  30. package/docs/advanced-selectors.md +18 -0
  31. package/docs/api-step-definitions.md +45 -0
  32. package/docs/global-settings.md +267 -0
  33. package/docs/install-varbase-e2e/ddev-varbase-e2e.md +52 -0
  34. package/docs/install-varbase-e2e.md +34 -0
  35. package/docs/overview.md +40 -0
  36. package/docs/step-definitions/given-i-am-anonymous-user.md +67 -0
  37. package/docs/step-definitions/given-i-am-homepage.md +68 -0
  38. package/docs/step-definitions/given-i-am-specific-page.md +72 -0
  39. package/docs/step-definitions/then-i-should-be.md +56 -0
  40. package/docs/step-definitions/then-i-should-see.md +62 -0
  41. package/docs/step-definitions/then-item-checkbox.md +54 -0
  42. package/docs/step-definitions/then-item-link-should-contain-url.md +84 -0
  43. package/docs/step-definitions/then-item-should-contain.md +67 -0
  44. package/docs/step-definitions/then-radio-button-value-should-be-selected.md +68 -0
  45. package/docs/step-definitions/then-response-status-code-should-be-number.md +69 -0
  46. package/docs/step-definitions/then-value-radio-button-selected.md +62 -0
  47. package/docs/step-definitions/when-i-attach-file-file-name-element.md +71 -0
  48. package/docs/step-definitions/when-i-check-checkbox.md +60 -0
  49. package/docs/step-definitions/when-i-click-link.md +51 -0
  50. package/docs/step-definitions/when-i-click-operation-text-row.md +71 -0
  51. package/docs/step-definitions/when-i-fill.md +59 -0
  52. package/docs/step-definitions/when-i-go-homepage.md +60 -0
  53. package/docs/step-definitions/when-i-go-specific-page.md +67 -0
  54. package/docs/step-definitions/when-i-move-backward-one-page.md +49 -0
  55. package/docs/step-definitions/when-i-move-forward-one-page.md +58 -0
  56. package/docs/step-definitions/when-i-press-button.md +58 -0
  57. package/docs/step-definitions/when-i-reload-page.md +51 -0
  58. package/docs/step-definitions/when-i-scroll.md +73 -0
  59. package/docs/step-definitions/when-i-select-option-select-list.md +53 -0
  60. package/docs/step-definitions/when-i-select-radio-button-value.md +62 -0
  61. package/docs/step-definitions/when-i-uncheck-checkbox.md +57 -0
  62. package/docs/step-definitions/when-i-wait-ajax-finish.md +64 -0
  63. package/docs/step-definitions/when-i-wait.md +62 -0
  64. package/docs/step-definitions.md +82 -0
  65. package/docs/steps/README.md +137 -0
  66. package/docs/steps/a11y.md +731 -0
  67. package/docs/steps/action.md +181 -0
  68. package/docs/steps/api.md +679 -0
  69. package/docs/steps/assertion.md +328 -0
  70. package/docs/steps/auth.md +108 -0
  71. package/docs/steps/clock.md +171 -0
  72. package/docs/steps/cookie.md +293 -0
  73. package/docs/steps/debug.md +52 -0
  74. package/docs/steps/dialog.md +226 -0
  75. package/docs/steps/drupal-canvas.md +414 -0
  76. package/docs/steps/drupal-ckeditor.md +118 -0
  77. package/docs/steps/drupal-core.md +688 -0
  78. package/docs/steps/drupal-layout-builder.md +437 -0
  79. package/docs/steps/drupal-media.md +107 -0
  80. package/docs/steps/drupal-moderation.md +81 -0
  81. package/docs/steps/drupal-paragraphs.md +36 -0
  82. package/docs/steps/element.md +455 -0
  83. package/docs/steps/field.md +648 -0
  84. package/docs/steps/file-download.md +235 -0
  85. package/docs/steps/form.md +344 -0
  86. package/docs/steps/iframe.md +248 -0
  87. package/docs/steps/input.md +220 -0
  88. package/docs/steps/javascript.md +111 -0
  89. package/docs/steps/keyboard.md +107 -0
  90. package/docs/steps/link.md +217 -0
  91. package/docs/steps/metatag.md +99 -0
  92. package/docs/steps/modal.md +232 -0
  93. package/docs/steps/navigation.md +275 -0
  94. package/docs/steps/network.md +284 -0
  95. package/docs/steps/path.md +203 -0
  96. package/docs/steps/response.md +102 -0
  97. package/docs/steps/responsive.md +139 -0
  98. package/docs/steps/rest.md +140 -0
  99. package/docs/steps/screenshot.md +211 -0
  100. package/docs/steps/scroll.md +266 -0
  101. package/docs/steps/selectors.md +848 -0
  102. package/docs/steps/storage.md +254 -0
  103. package/docs/steps/table.md +216 -0
  104. package/docs/steps/varbase.md +391 -0
  105. package/docs/steps/video.md +111 -0
  106. package/docs/steps/wait.md +553 -0
  107. package/docs/steps/web-first.md +289 -0
  108. package/docs/steps/xml.md +505 -0
  109. package/docs/steps/yaml.md +951 -0
  110. package/examples/a11y.html +31 -0
  111. package/examples/about-us.html +11 -0
  112. package/examples/account.html +32 -0
  113. package/examples/ajax-wait-examples.html +147 -0
  114. package/examples/contact-us.html +10 -0
  115. package/examples/cookies.html +10 -0
  116. package/examples/date.html +12 -0
  117. package/examples/dynamic-examples.html +498 -0
  118. package/examples/element.html +11 -0
  119. package/examples/example-api.json +28 -0
  120. package/examples/existing-page.html +11 -0
  121. package/examples/field.html +24 -0
  122. package/examples/index.html +37 -0
  123. package/examples/keyboard.html +11 -0
  124. package/examples/links.html +9 -0
  125. package/examples/modal.html +14 -0
  126. package/examples/path.html +3 -0
  127. package/examples/table.html +13 -0
  128. package/examples/test--modal-dialogs.html +252 -0
  129. package/examples/test--then--i-should--not--see-text-in-element.html +48 -0
  130. package/examples/test--then--i-should--see-text-in-element.html +48 -0
  131. package/examples/test--then--i-should--see-text-matching-pattern.html +31 -0
  132. package/examples/test--then--i-should-see-text-in-table-row.html +232 -0
  133. package/examples/test--then--the-checkbox-checked.html +68 -0
  134. package/examples/test--then--the-element-should_not-contain-cssproperty.html +47 -0
  135. package/examples/test--then--the-link-should-contain.html +21 -0
  136. package/examples/test--when--i-additionally-select.html +27 -0
  137. package/examples/test--when--i-attach-file-to-feild.html +50 -0
  138. package/examples/test--when--i-check-uncheck-checkbox.html +47 -0
  139. package/examples/test--when--i-click-link.html +18 -0
  140. package/examples/test--when--i-click-text-in-table-row.html +178 -0
  141. package/examples/test--when--i-fill-in.html +48 -0
  142. package/examples/test--when--i-press-button.html +31 -0
  143. package/examples/test--when--i-reload-page.html +27 -0
  144. package/examples/test--when--i-scroll-functionality.html +177 -0
  145. package/examples/test--when--i-scroll-left-right-functionality.html +149 -0
  146. package/examples/test--when--i-select-option-from-dropdownlist.html +35 -0
  147. package/examples/test--when--i-select-radio-button.html +95 -0
  148. package/examples/test--when-i-wait-max-of-time.html +39 -0
  149. package/examples/test--when-i-wait-page-until-is-loaded.html +47 -0
  150. package/examples/test--when-i-wait-specific-time.html +39 -0
  151. package/examples/test-acceptable-url-path.html +11 -0
  152. package/examples/video-demo.html +43 -0
  153. package/package.json +56 -0
  154. package/playwright.config.ts +39 -0
  155. package/tests/assets/test-document.pdf +14 -0
  156. package/tests/assets/vardot.png +0 -0
  157. package/tests/features/a11y.feature +39 -0
  158. package/tests/features/ajax-wait-examples.feature +24 -0
  159. package/tests/features/api-step-definitions-individual.feature +185 -0
  160. package/tests/features/complete-api-testing-examples.feature +205 -0
  161. package/tests/features/cookie.feature +13 -0
  162. package/tests/features/date.feature +11 -0
  163. package/tests/features/dynamic-content-examples.feature +156 -0
  164. package/tests/features/element.feature +16 -0
  165. package/tests/features/field.feature +25 -0
  166. package/tests/features/keyboard.feature +10 -0
  167. package/tests/features/link.feature +11 -0
  168. package/tests/features/modal-advanced-example.feature +54 -0
  169. package/tests/features/modal-basic-example.feature +42 -0
  170. package/tests/features/modal-error-handling-example.feature +37 -0
  171. package/tests/features/modal.feature +11 -0
  172. package/tests/features/path.feature +16 -0
  173. package/tests/features/response.feature +8 -0
  174. package/tests/features/responsive.feature +10 -0
  175. package/tests/features/table.feature +18 -0
  176. package/tests/features/test--given--i-am-an-anonymous-user.feature +10 -0
  177. package/tests/features/test--given--i-am-on-homepage.feature +16 -0
  178. package/tests/features/test--then--i-save-screenshot.feature +33 -0
  179. package/tests/features/test--then--i-see-locator.feature +320 -0
  180. package/tests/features/test--then--i-should--not--see-text-in-element.feature +13 -0
  181. package/tests/features/test--then--i-should--see-text-in-element-by-attr.feature +10 -0
  182. package/tests/features/test--then--i-should--see-text-in-element.feature +10 -0
  183. package/tests/features/test--then--i-should--see-text-matching-pattern-in-element.feature +13 -0
  184. package/tests/features/test--then--i-should--see-text-matching-pattern.feature +18 -0
  185. package/tests/features/test--then--i-should-be-on-page.feature +10 -0
  186. package/tests/features/test--then--i-should-be-on-the-homepage.feature +10 -0
  187. package/tests/features/test--then--i-should-not-see-an-element-by-attr.feature +8 -0
  188. package/tests/features/test--then--i-should-not-see-an-element.feature +8 -0
  189. package/tests/features/test--then--i-should-see-an-element-by-attr.feature +8 -0
  190. package/tests/features/test--then--i-should-see-an-element.feature +8 -0
  191. package/tests/features/test--then--i-should-see-n-elements.feature +24 -0
  192. package/tests/features/test--then--i-should-see-text-in-table-row.feature +96 -0
  193. package/tests/features/test--then--print-current-url-and-response.feature +21 -0
  194. package/tests/features/test--then--the-checkbox-is-checked.feature +18 -0
  195. package/tests/features/test--then--the-checkbox-should-be-checked.feature +16 -0
  196. package/tests/features/test--then--the-element-should-contain-cssproperty.feature +9 -0
  197. package/tests/features/test--then--the-element-should-not-contain-cssproperty.feature +10 -0
  198. package/tests/features/test--then--the-field-should-contain-value.feature +11 -0
  199. package/tests/features/test--then--the-response-should-contain.feature +11 -0
  200. package/tests/features/test--then--the-response-status-code-should-be.feature +31 -0
  201. package/tests/features/test--then--the-url-should-match.feature +7 -0
  202. package/tests/features/test--then-the-link-should-contain-by-attribute.feature +18 -0
  203. package/tests/features/test--then-the-link-should-contain.feature +7 -0
  204. package/tests/features/test--when--i-additionally-select.feature +30 -0
  205. package/tests/features/test--when--i-attach-file-to-feild.feature +9 -0
  206. package/tests/features/test--when--i-check-checkbox.feature +21 -0
  207. package/tests/features/test--when--i-click-link-by-attribute.feature +21 -0
  208. package/tests/features/test--when--i-click-link.feature +9 -0
  209. package/tests/features/test--when--i-click-text-in-table-row.feature +37 -0
  210. package/tests/features/test--when--i-fill-in-by-attr.feature +31 -0
  211. package/tests/features/test--when--i-fill-in-table-by-attr.feature +19 -0
  212. package/tests/features/test--when--i-fill-in.feature +32 -0
  213. package/tests/features/test--when--i-follow.feature +22 -0
  214. package/tests/features/test--when--i-go-to-homepage.feature +9 -0
  215. package/tests/features/test--when--i-move-forward-backward-one-page.feature +14 -0
  216. package/tests/features/test--when--i-press-button-by-attribute.feature +18 -0
  217. package/tests/features/test--when--i-press-button.feature +13 -0
  218. package/tests/features/test--when--i-reload-page.feature +11 -0
  219. package/tests/features/test--when--i-scroll-left-right.feature +48 -0
  220. package/tests/features/test--when--i-scroll.feature +48 -0
  221. package/tests/features/test--when--i-select-option-from-dropdownlist.feature +21 -0
  222. package/tests/features/test--when--i-select-radio-button.feature +37 -0
  223. package/tests/features/test--when--i-wait-page-is-loaded.feature +8 -0
  224. package/tests/features/test--when--i-wait-time.feature +21 -0
  225. package/tests/features/video.feature +18 -0
  226. package/tests/features/wait.feature +7 -0
  227. package/tests/features/yaml.feature +102 -0
  228. package/tests/selectors/_canonical-keys.json +113 -0
  229. package/tests/selectors/back-end-selectors.json +19 -0
  230. package/tests/selectors/cms-contentful-admin.json +35 -0
  231. package/tests/selectors/cms-craft-admin.json +39 -0
  232. package/tests/selectors/cms-drupal-cms-gin.json +56 -0
  233. package/tests/selectors/cms-drupal-core-claro.json +50 -0
  234. package/tests/selectors/cms-generic-admin.json +34 -0
  235. package/tests/selectors/cms-ghost-admin.json +34 -0
  236. package/tests/selectors/cms-joomla-admin.json +41 -0
  237. package/tests/selectors/cms-magento2-admin.json +32 -0
  238. package/tests/selectors/cms-prestashop-admin.json +38 -0
  239. package/tests/selectors/cms-shopify-admin.json +25 -0
  240. package/tests/selectors/cms-strapi-admin.json +42 -0
  241. package/tests/selectors/cms-typo3-admin.json +41 -0
  242. package/tests/selectors/cms-woocommerce-front.json +50 -0
  243. package/tests/selectors/cms-wordpress-admin.json +31 -0
  244. package/tests/selectors/framework-ant-design.json +80 -0
  245. package/tests/selectors/framework-bootstrap.json +86 -0
  246. package/tests/selectors/framework-bulma.json +74 -0
  247. package/tests/selectors/framework-chakra.json +58 -0
  248. package/tests/selectors/framework-foundation.json +51 -0
  249. package/tests/selectors/framework-material-ui.json +72 -0
  250. package/tests/selectors/framework-shadcn.json +51 -0
  251. package/tests/selectors/framework-tailwind.json +40 -0
  252. package/tests/selectors/framework-vuetify.json +61 -0
  253. package/tests/selectors/front-end-selectors.json +16 -0
  254. package/tests/selectors/homepage-selectors.json +13 -0
  255. package/tests/step-definitions/a11y.steps.js +800 -0
  256. package/tests/step-definitions/action.steps.js +167 -0
  257. package/tests/step-definitions/api.steps.js +732 -0
  258. package/tests/step-definitions/assertion.steps.js +352 -0
  259. package/tests/step-definitions/auth.steps.js +126 -0
  260. package/tests/step-definitions/clock.steps.js +128 -0
  261. package/tests/step-definitions/cookie.steps.js +216 -0
  262. package/tests/step-definitions/debug.steps.js +39 -0
  263. package/tests/step-definitions/dialog.steps.js +193 -0
  264. package/tests/step-definitions/drupal-canvas.steps.js +764 -0
  265. package/tests/step-definitions/drupal-ckeditor.steps.js +108 -0
  266. package/tests/step-definitions/drupal-core.steps.js +665 -0
  267. package/tests/step-definitions/drupal-helpers.js +371 -0
  268. package/tests/step-definitions/drupal-layout-builder.steps.js +466 -0
  269. package/tests/step-definitions/drupal-media.steps.js +99 -0
  270. package/tests/step-definitions/drupal-moderation.steps.js +71 -0
  271. package/tests/step-definitions/drupal-paragraphs.steps.js +50 -0
  272. package/tests/step-definitions/element.steps.js +356 -0
  273. package/tests/step-definitions/field.steps.js +653 -0
  274. package/tests/step-definitions/file-download.steps.js +211 -0
  275. package/tests/step-definitions/form.steps.js +333 -0
  276. package/tests/step-definitions/iframe.steps.js +228 -0
  277. package/tests/step-definitions/input.steps.js +202 -0
  278. package/tests/step-definitions/javascript.steps.js +232 -0
  279. package/tests/step-definitions/keyboard.steps.js +112 -0
  280. package/tests/step-definitions/link.steps.js +142 -0
  281. package/tests/step-definitions/metatag.steps.js +91 -0
  282. package/tests/step-definitions/modal.steps.js +244 -0
  283. package/tests/step-definitions/navigation.steps.js +218 -0
  284. package/tests/step-definitions/network.steps.js +280 -0
  285. package/tests/step-definitions/path.steps.js +141 -0
  286. package/tests/step-definitions/response.steps.js +98 -0
  287. package/tests/step-definitions/responsive.steps.js +125 -0
  288. package/tests/step-definitions/rest.steps.js +111 -0
  289. package/tests/step-definitions/screenshot.steps.js +423 -0
  290. package/tests/step-definitions/scroll.steps.js +189 -0
  291. package/tests/step-definitions/selectors.steps.js +1003 -0
  292. package/tests/step-definitions/storage.steps.js +194 -0
  293. package/tests/step-definitions/table.steps.js +181 -0
  294. package/tests/step-definitions/varbase-e2e.js +897 -0
  295. package/tests/step-definitions/varbase.steps.js +458 -0
  296. package/tests/step-definitions/video.steps.js +178 -0
  297. package/tests/step-definitions/wait.steps.js +400 -0
  298. package/tests/step-definitions/web-first.steps.js +298 -0
  299. package/tests/step-definitions/xml.steps.js +411 -0
  300. package/tests/step-definitions/yaml.steps.js +913 -0
  301. package/tsconfig.json +26 -0
@@ -0,0 +1,731 @@
1
+ # Accessibility steps
2
+
3
+ 26 steps, defined in `tests/step-definitions/a11y.steps.js`.
4
+
5
+ Cucumber-js loads every `*.steps.js` in that directory automatically — you never `require()` a step file from a feature.
6
+
7
+ | # | Step |
8
+ | --- | --- |
9
+ | 1 | `Then every image should have an alt attribute` |
10
+ | 2 | `Then every form field should have an accessible label` |
11
+ | 3 | `Then the page should have a main landmark` |
12
+ | 4 | `Then the page should have a navigation landmark` |
13
+ | 5 | `Then the page should have exactly one h1` |
14
+ | 6 | `Then the focused element should match "#email"` |
15
+ | 7 | `Then the focused element should be labeled "Email"` |
16
+ | 8 | `Then the page should declare a language` |
17
+ | 9 | `Then the page language should be "en"` |
18
+ | 10 | `Then the heading hierarchy should be valid` |
19
+ | 11 | `Then the page should have a skip link` |
20
+ | 12 | `Then every button should have an accessible name` |
21
+ | 13 | `Then every link should have an accessible name` |
22
+ | 14 | `Then no element should have a positive tabindex` |
23
+ | 15 | `Then every ARIA reference should resolve` |
24
+ | 16 | `Then every ARIA role should be valid` |
25
+ | 17 | `Then required fields should be consistently marked` |
26
+ | 18 | `Then the page should have a title` |
27
+ | 19 | `Then user zoom should be allowed` |
28
+ | 20 | `Then the page should pass an accessibility audit` |
29
+ | 21 | `Then the page should have no critical accessibility violations` |
30
+ | 22 | `Then the element "main" should pass an accessibility audit` |
31
+ | 23 | `Then the page should pass an accessibility audit excluding "iframe.payment"` |
32
+ | 24 | `Then the page should not violate the accessibility rule "color-contrast"` |
33
+ | 25 | `Then I print accessibility violations` |
34
+ | 26 | `Then the page should pass the accessibility rules "image-alt, label"` |
35
+
36
+ ---
37
+
38
+ ## 1. Then every image should have an alt attribute
39
+
40
+ Assert every <img> on the page has an `alt` attribute.
41
+
42
+ Empty `alt=""` is allowed (decorative images per WCAG); `role="presentation"`
43
+ is also allowed. Only a missing `alt` attribute fails. Iterates every <img>
44
+ in the document, including those nested in shadow-light DOM that the page
45
+ has rendered, so it works on infinite-scroll lists and image galleries too.
46
+
47
+ **Keyword**: `Then`
48
+
49
+ **Pattern**
50
+
51
+ ```js
52
+ /^every image should have an alt attribute$/
53
+ ```
54
+
55
+ **Examples**
56
+
57
+ ```gherkin
58
+ Then every image should have an alt attribute
59
+ And every image should have an alt attribute
60
+ Given I am on "/photos"
61
+ Then every image should have an alt attribute
62
+ When I scroll to the bottom
63
+ Then every image should have an alt attribute
64
+ When I follow "Gallery"
65
+ Then every image should have an alt attribute
66
+ ```
67
+
68
+ ## 2. Then every form field should have an accessible label
69
+
70
+ Assert every form input/select/textarea has an accessible name.
71
+
72
+ Pass condition: each field has at least ONE of —
73
+ - `aria-label` attribute
74
+ - `aria-labelledby` attribute pointing at a real element
75
+ - matching `<label for="...">`
76
+ - wrapping `<label>` element
77
+
78
+ Submit/reset/button/image inputs and `type="hidden"` are exempt because
79
+ they get their accessible name from `value` or `alt`.
80
+
81
+ **Keyword**: `Then`
82
+
83
+ **Pattern**
84
+
85
+ ```js
86
+ /^every form field should have an accessible label$/
87
+ ```
88
+
89
+ **Examples**
90
+
91
+ ```gherkin
92
+ Then every form field should have an accessible label
93
+ Given I am on "/register"
94
+ Then every form field should have an accessible label
95
+ When I open the settings modal
96
+ Then every form field should have an accessible label
97
+ And every form field should have an accessible label
98
+ Then the page should have a main landmark
99
+ And every form field should have an accessible label
100
+ ```
101
+
102
+ ## 3. Then the page should have a main landmark
103
+
104
+ Assert the page has a primary `<main>` (or `[role="main"]`) landmark.
105
+
106
+ Screen readers announce landmarks; missing the main landmark forces users
107
+ to navigate the page linearly. WCAG 2.4.1 (Bypass Blocks) requires it.
108
+
109
+ **Keyword**: `Then`
110
+
111
+ **Pattern**
112
+
113
+ ```js
114
+ /^the page should have a main landmark$/
115
+ ```
116
+
117
+ **Examples**
118
+
119
+ ```gherkin
120
+ Then the page should have a main landmark
121
+ Given I am on "/about"
122
+ Then the page should have a main landmark
123
+ When I follow "Pricing"
124
+ Then the page should have a main landmark
125
+ And the page should have a main landmark
126
+ Then the page should have a navigation landmark
127
+ And the page should have a main landmark
128
+ ```
129
+
130
+ ## 4. Then the page should have a navigation landmark
131
+
132
+ Assert the page has at least one navigation landmark.
133
+
134
+ Counts both `<nav>` and `[role="navigation"]`.
135
+
136
+ **Keyword**: `Then`
137
+
138
+ **Pattern**
139
+
140
+ ```js
141
+ /^the page should have a navigation landmark$/
142
+ ```
143
+
144
+ **Examples**
145
+
146
+ ```gherkin
147
+ Then the page should have a navigation landmark
148
+ Given I am on the homepage
149
+ Then the page should have a navigation landmark
150
+ Given I am on "/admin"
151
+ Then the page should have a navigation landmark
152
+ And the page should have a navigation landmark
153
+ Then the page should have exactly one h1
154
+ And the page should have a navigation landmark
155
+ ```
156
+
157
+ ## 5. Then the page should have exactly one h1
158
+
159
+ Assert the page has exactly one `<h1>`.
160
+
161
+ Multiple h1s are valid HTML5 with sectioning roots, but most assistive tech
162
+ still treats the document outline as flat. One h1 per page is the safe
163
+ default.
164
+
165
+ **Keyword**: `Then`
166
+
167
+ **Pattern**
168
+
169
+ ```js
170
+ /^the page should have exactly one h1$/
171
+ ```
172
+
173
+ **Examples**
174
+
175
+ ```gherkin
176
+ Then the page should have exactly one h1
177
+ Given I am on the homepage
178
+ Then the page should have exactly one h1
179
+ When I follow "About"
180
+ Then the page should have exactly one h1
181
+ And the page should have exactly one h1
182
+ Then the page should have a main landmark
183
+ And the page should have exactly one h1
184
+ ```
185
+
186
+ ## 6. Then the focused element should match "#email"
187
+
188
+ Assert the currently focused element matches a CSS selector.
189
+
190
+ Useful for tab-order and skip-link tests.
191
+
192
+ **Keyword**: `Then`
193
+
194
+ **Pattern**
195
+
196
+ ```js
197
+ /^the focused element should match "([^"]*)"$/
198
+ ```
199
+
200
+ **Examples**
201
+
202
+ ```gherkin
203
+ Then the focused element should match "#email"
204
+ When I press the "Tab" key
205
+ Then the focused element should match "#email"
206
+ When I press the "Tab" key 2 times
207
+ Then the focused element should match "#password"
208
+ When I focus on the element "#search"
209
+ Then the focused element should match "input[type=search]"
210
+ When I follow "Skip to main content"
211
+ Then the focused element should match "main h1"
212
+ ```
213
+
214
+ ## 7. Then the focused element should be labeled "Email"
215
+
216
+ Assert the currently focused element's accessible name contains text.
217
+
218
+ Resolution order: aria-label → aria-labelledby → label[for] → wrapping
219
+ <label> → value/placeholder/title/textContent.
220
+
221
+ **Keyword**: `Then`
222
+
223
+ **Pattern**
224
+
225
+ ```js
226
+ /^the focused element should be labeled "([^"]*)"$/
227
+ ```
228
+
229
+ **Examples**
230
+
231
+ ```gherkin
232
+ Then the focused element should be labeled "Email"
233
+ When I press the "Tab" key
234
+ Then the focused element should be labeled "Email"
235
+ When I press the "Tab" key 2 times
236
+ Then the focused element should be labeled "Password"
237
+ When I press the "Tab" key 3 times
238
+ Then the focused element should be labeled "Sign in"
239
+ When I focus on the element "#agree"
240
+ Then the focused element should be labeled "I agree"
241
+ ```
242
+
243
+ ## 8. Then the page should declare a language
244
+
245
+ Assert the document has a `lang` attribute on `<html>`.
246
+
247
+ Required by WCAG 3.1.1 — screen readers use `lang` to pick the right
248
+ pronunciation engine. Missing or empty `lang` fails.
249
+
250
+ **Keyword**: `Then`
251
+
252
+ **Pattern**
253
+
254
+ ```js
255
+ /^the page should declare a language$/
256
+ ```
257
+
258
+ **Examples**
259
+
260
+ ```gherkin
261
+ Then the page should declare a language
262
+ Given I am on the homepage
263
+ Then the page should declare a language
264
+ When I follow "About"
265
+ Then the page should declare a language
266
+ And the page should declare a language
267
+ Then the page should have a main landmark
268
+ And the page should declare a language
269
+ ```
270
+
271
+ ## 9. Then the page language should be "en"
272
+
273
+ Assert the document `lang` equals an expected value.
274
+
275
+ **Keyword**: `Then`
276
+
277
+ **Pattern**
278
+
279
+ ```js
280
+ /^the page language should be "([^"]*)"$/
281
+ ```
282
+
283
+ **Examples**
284
+
285
+ ```gherkin
286
+ Then the page language should be "en"
287
+ Given I am on the homepage
288
+ Then the page language should be "en"
289
+ When I switch to the French version
290
+ Then the page language should be "fr"
291
+ When I switch to the Arabic version
292
+ Then the page language should be "ar"
293
+ And the page language should be "en-US"
294
+ ```
295
+
296
+ ## 10. Then the heading hierarchy should be valid
297
+
298
+ Assert headings appear in non-skipping order (h1 → h2 → h3 — never
299
+ h1 → h3). Required by WCAG 1.3.1 / 2.4.6 for screen-reader navigation.
300
+
301
+ **Keyword**: `Then`
302
+
303
+ **Pattern**
304
+
305
+ ```js
306
+ /^the heading hierarchy should be valid$/
307
+ ```
308
+
309
+ **Examples**
310
+
311
+ ```gherkin
312
+ Then the heading hierarchy should be valid
313
+ Given I am on the homepage
314
+ Then the heading hierarchy should be valid
315
+ When I follow "About"
316
+ Then the heading hierarchy should be valid
317
+ And the heading hierarchy should be valid
318
+ Then the page should have exactly one h1
319
+ And the heading hierarchy should be valid
320
+ ```
321
+
322
+ ## 11. Then the page should have a skip link
323
+
324
+ Assert the page exposes a skip-link as the first focusable element. WCAG
325
+ 2.4.1 requires a mechanism to bypass repeated content blocks.
326
+
327
+ Detection: anchor with href="#..." that resolves to an existing target
328
+ AND text matching "skip" / "main content" / "to content".
329
+
330
+ **Keyword**: `Then`
331
+
332
+ **Pattern**
333
+
334
+ ```js
335
+ /^the page should have a skip link$/
336
+ ```
337
+
338
+ **Examples**
339
+
340
+ ```gherkin
341
+ Then the page should have a skip link
342
+ Given I am on the homepage
343
+ Then the page should have a skip link
344
+ When I press the key "Tab"
345
+ Then the focused element should match "a[href^='#']"
346
+ And the page should have a skip link
347
+ Then the page should have a skip link
348
+ And the page should have a main landmark
349
+ When I follow "About"
350
+ Then the page should have a skip link
351
+ ```
352
+
353
+ ## 12. Then every button should have an accessible name
354
+
355
+ Assert every <button> / role="button" has an accessible name (visible
356
+ text, aria-label, or aria-labelledby).
357
+
358
+ **Keyword**: `Then`
359
+
360
+ **Pattern**
361
+
362
+ ```js
363
+ /^every button should have an accessible name$/
364
+ ```
365
+
366
+ **Examples**
367
+
368
+ ```gherkin
369
+ Then every button should have an accessible name
370
+ Given I am on the homepage
371
+ Then every button should have an accessible name
372
+ When I am on "/checkout"
373
+ Then every button should have an accessible name
374
+ And every button should have an accessible name
375
+ Then every button should have an accessible name
376
+ And every link should have an accessible name
377
+ ```
378
+
379
+ ## 13. Then every link should have an accessible name
380
+
381
+ Assert every <a> has an accessible name.
382
+
383
+ **Keyword**: `Then`
384
+
385
+ **Pattern**
386
+
387
+ ```js
388
+ /^every link should have an accessible name$/
389
+ ```
390
+
391
+ **Examples**
392
+
393
+ ```gherkin
394
+ Then every link should have an accessible name
395
+ Given I am on the homepage
396
+ Then every link should have an accessible name
397
+ When I am on "/blog"
398
+ Then every link should have an accessible name
399
+ And every link should have an accessible name
400
+ Then every button should have an accessible name
401
+ And every link should have an accessible name
402
+ ```
403
+
404
+ ## 14. Then no element should have a positive tabindex
405
+
406
+ Assert NO interactive element uses a positive `tabindex`. Positive values
407
+ break natural focus order — WCAG 2.4.3 violation in practice.
408
+
409
+ **Keyword**: `Then`
410
+
411
+ **Pattern**
412
+
413
+ ```js
414
+ /^no element should have a positive tabindex$/
415
+ ```
416
+
417
+ **Examples**
418
+
419
+ ```gherkin
420
+ Then no element should have a positive tabindex
421
+ Given I am on the homepage
422
+ Then no element should have a positive tabindex
423
+ When I am on "/form"
424
+ Then no element should have a positive tabindex
425
+ And no element should have a positive tabindex
426
+ Then no element should have a positive tabindex
427
+ And the heading hierarchy should be valid
428
+ ```
429
+
430
+ ## 15. Then every ARIA reference should resolve
431
+
432
+ Assert every `aria-labelledby` / `aria-describedby` / `aria-controls`
433
+ / `aria-owns` references an existing element id.
434
+
435
+ **Keyword**: `Then`
436
+
437
+ **Pattern**
438
+
439
+ ```js
440
+ /^every ARIA reference should resolve$/
441
+ ```
442
+
443
+ **Examples**
444
+
445
+ ```gherkin
446
+ Then every ARIA reference should resolve
447
+ Given I am on the homepage
448
+ Then every ARIA reference should resolve
449
+ When I am on "/dashboard"
450
+ Then every ARIA reference should resolve
451
+ And every ARIA reference should resolve
452
+ Then every ARIA reference should resolve
453
+ And every button should have an accessible name
454
+ ```
455
+
456
+ ## 16. Then every ARIA role should be valid
457
+
458
+ Assert no element uses an invalid ARIA role. Reference: WAI-ARIA 1.2.
459
+
460
+ **Keyword**: `Then`
461
+
462
+ **Pattern**
463
+
464
+ ```js
465
+ /^every ARIA role should be valid$/
466
+ ```
467
+
468
+ **Examples**
469
+
470
+ ```gherkin
471
+ Then every ARIA role should be valid
472
+ Given I am on the homepage
473
+ Then every ARIA role should be valid
474
+ When I am on "/dashboard"
475
+ Then every ARIA role should be valid
476
+ And every ARIA role should be valid
477
+ Then every ARIA role should be valid
478
+ And every ARIA reference should resolve
479
+ ```
480
+
481
+ ## 17. Then required fields should be consistently marked
482
+
483
+ Assert every form field exposing `aria-required="true"` is also marked
484
+ with the native `required` attribute (or vice versa). Mismatch confuses
485
+ assistive tech.
486
+
487
+ **Keyword**: `Then`
488
+
489
+ **Pattern**
490
+
491
+ ```js
492
+ /^required fields should be consistently marked$/
493
+ ```
494
+
495
+ **Examples**
496
+
497
+ ```gherkin
498
+ Then required fields should be consistently marked
499
+ Given I am on "/signup"
500
+ Then required fields should be consistently marked
501
+ When I am on "/checkout"
502
+ Then required fields should be consistently marked
503
+ And required fields should be consistently marked
504
+ Then every form field should have an accessible label
505
+ And required fields should be consistently marked
506
+ ```
507
+
508
+ ## 18. Then the page should have a title
509
+
510
+ Assert the document has a non-empty `<title>`. WCAG 2.4.2.
511
+
512
+ **Keyword**: `Then`
513
+
514
+ **Pattern**
515
+
516
+ ```js
517
+ /^the page should have a title$/
518
+ ```
519
+
520
+ **Examples**
521
+
522
+ ```gherkin
523
+ Then the page should have a title
524
+ Given I am on the homepage
525
+ Then the page should have a title
526
+ When I am on "/about"
527
+ Then the page should have a title
528
+ And the page should have a title
529
+ Then the page should have a title
530
+ And the page should declare a language
531
+ ```
532
+
533
+ ## 19. Then user zoom should be allowed
534
+
535
+ Assert the viewport meta tag does not disable user-scaling
536
+ (`user-scalable=no` or `maximum-scale=1`). WCAG 1.4.4 / 1.4.10.
537
+
538
+ **Keyword**: `Then`
539
+
540
+ **Pattern**
541
+
542
+ ```js
543
+ /^user zoom should be allowed$/
544
+ ```
545
+
546
+ **Examples**
547
+
548
+ ```gherkin
549
+ Then user zoom should be allowed
550
+ Given I am on the homepage
551
+ Then user zoom should be allowed
552
+ When I am on "/article"
553
+ Then user zoom should be allowed
554
+ And user zoom should be allowed
555
+ Then user zoom should be allowed
556
+ And the page should declare a language
557
+ ```
558
+
559
+ ## 20. Then the page should pass an accessibility audit
560
+
561
+ Run an axe-core audit and assert ZERO violations across the supplied WCAG
562
+ level. Defaults to AA — the level mandated by most accessibility laws.
563
+
564
+ **Keyword**: `Then`
565
+
566
+ **Pattern**
567
+
568
+ ```js
569
+ /^the page should pass an accessibility audit(?: at level "(A|AA|AAA)")?$/
570
+ ```
571
+
572
+ **Examples**
573
+
574
+ ```gherkin
575
+ Then the page should pass an accessibility audit
576
+ Then the page should pass an accessibility audit at level "A"
577
+ Then the page should pass an accessibility audit at level "AA"
578
+ And the page should pass an accessibility audit at level "AAA"
579
+ When I am on "/dashboard"
580
+ Then the page should pass an accessibility audit at level "AA"
581
+ ```
582
+
583
+ ## 21. Then the page should have no critical accessibility violations
584
+
585
+ Assert NO axe violations of the given impact severity.
586
+ Levels: `minor`, `moderate`, `serious`, `critical`.
587
+
588
+ Use to gate merges on critical/serious issues while triaging minor regressions.
589
+
590
+ **Keyword**: `Then`
591
+
592
+ **Pattern**
593
+
594
+ ```js
595
+ /^the page should have no (critical|serious|moderate|minor) accessibility violations$/
596
+ ```
597
+
598
+ **Examples**
599
+
600
+ ```gherkin
601
+ Then the page should have no critical accessibility violations
602
+ Then the page should have no serious accessibility violations
603
+ And the page should have no moderate accessibility violations
604
+ When I am on "/checkout"
605
+ Then the page should have no critical accessibility violations
606
+ Then the page should have no critical accessibility violations
607
+ And the page should have no serious accessibility violations
608
+ ```
609
+
610
+ ## 22. Then the element "main" should pass an accessibility audit
611
+
612
+ Audit ONLY the subtree under a CSS selector. Use when third-party widgets
613
+ are known-bad and you only want to gate your own components.
614
+
615
+ **Keyword**: `Then`
616
+
617
+ **Pattern**
618
+
619
+ ```js
620
+ /^the element "([^"]*)" should pass an accessibility audit$/
621
+ ```
622
+
623
+ **Examples**
624
+
625
+ ```gherkin
626
+ Then the element "main" should pass an accessibility audit
627
+ Then the element "#checkout-form" should pass an accessibility audit
628
+ And the element ".product-card" should pass an accessibility audit
629
+ Then the element "#dashboard-widget" should pass an accessibility audit
630
+ Then the element "[data-testid=signup]" should pass an accessibility audit
631
+ ```
632
+
633
+ ## 23. Then the page should pass an accessibility audit excluding "iframe.payment"
634
+
635
+ Audit the page but EXCLUDE the subtree under a CSS selector. Skip
636
+ known-bad embedded content (third-party iframes, legacy widgets, ads).
637
+
638
+ **Keyword**: `Then`
639
+
640
+ **Pattern**
641
+
642
+ ```js
643
+ /^the page should pass an accessibility audit excluding "([^"]*)"$/
644
+ ```
645
+
646
+ **Examples**
647
+
648
+ ```gherkin
649
+ Then the page should pass an accessibility audit excluding "iframe.payment"
650
+ Then the page should pass an accessibility audit excluding "#chat-widget"
651
+ And the page should pass an accessibility audit excluding ".legacy-banner"
652
+ Then the page should pass an accessibility audit excluding "[data-third-party]"
653
+ Then the page should pass an accessibility audit excluding ".ads"
654
+ ```
655
+
656
+ ## 24. Then the page should not violate the accessibility rule "color-contrast"
657
+
658
+ Assert a specific axe rule does not fire. Use for fine-grained gates —
659
+ e.g. ensure color-contrast is fixed even if other rules still red.
660
+
661
+ Common rule ids: `color-contrast`, `image-alt`, `label`, `link-name`,
662
+ `button-name`, `heading-order`, `landmark-one-main`, `region`,
663
+ `aria-valid-attr`, `tabindex`, `bypass`, `frame-title`, `meta-viewport`,
664
+ `valid-lang`, `duplicate-id-aria`, `list`, `listitem`.
665
+
666
+ **Keyword**: `Then`
667
+
668
+ **Pattern**
669
+
670
+ ```js
671
+ /^the page should not violate the accessibility rule "([^"]*)"$/
672
+ ```
673
+
674
+ **Examples**
675
+
676
+ ```gherkin
677
+ Then the page should not violate the accessibility rule "color-contrast"
678
+ Then the page should not violate the accessibility rule "image-alt"
679
+ And the page should not violate the accessibility rule "label"
680
+ Then the page should not violate the accessibility rule "link-name"
681
+ Then the page should not violate the accessibility rule "heading-order"
682
+ ```
683
+
684
+ ## 25. Then I print accessibility violations
685
+
686
+ Print every axe violation to stdout (debug aid). Strip from CI runs.
687
+
688
+ **Keyword**: `Then`
689
+
690
+ **Pattern**
691
+
692
+ ```js
693
+ /^(I |we )*print accessibility violations$/
694
+ ```
695
+
696
+ **Examples**
697
+
698
+ ```gherkin
699
+ Then I print accessibility violations
700
+ When I am on "/checkout"
701
+ Then I print accessibility violations
702
+ And we print accessibility violations
703
+ Then I print accessibility violations
704
+ And the page should have no critical accessibility violations
705
+ When I follow "Pricing"
706
+ Then I print accessibility violations
707
+ ```
708
+
709
+ ## 26. Then the page should pass the accessibility rules "image-alt, label"
710
+
711
+ Assert axe finds zero violations of an exact list of rules. Use for
712
+ smoke-test gating: pin the small list of rules you absolutely require to
713
+ pass, run them on every page.
714
+
715
+ **Keyword**: `Then`
716
+
717
+ **Pattern**
718
+
719
+ ```js
720
+ /^the page should pass the accessibility rules "([^"]*)"$/
721
+ ```
722
+
723
+ **Examples**
724
+
725
+ ```gherkin
726
+ Then the page should pass the accessibility rules "image-alt, label"
727
+ Then the page should pass the accessibility rules "color-contrast"
728
+ And the page should pass the accessibility rules "button-name, link-name"
729
+ Then the page should pass the accessibility rules "landmark-one-main, region"
730
+ Then the page should pass the accessibility rules "aria-valid-attr, aria-valid-attr-value"
731
+ ```