@baseline-ui/test-utils 3.4.0 → 3.5.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/dist/index.d.mts +19 -6
- package/dist/index.d.ts +19 -6
- package/dist/index.js +11 -11
- package/dist/index.mjs +11 -11
- package/package.json +2 -2
- package/sbom.json +6 -6
package/dist/index.d.mts
CHANGED
|
@@ -99,10 +99,6 @@ declare const expect: playwright_test.Expect<{
|
|
|
99
99
|
prefix?: string | string[];
|
|
100
100
|
ignoreHashed?: boolean;
|
|
101
101
|
}): Promise<{
|
|
102
|
-
message: () => string;
|
|
103
|
-
pass: false;
|
|
104
|
-
shouldNotRetryTest?: undefined;
|
|
105
|
-
} | {
|
|
106
102
|
message: () => string;
|
|
107
103
|
pass: true;
|
|
108
104
|
shouldNotRetryTest?: undefined;
|
|
@@ -110,6 +106,10 @@ declare const expect: playwright_test.Expect<{
|
|
|
110
106
|
message: () => string;
|
|
111
107
|
pass: false;
|
|
112
108
|
shouldNotRetryTest: boolean;
|
|
109
|
+
} | {
|
|
110
|
+
message: () => string;
|
|
111
|
+
pass: false;
|
|
112
|
+
shouldNotRetryTest?: undefined;
|
|
113
113
|
}>;
|
|
114
114
|
}>;
|
|
115
115
|
|
|
@@ -331,7 +331,16 @@ interface FileUploaderTester {
|
|
|
331
331
|
* `trigger` when the Menu renders a custom trigger via `renderTrigger` that
|
|
332
332
|
* doesn't carry that test id.
|
|
333
333
|
*/
|
|
334
|
-
|
|
334
|
+
interface MenuTesterOptions extends BaseTesterOptions {
|
|
335
|
+
/**
|
|
336
|
+
* For a Menu with `trigger="longPress"`. `open()` then holds the pointer
|
|
337
|
+
* instead of clicking, and keyboard opens press the key with `Alt` held, the
|
|
338
|
+
* long-press menu's keyboard opener.
|
|
339
|
+
*
|
|
340
|
+
* @default false
|
|
341
|
+
*/
|
|
342
|
+
longPress?: boolean;
|
|
343
|
+
}
|
|
335
344
|
/**
|
|
336
345
|
* Locators + verbs shared by a menu and each of its submenus. A submenu is
|
|
337
346
|
* itself a menu opened from a `menuitem`, so the recursive surface (items,
|
|
@@ -368,7 +377,11 @@ interface MenuTester extends MenuLevelTester {
|
|
|
368
377
|
readonly trigger: Locator;
|
|
369
378
|
/**
|
|
370
379
|
* Opens the root menu if it isn't already open. Idempotent (checks
|
|
371
|
-
* `aria-expanded`). Defaults to `"click"`.
|
|
380
|
+
* `aria-expanded`). Defaults to `"click"`. With `longPress`, `"click"` holds
|
|
381
|
+
* the pointer on the trigger's button and a key is pressed with `Alt` held;
|
|
382
|
+
* `"ArrowLeft"` and `"ArrowRight"` throw, since they never open a long-press
|
|
383
|
+
* menu and `open()` would otherwise do nothing. (`Alt` with either is also
|
|
384
|
+
* Back/Forward in desktop browsers on Windows and Linux.)
|
|
372
385
|
*/
|
|
373
386
|
open: (how?: DisclosureOpenWith) => Promise<void>;
|
|
374
387
|
/** Closes the menu if open, via `Escape`. Idempotent. */
|
package/dist/index.d.ts
CHANGED
|
@@ -99,10 +99,6 @@ declare const expect: playwright_test.Expect<{
|
|
|
99
99
|
prefix?: string | string[];
|
|
100
100
|
ignoreHashed?: boolean;
|
|
101
101
|
}): Promise<{
|
|
102
|
-
message: () => string;
|
|
103
|
-
pass: false;
|
|
104
|
-
shouldNotRetryTest?: undefined;
|
|
105
|
-
} | {
|
|
106
102
|
message: () => string;
|
|
107
103
|
pass: true;
|
|
108
104
|
shouldNotRetryTest?: undefined;
|
|
@@ -110,6 +106,10 @@ declare const expect: playwright_test.Expect<{
|
|
|
110
106
|
message: () => string;
|
|
111
107
|
pass: false;
|
|
112
108
|
shouldNotRetryTest: boolean;
|
|
109
|
+
} | {
|
|
110
|
+
message: () => string;
|
|
111
|
+
pass: false;
|
|
112
|
+
shouldNotRetryTest?: undefined;
|
|
113
113
|
}>;
|
|
114
114
|
}>;
|
|
115
115
|
|
|
@@ -331,7 +331,16 @@ interface FileUploaderTester {
|
|
|
331
331
|
* `trigger` when the Menu renders a custom trigger via `renderTrigger` that
|
|
332
332
|
* doesn't carry that test id.
|
|
333
333
|
*/
|
|
334
|
-
|
|
334
|
+
interface MenuTesterOptions extends BaseTesterOptions {
|
|
335
|
+
/**
|
|
336
|
+
* For a Menu with `trigger="longPress"`. `open()` then holds the pointer
|
|
337
|
+
* instead of clicking, and keyboard opens press the key with `Alt` held, the
|
|
338
|
+
* long-press menu's keyboard opener.
|
|
339
|
+
*
|
|
340
|
+
* @default false
|
|
341
|
+
*/
|
|
342
|
+
longPress?: boolean;
|
|
343
|
+
}
|
|
335
344
|
/**
|
|
336
345
|
* Locators + verbs shared by a menu and each of its submenus. A submenu is
|
|
337
346
|
* itself a menu opened from a `menuitem`, so the recursive surface (items,
|
|
@@ -368,7 +377,11 @@ interface MenuTester extends MenuLevelTester {
|
|
|
368
377
|
readonly trigger: Locator;
|
|
369
378
|
/**
|
|
370
379
|
* Opens the root menu if it isn't already open. Idempotent (checks
|
|
371
|
-
* `aria-expanded`). Defaults to `"click"`.
|
|
380
|
+
* `aria-expanded`). Defaults to `"click"`. With `longPress`, `"click"` holds
|
|
381
|
+
* the pointer on the trigger's button and a key is pressed with `Alt` held;
|
|
382
|
+
* `"ArrowLeft"` and `"ArrowRight"` throw, since they never open a long-press
|
|
383
|
+
* menu and `open()` would otherwise do nothing. (`Alt` with either is also
|
|
384
|
+
* Back/Forward in desktop browsers on Windows and Linux.)
|
|
372
385
|
*/
|
|
373
386
|
open: (how?: DisclosureOpenWith) => Promise<void>;
|
|
374
387
|
/** Closes the menu if open, via `Escape`. Idempotent. */
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
'use strict';var test=require('@playwright/test'),W=require('axe-core'),promises=require('fs/promises'),
|
|
1
|
+
'use strict';var test=require('@playwright/test'),W=require('axe-core'),promises=require('fs/promises'),F=require('path');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var W__default=/*#__PURE__*/_interopDefault(W);var F__default=/*#__PURE__*/_interopDefault(F);/**
|
|
2
2
|
* Copyright (c) 2023-2024 PSPDFKit GmbH. All rights reserved.
|
|
3
3
|
*
|
|
4
4
|
* THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
|
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
* This notice may not be removed from this file.
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
|
-
var
|
|
11
|
-
`):d(
|
|
12
|
-
`)}var
|
|
13
|
-
${
|
|
14
|
-
`);return ` \u2022 [${
|
|
15
|
-
${
|
|
16
|
-
${
|
|
10
|
+
var L=["wcag2a","wcag2aa","wcag21a","wcag21aa","best-practice"],O=["landmark-one-main","page-has-heading-one","region","aria-allowed-role","duplicate-id","scrollable-region-focusable"],E=["aria-valid-attr-value","aria-valid-attr","aria-required-attr"];function C(e,t={}){let{prefix:o="BaselineUI-",ignoreHashed:n=false}=t,s=Array.isArray(o)?o:[o];function r(c){return c.split(/[-_]/).some(p=>p.length>=5&&/^[a-z\d]+$/.test(p)&&/[a-z]/.test(p)&&/\d/.test(p))}function l(c){return [...c.classList].filter(p=>s.some(m=>p.startsWith(m))&&(!n||!r(p)))}function u(c,p=false){let m=[],b=c.shadowRoot?c.shadowRoot.children:c.children;for(let f of b){let h=p||f.getAttribute("aria-hidden")==="true",x=l(f),w=u(f,h);x.length>0?m.push({classes:x,children:w,hidden:h}):m.push(...w);}return m}function d(c,p="",m=false){let b=[];for(let f=0;f<c.length;f++){let h=c[f],x=f===c.length-1,w=m?"":x?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 ",N=m?"":p+(x?" ":"\u2502 "),_=h.classes.join(", ");b.push(p+w+_+(h.hidden?" [aria-hidden]":""),...d(h.children,N));}return b}let a=l(e),i=u(e);return a.length===0&&i.length===0?"":a.length>0?d([{classes:a,children:i,hidden:false}],"",true).join(`
|
|
11
|
+
`):d(i,"",true).join(`
|
|
12
|
+
`)}var T=e=>{if(e instanceof Error)return e.message;if(typeof e=="string")return e;if(e==null)return "unknown error";try{return JSON.stringify(e)}catch{return "unstringifiable error"}},z=async e=>{let t=typeof e.goto=="function",o=!t&&typeof e.owner=="function";if(t||o)return e.locator("body");let n=e;return await n.first().evaluate(r=>r.tagName?.toLowerCase()==="iframe").catch(()=>false)?n.contentFrame().locator("body"):n},V=async e=>{await e.first().evaluate((t,o)=>{if(window.axe)return;let s=document.createElement("script");s.textContent=o,document.head.append(s);},W__default.default.source);},G=["document-title","html-has-lang","html-lang-valid","html-xml-lang-mismatch"],q=(e,t=[])=>{let o=new Set([...O,...t,...e.disableRules??[]]);return {runOnly:{type:"tag",values:[...e.tags??L]},rules:Object.fromEntries([...o].map(n=>[n,{enabled:false}]))}},I=e=>{if(e!==void 0)return typeof e=="string"?[e]:[...e]},J=async(e,t,o)=>{let n=await e.count();if(n===0)throw new Error("toBeAccessible: locator resolved to zero elements; nothing to scan.");await V(e);let s=I(t.include),r=I(t.exclude);return n===1?await e.evaluate(async(l,u)=>{let d=window,a={include:[l,...u.userInclude??[]],exclude:u.userExclude??[]};return await d.axe.run(a,u.runOptions)},{userInclude:s,userExclude:r,runOptions:o}):await e.evaluateAll(async(l,u)=>{let d=window,a={include:[...l,...u.userInclude??[]],exclude:u.userExclude??[]};return await d.axe.run(a,u.runOptions)},{userInclude:s,userExclude:r,runOptions:o})},A="BaselineUI-";function S(e){return e.replaceAll(/\W+/g,"-").replaceAll(/^-|-$/g,"")}function K(e){return Array.isArray(e)?e.length===1&&e[0]===A:e===A}function X(e){return (Array.isArray(e)?e:[e]).join("+")}function Q(e,t,o,n,s){let r=t.slice(1).map(l=>S(l));return o&&r.push(S(o)),n!==void 0&&!K(n)&&r.push(S(X(n))),s&&r.push("ignoreHashed"),F__default.default.join(e,`${r.join("--")}.txt`)}var B=(e,t)=>{if(typeof e?.callCount!="number")throw new TypeError(`${t}: expected a sinon spy.`)},Y=test.expect.extend({toBeWithinDelta(e,t,o){let n=Math.abs(e-t);if(Number.isNaN(n)||Number.isNaN(Number(o))||o<0)throw new TypeError(`toBeWithinDelta: cannot compare ${e} with ${t} within ${o}; expected numbers and a non-negative delta.`);return n<=o?{message:()=>`expected ${e} not to be within ${o} of ${t}`,pass:true}:{message:()=>`expected ${e} to be within ${o} of ${t}`,pass:false}},async toBeCalledOnce(e){B(e,"toBeCalledOnce");try{return await test.expect.poll(()=>e.callCount).toBe(1),{message:()=>"Expected function to be called once.",pass:!0}}catch(t){return {message:()=>T(t),pass:false}}},async toBeCalledTwice(e){B(e,"toBeCalledTwice");try{return await test.expect.poll(()=>e.callCount).toBe(2),{message:()=>"Expected function to be called twice.",pass:!0}}catch(t){return {message:()=>T(t),pass:false}}},async toBeCalledThrice(e){B(e,"toBeCalledThrice");try{return await test.expect.poll(()=>e.callCount).toBe(3),{message:()=>"Expected function to be called thrice.",pass:!0}}catch(t){return {message:()=>T(t),pass:false}}},async toBeCalledWith(e,...t){B(e,"toBeCalledWith");try{return await test.expect.poll(()=>({called:e.called,args:t.map((o,n)=>e.lastCall?.args[n])})).toStrictEqual({called:!0,args:t}),{message:()=>`Expected function to be called with ${t.join(", ")}.`,pass:!0}}catch(o){return {message:()=>T(o),pass:false}}},async toBeCalledWithExactly(e,...t){B(e,"toBeCalledWithExactly");try{return await test.expect.poll(()=>e.lastCall?.args).toStrictEqual(t),{message:()=>`Expected function to be called with exactly ${t.join(", ")}.`,pass:!0}}catch(o){return {message:()=>T(o),pass:false}}},toBeCalled(e){B(e,"toBeCalled");try{return test.expect(e.called).toBeTruthy(),{message:()=>"Expected function to be called.",pass:!0}}catch(t){return {message:()=>T(t),pass:false}}},async toBeCalledTimes(e,t){if(B(e,"toBeCalledTimes"),!Number.isInteger(t)||t<0)throw new TypeError("toBeCalledTimes: times must be a non-negative integer.");try{return await test.expect.poll(()=>e.callCount).toBe(t),{message:()=>`Expected function to be called ${t} times.`,pass:!0}}catch(o){return {message:()=>T(o),pass:false}}},async toBeAccessible(e,t={}){let o=await z(e),n=await o.first().evaluate(i=>{let c=i.ownerDocument?.defaultView;return c!==null&&c!==window.top}).catch(()=>false),s=q(t,n?G:[]),r,l=async()=>{let i=await J(o,t,s),c=t.treatIncompleteAsViolationIds??E,p=[...i.violations,...i.incomplete.filter(m=>c.includes(m.id))];return r={result:i,violations:p},p},u;try{if(t.timeout&&t.timeout>0){let i=test.expect.poll(l,{timeout:t.timeout});await(this.isNot?i.not.toHaveLength(0):i.toHaveLength(0));}else await l();}catch(i){u=i;}if(!r)throw new Error(`toBeAccessible: axe scan never completed.
|
|
13
|
+
${T(u)}`);if(r.violations.length===0)return {message:()=>"Expected accessibility violations but found none.",pass:true};this.isNot||await test.test.info().attach(t.attachmentName??"accessibility-scan-results",{body:JSON.stringify(r.result,null,2),contentType:"application/json"});let d=r.violations.map(i=>{let c=i.nodes.map(p=>Array.isArray(p.target)?p.target.join(" "):String(p.target)).join(`
|
|
14
|
+
`);return ` \u2022 [${i.id}] ${i.help}
|
|
15
|
+
${c}
|
|
16
|
+
${i.helpUrl??""}`}).join(`
|
|
17
17
|
`),a=r.violations.length;return {message:()=>`Found ${a} accessibility violation${a===1?"":"s"}:
|
|
18
|
-
${d}`,pass:false}},async toMatchClassTree(e,t,o={}){let{prefix:n=
|
|
19
|
-
`,
|
|
18
|
+
${d}`,pass:false}},async toMatchClassTree(e,t,o={}){let{prefix:n=A,ignoreHashed:s=false}=o,r=await e.evaluate(C,{prefix:n,ignoreHashed:s});if(!r){let p=Array.isArray(n)?n.map(m=>`${m}*`).join(" / "):`${n}*`;throw new Error(`No ${p} classes found in the DOM tree.`)}let l=`${r}
|
|
19
|
+
`,u=test.test.info();if(u.config.ignoreSnapshots)return {message:()=>"Class tree snapshot check skipped.",pass:true};let a=Q(u.project.snapshotDir,u.titlePath,t,n,s),i=u.config.updateSnapshots,c;try{c=await promises.readFile(a,"utf8");}catch(p){let m=p.code;if(m==="ENOENT"&&this.isNot)return {message:()=>`A class tree snapshot doesn't exist at ${a}, matchers using ".not" won't write them automatically.`,pass:true};if(m==="ENOENT"&&i!=="none")return await promises.mkdir(F__default.default.dirname(a),{recursive:true}),await promises.writeFile(a,l),i==="missing"?{message:()=>`A class tree snapshot doesn't exist at ${a}, writing actual.`,pass:false,shouldNotRetryTest:true}:{message:()=>"Class tree snapshot created.",pass:true};throw p}if(c!==l){if(!this.isNot&&(i==="all"||i==="changed"))return await promises.writeFile(a,l),{message:()=>"Class tree snapshot updated.",pass:true};try{test.expect(l).toBe(c);}catch(p){return {message:()=>`Class tree snapshot mismatch at ${a}
|
|
20
20
|
|
|
21
|
-
${p.message}`,pass
|
|
21
|
+
${p.message}`,pass:false}}}return {message:()=>"Class tree matched snapshot.",pass:true}}});function g(e){let t=async()=>{if(await e.getAttribute("aria-expanded")!==null)return e;let s=e.locator("[aria-expanded]").first();return await s.count()>0?s:e},o=async()=>await(await t()).getAttribute("aria-expanded")==="true";return {button:t,isOpen:o,open:async(s="click")=>{if(await o())return;if(s==="click"){await e.click();return}await(await t()).press(s);}}}function v({frame:e,trigger:t,toggleButton:o}){let n=t??e.getByRole("combobox",{includeHidden:true}),s=o??e.getByTestId("combobox-toggle-button"),r=a=>e.getByRole("option",{name:a,exact:typeof a=="string"}),{isOpen:l,open:u}=g(n),d=async(a="click")=>{if(!await l()){if(a==="click"){await s.click();return}await u(a);}};return {input:n,toggleButton:s,listbox:e.getByRole("listbox"),option:r,isOpen:l,open:d,async selectOption(a){await d(),await r(a).click();},async filter(a){await n.fill(a);}}}function D({frame:e,root:t}){let o=t??e.locator(".BaselineUI-Editor"),n=o.getByTestId("editor-font-color-button"),s=o.getByTestId("editor-background-color-button"),r=o.getByTestId("editor-add-link-button"),l=o.getByTestId("editor-help-button"),u=o.getByTestId("editor-mention-button"),d=o.getByRole("combobox"),a=e.getByTestId("editor-mention-listbox"),i=e.getByTestId("editor-help-dialog-close-button"),c=e.getByRole("dialog").filter({has:i}),p=e.getByRole("dialog").filter({has:e.getByTestId(/^editor-link-/)});return {root:o,textbox:o.getByRole("textbox"),placeholder:o.getByTestId("editor-placeholder"),toolbar:o.getByRole("toolbar"),boldButton:o.getByTestId("editor-bold-button"),italicButton:o.getByTestId("editor-italic-button"),underlineButton:o.getByTestId("editor-underline-button"),fontColorButton:n,backgroundColorButton:s,openFontColorPicker:g(n).open,openBackgroundColorPicker:g(s).open,addLinkButton:r,linkDialog:p,linkInput:e.getByTestId("editor-link-input"),linkAddButton:e.getByTestId("editor-link-add-button"),linkEditButton:e.getByTestId("editor-link-edit-button"),linkRemoveButton:e.getByTestId("editor-link-remove-button"),linkCancelButton:e.getByTestId("editor-link-cancel-button"),linkSaveButton:e.getByTestId("editor-link-save-button"),openLinkDialog:g(r).open,saveButton:o.getByTestId("editor-save-button"),cancelButton:o.getByTestId("editor-cancel-button"),mentionButton:u,helpButton:l,helpDialog:c,helpDialogCloseButton:i,async openHelpDialog(){await c.isVisible()||await l.click();},mentionInput:d,mentionListbox:a,mentionOption:m=>a.getByRole("option",{name:m}),async startMention(){await d.isVisible()||await u.click();}}}function P({frame:e,trigger:t,variant:o="dropzone"}){let n=e.getByTestId("file-uploader"),s=n.locator('input[type="file"]'),r=t??(o==="button"?n.getByRole("button"):e.getByTestId("file-uploader-dropzone"));return {root:n,button:r,input:s,dropzone:e.getByTestId("file-uploader-dropzone"),errorMessage:n.getByRole("alert"),async upload(l){await s.setInputFiles(l);}}}function U(e,t,o){let n=l=>t.getByRole("menuitem",{name:l}).or(t.getByRole("menuitemradio",{name:l})).or(t.getByRole("menuitemcheckbox",{name:l})),s=l=>t.getByRole("group",{name:l}),{isOpen:r}=g(o);return {menu:t,item:n,section:s,isOpen:r,submenu(l){let u=n(l),d=e.getByRole("menu",{name:l}),a=U(e,d,u),{open:i}=g(u);return {...a,trigger:u,open:async(c="Enter")=>i(c)}}}}function M({frame:e,trigger:t,longPress:o=false}){let n=t??e.getByTestId("menu-trigger"),s=e.getByRole("menu").first(),r=U(e,s,n),{button:l,open:u,isOpen:d}=g(n),a=async(i="click")=>{if(!o)return u(i);if(i==="ArrowLeft"||i==="ArrowRight")throw new Error(`menuTester: cannot open a long-press menu with "${i}"; use "click", "Enter", "Space", "ArrowDown" or "ArrowUp"`);if(await d())return;let c=await l();if(i==="click"){await c.click({delay:800});return}await c.press(`Alt+${i}`);};return {...r,trigger:n,open:a,isOpen:d,async close(){await d()&&await n.press("Escape");},async selectItem(i){await a(),await r.item(i).click();}}}var $=(e=>{let o=Z(e);return e.multiple&&(o=ee(o,e.frame)),e.autocomplete&&(o=te(o,e.frame)),o});function Z({frame:e,trigger:t}){let o=t??e.getByTestId("select-button"),n=r=>e.getByRole("option",{name:r,exact:typeof r=="string"}),{open:s}=g(o);return {trigger:o,listbox:e.getByRole("listbox"),option:n,open:s,async selectOption(r){await s(),await n(r).click();}}}function ee(e,t){return {...e,selectAllButton:t.getByTestId("select-all-button"),clearButton:t.getByTestId("select-clear-button"),tag:o=>e.trigger.getByRole("row",{name:o}),removeTagButton:o=>e.trigger.getByRole("row",{name:o}).getByRole("button")}}function te(e,t){let o=t.getByRole("searchbox");return {...e,searchbox:o,async filter(n){await o.fill(n);}}}function oe(e){return {select:((t={})=>$({frame:e,...t})),comboBox:(t={})=>v({frame:e,...t}),editor:(t={})=>D({frame:e,...t}),fileUploader:(t={})=>P({frame:e,...t}),menu:(t={})=>M({frame:e,...t})}}exports.DEFAULT_CRITICAL_INCOMPLETE_RULES=E;exports.DEFAULT_DISABLED_RULES=O;exports.DEFAULT_WCAG_TAGS=L;exports.expect=Y;exports.testers=oe;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {expect,test}from'@playwright/test';import W from'axe-core';import {mkdir,
|
|
1
|
+
import {expect,test}from'@playwright/test';import W from'axe-core';import {readFile,mkdir,writeFile}from'fs/promises';import F from'path';/**
|
|
2
2
|
* Copyright (c) 2023-2024 PSPDFKit GmbH. All rights reserved.
|
|
3
3
|
*
|
|
4
4
|
* THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
|
|
@@ -7,15 +7,15 @@ import {expect,test}from'@playwright/test';import W from'axe-core';import {mkdir
|
|
|
7
7
|
* This notice may not be removed from this file.
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
|
-
var
|
|
11
|
-
`):d(
|
|
12
|
-
`)}var
|
|
13
|
-
${
|
|
14
|
-
`);return ` \u2022 [${
|
|
15
|
-
${
|
|
16
|
-
${
|
|
10
|
+
var L=["wcag2a","wcag2aa","wcag21a","wcag21aa","best-practice"],O=["landmark-one-main","page-has-heading-one","region","aria-allowed-role","duplicate-id","scrollable-region-focusable"],E=["aria-valid-attr-value","aria-valid-attr","aria-required-attr"];function C(e,t={}){let{prefix:o="BaselineUI-",ignoreHashed:n=false}=t,s=Array.isArray(o)?o:[o];function r(c){return c.split(/[-_]/).some(p=>p.length>=5&&/^[a-z\d]+$/.test(p)&&/[a-z]/.test(p)&&/\d/.test(p))}function l(c){return [...c.classList].filter(p=>s.some(m=>p.startsWith(m))&&(!n||!r(p)))}function u(c,p=false){let m=[],b=c.shadowRoot?c.shadowRoot.children:c.children;for(let f of b){let h=p||f.getAttribute("aria-hidden")==="true",x=l(f),w=u(f,h);x.length>0?m.push({classes:x,children:w,hidden:h}):m.push(...w);}return m}function d(c,p="",m=false){let b=[];for(let f=0;f<c.length;f++){let h=c[f],x=f===c.length-1,w=m?"":x?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 ",N=m?"":p+(x?" ":"\u2502 "),_=h.classes.join(", ");b.push(p+w+_+(h.hidden?" [aria-hidden]":""),...d(h.children,N));}return b}let a=l(e),i=u(e);return a.length===0&&i.length===0?"":a.length>0?d([{classes:a,children:i,hidden:false}],"",true).join(`
|
|
11
|
+
`):d(i,"",true).join(`
|
|
12
|
+
`)}var T=e=>{if(e instanceof Error)return e.message;if(typeof e=="string")return e;if(e==null)return "unknown error";try{return JSON.stringify(e)}catch{return "unstringifiable error"}},z=async e=>{let t=typeof e.goto=="function",o=!t&&typeof e.owner=="function";if(t||o)return e.locator("body");let n=e;return await n.first().evaluate(r=>r.tagName?.toLowerCase()==="iframe").catch(()=>false)?n.contentFrame().locator("body"):n},V=async e=>{await e.first().evaluate((t,o)=>{if(window.axe)return;let s=document.createElement("script");s.textContent=o,document.head.append(s);},W.source);},G=["document-title","html-has-lang","html-lang-valid","html-xml-lang-mismatch"],q=(e,t=[])=>{let o=new Set([...O,...t,...e.disableRules??[]]);return {runOnly:{type:"tag",values:[...e.tags??L]},rules:Object.fromEntries([...o].map(n=>[n,{enabled:false}]))}},I=e=>{if(e!==void 0)return typeof e=="string"?[e]:[...e]},J=async(e,t,o)=>{let n=await e.count();if(n===0)throw new Error("toBeAccessible: locator resolved to zero elements; nothing to scan.");await V(e);let s=I(t.include),r=I(t.exclude);return n===1?await e.evaluate(async(l,u)=>{let d=window,a={include:[l,...u.userInclude??[]],exclude:u.userExclude??[]};return await d.axe.run(a,u.runOptions)},{userInclude:s,userExclude:r,runOptions:o}):await e.evaluateAll(async(l,u)=>{let d=window,a={include:[...l,...u.userInclude??[]],exclude:u.userExclude??[]};return await d.axe.run(a,u.runOptions)},{userInclude:s,userExclude:r,runOptions:o})},A="BaselineUI-";function S(e){return e.replaceAll(/\W+/g,"-").replaceAll(/^-|-$/g,"")}function K(e){return Array.isArray(e)?e.length===1&&e[0]===A:e===A}function X(e){return (Array.isArray(e)?e:[e]).join("+")}function Q(e,t,o,n,s){let r=t.slice(1).map(l=>S(l));return o&&r.push(S(o)),n!==void 0&&!K(n)&&r.push(S(X(n))),s&&r.push("ignoreHashed"),F.join(e,`${r.join("--")}.txt`)}var B=(e,t)=>{if(typeof e?.callCount!="number")throw new TypeError(`${t}: expected a sinon spy.`)},Y=expect.extend({toBeWithinDelta(e,t,o){let n=Math.abs(e-t);if(Number.isNaN(n)||Number.isNaN(Number(o))||o<0)throw new TypeError(`toBeWithinDelta: cannot compare ${e} with ${t} within ${o}; expected numbers and a non-negative delta.`);return n<=o?{message:()=>`expected ${e} not to be within ${o} of ${t}`,pass:true}:{message:()=>`expected ${e} to be within ${o} of ${t}`,pass:false}},async toBeCalledOnce(e){B(e,"toBeCalledOnce");try{return await expect.poll(()=>e.callCount).toBe(1),{message:()=>"Expected function to be called once.",pass:!0}}catch(t){return {message:()=>T(t),pass:false}}},async toBeCalledTwice(e){B(e,"toBeCalledTwice");try{return await expect.poll(()=>e.callCount).toBe(2),{message:()=>"Expected function to be called twice.",pass:!0}}catch(t){return {message:()=>T(t),pass:false}}},async toBeCalledThrice(e){B(e,"toBeCalledThrice");try{return await expect.poll(()=>e.callCount).toBe(3),{message:()=>"Expected function to be called thrice.",pass:!0}}catch(t){return {message:()=>T(t),pass:false}}},async toBeCalledWith(e,...t){B(e,"toBeCalledWith");try{return await expect.poll(()=>({called:e.called,args:t.map((o,n)=>e.lastCall?.args[n])})).toStrictEqual({called:!0,args:t}),{message:()=>`Expected function to be called with ${t.join(", ")}.`,pass:!0}}catch(o){return {message:()=>T(o),pass:false}}},async toBeCalledWithExactly(e,...t){B(e,"toBeCalledWithExactly");try{return await expect.poll(()=>e.lastCall?.args).toStrictEqual(t),{message:()=>`Expected function to be called with exactly ${t.join(", ")}.`,pass:!0}}catch(o){return {message:()=>T(o),pass:false}}},toBeCalled(e){B(e,"toBeCalled");try{return expect(e.called).toBeTruthy(),{message:()=>"Expected function to be called.",pass:!0}}catch(t){return {message:()=>T(t),pass:false}}},async toBeCalledTimes(e,t){if(B(e,"toBeCalledTimes"),!Number.isInteger(t)||t<0)throw new TypeError("toBeCalledTimes: times must be a non-negative integer.");try{return await expect.poll(()=>e.callCount).toBe(t),{message:()=>`Expected function to be called ${t} times.`,pass:!0}}catch(o){return {message:()=>T(o),pass:false}}},async toBeAccessible(e,t={}){let o=await z(e),n=await o.first().evaluate(i=>{let c=i.ownerDocument?.defaultView;return c!==null&&c!==window.top}).catch(()=>false),s=q(t,n?G:[]),r,l=async()=>{let i=await J(o,t,s),c=t.treatIncompleteAsViolationIds??E,p=[...i.violations,...i.incomplete.filter(m=>c.includes(m.id))];return r={result:i,violations:p},p},u;try{if(t.timeout&&t.timeout>0){let i=expect.poll(l,{timeout:t.timeout});await(this.isNot?i.not.toHaveLength(0):i.toHaveLength(0));}else await l();}catch(i){u=i;}if(!r)throw new Error(`toBeAccessible: axe scan never completed.
|
|
13
|
+
${T(u)}`);if(r.violations.length===0)return {message:()=>"Expected accessibility violations but found none.",pass:true};this.isNot||await test.info().attach(t.attachmentName??"accessibility-scan-results",{body:JSON.stringify(r.result,null,2),contentType:"application/json"});let d=r.violations.map(i=>{let c=i.nodes.map(p=>Array.isArray(p.target)?p.target.join(" "):String(p.target)).join(`
|
|
14
|
+
`);return ` \u2022 [${i.id}] ${i.help}
|
|
15
|
+
${c}
|
|
16
|
+
${i.helpUrl??""}`}).join(`
|
|
17
17
|
`),a=r.violations.length;return {message:()=>`Found ${a} accessibility violation${a===1?"":"s"}:
|
|
18
|
-
${d}`,pass:false}},async toMatchClassTree(e,t,o={}){let{prefix:n=
|
|
19
|
-
`,
|
|
18
|
+
${d}`,pass:false}},async toMatchClassTree(e,t,o={}){let{prefix:n=A,ignoreHashed:s=false}=o,r=await e.evaluate(C,{prefix:n,ignoreHashed:s});if(!r){let p=Array.isArray(n)?n.map(m=>`${m}*`).join(" / "):`${n}*`;throw new Error(`No ${p} classes found in the DOM tree.`)}let l=`${r}
|
|
19
|
+
`,u=test.info();if(u.config.ignoreSnapshots)return {message:()=>"Class tree snapshot check skipped.",pass:true};let a=Q(u.project.snapshotDir,u.titlePath,t,n,s),i=u.config.updateSnapshots,c;try{c=await readFile(a,"utf8");}catch(p){let m=p.code;if(m==="ENOENT"&&this.isNot)return {message:()=>`A class tree snapshot doesn't exist at ${a}, matchers using ".not" won't write them automatically.`,pass:true};if(m==="ENOENT"&&i!=="none")return await mkdir(F.dirname(a),{recursive:true}),await writeFile(a,l),i==="missing"?{message:()=>`A class tree snapshot doesn't exist at ${a}, writing actual.`,pass:false,shouldNotRetryTest:true}:{message:()=>"Class tree snapshot created.",pass:true};throw p}if(c!==l){if(!this.isNot&&(i==="all"||i==="changed"))return await writeFile(a,l),{message:()=>"Class tree snapshot updated.",pass:true};try{expect(l).toBe(c);}catch(p){return {message:()=>`Class tree snapshot mismatch at ${a}
|
|
20
20
|
|
|
21
|
-
${p.message}`,pass
|
|
21
|
+
${p.message}`,pass:false}}}return {message:()=>"Class tree matched snapshot.",pass:true}}});function g(e){let t=async()=>{if(await e.getAttribute("aria-expanded")!==null)return e;let s=e.locator("[aria-expanded]").first();return await s.count()>0?s:e},o=async()=>await(await t()).getAttribute("aria-expanded")==="true";return {button:t,isOpen:o,open:async(s="click")=>{if(await o())return;if(s==="click"){await e.click();return}await(await t()).press(s);}}}function v({frame:e,trigger:t,toggleButton:o}){let n=t??e.getByRole("combobox",{includeHidden:true}),s=o??e.getByTestId("combobox-toggle-button"),r=a=>e.getByRole("option",{name:a,exact:typeof a=="string"}),{isOpen:l,open:u}=g(n),d=async(a="click")=>{if(!await l()){if(a==="click"){await s.click();return}await u(a);}};return {input:n,toggleButton:s,listbox:e.getByRole("listbox"),option:r,isOpen:l,open:d,async selectOption(a){await d(),await r(a).click();},async filter(a){await n.fill(a);}}}function D({frame:e,root:t}){let o=t??e.locator(".BaselineUI-Editor"),n=o.getByTestId("editor-font-color-button"),s=o.getByTestId("editor-background-color-button"),r=o.getByTestId("editor-add-link-button"),l=o.getByTestId("editor-help-button"),u=o.getByTestId("editor-mention-button"),d=o.getByRole("combobox"),a=e.getByTestId("editor-mention-listbox"),i=e.getByTestId("editor-help-dialog-close-button"),c=e.getByRole("dialog").filter({has:i}),p=e.getByRole("dialog").filter({has:e.getByTestId(/^editor-link-/)});return {root:o,textbox:o.getByRole("textbox"),placeholder:o.getByTestId("editor-placeholder"),toolbar:o.getByRole("toolbar"),boldButton:o.getByTestId("editor-bold-button"),italicButton:o.getByTestId("editor-italic-button"),underlineButton:o.getByTestId("editor-underline-button"),fontColorButton:n,backgroundColorButton:s,openFontColorPicker:g(n).open,openBackgroundColorPicker:g(s).open,addLinkButton:r,linkDialog:p,linkInput:e.getByTestId("editor-link-input"),linkAddButton:e.getByTestId("editor-link-add-button"),linkEditButton:e.getByTestId("editor-link-edit-button"),linkRemoveButton:e.getByTestId("editor-link-remove-button"),linkCancelButton:e.getByTestId("editor-link-cancel-button"),linkSaveButton:e.getByTestId("editor-link-save-button"),openLinkDialog:g(r).open,saveButton:o.getByTestId("editor-save-button"),cancelButton:o.getByTestId("editor-cancel-button"),mentionButton:u,helpButton:l,helpDialog:c,helpDialogCloseButton:i,async openHelpDialog(){await c.isVisible()||await l.click();},mentionInput:d,mentionListbox:a,mentionOption:m=>a.getByRole("option",{name:m}),async startMention(){await d.isVisible()||await u.click();}}}function P({frame:e,trigger:t,variant:o="dropzone"}){let n=e.getByTestId("file-uploader"),s=n.locator('input[type="file"]'),r=t??(o==="button"?n.getByRole("button"):e.getByTestId("file-uploader-dropzone"));return {root:n,button:r,input:s,dropzone:e.getByTestId("file-uploader-dropzone"),errorMessage:n.getByRole("alert"),async upload(l){await s.setInputFiles(l);}}}function U(e,t,o){let n=l=>t.getByRole("menuitem",{name:l}).or(t.getByRole("menuitemradio",{name:l})).or(t.getByRole("menuitemcheckbox",{name:l})),s=l=>t.getByRole("group",{name:l}),{isOpen:r}=g(o);return {menu:t,item:n,section:s,isOpen:r,submenu(l){let u=n(l),d=e.getByRole("menu",{name:l}),a=U(e,d,u),{open:i}=g(u);return {...a,trigger:u,open:async(c="Enter")=>i(c)}}}}function M({frame:e,trigger:t,longPress:o=false}){let n=t??e.getByTestId("menu-trigger"),s=e.getByRole("menu").first(),r=U(e,s,n),{button:l,open:u,isOpen:d}=g(n),a=async(i="click")=>{if(!o)return u(i);if(i==="ArrowLeft"||i==="ArrowRight")throw new Error(`menuTester: cannot open a long-press menu with "${i}"; use "click", "Enter", "Space", "ArrowDown" or "ArrowUp"`);if(await d())return;let c=await l();if(i==="click"){await c.click({delay:800});return}await c.press(`Alt+${i}`);};return {...r,trigger:n,open:a,isOpen:d,async close(){await d()&&await n.press("Escape");},async selectItem(i){await a(),await r.item(i).click();}}}var $=(e=>{let o=Z(e);return e.multiple&&(o=ee(o,e.frame)),e.autocomplete&&(o=te(o,e.frame)),o});function Z({frame:e,trigger:t}){let o=t??e.getByTestId("select-button"),n=r=>e.getByRole("option",{name:r,exact:typeof r=="string"}),{open:s}=g(o);return {trigger:o,listbox:e.getByRole("listbox"),option:n,open:s,async selectOption(r){await s(),await n(r).click();}}}function ee(e,t){return {...e,selectAllButton:t.getByTestId("select-all-button"),clearButton:t.getByTestId("select-clear-button"),tag:o=>e.trigger.getByRole("row",{name:o}),removeTagButton:o=>e.trigger.getByRole("row",{name:o}).getByRole("button")}}function te(e,t){let o=t.getByRole("searchbox");return {...e,searchbox:o,async filter(n){await o.fill(n);}}}function oe(e){return {select:((t={})=>$({frame:e,...t})),comboBox:(t={})=>v({frame:e,...t}),editor:(t={})=>D({frame:e,...t}),fileUploader:(t={})=>P({frame:e,...t}),menu:(t={})=>M({frame:e,...t})}}export{E as DEFAULT_CRITICAL_INCOMPLETE_RULES,O as DEFAULT_DISABLED_RULES,L as DEFAULT_WCAG_TAGS,Y as expect,oe as testers};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baseline-ui/test-utils",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "A collection of test utilities for Baseline UI",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"axe-core": "^4.11.4",
|
|
54
54
|
"react-frame-component": "^5.3.2",
|
|
55
|
-
"@baseline-ui/core": "3.
|
|
55
|
+
"@baseline-ui/core": "3.5.0"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsup",
|
package/sbom.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.6",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:a99b0318-3c2d-44e5-b08d-a0b18a942c5c",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-10-
|
|
8
|
+
"timestamp": "2026-10-20T10:29:02.909Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "pre-build"
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"component": {
|
|
24
24
|
"type": "library",
|
|
25
25
|
"name": "@baseline-ui/test-utils",
|
|
26
|
-
"version": "3.
|
|
27
|
-
"purl": "pkg:npm/@baseline-ui/test-utils@3.
|
|
28
|
-
"bom-ref": "pkg:npm/@baseline-ui/test-utils@3.
|
|
26
|
+
"version": "3.5.0",
|
|
27
|
+
"purl": "pkg:npm/@baseline-ui/test-utils@3.5.0",
|
|
28
|
+
"bom-ref": "pkg:npm/@baseline-ui/test-utils@3.5.0",
|
|
29
29
|
"licenses": [
|
|
30
30
|
{
|
|
31
31
|
"license": {
|
|
@@ -500,7 +500,7 @@
|
|
|
500
500
|
],
|
|
501
501
|
"dependencies": [
|
|
502
502
|
{
|
|
503
|
-
"ref": "pkg:npm/@baseline-ui/test-utils@3.
|
|
503
|
+
"ref": "pkg:npm/@baseline-ui/test-utils@3.5.0",
|
|
504
504
|
"dependsOn": [
|
|
505
505
|
"pkg:npm/%40neondatabase/serverless@1.1.0",
|
|
506
506
|
"pkg:npm/%40playwright/test@1.62.1",
|