@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,216 @@
1
+ 'use strict';
2
+
3
+ // Cookie existence and value assertions (exact and partial matching) using
4
+ // Playwright's `BrowserContext.cookies()` API.
5
+
6
+ const { Then } = require('@cucumber/cucumber');
7
+ const assert = require('assert');
8
+
9
+ async function getCookies(world) {
10
+ return world.context ? await world.context.cookies() : [];
11
+ }
12
+
13
+ function findByName(cookies, name) {
14
+ return cookies.find((c) => c.name === name);
15
+ }
16
+
17
+ function findByPartialName(cookies, partial) {
18
+ return cookies.find((c) => c.name.indexOf(partial) !== -1);
19
+ }
20
+
21
+ /**
22
+ * Assert that a cookie with the exact name exists in the browser context.
23
+ *
24
+ * Example #1: Then a cookie with the name "session_id" should exist
25
+ * Example #2: Then a cookie with the name "auth_token" should exist
26
+ * Example #3: And a cookie with the name "lang" should exist
27
+ * Example #4: Then a cookie with the name "consent" should exist
28
+ * Example #5: When I press "Sign in"
29
+ * Then a cookie with the name "session" should exist
30
+ *
31
+ */
32
+ Then('a cookie with the name {string} should exist', async function (name) {
33
+ const cookies = await getCookies(this);
34
+ assert.ok(findByName(cookies, name), `Cookie "${name}" was not found.`);
35
+ });
36
+
37
+ /**
38
+ * Assert that a cookie with the exact name AND exact value exists.
39
+ *
40
+ * Example #1: Then a cookie with the name "lang" and the value "en" should exist
41
+ * Example #2: Then a cookie with the name "consent" and the value "accepted" should exist
42
+ * Example #3: And a cookie with the name "ab_bucket" and the value "variant-b" should exist
43
+ * Example #4: Then a cookie with the name "theme" and the value "dark" should exist
44
+ * Example #5: When I check "Remember me"
45
+ * And I press "Sign in"
46
+ * Then a cookie with the name "remember" and the value "1" should exist
47
+ *
48
+ */
49
+ Then('a cookie with the name {string} and the value {string} should exist', async function (name, value) {
50
+ const cookies = await getCookies(this);
51
+ const c = findByName(cookies, name);
52
+ assert.ok(c, `Cookie "${name}" was not found.`);
53
+ assert.strictEqual(c.value, value, `Cookie "${name}" has value "${c.value}", expected "${value}".`);
54
+ });
55
+
56
+ /**
57
+ * Assert a cookie with the exact name has a value containing a substring.
58
+ *
59
+ * Example #1: Then a cookie with the name "session_id" and a value containing "abc" should exist
60
+ * Example #2: Then a cookie with the name "auth" and a value containing "Bearer" should exist
61
+ * Example #3: And a cookie with the name "preferences" and a value containing "darkmode" should exist
62
+ * Example #4: Then a cookie with the name "tracking" and a value containing "v2-" should exist
63
+ * Example #5: Then a cookie with the name "csrf" and a value containing "token=" should exist
64
+ *
65
+ */
66
+ Then('a cookie with the name {string} and a value containing {string} should exist', async function (name, partial) {
67
+ const cookies = await getCookies(this);
68
+ const c = findByName(cookies, name);
69
+ assert.ok(c, `Cookie "${name}" was not found.`);
70
+ assert.ok(c.value.indexOf(partial) !== -1, `Cookie "${name}" value "${c.value}" does not contain "${partial}".`);
71
+ });
72
+
73
+ /**
74
+ * Assert a cookie whose name contains a substring exists.
75
+ *
76
+ * Example #1: Then a cookie with a name containing "session" should exist
77
+ * Example #2: Then a cookie with a name containing "_csrf" should exist
78
+ * Example #3: And a cookie with a name containing "tracking" should exist
79
+ * Example #4: Then a cookie with a name containing "auth" should exist
80
+ * Example #5: Then a cookie with a name containing "lang_" should exist
81
+ *
82
+ */
83
+ Then('a cookie with a name containing {string} should exist', async function (partial) {
84
+ const cookies = await getCookies(this);
85
+ assert.ok(findByPartialName(cookies, partial), `No cookie with name containing "${partial}" was found.`);
86
+ });
87
+
88
+ /**
89
+ * Assert a cookie whose name contains a substring has the exact value.
90
+ *
91
+ * Example #1: Then a cookie with a name containing "session" and the value "active" should exist
92
+ * Example #2: Then a cookie with a name containing "auth" and the value "1" should exist
93
+ * Example #3: And a cookie with a name containing "lang_" and the value "en" should exist
94
+ * Example #4: Then a cookie with a name containing "feature_" and the value "on" should exist
95
+ * Example #5: Then a cookie with a name containing "_pref" and the value "dark" should exist
96
+ *
97
+ */
98
+ Then('a cookie with a name containing {string} and the value {string} should exist', async function (partial, value) {
99
+ const cookies = await getCookies(this);
100
+ const found = cookies.find((c) => c.name.indexOf(partial) !== -1 && c.value === value);
101
+ assert.ok(found, `No cookie with name containing "${partial}" and value "${value}" was found.`);
102
+ });
103
+
104
+ /**
105
+ * Assert a cookie whose name contains substring A has a value containing substring B.
106
+ *
107
+ * Example #1: Then a cookie with a name containing "session" and a value containing "active" should exist
108
+ * Example #2: Then a cookie with a name containing "auth" and a value containing "Bearer" should exist
109
+ * Example #3: And a cookie with a name containing "tracking" and a value containing "v2-" should exist
110
+ * Example #4: Then a cookie with a name containing "csrf" and a value containing "token=" should exist
111
+ * Example #5: Then a cookie with a name containing "_pref" and a value containing "dark" should exist
112
+ *
113
+ */
114
+ Then('a cookie with a name containing {string} and a value containing {string} should exist', async function (partialName, partialValue) {
115
+ const cookies = await getCookies(this);
116
+ const found = cookies.find((c) => c.name.indexOf(partialName) !== -1 && c.value.indexOf(partialValue) !== -1);
117
+ assert.ok(found, `No cookie with name containing "${partialName}" and value containing "${partialValue}" was found.`);
118
+ });
119
+
120
+ /**
121
+ * Assert a cookie with the exact name does NOT exist.
122
+ *
123
+ * Example #1: Then a cookie with the name "session_id" should not exist
124
+ * Example #2: Then a cookie with the name "auth_token" should not exist
125
+ * Example #3: And a cookie with the name "tracking" should not exist
126
+ * Example #4: When I press "Logout"
127
+ * Then a cookie with the name "session" should not exist
128
+ * Example #5: Given all cookies are cleared
129
+ * Then a cookie with the name "lang" should not exist
130
+ *
131
+ */
132
+ Then('a cookie with the name {string} should not exist', async function (name) {
133
+ const cookies = await getCookies(this);
134
+ assert.ok(!findByName(cookies, name), `Cookie "${name}" should not exist but it does.`);
135
+ });
136
+
137
+ /**
138
+ * Assert a cookie with the exact name+value does NOT exist.
139
+ *
140
+ * Example #1: Then a cookie with the name "lang" and the value "fr" should not exist
141
+ * Example #2: Then a cookie with the name "consent" and the value "rejected" should not exist
142
+ * Example #3: And a cookie with the name "theme" and the value "dark" should not exist
143
+ * Example #4: Then a cookie with the name "ab_bucket" and the value "variant-c" should not exist
144
+ * Example #5: Then a cookie with the name "remember" and the value "0" should not exist
145
+ *
146
+ */
147
+ Then('a cookie with the name {string} and the value {string} should not exist', async function (name, value) {
148
+ const cookies = await getCookies(this);
149
+ const c = findByName(cookies, name);
150
+ assert.ok(!c || c.value !== value, `Cookie "${name}" with value "${value}" should not exist.`);
151
+ });
152
+
153
+ /**
154
+ * Assert a cookie with the exact name does not contain a substring in its value.
155
+ *
156
+ * Example #1: Then a cookie with the name "preferences" and a value containing "lightmode" should not exist
157
+ * Example #2: Then a cookie with the name "tracking" and a value containing "v1-" should not exist
158
+ * Example #3: And a cookie with the name "auth" and a value containing "Basic" should not exist
159
+ * Example #4: Then a cookie with the name "session" and a value containing "expired" should not exist
160
+ * Example #5: Then a cookie with the name "csrf" and a value containing "invalid" should not exist
161
+ *
162
+ */
163
+ Then('a cookie with the name {string} and a value containing {string} should not exist', async function (name, partial) {
164
+ const cookies = await getCookies(this);
165
+ const c = findByName(cookies, name);
166
+ assert.ok(!c || c.value.indexOf(partial) === -1, `Cookie "${name}" should not contain value "${partial}".`);
167
+ });
168
+
169
+ /**
170
+ * Assert no cookie name contains a substring.
171
+ *
172
+ * Example #1: Then a cookie with a name containing "old_" should not exist
173
+ * Example #2: Then a cookie with a name containing "_legacy" should not exist
174
+ * Example #3: And a cookie with a name containing "deprecated" should not exist
175
+ * Example #4: Given all cookies are cleared
176
+ * Then a cookie with a name containing "session" should not exist
177
+ * Example #5: When I press "Logout"
178
+ * Then a cookie with a name containing "auth" should not exist
179
+ *
180
+ */
181
+ Then('a cookie with a name containing {string} should not exist', async function (partial) {
182
+ const cookies = await getCookies(this);
183
+ assert.ok(!findByPartialName(cookies, partial), `Cookie with name containing "${partial}" should not exist.`);
184
+ });
185
+
186
+ /**
187
+ * Assert no cookie name+value pair (with partial name) matches.
188
+ *
189
+ * Example #1: Then a cookie with a name containing "session" and the value "expired" should not exist
190
+ * Example #2: Then a cookie with a name containing "auth" and the value "anonymous" should not exist
191
+ * Example #3: And a cookie with a name containing "feature_" and the value "off" should not exist
192
+ * Example #4: Then a cookie with a name containing "lang_" and the value "xx" should not exist
193
+ * Example #5: Then a cookie with a name containing "_pref" and the value "default" should not exist
194
+ *
195
+ */
196
+ Then('a cookie with a name containing {string} and the value {string} should not exist', async function (partial, value) {
197
+ const cookies = await getCookies(this);
198
+ const found = cookies.find((c) => c.name.indexOf(partial) !== -1 && c.value === value);
199
+ assert.ok(!found, `Cookie with name containing "${partial}" and value "${value}" should not exist.`);
200
+ });
201
+
202
+ /**
203
+ * Assert no cookie partial-name + partial-value pair matches.
204
+ *
205
+ * Example #1: Then a cookie with a name containing "session" and a value containing "old" should not exist
206
+ * Example #2: Then a cookie with a name containing "auth" and a value containing "Basic" should not exist
207
+ * Example #3: And a cookie with a name containing "tracking" and a value containing "v0-" should not exist
208
+ * Example #4: Then a cookie with a name containing "_pref" and a value containing "lightmode" should not exist
209
+ * Example #5: Then a cookie with a name containing "csrf" and a value containing "invalid" should not exist
210
+ *
211
+ */
212
+ Then('a cookie with a name containing {string} and a value containing {string} should not exist', async function (partialName, partialValue) {
213
+ const cookies = await getCookies(this);
214
+ const found = cookies.find((c) => c.name.indexOf(partialName) !== -1 && c.value.indexOf(partialValue) !== -1);
215
+ assert.ok(!found, `Cookie with name containing "${partialName}" and value containing "${partialValue}" should not exist.`);
216
+ });
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ // Debug helpers — print current URL, full HTML response, etc. Use sparingly
4
+ // and strip before merging.
5
+
6
+ const { Then } = require('@cucumber/cucumber');
7
+
8
+ /**
9
+ * Print the current page URL to console (debug).
10
+ *
11
+ * Example #1: Then print current URL
12
+ * Example #2: When print current URL
13
+ * Example #3: And print current URL
14
+ */
15
+ Then(/^print current URL$/, function () {
16
+ try {
17
+ console.log('\n--- Current URL ---');
18
+ console.log(` ${this.page.url()}`);
19
+ } catch (e) {
20
+ console.log(`\n--- Current URL unavailable: ${e.message} ---`);
21
+ }
22
+ });
23
+
24
+ /**
25
+ * Print the full page HTML (last response) to console (debug).
26
+ *
27
+ * Example #1: Then print last response
28
+ * Example #2: When print last response
29
+ * Example #3: And print last response
30
+ */
31
+ Then(/^print last response$/, async function () {
32
+ try {
33
+ const html = await this.page.content();
34
+ console.log('\n--- Last Response (HTML) ---');
35
+ console.log(html);
36
+ } catch (e) {
37
+ console.log(`\n--- Last response unavailable: ${e.message} ---`);
38
+ }
39
+ });
@@ -0,0 +1,193 @@
1
+ 'use strict';
2
+
3
+ // Browser dialog handlers (alert / confirm / prompt / beforeunload).
4
+ // Mirrors Playwright's `page.on('dialog')` API with BDD-friendly phrasing.
5
+ //
6
+ // Attach a handler BEFORE the action that triggers the dialog. Handlers
7
+ // queue dialog messages onto `this._lastDialog` so assertions can inspect
8
+ // them after the action completes.
9
+
10
+ const { Given, Then } = require('@cucumber/cucumber');
11
+ const assert = require('assert');
12
+
13
+ // Single shared attach() — handles single-shot ("next") and persistent
14
+ // ("all") variants via the `once` flag.
15
+ function attach(world, action, promptInput, once) {
16
+ if (world._dialogHandler) {
17
+ try { world.page.off('dialog', world._dialogHandler); } catch { /* ignore */ }
18
+ }
19
+ world._lastDialog = null;
20
+ world._dialogHandler = async (dialog) => {
21
+ world._lastDialog = {
22
+ type: dialog.type(),
23
+ message: dialog.message(),
24
+ defaultValue: dialog.defaultValue(),
25
+ };
26
+ try {
27
+ if (action === 'accept') {
28
+ if (typeof promptInput === 'string' && dialog.type() === 'prompt') {
29
+ await dialog.accept(promptInput);
30
+ } else {
31
+ await dialog.accept();
32
+ }
33
+ } else {
34
+ await dialog.dismiss();
35
+ }
36
+ } catch { /* dialog may have closed already */ }
37
+ if (once) {
38
+ try { world.page.off('dialog', world._dialogHandler); } catch { /* ignore */ }
39
+ world._dialogHandler = null;
40
+ }
41
+ };
42
+ world.page.on('dialog', world._dialogHandler);
43
+ }
44
+
45
+ /**
46
+ * Auto-accept the next native browser dialog (alert / confirm / prompt).
47
+ *
48
+ * Example #1: Given I will accept the next dialog
49
+ * When I click "Delete"
50
+ * Example #2: Given we will accept the next dialog
51
+ * When I press "Confirm"
52
+ * Example #3: Given I will accept the next dialog
53
+ * When I click "Save changes"
54
+ * Then I should see "Saved"
55
+ * Example #4: Given I will accept the next dialog
56
+ * When I follow "Leave page"
57
+ * Example #5: Given I will accept the next dialog
58
+ * When I press "Reset password"
59
+ * Then the last dialog type should be "confirm"
60
+ *
61
+ */
62
+ Given(/^(I |we )*will accept the next dialog$/, function () {
63
+ attach(this, 'accept', undefined, true);
64
+ });
65
+
66
+ /**
67
+ * Auto-dismiss the next native browser dialog.
68
+ *
69
+ * Example #1: Given I will dismiss the next dialog
70
+ * When I click "Leave page"
71
+ * Example #2: Given we will dismiss the next dialog
72
+ * When I press "Cancel reset"
73
+ * Example #3: Given I will dismiss the next dialog
74
+ * When I follow "Other site"
75
+ * Then I should be on the homepage
76
+ * Example #4: Given I will dismiss the next dialog
77
+ * When I press "Delete"
78
+ * Then "<.row>" should still be visible
79
+ * Example #5: Given I will dismiss the next dialog
80
+ * When I close the tab
81
+ *
82
+ */
83
+ Given(/^(I |we )*will dismiss the next dialog$/, function () {
84
+ attach(this, 'dismiss', undefined, true);
85
+ });
86
+
87
+ /**
88
+ * Auto-accept the next prompt with the supplied text input.
89
+ *
90
+ * Example #1: Given I will accept the next dialog with "alice@example.com"
91
+ * When I press "Reset password"
92
+ * Example #2: Given I will accept the next dialog with "Yes"
93
+ * When I click "Continue"
94
+ * Example #3: Given we will accept the next dialog with "Bug report"
95
+ * When I press "Open feedback prompt"
96
+ * Example #4: Given I will accept the next dialog with "12345"
97
+ * When I follow "Set order ID"
98
+ * Example #5: Given I will accept the next dialog with "https://example.com"
99
+ * When I press "Add link"
100
+ *
101
+ */
102
+ Given(/^(I |we )*will accept the next dialog with "([^"]*)"$/, function (pronoun, input) {
103
+ attach(this, 'accept', input, true);
104
+ });
105
+
106
+ /**
107
+ * Assert the most recently captured dialog message equals an expected value.
108
+ *
109
+ * Example #1: Then the last dialog message should be "Are you sure?"
110
+ * Example #2: Then the last dialog message should be "Delete this item?"
111
+ * Example #3: And the last dialog message should be "Leave site?"
112
+ * Example #4: Then the last dialog message should be "Save changes?"
113
+ * Example #5: When I press "Reset"
114
+ * Then the last dialog message should be "Reset all settings?"
115
+ *
116
+ */
117
+ Then(/^the last dialog message should be "([^"]*)"$/, function (text) {
118
+ assert.ok(this._lastDialog, 'No dialog has been captured.');
119
+ assert.strictEqual(this._lastDialog.message, text,
120
+ `Expected dialog message "${text}", got "${this._lastDialog.message}".`);
121
+ });
122
+
123
+ /**
124
+ * Assert the most recently captured dialog message contains an expected substring.
125
+ *
126
+ * Example #1: Then the last dialog message should contain "Are you sure"
127
+ * Example #2: Then the last dialog message should contain "delete"
128
+ * Example #3: And the last dialog message should contain "leave"
129
+ * Example #4: Then the last dialog message should contain "save"
130
+ * Example #5: When I click "Cancel order"
131
+ * Then the last dialog message should contain "cancel"
132
+ *
133
+ */
134
+ Then(/^the last dialog message should contain "([^"]*)"$/, function (text) {
135
+ assert.ok(this._lastDialog, 'No dialog has been captured.');
136
+ assert.ok(this._lastDialog.message.includes(text),
137
+ `Dialog message "${this._lastDialog.message}" does not contain "${text}".`);
138
+ });
139
+
140
+ /**
141
+ * Assert the most recently captured dialog type matches one of `alert`,
142
+ * `confirm`, `prompt`, or `beforeunload`.
143
+ *
144
+ * Example #1: Then the last dialog type should be "confirm"
145
+ * Example #2: Then the last dialog type should be "alert"
146
+ * Example #3: And the last dialog type should be "prompt"
147
+ * Example #4: Then the last dialog type should be "beforeunload"
148
+ * Example #5: When I press "Reset password"
149
+ * Then the last dialog type should be "prompt"
150
+ *
151
+ */
152
+ Then(/^the last dialog type should be "([^"]*)"$/, function (type) {
153
+ assert.ok(this._lastDialog, 'No dialog has been captured.');
154
+ assert.strictEqual(this._lastDialog.type, type,
155
+ `Expected dialog type "${type}", got "${this._lastDialog.type}".`);
156
+ });
157
+
158
+ /**
159
+ * Auto-accept EVERY native browser dialog raised in this scenario.
160
+ *
161
+ * Persistent variant of "I will accept the next dialog" — handler stays
162
+ * attached for the rest of the scenario.
163
+ *
164
+ * Example #1: Given I accept all confirmation dialogs
165
+ * Example #2: Given we accept all confirmation dialogs
166
+ * When I click "Delete all"
167
+ * Example #3: And I accept all confirmation dialogs
168
+ * Example #4: Given I accept all confirmation dialogs
169
+ * When I press "Reset settings"
170
+ * Example #5: Given I accept all confirmation dialogs
171
+ * When I follow "Discard changes"
172
+ *
173
+ */
174
+ Given(/^(I |we )*accept all confirmation dialogs$/, function () {
175
+ attach(this, 'accept', undefined, false);
176
+ });
177
+
178
+ /**
179
+ * Auto-dismiss EVERY native browser dialog raised in this scenario.
180
+ *
181
+ * Example #1: Given I do not accept any confirmation dialogs
182
+ * Example #2: Given we do not accept any confirmation dialogs
183
+ * When I click "Delete"
184
+ * Example #3: And I do not accept any confirmation dialogs
185
+ * Example #4: Given I do not accept any confirmation dialogs
186
+ * When I press "Cancel reset"
187
+ * Example #5: Given I do not accept any confirmation dialogs
188
+ * When I follow "Leave page"
189
+ *
190
+ */
191
+ Given(/^(I |we )*do not accept any confirmation dialogs$/, function () {
192
+ attach(this, 'dismiss', undefined, false);
193
+ });