@brandlab-digital/conformance 3.7.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/current-release.js +5 -5
- package/dist/current-release.js.map +1 -1
- package/dist/public-copy.d.ts +7 -0
- package/dist/public-copy.d.ts.map +1 -1
- package/dist/public-copy.js +8 -3
- package/dist/public-copy.js.map +1 -1
- package/dist/ready-cli.d.ts +38 -0
- package/dist/ready-cli.d.ts.map +1 -0
- package/dist/ready-cli.js +327 -0
- package/dist/ready-cli.js.map +1 -0
- package/dist/site-system/checks.d.ts +389 -0
- package/dist/site-system/checks.d.ts.map +1 -0
- package/dist/site-system/checks.js +96 -0
- package/dist/site-system/checks.js.map +1 -0
- package/dist/site-system/config.d.ts +40 -0
- package/dist/site-system/config.d.ts.map +1 -0
- package/dist/site-system/config.js +214 -0
- package/dist/site-system/config.js.map +1 -0
- package/dist/site-system/index.d.ts +14 -0
- package/dist/site-system/index.d.ts.map +1 -0
- package/dist/site-system/index.js +14 -0
- package/dist/site-system/index.js.map +1 -0
- package/dist/site-system/journeys/access.d.ts +7 -0
- package/dist/site-system/journeys/access.d.ts.map +1 -0
- package/dist/site-system/journeys/access.js +263 -0
- package/dist/site-system/journeys/access.js.map +1 -0
- package/dist/site-system/journeys/browser.d.ts +79 -0
- package/dist/site-system/journeys/browser.d.ts.map +1 -0
- package/dist/site-system/journeys/browser.js +218 -0
- package/dist/site-system/journeys/browser.js.map +1 -0
- package/dist/site-system/journeys/conversion.d.ts +3 -0
- package/dist/site-system/journeys/conversion.d.ts.map +1 -0
- package/dist/site-system/journeys/conversion.js +152 -0
- package/dist/site-system/journeys/conversion.js.map +1 -0
- package/dist/site-system/journeys/menu.d.ts +11 -0
- package/dist/site-system/journeys/menu.d.ts.map +1 -0
- package/dist/site-system/journeys/menu.js +395 -0
- package/dist/site-system/journeys/menu.js.map +1 -0
- package/dist/site-system/journeys/page-helpers.d.ts +146 -0
- package/dist/site-system/journeys/page-helpers.d.ts.map +1 -0
- package/dist/site-system/journeys/page-helpers.js +426 -0
- package/dist/site-system/journeys/page-helpers.js.map +1 -0
- package/dist/site-system/journeys/plan.d.ts +82 -0
- package/dist/site-system/journeys/plan.d.ts.map +1 -0
- package/dist/site-system/journeys/plan.js +112 -0
- package/dist/site-system/journeys/plan.js.map +1 -0
- package/dist/site-system/journeys/shell.d.ts +24 -0
- package/dist/site-system/journeys/shell.d.ts.map +1 -0
- package/dist/site-system/journeys/shell.js +269 -0
- package/dist/site-system/journeys/shell.js.map +1 -0
- package/dist/site-system/playwright-report.d.ts +22 -0
- package/dist/site-system/playwright-report.d.ts.map +1 -0
- package/dist/site-system/playwright-report.js +131 -0
- package/dist/site-system/playwright-report.js.map +1 -0
- package/dist/site-system/records.d.ts +30 -0
- package/dist/site-system/records.d.ts.map +1 -0
- package/dist/site-system/records.js +98 -0
- package/dist/site-system/records.js.map +1 -0
- package/dist/site-system/report.d.ts +116 -0
- package/dist/site-system/report.d.ts.map +1 -0
- package/dist/site-system/report.js +366 -0
- package/dist/site-system/report.js.map +1 -0
- package/dist/site-system/rule-modules.d.ts +8 -0
- package/dist/site-system/rule-modules.d.ts.map +1 -0
- package/dist/site-system/rule-modules.js +12 -0
- package/dist/site-system/rule-modules.js.map +1 -0
- package/dist/site-system/rules/design/d4-distinctness.d.ts +36 -0
- package/dist/site-system/rules/design/d4-distinctness.d.ts.map +1 -0
- package/dist/site-system/rules/design/d4-distinctness.js +207 -0
- package/dist/site-system/rules/design/d4-distinctness.js.map +1 -0
- package/dist/site-system/rules/design/index.d.ts +17 -0
- package/dist/site-system/rules/design/index.d.ts.map +1 -0
- package/dist/site-system/rules/design/index.js +7 -0
- package/dist/site-system/rules/design/index.js.map +1 -0
- package/dist/site-system/rules/enquiry/c2-quote-form.d.ts +31 -0
- package/dist/site-system/rules/enquiry/c2-quote-form.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/c2-quote-form.js +211 -0
- package/dist/site-system/rules/enquiry/c2-quote-form.js.map +1 -0
- package/dist/site-system/rules/enquiry/common.d.ts +37 -0
- package/dist/site-system/rules/enquiry/common.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/common.js +59 -0
- package/dist/site-system/rules/enquiry/common.js.map +1 -0
- package/dist/site-system/rules/enquiry/e1-recorded-titles.d.ts +8 -0
- package/dist/site-system/rules/enquiry/e1-recorded-titles.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/e1-recorded-titles.js +59 -0
- package/dist/site-system/rules/enquiry/e1-recorded-titles.js.map +1 -0
- package/dist/site-system/rules/enquiry/e9-cost-guides.d.ts +22 -0
- package/dist/site-system/rules/enquiry/e9-cost-guides.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/e9-cost-guides.js +55 -0
- package/dist/site-system/rules/enquiry/e9-cost-guides.js.map +1 -0
- package/dist/site-system/rules/enquiry/html.d.ts +53 -0
- package/dist/site-system/rules/enquiry/html.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/html.js +294 -0
- package/dist/site-system/rules/enquiry/html.js.map +1 -0
- package/dist/site-system/rules/enquiry/index.d.ts +17 -0
- package/dist/site-system/rules/enquiry/index.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/index.js +26 -0
- package/dist/site-system/rules/enquiry/index.js.map +1 -0
- package/dist/site-system/rules/enquiry/k8-sections.d.ts +17 -0
- package/dist/site-system/rules/enquiry/k8-sections.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/k8-sections.js +76 -0
- package/dist/site-system/rules/enquiry/k8-sections.js.map +1 -0
- package/dist/site-system/rules/enquiry/literal.d.ts +29 -0
- package/dist/site-system/rules/enquiry/literal.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/literal.js +189 -0
- package/dist/site-system/rules/enquiry/literal.js.map +1 -0
- package/dist/site-system/rules/enquiry/t11-licence-classes.d.ts +73 -0
- package/dist/site-system/rules/enquiry/t11-licence-classes.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/t11-licence-classes.js +275 -0
- package/dist/site-system/rules/enquiry/t11-licence-classes.js.map +1 -0
- package/dist/site-system/rules/enquiry/t5-reviews.d.ts +23 -0
- package/dist/site-system/rules/enquiry/t5-reviews.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/t5-reviews.js +91 -0
- package/dist/site-system/rules/enquiry/t5-reviews.js.map +1 -0
- package/dist/site-system/rules/shell/banned-wording.d.ts +24 -0
- package/dist/site-system/rules/shell/banned-wording.d.ts.map +1 -0
- package/dist/site-system/rules/shell/banned-wording.js +212 -0
- package/dist/site-system/rules/shell/banned-wording.js.map +1 -0
- package/dist/site-system/rules/shell/budgets.d.ts +43 -0
- package/dist/site-system/rules/shell/budgets.d.ts.map +1 -0
- package/dist/site-system/rules/shell/budgets.js +297 -0
- package/dist/site-system/rules/shell/budgets.js.map +1 -0
- package/dist/site-system/rules/shell/cta-labels.d.ts +17 -0
- package/dist/site-system/rules/shell/cta-labels.d.ts.map +1 -0
- package/dist/site-system/rules/shell/cta-labels.js +86 -0
- package/dist/site-system/rules/shell/cta-labels.js.map +1 -0
- package/dist/site-system/rules/shell/facts.d.ts +33 -0
- package/dist/site-system/rules/shell/facts.d.ts.map +1 -0
- package/dist/site-system/rules/shell/facts.js +217 -0
- package/dist/site-system/rules/shell/facts.js.map +1 -0
- package/dist/site-system/rules/shell/headings.d.ts +5 -0
- package/dist/site-system/rules/shell/headings.d.ts.map +1 -0
- package/dist/site-system/rules/shell/headings.js +23 -0
- package/dist/site-system/rules/shell/headings.js.map +1 -0
- package/dist/site-system/rules/shell/hero-location.d.ts +4 -0
- package/dist/site-system/rules/shell/hero-location.d.ts.map +1 -0
- package/dist/site-system/rules/shell/hero-location.js +45 -0
- package/dist/site-system/rules/shell/hero-location.js.map +1 -0
- package/dist/site-system/rules/shell/html.d.ts +53 -0
- package/dist/site-system/rules/shell/html.d.ts.map +1 -0
- package/dist/site-system/rules/shell/html.js +341 -0
- package/dist/site-system/rules/shell/html.js.map +1 -0
- package/dist/site-system/rules/shell/index.d.ts +18 -0
- package/dist/site-system/rules/shell/index.d.ts.map +1 -0
- package/dist/site-system/rules/shell/index.js +65 -0
- package/dist/site-system/rules/shell/index.js.map +1 -0
- package/dist/site-system/rules/shell/licence.d.ts +4 -0
- package/dist/site-system/rules/shell/licence.d.ts.map +1 -0
- package/dist/site-system/rules/shell/licence.js +60 -0
- package/dist/site-system/rules/shell/licence.js.map +1 -0
- package/dist/site-system/rules/shell/location-wording.d.ts +13 -0
- package/dist/site-system/rules/shell/location-wording.d.ts.map +1 -0
- package/dist/site-system/rules/shell/location-wording.js +15 -0
- package/dist/site-system/rules/shell/location-wording.js.map +1 -0
- package/dist/site-system/rules/shell/navigation.d.ts +5 -0
- package/dist/site-system/rules/shell/navigation.d.ts.map +1 -0
- package/dist/site-system/rules/shell/navigation.js +272 -0
- package/dist/site-system/rules/shell/navigation.js.map +1 -0
- package/dist/site-system/rules/shell/overlays.d.ts +4 -0
- package/dist/site-system/rules/shell/overlays.d.ts.map +1 -0
- package/dist/site-system/rules/shell/overlays.js +93 -0
- package/dist/site-system/rules/shell/overlays.js.map +1 -0
- package/dist/site-system/rules/shell/page.d.ts +51 -0
- package/dist/site-system/rules/shell/page.d.ts.map +1 -0
- package/dist/site-system/rules/shell/page.js +256 -0
- package/dist/site-system/rules/shell/page.js.map +1 -0
- package/dist/site-system/rules/shell/phone.d.ts +13 -0
- package/dist/site-system/rules/shell/phone.d.ts.map +1 -0
- package/dist/site-system/rules/shell/phone.js +99 -0
- package/dist/site-system/rules/shell/phone.js.map +1 -0
- package/dist/site-system/rules/shell/proof-numbers.d.ts +7 -0
- package/dist/site-system/rules/shell/proof-numbers.d.ts.map +1 -0
- package/dist/site-system/rules/shell/proof-numbers.js +81 -0
- package/dist/site-system/rules/shell/proof-numbers.js.map +1 -0
- package/dist/site-system/rules/shell/types.d.ts +80 -0
- package/dist/site-system/rules/shell/types.d.ts.map +1 -0
- package/dist/site-system/rules/shell/types.js +17 -0
- package/dist/site-system/rules/shell/types.js.map +1 -0
- package/dist/site-system/runners.d.ts +37 -0
- package/dist/site-system/runners.d.ts.map +1 -0
- package/dist/site-system/runners.js +48 -0
- package/dist/site-system/runners.js.map +1 -0
- package/dist/site-system/samples.d.ts +43 -0
- package/dist/site-system/samples.d.ts.map +1 -0
- package/dist/site-system/samples.js +99 -0
- package/dist/site-system/samples.js.map +1 -0
- package/dist/site-system/steps.d.ts +66 -0
- package/dist/site-system/steps.d.ts.map +1 -0
- package/dist/site-system/steps.js +45 -0
- package/dist/site-system/steps.js.map +1 -0
- package/dist/site-system/streamed-html.d.ts +9 -0
- package/dist/site-system/streamed-html.d.ts.map +1 -0
- package/dist/site-system/streamed-html.js +43 -0
- package/dist/site-system/streamed-html.js.map +1 -0
- package/dist/site-system-e2e.d.ts +6 -0
- package/dist/site-system-e2e.d.ts.map +1 -0
- package/dist/site-system-e2e.js +54 -0
- package/dist/site-system-e2e.js.map +1 -0
- package/package.json +22 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversion.js","sourceRoot":"","sources":["../../../src/site-system/journeys/conversion.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACvF,OAAO,EAAE,4BAA4B,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,sBAAsB,IAAI,KAAK,EAA8B,MAAM,WAAW,CAAC;AAClJ,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,2GAA2G;AAC3G,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B,MAAM,UAAU,wBAAwB,CAAC,IAA2B,EAAE,IAAyB;IAC7F,MAAM,KAAK,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IAE7E,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACnB,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;YAChC,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACrB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACjC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,2BAA2B,CAAC,EAAE,GAAG,EAAE;oBAClD,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAChC,CAAC,CAAC,CAAC;YACL,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBAC9B,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,IAAI,6GAA6G,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;oBACzJ,MAAM,QAAQ,GAAa,EAAE,CAAC;oBAC9B,MAAM,IAAI,CAAC,eAAe,CAAC,EAAE,GAAG,0BAA0B,EAAE,CAAC,CAAC;oBAC9D,MAAM,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;oBACrC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE;wBACzC,MAAM,OAAO,GAAG,MAAM,CAAC,oBAAoB,CAAC;wBAC5C,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;wBACjD,MAAM,MAAM,GAAG,CAAC,OAAgB,EAAE,EAAE;4BAClC,MAAM,IAAI,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;4BAC7C,OAAO,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,WAAW,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;wBAC7G,CAAC,CAAC;wBACF,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;wBACrF,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;wBACxE,OAAO;4BACL,OAAO,EAAE,OAAO,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,IAAI,CAAC,IAAI,UAAU,GAAG,MAAM,CAAC,WAAW;4BAC3G,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;4BACvH,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;yBAC9D,CAAC;oBACJ,CAAC,EAAE,KAAK,CAAC,CAAC;oBACV,IAAI,CAAC,KAAK,CAAC,OAAO;wBAAE,QAAQ,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;oBAC1F,IAAI,CAAC,KAAK,CAAC,IAAI;wBAAE,QAAQ,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;oBAChF,IAAI,CAAC,KAAK,CAAC,KAAK;wBAAE,QAAQ,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;oBAClF,MAAM,IAAI,CAAC,eAAe,CAAC,EAAE,GAAG,4BAA4B,EAAE,CAAC,CAAC;oBAChE,MAAM,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;oBACrC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;wBACpC,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;wBACjD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,UAAU,CAAC,OAAO,CAAC;+BAC1H,OAAO,CAAC,aAAa,CAAC,kDAAkD,CAAC,KAAK,IAAI,CAAC,CAAC;wBACzF,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC;wBACzG,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,4BAA4B,CAAC,EAAE,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;6BAC9G,MAAM,CAAC,CAAC,OAAO,EAAsB,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAQ,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC;wBAC5F,OAAO;4BACL,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC;4BAC3F,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;yBAC5F,CAAC;oBACJ,CAAC,CAAC,CAAC;oBACH,IAAI,CAAC,IAAI,CAAC,QAAQ;wBAAE,QAAQ,CAAC,IAAI,CAAC,wFAAwF,CAAC,CAAC;yBACvH,IAAI,CAAC,IAAI,CAAC,cAAc;wBAAE,QAAQ,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAC;oBACnH,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAC/B,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO;IAE5B,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QACrC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,iBAAiB,IAAI,4BAA4B,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE;gBAChG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAChC,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAAC,IAAI,CAAC,CAAC;gBACrD,kEAAkE;gBAClE,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;oBACxE,IAAK,KAAK,CAAC,MAAyB,EAAE,OAAO,EAAE,CAAC,iBAAiB,CAAC;wBAAE,KAAK,CAAC,cAAc,EAAE,CAAC;gBAC7F,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;gBACV,MAAM,QAAQ,GAAa,EAAE,CAAC;gBAC9B,IAAI,SAA8B,CAAC;gBACnC,IAAI,IAAI,GAAG,CAAC,CAAC;gBACb,IAAI,QAAQ,GAAG,CAAC,CAAC;gBACjB,MAAM,GAAG,GAAG,KAAK,EAAE,IAAY,EAAE,IAAwE,EAAE,KAAa,EAAE,EAAE;oBAC1H,QAAQ,IAAI,CAAC,CAAC;oBACd,IAAI,IAAI,CAAC,OAAO;wBAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,IAAI,CAAC,WAAW,kCAAkC,IAAI,CAAC,OAAO,yCAAyC,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,+BAA+B,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;oBAC5O,IAAI,CAAC,SAAS;wBAAE,OAAO;oBACvB,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC,MAAM,CAAC;oBACpE,MAAM,IAAI,CAAC,OAAO,CAAC,4BAA4B,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC;oBACzE,MAAM,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,KAAK,CAAC,CAAC;oBAClG,MAAM,IAAI,GAAG,eAAe,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;oBACzE,IAAI,IAAI,CAAC,CAAC;oBACV,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;wBAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,cAAc,IAAI,CAAC,WAAW,SAAS,IAAI,CAAC,MAAM,4BAA4B,CAAC,CAAC;oBAC7H,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;oBAC1D,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,4BAA4B,CAAC,QAAQ,CAAC,SAAS,CAAC;wBAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,cAAc,IAAI,CAAC,WAAW,wBAAwB,SAAS,yBAAyB,CAAC,CAAC;yBACrL,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,SAAS,KAAK,IAAI,CAAC,SAAS;wBAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,cAAc,IAAI,CAAC,WAAW,wBAAwB,SAAS,wBAAwB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;oBAC1L,MAAM,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC;gBAC9C,CAAC,CAAC;gBACF,KAAK,MAAM,QAAQ,IAAI,CAAC,4BAA4B,EAAE,0BAA0B,CAAC,EAAE,CAAC;oBAClF,MAAM,IAAI,CAAC,eAAe,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;oBAC5C,MAAM,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;oBACrC,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;oBACvB,SAAS,KAAK,MAAM,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;oBAClG,MAAM,KAAK,GAAG,GAAG,QAAQ,CAAC,KAAK,KAAK,CAAC;oBACrC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;wBACrC,MAAM,OAAO,GAAG,MAAM,CAAC,oBAAoB,CAAC;wBAC5C,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;4BAC7G,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,KAAK,EAAE,CAAC,CAAC;4BAC1C,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;wBAC/G,CAAC,CAAC,CAAC;oBACL,CAAC,CAAC,CAAC;oBACH,KAAK,MAAM,IAAI,IAAI,KAAK;wBAAE,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;oBAC5D,IAAI,QAAQ,CAAC,KAAK,GAAG,IAAI,EAAE,CAAC;wBAC1B,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;wBACrC,IAAI,GAAG,EAAE,CAAC;4BACR,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;gCACrC,MAAM,OAAO,GAAG,MAAM,CAAC,oBAAoB,CAAC;gCAC5C,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,qCAAqC,CAAC,CAAC;gCAC3E,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;oCAChH,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,WAAW,KAAK,EAAE,CAAC,CAAC;oCAC9C,OAAO,WAAW,KAAK,EAAE,CAAC;gCAC5B,CAAC,CAAC,CAAC;4BACL,CAAC,CAAC,CAAC;4BACH,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE;gCAAE,MAAM,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,CAAE,EAAE,GAAG,KAAK,WAAW,CAAC,CAAC;wBACjG,CAAC;oBACH,CAAC;gBACH,CAAC;gBACD,MAAM,CAAC,QAAQ,EAAE,GAAG,IAAI,sBAAsB,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;gBACnE,IAAI,CAAC,SAAS;oBAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,iMAAiM,CAAC,CAAC;;oBACpO,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,KAAK,IAAI,6DAA6D,CAAC,CAAC;gBACnG,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC/B,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * C1 and C4. C1 (enquiry tier and above): on each money page the H1, a call action and a quote action show in the\n * first 390 x 844 screen, and the enquiry form shows in the first 1440 x 900 screen. C4: every phone tap reports an\n * allowed placement. Where the build renders the Google tag (a test measurement ID), each visible phone link is\n * tapped and its `phone_link_click` must carry the placement the link declares; without the tag the declared\n * placements are checked and the run says so (the analytics journey fails a build without its tag).\n */\nimport { expect, test } from '@playwright/test';\nimport { analyticsEvents, recordAnalyticsCommands } from '../../analytics-e2e.js';\nimport { note, openSitePage, raiseTimeout, siteHeader, waitUntil } from './browser.js';\nimport { SITE_SYSTEM_PHONE_PLACEMENTS } from './page-helpers.js';\nimport { SITE_SYSTEM_DESKTOP_VIEWPORT, SITE_SYSTEM_PHONE_VIEWPORT, siteSystemJourneyTitle as title, type SiteSystemJourneyPlan } from './plan.js';\nimport { callBarLinks } from './shell.js';\n\n/** The shared listener ignores a second tap on the same number within 1.2 s; taps wait this long apart. */\nconst PHONE_TAP_GAP_MS = 1_500;\n\nexport function defineConversionJourneys(plan: SiteSystemJourneyPlan, runs: ReadonlySet<string>): void {\n const quote = { source: plan.quoteName.source, flags: plan.quoteName.flags };\n\n if (runs.has('C1')) {\n test.describe('money pages', () => {\n if (plan.problems.C1) {\n const problem = plan.problems.C1;\n test(title('C1', 'the money pages are named'), () => {\n expect([problem]).toEqual([]);\n });\n }\n for (const path of plan.money) {\n test(title('C1', `${path} shows the H1, a call and a quote action in the first phone screen and the form in the first desktop screen`), async ({ page }) => {\n const problems: string[] = [];\n await page.setViewportSize({ ...SITE_SYSTEM_PHONE_VIEWPORT });\n await openSitePage(page, path, plan);\n const phone = await page.evaluate((name) => {\n const helpers = window.__brandlabSiteSystem;\n window.scrollTo({ top: 0, behavior: 'instant' });\n const inView = (element: Element) => {\n const rect = element.getBoundingClientRect();\n return rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0;\n };\n const heading = Array.from(document.querySelectorAll('h1')).find(helpers.isRendered);\n const headingTop = heading ? heading.getBoundingClientRect().top : null;\n return {\n heading: heading !== undefined && headingTop !== null && headingTop >= 0 && headingTop < window.innerHeight,\n call: Array.from(document.querySelectorAll('a[href^=\"tel:\"]')).some((link) => helpers.isRendered(link) && inView(link)),\n quote: helpers.quoteActions(document.body, name).some(inView),\n };\n }, quote);\n if (!phone.heading) problems.push('390 x 844: the H1 does not start in the first screen');\n if (!phone.call) problems.push('390 x 844: no call action in the first screen');\n if (!phone.quote) problems.push('390 x 844: no quote action in the first screen');\n await page.setViewportSize({ ...SITE_SYSTEM_DESKTOP_VIEWPORT });\n await openSitePage(page, path, plan);\n const form = await page.evaluate(() => {\n window.scrollTo({ top: 0, behavior: 'instant' });\n const forms = Array.from(document.querySelectorAll('form')).filter((element) => window.__brandlabSiteSystem.isRendered(element)\n && element.querySelector('input[type=\"tel\"], input[type=\"email\"], textarea') !== null);\n const heading = Array.from(document.querySelectorAll('h1')).find(window.__brandlabSiteSystem.isRendered);\n const first = [heading?.closest('section, [data-bl-section]'), document.querySelector('main')?.firstElementChild]\n .filter((element): element is Element => Boolean(element) && element!.tagName !== 'MAIN');\n return {\n inScreen: forms.some((element) => element.getBoundingClientRect().top < window.innerHeight),\n inFirstSection: forms.some((element) => first.some((section) => section.contains(element))),\n };\n });\n if (!form.inScreen) problems.push('1440 x 900: no enquiry form (with a phone, email or message field) in the first screen');\n else if (!form.inFirstSection) problems.push('the enquiry form is not in the hero or the first section of <main>');\n expect(problems).toEqual([]);\n });\n }\n });\n }\n\n if (!runs.has('C4')) return;\n\n test.describe('phone placements', () => {\n for (const path of plan.sampled) {\n test(title('C4', `phone taps on ${path} report allowed placements`), async ({ page }, testInfo) => {\n raiseTimeout(testInfo, 180_000);\n const commands = await recordAnalyticsCommands(page);\n // Keep the page in place; the shared listener still sees the tap.\n await page.addInitScript(() => window.addEventListener('click', (event) => {\n if ((event.target as Element | null)?.closest?.('a[href^=\"tel:\"]')) event.preventDefault();\n }, true));\n const problems: string[] = [];\n let analytics: boolean | undefined;\n let taps = 0;\n let declared = 0;\n const tap = async (mark: string, link: { description: string; placement: string; invalid: string | null }, where: string) => {\n declared += 1;\n if (link.invalid) problems.push(`${where}: ${link.description} declares data-phone-location \"${link.invalid}\", which is not an allowed placement (${SITE_SYSTEM_PHONE_PLACEMENTS.join(', ')}); its taps are reported as ${link.placement}`);\n if (!analytics) return;\n const before = analyticsEvents(commands, 'phone_link_click').length;\n await page.locator(`[data-brandlab-journey~=\"${mark}\"]`).first().click();\n await waitUntil(async () => analyticsEvents(commands, 'phone_link_click').length > before, 2_000);\n const sent = analyticsEvents(commands, 'phone_link_click').slice(before);\n taps += 1;\n if (sent.length !== 1) problems.push(`${where}: a tap on ${link.description} sent ${sent.length} phone_link_click event(s)`);\n const placement = String(sent[0]?.params.placement ?? '');\n if (sent.length > 0 && !SITE_SYSTEM_PHONE_PLACEMENTS.includes(placement)) problems.push(`${where}: a tap on ${link.description} reported placement \"${placement}\", which is not allowed`);\n else if (sent.length > 0 && placement !== link.placement) problems.push(`${where}: a tap on ${link.description} reported placement \"${placement}\" (the link declares ${link.placement})`);\n await page.waitForTimeout(PHONE_TAP_GAP_MS);\n };\n for (const viewport of [SITE_SYSTEM_DESKTOP_VIEWPORT, SITE_SYSTEM_PHONE_VIEWPORT]) {\n await page.setViewportSize({ ...viewport });\n await openSitePage(page, path, plan);\n await siteHeader(page);\n analytics ??= await waitUntil(async () => commands.some((command) => command[0] === 'js'), 3_000);\n const where = `${viewport.width} px`;\n const links = await page.evaluate(() => {\n const helpers = window.__brandlabSiteSystem;\n return Array.from(document.querySelectorAll('a[href^=\"tel:\"]')).filter(helpers.isRendered).map((link, index) => {\n helpers.markElement(link, `tel-${index}`);\n return { mark: `tel-${index}`, description: helpers.describeElement(link), ...helpers.phonePlacement(link) };\n });\n });\n for (const link of links) await tap(link.mark, link, where);\n if (viewport.width < 1024) {\n const bar = await callBarLinks(page);\n if (bar) {\n const marks = await page.evaluate(() => {\n const helpers = window.__brandlabSiteSystem;\n const root = document.querySelector('[data-brandlab-journey~=\"call-bar\"]');\n return Array.from(root?.querySelectorAll('a[href^=\"tel:\"]') ?? []).filter(helpers.isRendered).map((link, index) => {\n helpers.markElement(link, `bar-tel-${index}`);\n return `bar-tel-${index}`;\n });\n });\n for (const [index, mark] of marks.entries()) await tap(mark, bar[index]!, `${where} call bar`);\n }\n }\n }\n expect(declared, `${path} shows no phone link`).toBeGreaterThan(0);\n if (!analytics) note(testInfo, `${path}: the build sends no analytics (no Google tag ran), so the declared placements were checked without tapping; build the e2e target with a test NEXT_PUBLIC_GA_MEASUREMENT_ID to check the events`);\n else note(testInfo, `${path}: ${taps} phone tap(s) checked against their phone_link_click events`);\n expect(problems).toEqual([]);\n });\n }\n });\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type SiteSystemJourneyPlan } from './plan.js';
|
|
2
|
+
export declare const HOVER_OPEN_WINDOW_MS: Readonly<{
|
|
3
|
+
min: 50;
|
|
4
|
+
max: 400;
|
|
5
|
+
}>;
|
|
6
|
+
export declare const MOUSE_OUT_CLOSE_WINDOW_MS: Readonly<{
|
|
7
|
+
min: 120;
|
|
8
|
+
max: 600;
|
|
9
|
+
}>;
|
|
10
|
+
export declare function defineMenuJourneys(plan: SiteSystemJourneyPlan, runs: ReadonlySet<string>): void;
|
|
11
|
+
//# sourceMappingURL=menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../../src/site-system/journeys/menu.ts"],"names":[],"mappings":"AA6BA,OAAO,EAAiE,KAAK,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAGtH,eAAO,MAAM,oBAAoB;;;EAAuC,CAAC;AACzE,eAAO,MAAM,yBAAyB;;;EAAwC,CAAC;AA+D/E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAmT/F"}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* N3 to N5: the desktop menu at 1440 px. Hover opens after a short delay and survives a diagonal path; mouse-out
|
|
3
|
+
* closes; nothing sticks open; clicks, Enter and outside clicks work; ARIA, Arrow keys, Escape and axe. Timings are
|
|
4
|
+
* outcomes with tolerances (exact timings are package tests): a hover opens 50 to 400 ms after the pointer arrives and
|
|
5
|
+
* a mouse-out closes 120 to 600 ms after it leaves; a timing outside the window is measured once more.
|
|
6
|
+
*/
|
|
7
|
+
import { expect, test } from '@playwright/test';
|
|
8
|
+
import { isolateFromNetwork } from '../../e2e-support.js';
|
|
9
|
+
import { SITE_SYSTEM_AXE_TAGS, describeAxe, hiddenMenuLinks, hoverPoint, mainNavigation, menuDisclosures, note, openPanels, raiseTimeout, openSitePage, panelLocator, panelOpen, restingPoint, runAxe, seriousOrCritical, settle, siteHeader, waitUntil, } from './browser.js';
|
|
10
|
+
import { SITE_SYSTEM_DESKTOP_VIEWPORT, siteSystemJourneyTitle as title } from './plan.js';
|
|
11
|
+
export const HOVER_OPEN_WINDOW_MS = Object.freeze({ min: 50, max: 400 });
|
|
12
|
+
export const MOUSE_OUT_CLOSE_WINDOW_MS = Object.freeze({ min: 120, max: 600 });
|
|
13
|
+
const within = (value, window) => value >= window.min && value <= window.max;
|
|
14
|
+
/** The visible main menu and its disclosures; fails when the menu is missing or hides links without disclosures. */
|
|
15
|
+
async function prepareMenu(page, plan, testInfo, path = plan.home) {
|
|
16
|
+
await openSitePage(page, path, plan);
|
|
17
|
+
await siteHeader(page);
|
|
18
|
+
const nav = await mainNavigation(page, plan);
|
|
19
|
+
expect(nav, `no navigation named "${plan.navName}" is visible at 1440 px on ${path}: name the header menu "${plan.navName}" (or pass navName)`).not.toBeNull();
|
|
20
|
+
const disclosures = await menuDisclosures(nav);
|
|
21
|
+
if (disclosures.length === 0) {
|
|
22
|
+
const hidden = await hiddenMenuLinks(nav);
|
|
23
|
+
expect(hidden, `the main menu hides ${hidden} link(s) in submenus but has no disclosure button (button[aria-expanded][aria-controls]) to open them`).toBe(0);
|
|
24
|
+
note(testInfo, 'The main menu has no panels; the panel checks have nothing to test.');
|
|
25
|
+
}
|
|
26
|
+
return { nav: nav, disclosures };
|
|
27
|
+
}
|
|
28
|
+
async function watch(page) {
|
|
29
|
+
return page.evaluate(() => window.__brandlabSiteSystem.watchState());
|
|
30
|
+
}
|
|
31
|
+
async function rest(page, disclosures) {
|
|
32
|
+
const point = await restingPoint(page);
|
|
33
|
+
await page.mouse.move(point.x, point.y);
|
|
34
|
+
await waitUntil(async () => (await openPanels(page, disclosures)).length === 0, 1_000);
|
|
35
|
+
await page.waitForTimeout(100);
|
|
36
|
+
}
|
|
37
|
+
/** Milliseconds from the pointer entering the item to the panel rendering, or null when it never opened. */
|
|
38
|
+
async function measureHoverOpen(page, disclosure, disclosures) {
|
|
39
|
+
await rest(page, disclosures);
|
|
40
|
+
const target = await hoverPoint(disclosure);
|
|
41
|
+
await disclosure.trigger.evaluate((trigger, id) => window.__brandlabSiteSystem.watchDisclosure(trigger, id), disclosure.panelId);
|
|
42
|
+
const start = await page.evaluate(() => performance.now());
|
|
43
|
+
await page.mouse.move(target.x, target.y);
|
|
44
|
+
await waitUntil(async () => (await watch(page))?.opened != null, 1_000);
|
|
45
|
+
const state = await watch(page);
|
|
46
|
+
return state?.opened == null ? null : state.opened - (state.enter ?? start);
|
|
47
|
+
}
|
|
48
|
+
/** Open by hover (and confirm with a click when asked), then leave: milliseconds from leaving to the panel hiding. */
|
|
49
|
+
async function measureMouseOut(page, disclosure, disclosures, confirmWithClick) {
|
|
50
|
+
await rest(page, disclosures);
|
|
51
|
+
const target = await hoverPoint(disclosure);
|
|
52
|
+
await page.mouse.move(target.x, target.y);
|
|
53
|
+
if (!(await waitUntil(() => panelOpen(page, disclosure), 1_000)))
|
|
54
|
+
return { delay: null, problem: 'hovering did not open the panel within 1 s' };
|
|
55
|
+
if (confirmWithClick) {
|
|
56
|
+
await disclosure.trigger.click();
|
|
57
|
+
await page.waitForTimeout(100);
|
|
58
|
+
if (!(await panelOpen(page, disclosure)))
|
|
59
|
+
return { delay: null, problem: 'a click on the button after a hover closed the panel' };
|
|
60
|
+
}
|
|
61
|
+
await disclosure.trigger.evaluate((trigger, id) => window.__brandlabSiteSystem.watchDisclosure(trigger, id), disclosure.panelId);
|
|
62
|
+
const point = await restingPoint(page);
|
|
63
|
+
const start = await page.evaluate(() => performance.now());
|
|
64
|
+
await page.mouse.move(point.x, point.y);
|
|
65
|
+
await waitUntil(async () => (await watch(page))?.closed != null, 1_500);
|
|
66
|
+
const state = await watch(page);
|
|
67
|
+
if (state?.closed == null)
|
|
68
|
+
return { delay: null, problem: 'the panel was still open 1.5 s after the pointer left' };
|
|
69
|
+
return { delay: state.closed - (state.leave ?? start) };
|
|
70
|
+
}
|
|
71
|
+
export function defineMenuJourneys(plan, runs) {
|
|
72
|
+
test.describe('menu at 1440 px', () => {
|
|
73
|
+
test.use({ viewport: { ...SITE_SYSTEM_DESKTOP_VIEWPORT } });
|
|
74
|
+
if (runs.has('N3')) {
|
|
75
|
+
test(title('N3', 'hover opens each menu panel after a short delay'), async ({ page }, testInfo) => {
|
|
76
|
+
const { disclosures } = await prepareMenu(page, plan, testInfo);
|
|
77
|
+
const problems = [];
|
|
78
|
+
for (const disclosure of disclosures) {
|
|
79
|
+
let delay = await measureHoverOpen(page, disclosure, disclosures);
|
|
80
|
+
if (delay === null || !within(delay, HOVER_OPEN_WINDOW_MS)) {
|
|
81
|
+
note(testInfo, `${disclosure.label}: first hover measured ${delay === null ? 'no opening' : `${Math.round(delay)} ms`}; measured once more`);
|
|
82
|
+
delay = await measureHoverOpen(page, disclosure, disclosures);
|
|
83
|
+
}
|
|
84
|
+
if (delay === null)
|
|
85
|
+
problems.push(`${disclosure.label}: hovering did not open its panel within 1 s`);
|
|
86
|
+
else if (!within(delay, HOVER_OPEN_WINDOW_MS)) {
|
|
87
|
+
problems.push(`${disclosure.label}: the panel opened ${Math.round(delay)} ms after the pointer arrived (expected ${HOVER_OPEN_WINDOW_MS.min} to ${HOVER_OPEN_WINDOW_MS.max} ms)`);
|
|
88
|
+
}
|
|
89
|
+
else
|
|
90
|
+
note(testInfo, `${disclosure.label}: opened ${Math.round(delay)} ms after the pointer arrived`);
|
|
91
|
+
}
|
|
92
|
+
expect(problems).toEqual([]);
|
|
93
|
+
});
|
|
94
|
+
test(title('N3', 'a diagonal path from the menu item into its panel keeps the panel open'), async ({ page }, testInfo) => {
|
|
95
|
+
const { disclosures } = await prepareMenu(page, plan, testInfo);
|
|
96
|
+
const problems = [];
|
|
97
|
+
for (const disclosure of disclosures) {
|
|
98
|
+
await rest(page, disclosures);
|
|
99
|
+
const start = await hoverPoint(disclosure);
|
|
100
|
+
await page.mouse.move(start.x, start.y);
|
|
101
|
+
if (!(await waitUntil(() => panelOpen(page, disclosure), 1_000))) {
|
|
102
|
+
problems.push(`${disclosure.label}: hovering did not open its panel within 1 s`);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const target = await panelLocator(page, disclosure.panelId).evaluate((panel) => {
|
|
106
|
+
const links = Array.from(panel.querySelectorAll('a[href]')).filter((link) => window.__brandlabSiteSystem.isRendered(link));
|
|
107
|
+
const link = links[links.length - 1];
|
|
108
|
+
if (!link)
|
|
109
|
+
return null;
|
|
110
|
+
const rect = link.getBoundingClientRect();
|
|
111
|
+
return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2, name: window.__brandlabSiteSystem.describeElement(link) };
|
|
112
|
+
});
|
|
113
|
+
if (!target) {
|
|
114
|
+
problems.push(`${disclosure.label}: the open panel shows no link`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
await page.mouse.move(target.x, target.y, { steps: 12 });
|
|
118
|
+
await page.waitForTimeout(350);
|
|
119
|
+
const reached = await page.evaluate(({ id, x, y }) => {
|
|
120
|
+
const panel = document.getElementById(id);
|
|
121
|
+
const hit = document.elementFromPoint(x, y);
|
|
122
|
+
return Boolean(panel && hit && panel.contains(hit) && window.__brandlabSiteSystem.isRendered(panel));
|
|
123
|
+
}, { id: disclosure.panelId, x: target.x, y: target.y });
|
|
124
|
+
if (!reached)
|
|
125
|
+
problems.push(`${disclosure.label}: the panel closed on the way from the menu item to ${target.name}`);
|
|
126
|
+
}
|
|
127
|
+
expect(problems).toEqual([]);
|
|
128
|
+
});
|
|
129
|
+
test(title('N3', 'mouse-out closes the panel shortly after, even after a click'), async ({ page }, testInfo) => {
|
|
130
|
+
raiseTimeout(testInfo, 180_000);
|
|
131
|
+
const { disclosures } = await prepareMenu(page, plan, testInfo);
|
|
132
|
+
const problems = [];
|
|
133
|
+
for (const disclosure of disclosures) {
|
|
134
|
+
for (const confirmWithClick of [false, true]) {
|
|
135
|
+
const how = confirmWithClick ? 'after a hover and a click' : 'after a hover';
|
|
136
|
+
let result = await measureMouseOut(page, disclosure, disclosures, confirmWithClick);
|
|
137
|
+
if (!result.problem && (result.delay === null || !within(result.delay, MOUSE_OUT_CLOSE_WINDOW_MS))) {
|
|
138
|
+
note(testInfo, `${disclosure.label} ${how}: first mouse-out measured ${result.delay === null ? 'no closing' : `${Math.round(result.delay)} ms`}; measured once more`);
|
|
139
|
+
result = await measureMouseOut(page, disclosure, disclosures, confirmWithClick);
|
|
140
|
+
}
|
|
141
|
+
if (result.problem)
|
|
142
|
+
problems.push(`${disclosure.label} ${how}: ${result.problem}`);
|
|
143
|
+
else if (result.delay !== null && !within(result.delay, MOUSE_OUT_CLOSE_WINDOW_MS)) {
|
|
144
|
+
problems.push(`${disclosure.label} ${how}: the panel closed ${Math.round(result.delay)} ms after the pointer left (expected ${MOUSE_OUT_CLOSE_WINDOW_MS.min} to ${MOUSE_OUT_CLOSE_WINDOW_MS.max} ms)`);
|
|
145
|
+
}
|
|
146
|
+
else if (result.delay !== null)
|
|
147
|
+
note(testInfo, `${disclosure.label} ${how}: closed ${Math.round(result.delay)} ms after the pointer left`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
expect(problems).toEqual([]);
|
|
151
|
+
});
|
|
152
|
+
test(title('N3', 'sweeping across the menu and leaving leaves no panel open'), async ({ page }, testInfo) => {
|
|
153
|
+
const { disclosures } = await prepareMenu(page, plan, testInfo);
|
|
154
|
+
if (disclosures.length === 0)
|
|
155
|
+
return;
|
|
156
|
+
await rest(page, disclosures);
|
|
157
|
+
for (const disclosure of disclosures) {
|
|
158
|
+
const point = await hoverPoint(disclosure);
|
|
159
|
+
await page.mouse.move(point.x, point.y, { steps: 6 });
|
|
160
|
+
await page.waitForTimeout(180);
|
|
161
|
+
}
|
|
162
|
+
const point = await restingPoint(page);
|
|
163
|
+
await page.mouse.move(point.x, point.y, { steps: 6 });
|
|
164
|
+
await page.waitForTimeout(800);
|
|
165
|
+
expect(await openPanels(page, disclosures), 'panels still open 800 ms after the pointer left the menu').toEqual([]);
|
|
166
|
+
});
|
|
167
|
+
test(title('N3', 'without scripts, hovering a menu item still shows its panel'), async ({ browser, baseURL }) => {
|
|
168
|
+
const context = await browser.newContext({ javaScriptEnabled: false, viewport: { ...SITE_SYSTEM_DESKTOP_VIEWPORT }, baseURL, serviceWorkers: 'block' });
|
|
169
|
+
try {
|
|
170
|
+
const page = await context.newPage();
|
|
171
|
+
await isolateFromNetwork(page, { baseURL });
|
|
172
|
+
await page.goto(plan.home, { waitUntil: 'domcontentloaded', timeout: plan.navigationTimeoutMs });
|
|
173
|
+
await page.waitForLoadState('load', { timeout: plan.settleTimeoutMs }).catch(() => undefined);
|
|
174
|
+
const nav = page.getByRole('navigation', { name: plan.navName, exact: true }).filter({ visible: true }).first();
|
|
175
|
+
expect(await nav.count(), `no navigation named "${plan.navName}" in the server HTML of ${plan.home}`).toBe(1);
|
|
176
|
+
const triggers = nav.locator('button[aria-expanded][aria-controls]');
|
|
177
|
+
const problems = [];
|
|
178
|
+
for (let index = 0; index < (await triggers.count()); index += 1) {
|
|
179
|
+
const trigger = triggers.nth(index);
|
|
180
|
+
const panelId = (await trigger.getAttribute('aria-controls')) ?? '';
|
|
181
|
+
const label = (await trigger.getAttribute('aria-label')) ?? `menu button ${index + 1}`;
|
|
182
|
+
const panel = panelLocator(page, panelId);
|
|
183
|
+
if ((await panel.count()) !== 1) {
|
|
184
|
+
problems.push(`${label}: its panel #${panelId} is not in the server HTML`);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
// Hover the item's own visible link (panel links are hidden at rest), else the button itself.
|
|
188
|
+
const item = trigger.locator('xpath=ancestor::li[1]');
|
|
189
|
+
const link = item.locator('a[href]').filter({ visible: true }).first();
|
|
190
|
+
await ((await item.count()) && (await link.count()) ? link : trigger).hover();
|
|
191
|
+
await page.waitForTimeout(250);
|
|
192
|
+
const visible = await panel.evaluate((element) => getComputedStyle(element).visibility === 'visible' && getComputedStyle(element).display !== 'none' && element.getBoundingClientRect().height > 0);
|
|
193
|
+
if (!visible)
|
|
194
|
+
problems.push(`${label}: hovering its menu item before scripts run does not show the panel (add the CSS :hover and :focus-within fallback)`);
|
|
195
|
+
}
|
|
196
|
+
expect(problems).toEqual([]);
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
await context.close();
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (runs.has('N4')) {
|
|
204
|
+
test(title('N4', 'a click on the menu button after a hover keeps the panel open'), async ({ page }, testInfo) => {
|
|
205
|
+
const { disclosures } = await prepareMenu(page, plan, testInfo);
|
|
206
|
+
const problems = [];
|
|
207
|
+
for (const disclosure of disclosures) {
|
|
208
|
+
await rest(page, disclosures);
|
|
209
|
+
const point = await hoverPoint(disclosure);
|
|
210
|
+
await page.mouse.move(point.x, point.y);
|
|
211
|
+
if (!(await waitUntil(() => panelOpen(page, disclosure), 1_000))) {
|
|
212
|
+
problems.push(`${disclosure.label}: hovering did not open its panel within 1 s`);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
await disclosure.trigger.click();
|
|
216
|
+
await page.waitForTimeout(80);
|
|
217
|
+
const afterClick = await panelOpen(page, disclosure);
|
|
218
|
+
await page.waitForTimeout(300);
|
|
219
|
+
const later = await panelOpen(page, disclosure);
|
|
220
|
+
const expanded = await disclosure.trigger.getAttribute('aria-expanded');
|
|
221
|
+
if (!afterClick || !later)
|
|
222
|
+
problems.push(`${disclosure.label}: the first click after a hover ${afterClick ? 'let the panel close' : 'closed the panel'}`);
|
|
223
|
+
else if (expanded !== 'true')
|
|
224
|
+
problems.push(`${disclosure.label}: the panel is open but its button says aria-expanded="${expanded}"`);
|
|
225
|
+
}
|
|
226
|
+
expect(problems).toEqual([]);
|
|
227
|
+
});
|
|
228
|
+
test(title('N4', 'click, Enter and an outside click open and close the panels, one at a time'), async ({ page }, testInfo) => {
|
|
229
|
+
const { disclosures } = await prepareMenu(page, plan, testInfo);
|
|
230
|
+
const problems = [];
|
|
231
|
+
const state = async (disclosure) => ({ open: await panelOpen(page, disclosure), expanded: (await disclosure.trigger.getAttribute('aria-expanded')) === 'true' });
|
|
232
|
+
const expectState = async (disclosure, open, what) => {
|
|
233
|
+
await waitUntil(async () => (await state(disclosure)).open === open, 400);
|
|
234
|
+
const now = await state(disclosure);
|
|
235
|
+
if (now.open !== open || now.expanded !== open) {
|
|
236
|
+
problems.push(`${disclosure.label}: ${what} left the panel ${now.open ? 'open' : 'closed'} with aria-expanded="${now.expanded}"`);
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
};
|
|
241
|
+
for (const disclosure of disclosures) {
|
|
242
|
+
await rest(page, disclosures);
|
|
243
|
+
await disclosure.trigger.click();
|
|
244
|
+
if (!(await expectState(disclosure, true, 'a click')))
|
|
245
|
+
continue;
|
|
246
|
+
await disclosure.trigger.click();
|
|
247
|
+
if (!(await expectState(disclosure, false, 'a second click')))
|
|
248
|
+
continue;
|
|
249
|
+
await rest(page, disclosures);
|
|
250
|
+
await disclosure.trigger.focus();
|
|
251
|
+
await page.keyboard.press('Enter');
|
|
252
|
+
if (!(await expectState(disclosure, true, 'Enter on the button')))
|
|
253
|
+
continue;
|
|
254
|
+
await page.keyboard.press('Enter');
|
|
255
|
+
if (!(await expectState(disclosure, false, 'Enter again')))
|
|
256
|
+
continue;
|
|
257
|
+
await page.keyboard.press('Enter');
|
|
258
|
+
if (!(await expectState(disclosure, true, 'Enter on the button')))
|
|
259
|
+
continue;
|
|
260
|
+
const outside = await restingPoint(page);
|
|
261
|
+
await page.mouse.click(outside.x, outside.y);
|
|
262
|
+
await expectState(disclosure, false, 'a click outside the menu');
|
|
263
|
+
}
|
|
264
|
+
if (disclosures.length > 1) {
|
|
265
|
+
await rest(page, disclosures);
|
|
266
|
+
await disclosures[0].trigger.click();
|
|
267
|
+
await disclosures[1].trigger.click();
|
|
268
|
+
await page.waitForTimeout(150);
|
|
269
|
+
const open = await openPanels(page, disclosures);
|
|
270
|
+
if (open.length !== 1 || open[0] !== disclosures[1].label)
|
|
271
|
+
problems.push(`opening ${disclosures[1].label} after ${disclosures[0].label} left ${open.length} panel(s) open: ${open.join(', ') || 'none'}`);
|
|
272
|
+
}
|
|
273
|
+
expect(problems).toEqual([]);
|
|
274
|
+
});
|
|
275
|
+
test(title('N4', 'a link click in a panel closes it and loads the page'), async ({ page }, testInfo) => {
|
|
276
|
+
const { disclosures } = await prepareMenu(page, plan, testInfo);
|
|
277
|
+
const disclosure = disclosures[0];
|
|
278
|
+
if (!disclosure)
|
|
279
|
+
return;
|
|
280
|
+
await rest(page, disclosures);
|
|
281
|
+
await disclosure.trigger.click();
|
|
282
|
+
expect(await waitUntil(() => panelOpen(page, disclosure), 500), `${disclosure.label}: a click did not open the panel`).toBe(true);
|
|
283
|
+
const href = await panelLocator(page, disclosure.panelId).evaluate((panel) => {
|
|
284
|
+
const links = Array.from(panel.querySelectorAll('a[href]')).filter((link) => window.__brandlabSiteSystem.isRendered(link)
|
|
285
|
+
&& link.origin === location.origin && link.pathname !== location.pathname);
|
|
286
|
+
const link = links[1] ?? links[0];
|
|
287
|
+
if (!link)
|
|
288
|
+
return null;
|
|
289
|
+
window.__brandlabSiteSystem.markElement(link, 'panel-link');
|
|
290
|
+
return link.pathname;
|
|
291
|
+
});
|
|
292
|
+
expect(href, `${disclosure.label}: the open panel has no link to another page`).not.toBeNull();
|
|
293
|
+
await page.locator('[data-brandlab-journey~="panel-link"]').click();
|
|
294
|
+
await page.waitForURL((url) => url.pathname === href, { timeout: plan.navigationTimeoutMs });
|
|
295
|
+
await settle(page, plan.settleTimeoutMs);
|
|
296
|
+
const nav = await mainNavigation(page, plan);
|
|
297
|
+
expect(nav, `no navigation named "${plan.navName}" on ${href}`).not.toBeNull();
|
|
298
|
+
expect(await openPanels(page, await menuDisclosures(nav)), `panels open after following a menu link to ${href}`).toEqual([]);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
if (runs.has('N5')) {
|
|
302
|
+
test(title('N5', 'each menu button controls a panel that is hidden while closed and shown while open'), async ({ page }, testInfo) => {
|
|
303
|
+
const { disclosures } = await prepareMenu(page, plan, testInfo);
|
|
304
|
+
const problems = [];
|
|
305
|
+
for (const disclosure of disclosures) {
|
|
306
|
+
if (!disclosure.panelId || (await panelLocator(page, disclosure.panelId).count()) !== 1) {
|
|
307
|
+
problems.push(`${disclosure.label}: aria-controls names #${disclosure.panelId}, which is not in the page while the panel is closed (keep panels in the HTML, hidden)`);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
await rest(page, disclosures);
|
|
311
|
+
if ((await disclosure.trigger.getAttribute('aria-expanded')) !== 'false')
|
|
312
|
+
problems.push(`${disclosure.label}: aria-expanded is not "false" while the panel is closed`);
|
|
313
|
+
if (await panelOpen(page, disclosure))
|
|
314
|
+
problems.push(`${disclosure.label}: the panel shows while its button is collapsed`);
|
|
315
|
+
await disclosure.trigger.click();
|
|
316
|
+
await waitUntil(() => panelOpen(page, disclosure), 400);
|
|
317
|
+
const expanded = await disclosure.trigger.getAttribute('aria-expanded');
|
|
318
|
+
if (!(await panelOpen(page, disclosure)) || expanded !== 'true')
|
|
319
|
+
problems.push(`${disclosure.label}: after a click the panel is ${(await panelOpen(page, disclosure)) ? 'shown' : 'hidden'} and aria-expanded is "${expanded}"`);
|
|
320
|
+
await disclosure.trigger.click();
|
|
321
|
+
await waitUntil(async () => !(await panelOpen(page, disclosure)), 400);
|
|
322
|
+
}
|
|
323
|
+
expect(problems).toEqual([]);
|
|
324
|
+
});
|
|
325
|
+
test(title('N5', 'the current page is marked with aria-current in the menu'), async ({ page }, testInfo) => {
|
|
326
|
+
const { nav } = await prepareMenu(page, plan, testInfo);
|
|
327
|
+
const target = await nav.evaluate((element) => {
|
|
328
|
+
const links = Array.from(element.querySelectorAll('a[href]')).filter((link) => link.origin === location.origin && link.pathname !== location.pathname);
|
|
329
|
+
const inPanel = links.filter((link) => !window.__brandlabSiteSystem.isRendered(link));
|
|
330
|
+
return (inPanel[1] ?? inPanel[0] ?? links[0])?.pathname ?? null;
|
|
331
|
+
});
|
|
332
|
+
expect(target, 'the main menu links to no other page').not.toBeNull();
|
|
333
|
+
await openSitePage(page, target, plan);
|
|
334
|
+
const current = await mainNavigation(page, plan);
|
|
335
|
+
expect(current, `no navigation named "${plan.navName}" on ${target}`).not.toBeNull();
|
|
336
|
+
const marked = await current.evaluate((element) => Array.from(element.querySelectorAll('a[href]'))
|
|
337
|
+
.filter((link) => link.origin === location.origin && link.pathname.replace(/\/$/, '') === location.pathname.replace(/\/$/, ''))
|
|
338
|
+
.map((link) => link.getAttribute('aria-current')));
|
|
339
|
+
expect(marked.length, `the menu has no link to ${target}`).toBeGreaterThan(0);
|
|
340
|
+
expect(marked, `the menu link to ${target} does not carry aria-current="page" on that page`).toContain('page');
|
|
341
|
+
});
|
|
342
|
+
test(title('N5', 'Arrow keys move through a panel, and Escape closes it and returns focus to its button'), async ({ page }, testInfo) => {
|
|
343
|
+
const { disclosures } = await prepareMenu(page, plan, testInfo);
|
|
344
|
+
const problems = [];
|
|
345
|
+
const focusInPanel = (id) => page.evaluate((panelId) => Boolean(document.getElementById(panelId)?.contains(document.activeElement)), id);
|
|
346
|
+
for (const disclosure of disclosures) {
|
|
347
|
+
await rest(page, disclosures);
|
|
348
|
+
await disclosure.trigger.focus();
|
|
349
|
+
await page.keyboard.press('ArrowDown');
|
|
350
|
+
await waitUntil(async () => (await panelOpen(page, disclosure)) && (await focusInPanel(disclosure.panelId)), 500);
|
|
351
|
+
if (!(await panelOpen(page, disclosure))) {
|
|
352
|
+
problems.push(`${disclosure.label}: Arrow Down on the button did not open the panel`);
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (!(await focusInPanel(disclosure.panelId))) {
|
|
356
|
+
problems.push(`${disclosure.label}: Arrow Down did not move focus to the panel's first link`);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
const links = await panelLocator(page, disclosure.panelId).evaluate((panel) => Array.from(panel.querySelectorAll('a[href]')).filter((link) => window.__brandlabSiteSystem.isRendered(link)).length);
|
|
360
|
+
const first = await page.evaluate(() => document.activeElement?.outerHTML ?? '');
|
|
361
|
+
await page.keyboard.press('ArrowDown');
|
|
362
|
+
const second = await page.evaluate(() => document.activeElement?.outerHTML ?? '');
|
|
363
|
+
if (links > 1 && (first === second || !(await focusInPanel(disclosure.panelId))))
|
|
364
|
+
problems.push(`${disclosure.label}: Arrow Down did not move to the next link in the panel`);
|
|
365
|
+
}
|
|
366
|
+
await page.keyboard.press('Escape');
|
|
367
|
+
await waitUntil(async () => !(await panelOpen(page, disclosure)), 500);
|
|
368
|
+
if (await panelOpen(page, disclosure))
|
|
369
|
+
problems.push(`${disclosure.label}: Escape did not close the panel`);
|
|
370
|
+
else if (!(await disclosure.trigger.evaluate((element) => element === document.activeElement)))
|
|
371
|
+
problems.push(`${disclosure.label}: Escape did not return focus to the menu button`);
|
|
372
|
+
}
|
|
373
|
+
expect(problems).toEqual([]);
|
|
374
|
+
});
|
|
375
|
+
test(title('N5', 'axe finds no serious or critical issue in the header with the panels closed and open'), async ({ page }, testInfo) => {
|
|
376
|
+
const { disclosures } = await prepareMenu(page, plan, testInfo);
|
|
377
|
+
const scope = ['[data-brandlab-journey~="header"]', '[data-brandlab-journey~="nav"]'];
|
|
378
|
+
const closed = await runAxe(page, scope, { tags: SITE_SYSTEM_AXE_TAGS });
|
|
379
|
+
const problems = describeAxe(seriousOrCritical(closed.violations)).map((line) => `panels closed: ${line}`);
|
|
380
|
+
for (const disclosure of disclosures) {
|
|
381
|
+
await rest(page, disclosures);
|
|
382
|
+
await disclosure.trigger.click();
|
|
383
|
+
await waitUntil(() => panelOpen(page, disclosure), 400);
|
|
384
|
+
await page.waitForTimeout(250);
|
|
385
|
+
await panelLocator(page, disclosure.panelId).evaluate((panel) => window.__brandlabSiteSystem.markElement(panel, 'panel'));
|
|
386
|
+
const open = await runAxe(page, [...scope, '[data-brandlab-journey~="panel"]'], { tags: SITE_SYSTEM_AXE_TAGS });
|
|
387
|
+
problems.push(...describeAxe(seriousOrCritical(open.violations)).map((line) => `${disclosure.label} open: ${line}`));
|
|
388
|
+
await disclosure.trigger.click();
|
|
389
|
+
}
|
|
390
|
+
expect([...new Set(problems)]).toEqual([]);
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
//# sourceMappingURL=menu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.js","sourceRoot":"","sources":["../../../src/site-system/journeys/menu.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * N3 to N5: the desktop menu at 1440 px. Hover opens after a short delay and survives a diagonal path; mouse-out\n * closes; nothing sticks open; clicks, Enter and outside clicks work; ARIA, Arrow keys, Escape and axe. Timings are\n * outcomes with tolerances (exact timings are package tests): a hover opens 50 to 400 ms after the pointer arrives and\n * a mouse-out closes 120 to 600 ms after it leaves; a timing outside the window is measured once more.\n */\nimport { expect, test, type Page, type TestInfo } from '@playwright/test';\nimport { isolateFromNetwork } from '../../e2e-support.js';\nimport {\n SITE_SYSTEM_AXE_TAGS,\n describeAxe,\n hiddenMenuLinks,\n hoverPoint,\n mainNavigation,\n menuDisclosures,\n note,\n openPanels,\n raiseTimeout,\n openSitePage,\n panelLocator,\n panelOpen,\n restingPoint,\n runAxe,\n seriousOrCritical,\n settle,\n siteHeader,\n waitUntil,\n type Disclosure,\n} from './browser.js';\nimport { SITE_SYSTEM_DESKTOP_VIEWPORT, siteSystemJourneyTitle as title, type SiteSystemJourneyPlan } from './plan.js';\nimport type { DisclosureWatch } from './page-helpers.js';\n\nexport const HOVER_OPEN_WINDOW_MS = Object.freeze({ min: 50, max: 400 });\nexport const MOUSE_OUT_CLOSE_WINDOW_MS = Object.freeze({ min: 120, max: 600 });\n\nconst within = (value: number, window: { min: number; max: number }) => value >= window.min && value <= window.max;\n\n/** The visible main menu and its disclosures; fails when the menu is missing or hides links without disclosures. */\nasync function prepareMenu(page: Page, plan: SiteSystemJourneyPlan, testInfo: TestInfo, path = plan.home) {\n await openSitePage(page, path, plan);\n await siteHeader(page);\n const nav = await mainNavigation(page, plan);\n expect(nav, `no navigation named \"${plan.navName}\" is visible at 1440 px on ${path}: name the header menu \"${plan.navName}\" (or pass navName)`).not.toBeNull();\n const disclosures = await menuDisclosures(nav!);\n if (disclosures.length === 0) {\n const hidden = await hiddenMenuLinks(nav!);\n expect(hidden, `the main menu hides ${hidden} link(s) in submenus but has no disclosure button (button[aria-expanded][aria-controls]) to open them`).toBe(0);\n note(testInfo, 'The main menu has no panels; the panel checks have nothing to test.');\n }\n return { nav: nav!, disclosures };\n}\n\nasync function watch(page: Page): Promise<DisclosureWatch | null> {\n return page.evaluate(() => window.__brandlabSiteSystem.watchState());\n}\n\nasync function rest(page: Page, disclosures: readonly Disclosure[]): Promise<void> {\n const point = await restingPoint(page);\n await page.mouse.move(point.x, point.y);\n await waitUntil(async () => (await openPanels(page, disclosures)).length === 0, 1_000);\n await page.waitForTimeout(100);\n}\n\n/** Milliseconds from the pointer entering the item to the panel rendering, or null when it never opened. */\nasync function measureHoverOpen(page: Page, disclosure: Disclosure, disclosures: readonly Disclosure[]): Promise<number | null> {\n await rest(page, disclosures);\n const target = await hoverPoint(disclosure);\n await disclosure.trigger.evaluate((trigger, id) => window.__brandlabSiteSystem.watchDisclosure(trigger, id), disclosure.panelId);\n const start = await page.evaluate(() => performance.now());\n await page.mouse.move(target.x, target.y);\n await waitUntil(async () => (await watch(page))?.opened != null, 1_000);\n const state = await watch(page);\n return state?.opened == null ? null : state.opened - (state.enter ?? start);\n}\n\n/** Open by hover (and confirm with a click when asked), then leave: milliseconds from leaving to the panel hiding. */\nasync function measureMouseOut(page: Page, disclosure: Disclosure, disclosures: readonly Disclosure[], confirmWithClick: boolean): Promise<{ delay: number | null; problem?: string }> {\n await rest(page, disclosures);\n const target = await hoverPoint(disclosure);\n await page.mouse.move(target.x, target.y);\n if (!(await waitUntil(() => panelOpen(page, disclosure), 1_000))) return { delay: null, problem: 'hovering did not open the panel within 1 s' };\n if (confirmWithClick) {\n await disclosure.trigger.click();\n await page.waitForTimeout(100);\n if (!(await panelOpen(page, disclosure))) return { delay: null, problem: 'a click on the button after a hover closed the panel' };\n }\n await disclosure.trigger.evaluate((trigger, id) => window.__brandlabSiteSystem.watchDisclosure(trigger, id), disclosure.panelId);\n const point = await restingPoint(page);\n const start = await page.evaluate(() => performance.now());\n await page.mouse.move(point.x, point.y);\n await waitUntil(async () => (await watch(page))?.closed != null, 1_500);\n const state = await watch(page);\n if (state?.closed == null) return { delay: null, problem: 'the panel was still open 1.5 s after the pointer left' };\n return { delay: state.closed - (state.leave ?? start) };\n}\n\nexport function defineMenuJourneys(plan: SiteSystemJourneyPlan, runs: ReadonlySet<string>): void {\n test.describe('menu at 1440 px', () => {\n test.use({ viewport: { ...SITE_SYSTEM_DESKTOP_VIEWPORT } });\n\n if (runs.has('N3')) {\n test(title('N3', 'hover opens each menu panel after a short delay'), async ({ page }, testInfo) => {\n const { disclosures } = await prepareMenu(page, plan, testInfo);\n const problems: string[] = [];\n for (const disclosure of disclosures) {\n let delay = await measureHoverOpen(page, disclosure, disclosures);\n if (delay === null || !within(delay, HOVER_OPEN_WINDOW_MS)) {\n note(testInfo, `${disclosure.label}: first hover measured ${delay === null ? 'no opening' : `${Math.round(delay)} ms`}; measured once more`);\n delay = await measureHoverOpen(page, disclosure, disclosures);\n }\n if (delay === null) problems.push(`${disclosure.label}: hovering did not open its panel within 1 s`);\n else if (!within(delay, HOVER_OPEN_WINDOW_MS)) {\n problems.push(`${disclosure.label}: the panel opened ${Math.round(delay)} ms after the pointer arrived (expected ${HOVER_OPEN_WINDOW_MS.min} to ${HOVER_OPEN_WINDOW_MS.max} ms)`);\n } else note(testInfo, `${disclosure.label}: opened ${Math.round(delay)} ms after the pointer arrived`);\n }\n expect(problems).toEqual([]);\n });\n\n test(title('N3', 'a diagonal path from the menu item into its panel keeps the panel open'), async ({ page }, testInfo) => {\n const { disclosures } = await prepareMenu(page, plan, testInfo);\n const problems: string[] = [];\n for (const disclosure of disclosures) {\n await rest(page, disclosures);\n const start = await hoverPoint(disclosure);\n await page.mouse.move(start.x, start.y);\n if (!(await waitUntil(() => panelOpen(page, disclosure), 1_000))) {\n problems.push(`${disclosure.label}: hovering did not open its panel within 1 s`);\n continue;\n }\n const target = await panelLocator(page, disclosure.panelId).evaluate((panel) => {\n const links = Array.from(panel.querySelectorAll('a[href]')).filter((link) => window.__brandlabSiteSystem.isRendered(link));\n const link = links[links.length - 1];\n if (!link) return null;\n const rect = link.getBoundingClientRect();\n return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2, name: window.__brandlabSiteSystem.describeElement(link) };\n });\n if (!target) {\n problems.push(`${disclosure.label}: the open panel shows no link`);\n continue;\n }\n await page.mouse.move(target.x, target.y, { steps: 12 });\n await page.waitForTimeout(350);\n const reached = await page.evaluate(({ id, x, y }) => {\n const panel = document.getElementById(id);\n const hit = document.elementFromPoint(x, y);\n return Boolean(panel && hit && panel.contains(hit) && window.__brandlabSiteSystem.isRendered(panel));\n }, { id: disclosure.panelId, x: target.x, y: target.y });\n if (!reached) problems.push(`${disclosure.label}: the panel closed on the way from the menu item to ${target.name}`);\n }\n expect(problems).toEqual([]);\n });\n\n test(title('N3', 'mouse-out closes the panel shortly after, even after a click'), async ({ page }, testInfo) => {\n raiseTimeout(testInfo, 180_000);\n const { disclosures } = await prepareMenu(page, plan, testInfo);\n const problems: string[] = [];\n for (const disclosure of disclosures) {\n for (const confirmWithClick of [false, true]) {\n const how = confirmWithClick ? 'after a hover and a click' : 'after a hover';\n let result = await measureMouseOut(page, disclosure, disclosures, confirmWithClick);\n if (!result.problem && (result.delay === null || !within(result.delay, MOUSE_OUT_CLOSE_WINDOW_MS))) {\n note(testInfo, `${disclosure.label} ${how}: first mouse-out measured ${result.delay === null ? 'no closing' : `${Math.round(result.delay)} ms`}; measured once more`);\n result = await measureMouseOut(page, disclosure, disclosures, confirmWithClick);\n }\n if (result.problem) problems.push(`${disclosure.label} ${how}: ${result.problem}`);\n else if (result.delay !== null && !within(result.delay, MOUSE_OUT_CLOSE_WINDOW_MS)) {\n problems.push(`${disclosure.label} ${how}: the panel closed ${Math.round(result.delay)} ms after the pointer left (expected ${MOUSE_OUT_CLOSE_WINDOW_MS.min} to ${MOUSE_OUT_CLOSE_WINDOW_MS.max} ms)`);\n } else if (result.delay !== null) note(testInfo, `${disclosure.label} ${how}: closed ${Math.round(result.delay)} ms after the pointer left`);\n }\n }\n expect(problems).toEqual([]);\n });\n\n test(title('N3', 'sweeping across the menu and leaving leaves no panel open'), async ({ page }, testInfo) => {\n const { disclosures } = await prepareMenu(page, plan, testInfo);\n if (disclosures.length === 0) return;\n await rest(page, disclosures);\n for (const disclosure of disclosures) {\n const point = await hoverPoint(disclosure);\n await page.mouse.move(point.x, point.y, { steps: 6 });\n await page.waitForTimeout(180);\n }\n const point = await restingPoint(page);\n await page.mouse.move(point.x, point.y, { steps: 6 });\n await page.waitForTimeout(800);\n expect(await openPanels(page, disclosures), 'panels still open 800 ms after the pointer left the menu').toEqual([]);\n });\n\n test(title('N3', 'without scripts, hovering a menu item still shows its panel'), async ({ browser, baseURL }) => {\n const context = await browser.newContext({ javaScriptEnabled: false, viewport: { ...SITE_SYSTEM_DESKTOP_VIEWPORT }, baseURL, serviceWorkers: 'block' });\n try {\n const page = await context.newPage();\n await isolateFromNetwork(page, { baseURL });\n await page.goto(plan.home, { waitUntil: 'domcontentloaded', timeout: plan.navigationTimeoutMs });\n await page.waitForLoadState('load', { timeout: plan.settleTimeoutMs }).catch(() => undefined);\n const nav = page.getByRole('navigation', { name: plan.navName, exact: true }).filter({ visible: true }).first();\n expect(await nav.count(), `no navigation named \"${plan.navName}\" in the server HTML of ${plan.home}`).toBe(1);\n const triggers = nav.locator('button[aria-expanded][aria-controls]');\n const problems: string[] = [];\n for (let index = 0; index < (await triggers.count()); index += 1) {\n const trigger = triggers.nth(index);\n const panelId = (await trigger.getAttribute('aria-controls')) ?? '';\n const label = (await trigger.getAttribute('aria-label')) ?? `menu button ${index + 1}`;\n const panel = panelLocator(page, panelId);\n if ((await panel.count()) !== 1) {\n problems.push(`${label}: its panel #${panelId} is not in the server HTML`);\n continue;\n }\n // Hover the item's own visible link (panel links are hidden at rest), else the button itself.\n const item = trigger.locator('xpath=ancestor::li[1]');\n const link = item.locator('a[href]').filter({ visible: true }).first();\n await ((await item.count()) && (await link.count()) ? link : trigger).hover();\n await page.waitForTimeout(250);\n const visible = await panel.evaluate((element) => getComputedStyle(element).visibility === 'visible' && getComputedStyle(element).display !== 'none' && element.getBoundingClientRect().height > 0);\n if (!visible) problems.push(`${label}: hovering its menu item before scripts run does not show the panel (add the CSS :hover and :focus-within fallback)`);\n }\n expect(problems).toEqual([]);\n } finally {\n await context.close();\n }\n });\n }\n\n if (runs.has('N4')) {\n test(title('N4', 'a click on the menu button after a hover keeps the panel open'), async ({ page }, testInfo) => {\n const { disclosures } = await prepareMenu(page, plan, testInfo);\n const problems: string[] = [];\n for (const disclosure of disclosures) {\n await rest(page, disclosures);\n const point = await hoverPoint(disclosure);\n await page.mouse.move(point.x, point.y);\n if (!(await waitUntil(() => panelOpen(page, disclosure), 1_000))) {\n problems.push(`${disclosure.label}: hovering did not open its panel within 1 s`);\n continue;\n }\n await disclosure.trigger.click();\n await page.waitForTimeout(80);\n const afterClick = await panelOpen(page, disclosure);\n await page.waitForTimeout(300);\n const later = await panelOpen(page, disclosure);\n const expanded = await disclosure.trigger.getAttribute('aria-expanded');\n if (!afterClick || !later) problems.push(`${disclosure.label}: the first click after a hover ${afterClick ? 'let the panel close' : 'closed the panel'}`);\n else if (expanded !== 'true') problems.push(`${disclosure.label}: the panel is open but its button says aria-expanded=\"${expanded}\"`);\n }\n expect(problems).toEqual([]);\n });\n\n test(title('N4', 'click, Enter and an outside click open and close the panels, one at a time'), async ({ page }, testInfo) => {\n const { disclosures } = await prepareMenu(page, plan, testInfo);\n const problems: string[] = [];\n const state = async (disclosure: Disclosure) => ({ open: await panelOpen(page, disclosure), expanded: (await disclosure.trigger.getAttribute('aria-expanded')) === 'true' });\n const expectState = async (disclosure: Disclosure, open: boolean, what: string) => {\n await waitUntil(async () => (await state(disclosure)).open === open, 400);\n const now = await state(disclosure);\n if (now.open !== open || now.expanded !== open) {\n problems.push(`${disclosure.label}: ${what} left the panel ${now.open ? 'open' : 'closed'} with aria-expanded=\"${now.expanded}\"`);\n return false;\n }\n return true;\n };\n for (const disclosure of disclosures) {\n await rest(page, disclosures);\n await disclosure.trigger.click();\n if (!(await expectState(disclosure, true, 'a click'))) continue;\n await disclosure.trigger.click();\n if (!(await expectState(disclosure, false, 'a second click'))) continue;\n await rest(page, disclosures);\n await disclosure.trigger.focus();\n await page.keyboard.press('Enter');\n if (!(await expectState(disclosure, true, 'Enter on the button'))) continue;\n await page.keyboard.press('Enter');\n if (!(await expectState(disclosure, false, 'Enter again'))) continue;\n await page.keyboard.press('Enter');\n if (!(await expectState(disclosure, true, 'Enter on the button'))) continue;\n const outside = await restingPoint(page);\n await page.mouse.click(outside.x, outside.y);\n await expectState(disclosure, false, 'a click outside the menu');\n }\n if (disclosures.length > 1) {\n await rest(page, disclosures);\n await disclosures[0]!.trigger.click();\n await disclosures[1]!.trigger.click();\n await page.waitForTimeout(150);\n const open = await openPanels(page, disclosures);\n if (open.length !== 1 || open[0] !== disclosures[1]!.label) problems.push(`opening ${disclosures[1]!.label} after ${disclosures[0]!.label} left ${open.length} panel(s) open: ${open.join(', ') || 'none'}`);\n }\n expect(problems).toEqual([]);\n });\n\n test(title('N4', 'a link click in a panel closes it and loads the page'), async ({ page }, testInfo) => {\n const { disclosures } = await prepareMenu(page, plan, testInfo);\n const disclosure = disclosures[0];\n if (!disclosure) return;\n await rest(page, disclosures);\n await disclosure.trigger.click();\n expect(await waitUntil(() => panelOpen(page, disclosure), 500), `${disclosure.label}: a click did not open the panel`).toBe(true);\n const href = await panelLocator(page, disclosure.panelId).evaluate((panel) => {\n const links = Array.from(panel.querySelectorAll<HTMLAnchorElement>('a[href]')).filter((link) => window.__brandlabSiteSystem.isRendered(link)\n && link.origin === location.origin && link.pathname !== location.pathname);\n const link = links[1] ?? links[0];\n if (!link) return null;\n window.__brandlabSiteSystem.markElement(link, 'panel-link');\n return link.pathname;\n });\n expect(href, `${disclosure.label}: the open panel has no link to another page`).not.toBeNull();\n await page.locator('[data-brandlab-journey~=\"panel-link\"]').click();\n await page.waitForURL((url) => url.pathname === href, { timeout: plan.navigationTimeoutMs });\n await settle(page, plan.settleTimeoutMs);\n const nav = await mainNavigation(page, plan);\n expect(nav, `no navigation named \"${plan.navName}\" on ${href}`).not.toBeNull();\n expect(await openPanels(page, await menuDisclosures(nav!)), `panels open after following a menu link to ${href}`).toEqual([]);\n });\n }\n\n if (runs.has('N5')) {\n test(title('N5', 'each menu button controls a panel that is hidden while closed and shown while open'), async ({ page }, testInfo) => {\n const { disclosures } = await prepareMenu(page, plan, testInfo);\n const problems: string[] = [];\n for (const disclosure of disclosures) {\n if (!disclosure.panelId || (await panelLocator(page, disclosure.panelId).count()) !== 1) {\n problems.push(`${disclosure.label}: aria-controls names #${disclosure.panelId}, which is not in the page while the panel is closed (keep panels in the HTML, hidden)`);\n continue;\n }\n await rest(page, disclosures);\n if ((await disclosure.trigger.getAttribute('aria-expanded')) !== 'false') problems.push(`${disclosure.label}: aria-expanded is not \"false\" while the panel is closed`);\n if (await panelOpen(page, disclosure)) problems.push(`${disclosure.label}: the panel shows while its button is collapsed`);\n await disclosure.trigger.click();\n await waitUntil(() => panelOpen(page, disclosure), 400);\n const expanded = await disclosure.trigger.getAttribute('aria-expanded');\n if (!(await panelOpen(page, disclosure)) || expanded !== 'true') problems.push(`${disclosure.label}: after a click the panel is ${(await panelOpen(page, disclosure)) ? 'shown' : 'hidden'} and aria-expanded is \"${expanded}\"`);\n await disclosure.trigger.click();\n await waitUntil(async () => !(await panelOpen(page, disclosure)), 400);\n }\n expect(problems).toEqual([]);\n });\n\n test(title('N5', 'the current page is marked with aria-current in the menu'), async ({ page }, testInfo) => {\n const { nav } = await prepareMenu(page, plan, testInfo);\n const target = await nav.evaluate((element) => {\n const links = Array.from(element.querySelectorAll<HTMLAnchorElement>('a[href]')).filter((link) => link.origin === location.origin && link.pathname !== location.pathname);\n const inPanel = links.filter((link) => !window.__brandlabSiteSystem.isRendered(link));\n return (inPanel[1] ?? inPanel[0] ?? links[0])?.pathname ?? null;\n });\n expect(target, 'the main menu links to no other page').not.toBeNull();\n await openSitePage(page, target!, plan);\n const current = await mainNavigation(page, plan);\n expect(current, `no navigation named \"${plan.navName}\" on ${target}`).not.toBeNull();\n const marked = await current!.evaluate((element) => Array.from(element.querySelectorAll<HTMLAnchorElement>('a[href]'))\n .filter((link) => link.origin === location.origin && link.pathname.replace(/\\/$/, '') === location.pathname.replace(/\\/$/, ''))\n .map((link) => link.getAttribute('aria-current')));\n expect(marked.length, `the menu has no link to ${target}`).toBeGreaterThan(0);\n expect(marked, `the menu link to ${target} does not carry aria-current=\"page\" on that page`).toContain('page');\n });\n\n test(title('N5', 'Arrow keys move through a panel, and Escape closes it and returns focus to its button'), async ({ page }, testInfo) => {\n const { disclosures } = await prepareMenu(page, plan, testInfo);\n const problems: string[] = [];\n const focusInPanel = (id: string) => page.evaluate((panelId) => Boolean(document.getElementById(panelId)?.contains(document.activeElement)), id);\n for (const disclosure of disclosures) {\n await rest(page, disclosures);\n await disclosure.trigger.focus();\n await page.keyboard.press('ArrowDown');\n await waitUntil(async () => (await panelOpen(page, disclosure)) && (await focusInPanel(disclosure.panelId)), 500);\n if (!(await panelOpen(page, disclosure))) {\n problems.push(`${disclosure.label}: Arrow Down on the button did not open the panel`);\n continue;\n }\n if (!(await focusInPanel(disclosure.panelId))) {\n problems.push(`${disclosure.label}: Arrow Down did not move focus to the panel's first link`);\n } else {\n const links = await panelLocator(page, disclosure.panelId).evaluate((panel) => Array.from(panel.querySelectorAll('a[href]')).filter((link) => window.__brandlabSiteSystem.isRendered(link)).length);\n const first = await page.evaluate(() => document.activeElement?.outerHTML ?? '');\n await page.keyboard.press('ArrowDown');\n const second = await page.evaluate(() => document.activeElement?.outerHTML ?? '');\n if (links > 1 && (first === second || !(await focusInPanel(disclosure.panelId)))) problems.push(`${disclosure.label}: Arrow Down did not move to the next link in the panel`);\n }\n await page.keyboard.press('Escape');\n await waitUntil(async () => !(await panelOpen(page, disclosure)), 500);\n if (await panelOpen(page, disclosure)) problems.push(`${disclosure.label}: Escape did not close the panel`);\n else if (!(await disclosure.trigger.evaluate((element) => element === document.activeElement))) problems.push(`${disclosure.label}: Escape did not return focus to the menu button`);\n }\n expect(problems).toEqual([]);\n });\n\n test(title('N5', 'axe finds no serious or critical issue in the header with the panels closed and open'), async ({ page }, testInfo) => {\n const { disclosures } = await prepareMenu(page, plan, testInfo);\n const scope = ['[data-brandlab-journey~=\"header\"]', '[data-brandlab-journey~=\"nav\"]'];\n const closed = await runAxe(page, scope, { tags: SITE_SYSTEM_AXE_TAGS });\n const problems = describeAxe(seriousOrCritical(closed.violations)).map((line) => `panels closed: ${line}`);\n for (const disclosure of disclosures) {\n await rest(page, disclosures);\n await disclosure.trigger.click();\n await waitUntil(() => panelOpen(page, disclosure), 400);\n await page.waitForTimeout(250);\n await panelLocator(page, disclosure.panelId).evaluate((panel) => window.__brandlabSiteSystem.markElement(panel, 'panel'));\n const open = await runAxe(page, [...scope, '[data-brandlab-journey~=\"panel\"]'], { tags: SITE_SYSTEM_AXE_TAGS });\n problems.push(...describeAxe(seriousOrCritical(open.violations)).map((line) => `${disclosure.label} open: ${line}`));\n await disclosure.trigger.click();\n }\n expect([...new Set(problems)]).toEqual([]);\n });\n }\n });\n}\n"]}
|