@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,411 @@
1
+ 'use strict';
2
+
3
+ const { friendly } = require('./varbase-e2e');
4
+
5
+ // Parse and assert against an XML response loaded from a file or doc string.
6
+
7
+ const { Given, Then, When } = require('@cucumber/cucumber');
8
+ const assert = require('assert');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ let DOMParser;
13
+ try { ({ DOMParser } = require('@xmldom/xmldom')); }
14
+ catch (e) {
15
+ try { ({ DOMParser } = require('xmldom')); }
16
+ catch (e2) { /* will throw on use */ }
17
+ }
18
+
19
+ function ensureXml(world) {
20
+ if (!DOMParser) throw friendly('XML steps require an XML DOM parser. Install @xmldom/xmldom.');
21
+ if (!world._xml) throw friendly('No XML response has been set.');
22
+ return world._xml;
23
+ }
24
+
25
+ function evalXPath(doc, xpath) {
26
+ // Minimal XPath: support /a/b and /a/b[@attr] and /a/b/text()
27
+ // For richer XPath, users can install xpath module.
28
+ let xpathLib;
29
+ try { xpathLib = require('xpath'); } catch (e) { xpathLib = null; }
30
+ if (xpathLib) return xpathLib.select(xpath, doc);
31
+ // Fallback simple traversal
32
+ const parts = xpath.split('/').filter(Boolean);
33
+ let nodes = [doc.documentElement].filter((n) => n && n.nodeName === parts[0]);
34
+ for (const part of parts.slice(1)) {
35
+ const next = [];
36
+ for (const n of nodes) {
37
+ const children = Array.from(n.childNodes || []).filter((c) => c.nodeType === 1 && c.nodeName === part);
38
+ next.push(...children);
39
+ }
40
+ nodes = next;
41
+ }
42
+ return nodes;
43
+ }
44
+
45
+ /**
46
+ * Load XML response content from a file under `tests/assets/`.
47
+ *
48
+ * Example #1: Given the response content from the file "feed.xml"
49
+ * Example #2: Given the response content from the file "rss.xml"
50
+ * Example #3: And the response content from the file "sitemap.xml"
51
+ * Example #4: Given the response content from the file "soap-response.xml"
52
+ * Example #5: Given the response content from the file "/tmp/sample.xml"
53
+ *
54
+ */
55
+ Given('the response content from the file {string}', async function (filename) {
56
+ const p = path.isAbsolute(filename) ? filename : path.join(this.assetsFolder || './tests/assets', filename);
57
+ const content = fs.readFileSync(p, 'utf-8');
58
+ this._rawResponse = content;
59
+ this._xml = new DOMParser().parseFromString(content, 'text/xml');
60
+ });
61
+
62
+ /**
63
+ * Set XML response content inline from a Gherkin doc string.
64
+ *
65
+ * Example #1: Given the response content is the following:
66
+ * """
67
+ * <root><item id="1">Hello</item></root>
68
+ * """
69
+ * Example #2: Given the response content is the following:
70
+ * """
71
+ * <feed><entry><title>Sample feed</title></entry></feed>
72
+ * """
73
+ * Example #3: And the response content is the following:
74
+ * """
75
+ * <orders><order id="1"><status>shipped</status></order></orders>
76
+ * """
77
+ * Example #4: Given the response content is the following:
78
+ * """
79
+ * <products><product><name>Laptop</name></product></products>
80
+ * """
81
+ * Example #5: Given the response content is the following:
82
+ * """
83
+ * <error code="404">Not found</error>
84
+ * """
85
+ *
86
+ */
87
+ Given('the response content is the following:', async function (docString) {
88
+ this._rawResponse = docString;
89
+ this._xml = new DOMParser().parseFromString(docString, 'text/xml');
90
+ });
91
+
92
+ /**
93
+ * Assert the loaded response parses as XML.
94
+ *
95
+ * Example #1: Then the response should be in XML format
96
+ * Example #2: Given the response content from the file "rss.xml"
97
+ * Then the response should be in XML format
98
+ * Example #3: And the response should be in XML format
99
+ * Example #4: Given the response content is the following:
100
+ * """
101
+ * <root/>
102
+ * """
103
+ * Then the response should be in XML format
104
+ * Example #5: Then the response should be in XML format
105
+ * And the XML element "/root" should exist
106
+ *
107
+ */
108
+ Then('the response should be in XML format', async function () {
109
+ assert.ok(this._rawResponse, 'No response content set.');
110
+ const doc = new DOMParser().parseFromString(this._rawResponse, 'text/xml');
111
+ assert.ok(doc && doc.documentElement, 'Response is not valid XML.');
112
+ });
113
+
114
+ /**
115
+ * Assert the loaded response is NOT XML.
116
+ *
117
+ * Example #1: Then the response should not be in XML format
118
+ * Example #2: Given the response content is the following:
119
+ * """
120
+ * {"hello": "world"}
121
+ * """
122
+ * Then the response should not be in XML format
123
+ * Example #3: And the response should not be in XML format
124
+ * Example #4: Given the response content is the following:
125
+ * """
126
+ * plain text
127
+ * """
128
+ * Then the response should not be in XML format
129
+ * Example #5: Then the response should not be in XML format
130
+ * And I print last XML response
131
+ *
132
+ */
133
+ Then('the response should not be in XML format', async function () {
134
+ if (!this._rawResponse) return;
135
+ try {
136
+ const doc = new DOMParser().parseFromString(this._rawResponse, 'text/xml');
137
+ assert.ok(!doc || !doc.documentElement || doc.getElementsByTagName('parsererror').length > 0, 'Response is XML.');
138
+ } catch (e) { /* good */ }
139
+ });
140
+
141
+ /**
142
+ * Assert at least one XML element matches an XPath.
143
+ *
144
+ * Example #1: Then the XML element "/root/item" should exist
145
+ * Example #2: Then the XML element "//book/title" should exist
146
+ * Example #3: And the XML element "/order/customer" should exist
147
+ * Example #4: Then the XML element "//status" should exist
148
+ * Example #5: Then the XML element "/feed/entry[2]" should exist
149
+ *
150
+ */
151
+ Then('the XML element {string} should exist', async function (xpath) {
152
+ const doc = ensureXml(this);
153
+ const nodes = evalXPath(doc, xpath);
154
+ assert.ok(nodes && nodes.length > 0, `XML element "${xpath}" not found.`);
155
+ });
156
+
157
+ /**
158
+ * Assert NO XML element matches an XPath.
159
+ *
160
+ * Example #1: Then the XML element "/root/missing" should not exist
161
+ * Example #2: Then the XML element "//error" should not exist
162
+ * Example #3: And the XML element "/feed/legacy" should not exist
163
+ * Example #4: Then the XML element "//deprecated" should not exist
164
+ * Example #5: Then the XML element "/order/refund" should not exist
165
+ *
166
+ */
167
+ Then('the XML element {string} should not exist', async function (xpath) {
168
+ const doc = ensureXml(this);
169
+ const nodes = evalXPath(doc, xpath);
170
+ assert.ok(!nodes || nodes.length === 0, `XML element "${xpath}" should not exist.`);
171
+ });
172
+
173
+ /**
174
+ * Assert an XML element's text content equals an expected value (trimmed).
175
+ *
176
+ * Example #1: Then the XML element "/root/item" should be equal to "Hello"
177
+ * Example #2: Then the XML element "//status" should be equal to "shipped"
178
+ * Example #3: And the XML element "/order/customer/name" should be equal to "Alice"
179
+ * Example #4: Then the XML element "/book/title" should be equal to "Sample title"
180
+ * Example #5: Then the XML element "//count" should be equal to "5"
181
+ *
182
+ */
183
+ Then('the XML element {string} should be equal to {string}', async function (xpath, text) {
184
+ const doc = ensureXml(this);
185
+ const nodes = evalXPath(doc, xpath);
186
+ assert.ok(nodes && nodes.length > 0, `XML element "${xpath}" not found.`);
187
+ const value = (nodes[0].textContent || '').trim();
188
+ assert.strictEqual(value, text);
189
+ });
190
+
191
+ /**
192
+ * Assert an XML element's text content is NOT equal to an expected value.
193
+ *
194
+ * Example #1: Then the XML element "/root/item" should not be equal to "Bye"
195
+ * Example #2: Then the XML element "//status" should not be equal to "cancelled"
196
+ * Example #3: And the XML element "/order/customer/name" should not be equal to "Anonymous"
197
+ * Example #4: Then the XML element "/book/title" should not be equal to "Untitled"
198
+ * Example #5: Then the XML element "//count" should not be equal to "0"
199
+ *
200
+ */
201
+ Then('the XML element {string} should not be equal to {string}', async function (xpath, text) {
202
+ const doc = ensureXml(this);
203
+ const nodes = evalXPath(doc, xpath);
204
+ if (!nodes || nodes.length === 0) return;
205
+ assert.notStrictEqual((nodes[0].textContent || '').trim(), text);
206
+ });
207
+
208
+ /**
209
+ * Assert an XML element's text content contains a substring.
210
+ *
211
+ * Example #1: Then the XML element "/root/item" should contain "Hello"
212
+ * Example #2: Then the XML element "//status" should contain "ship"
213
+ * Example #3: And the XML element "/order/customer/name" should contain "Alice"
214
+ * Example #4: Then the XML element "/book/title" should contain "Varbase E2E"
215
+ * Example #5: Then the XML element "/feed/entry/summary" should contain "release"
216
+ *
217
+ */
218
+ Then('the XML element {string} should contain {string}', async function (xpath, text) {
219
+ const doc = ensureXml(this);
220
+ const nodes = evalXPath(doc, xpath);
221
+ assert.ok(nodes && nodes.length > 0, `XML element "${xpath}" not found.`);
222
+ assert.ok((nodes[0].textContent || '').indexOf(text) !== -1);
223
+ });
224
+
225
+ /**
226
+ * Assert an XML element's text content does NOT contain a substring.
227
+ *
228
+ * Example #1: Then the XML element "/root/item" should not contain "Error"
229
+ * Example #2: Then the XML element "//status" should not contain "fail"
230
+ * Example #3: And the XML element "/order/customer/name" should not contain "Anonymous"
231
+ * Example #4: Then the XML element "/book/title" should not contain "Untitled"
232
+ * Example #5: Then the XML element "/feed/entry/summary" should not contain "TODO"
233
+ *
234
+ */
235
+ Then('the XML element {string} should not contain {string}', async function (xpath, text) {
236
+ const doc = ensureXml(this);
237
+ const nodes = evalXPath(doc, xpath);
238
+ if (!nodes || nodes.length === 0) return;
239
+ assert.ok((nodes[0].textContent || '').indexOf(text) === -1);
240
+ });
241
+
242
+ /**
243
+ * Assert an XML element has the named attribute.
244
+ *
245
+ * Example #1: Then the XML attribute "id" on element "/root/item" should exist
246
+ * Example #2: Then the XML attribute "lang" on element "/feed" should exist
247
+ * Example #3: And the XML attribute "type" on element "//entry" should exist
248
+ * Example #4: Then the XML attribute "version" on element "/rss" should exist
249
+ * Example #5: Then the XML attribute "code" on element "/error" should exist
250
+ *
251
+ */
252
+ Then('the XML attribute {string} on element {string} should exist', async function (attr, xpath) {
253
+ const doc = ensureXml(this);
254
+ const nodes = evalXPath(doc, xpath);
255
+ assert.ok(nodes && nodes.length > 0, `XML element "${xpath}" not found.`);
256
+ assert.ok(nodes[0].getAttribute && nodes[0].hasAttribute(attr), `Attribute "${attr}" not found on "${xpath}".`);
257
+ });
258
+
259
+ /**
260
+ * Assert an XML element does NOT have the named attribute.
261
+ *
262
+ * Example #1: Then the XML attribute "deprecated" on element "/root/item" should not exist
263
+ * Example #2: Then the XML attribute "internal" on element "/feed" should not exist
264
+ * Example #3: And the XML attribute "secret" on element "//entry" should not exist
265
+ * Example #4: Then the XML attribute "private" on element "/rss" should not exist
266
+ * Example #5: Then the XML attribute "debug" on element "/error" should not exist
267
+ *
268
+ */
269
+ Then('the XML attribute {string} on element {string} should not exist', async function (attr, xpath) {
270
+ const doc = ensureXml(this);
271
+ const nodes = evalXPath(doc, xpath);
272
+ if (!nodes || nodes.length === 0) return;
273
+ assert.ok(!nodes[0].hasAttribute(attr), `Attribute "${attr}" should not exist on "${xpath}".`);
274
+ });
275
+
276
+ /**
277
+ * Assert an XML attribute equals an expected value.
278
+ *
279
+ * Example #1: Then the XML attribute "id" on element "/root/item" should be equal to "1"
280
+ * Example #2: Then the XML attribute "lang" on element "/feed" should be equal to "en"
281
+ * Example #3: And the XML attribute "type" on element "//entry" should be equal to "html"
282
+ * Example #4: Then the XML attribute "version" on element "/rss" should be equal to "2.0"
283
+ * Example #5: Then the XML attribute "code" on element "/error" should be equal to "404"
284
+ *
285
+ */
286
+ Then('the XML attribute {string} on element {string} should be equal to {string}', async function (attr, xpath, text) {
287
+ const doc = ensureXml(this);
288
+ const nodes = evalXPath(doc, xpath);
289
+ assert.ok(nodes && nodes.length > 0, `XML element "${xpath}" not found.`);
290
+ assert.strictEqual(nodes[0].getAttribute(attr), text);
291
+ });
292
+
293
+ /**
294
+ * Assert an XML attribute is NOT equal to an expected value.
295
+ *
296
+ * Example #1: Then the XML attribute "id" on element "/root/item" should not be equal to "0"
297
+ * Example #2: Then the XML attribute "lang" on element "/feed" should not be equal to "xx"
298
+ * Example #3: And the XML attribute "type" on element "//entry" should not be equal to "binary"
299
+ * Example #4: Then the XML attribute "version" on element "/rss" should not be equal to "0.9"
300
+ * Example #5: Then the XML attribute "code" on element "/error" should not be equal to "200"
301
+ *
302
+ */
303
+ Then('the XML attribute {string} on element {string} should not be equal to {string}', async function (attr, xpath, text) {
304
+ const doc = ensureXml(this);
305
+ const nodes = evalXPath(doc, xpath);
306
+ if (!nodes || nodes.length === 0) return;
307
+ assert.notStrictEqual(nodes[0].getAttribute(attr), text);
308
+ });
309
+
310
+ /**
311
+ * Assert an XML attribute contains a substring.
312
+ *
313
+ * Example #1: Then the XML attribute "id" on element "/root/item" should contain "abc"
314
+ * Example #2: Then the XML attribute "href" on element "//link" should contain "example.com"
315
+ * Example #3: And the XML attribute "type" on element "//entry" should contain "html"
316
+ * Example #4: Then the XML attribute "version" on element "/rss" should contain "2"
317
+ * Example #5: Then the XML attribute "code" on element "/error" should contain "4"
318
+ *
319
+ */
320
+ Then('the XML attribute {string} on element {string} should contain {string}', async function (attr, xpath, text) {
321
+ const doc = ensureXml(this);
322
+ const nodes = evalXPath(doc, xpath);
323
+ assert.ok(nodes && nodes.length > 0, `XML element "${xpath}" not found.`);
324
+ assert.ok((nodes[0].getAttribute(attr) || '').indexOf(text) !== -1);
325
+ });
326
+
327
+ /**
328
+ * Assert an XML attribute does NOT contain a substring.
329
+ *
330
+ * Example #1: Then the XML attribute "id" on element "/root/item" should not contain "old"
331
+ * Example #2: Then the XML attribute "href" on element "//link" should not contain "tracking"
332
+ * Example #3: And the XML attribute "type" on element "//entry" should not contain "binary"
333
+ * Example #4: Then the XML attribute "version" on element "/rss" should not contain "alpha"
334
+ * Example #5: Then the XML attribute "code" on element "/error" should not contain "5"
335
+ *
336
+ */
337
+ Then('the XML attribute {string} on element {string} should not contain {string}', async function (attr, xpath, text) {
338
+ const doc = ensureXml(this);
339
+ const nodes = evalXPath(doc, xpath);
340
+ if (!nodes || nodes.length === 0) return;
341
+ assert.ok((nodes[0].getAttribute(attr) || '').indexOf(text) === -1);
342
+ });
343
+
344
+ /**
345
+ * Assert exactly N elements match an XPath.
346
+ *
347
+ * Example #1: Then the XML element "//entry" should have 5 elements
348
+ * Example #2: Then the XML element "/root/item" should have 1 element
349
+ * Example #3: And the XML element "//error" should have 0 elements
350
+ * Example #4: Then the XML element "/feed/entry" should have 10 elements
351
+ * Example #5: Then the XML element "/orders/order" should have 3 elements
352
+ *
353
+ */
354
+ Then('the XML element {string} should have {int} element(s)', async function (xpath, count) {
355
+ const doc = ensureXml(this);
356
+ const nodes = evalXPath(doc, xpath);
357
+ assert.strictEqual((nodes || []).length, count);
358
+ });
359
+
360
+ /**
361
+ * Assert the XML root declares an `xmlns:*` namespace URI.
362
+ *
363
+ * Example #1: Then the XML should use the namespace "http://www.w3.org/2005/Atom"
364
+ * Example #2: Then the XML should use the namespace "http://www.sitemaps.org/schemas/sitemap/0.9"
365
+ * Example #3: And the XML should use the namespace "http://schemas.xmlsoap.org/soap/envelope/"
366
+ * Example #4: Then the XML should use the namespace "http://www.w3.org/1999/xhtml"
367
+ * Example #5: Then the XML should use the namespace "http://www.opengis.net/gml"
368
+ *
369
+ */
370
+ Then('the XML should use the namespace {string}', async function (ns) {
371
+ const doc = ensureXml(this);
372
+ const root = doc.documentElement;
373
+ const found = Array.from(root.attributes || []).some((a) => a.value === ns);
374
+ assert.ok(found, `Namespace "${ns}" not declared.`);
375
+ });
376
+
377
+ /**
378
+ * Assert the XML root does NOT declare a namespace URI.
379
+ *
380
+ * Example #1: Then the XML should not use the namespace "http://example.com/legacy"
381
+ * Example #2: Then the XML should not use the namespace "http://example.com/internal"
382
+ * Example #3: And the XML should not use the namespace "http://example.com/v1"
383
+ * Example #4: Then the XML should not use the namespace "http://example.com/deprecated"
384
+ * Example #5: Then the XML should not use the namespace "http://example.com/private"
385
+ *
386
+ */
387
+ Then('the XML should not use the namespace {string}', async function (ns) {
388
+ const doc = ensureXml(this);
389
+ const root = doc.documentElement;
390
+ const found = Array.from(root.attributes || []).some((a) => a.value === ns);
391
+ assert.ok(!found, `Namespace "${ns}" should not be declared.`);
392
+ });
393
+
394
+ /**
395
+ * Print the most recently set raw XML response to stdout (debug aid).
396
+ *
397
+ * Example #1: When I print last XML response
398
+ * Example #2: When I send a REST "GET" request to "/api/feed"
399
+ * And I print last XML response
400
+ * Example #3: And I print last XML response
401
+ * Example #4: Given the response content is the following:
402
+ * """
403
+ * <root/>
404
+ * """
405
+ * When I print last XML response
406
+ * Example #5: When I print last XML response
407
+ *
408
+ */
409
+ When(/^(I |we )*print last XML response$/, async function () {
410
+ console.log(this._rawResponse || '(no response)');
411
+ });