@baseline-ui/test-utils 3.2.1 → 3.4.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/client/index.js +3 -3
- package/client/index.mjs +3 -3
- package/dist/index.d.mts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +7 -7
- package/dist/index.mjs +7 -7
- package/package.json +4 -4
- package/sbom.json +85 -86
- package/test-reporter/test-reporter.d.mts +2 -1
- package/test-reporter/test-reporter.d.ts +2 -1
- package/test-reporter/test-reporter.js +7 -7
- package/test-reporter/test-reporter.mjs +7 -7
package/client/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
'use strict';var core=require('@baseline-ui/core'),
|
|
1
|
+
'use strict';var core=require('@baseline-ui/core'),c=require('react'),F=require('react-dom/client'),w=require('react-frame-component'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var c__default=/*#__PURE__*/_interopDefault(c);var F__default=/*#__PURE__*/_interopDefault(F);var w__default=/*#__PURE__*/_interopDefault(w);/**
|
|
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,5 +7,5 @@
|
|
|
7
7
|
* This notice may not be removed from this file.
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
|
-
function R(){let
|
|
11
|
-
`)}var b=
|
|
10
|
+
function R(){let t=[],r=document;for(let n=0;n<r.styleSheets.length;n++){let s=r.styleSheets[n];try{for(let a of s.cssRules)t.push(a.cssText);}catch{}}return t.join(`
|
|
11
|
+
`)}var b=c__default.default.forwardRef(({head:t=jsxRuntime.jsx("style",{children:R()}),...r},n)=>jsxRuntime.jsx(w__default.default,{style:{width:"100vw",height:"100vh",border:"none"},ref:n,...r,head:t}));b.displayName="IFrame";var E=c__default.default.forwardRef(({children:t,style:r},n)=>{let s=core.useObjectRef(n),a=c__default.default.useRef(null),i=c__default.default.useRef(null),m=c__default.default.useRef(null),l=c__default.default.useRef(false),u=c__default.default.useMemo(()=>R(),[]);return c.useEffect(()=>{if(l.current=false,s.current&&!a.current){let e=s.current.shadowRoot??s.current.attachShadow({mode:"open"});e.replaceChildren(),a.current=e;let o=document.createElement("style");o.textContent=u,e.append(o);let f=document.createElement("div");f.className="content-container",e.append(f);let P=document.createElement("div");P.className="overlay-portal",e.append(P),m.current=P;let y=F__default.default.createRoot(f);i.current=y;}return ()=>{l.current=true;let e=i.current;e&&(queueMicrotask(()=>{e.unmount();}),i.current=null),a.current=null,m.current=null;}},[u,s]),c.useEffect(()=>{if(l.current)return;let e=i.current,o=m.current;e&&o&&e.render(jsxRuntime.jsx(core.PortalContainerProvider,{portalContainer:o,children:t}));},[t]),jsxRuntime.jsx("div",{style:r,ref:s})});E.displayName="ShadowRoot";var H=()=>{let{direction:t}=core.useLocale(),{document:r}=w.useFrame();return c.useLayoutEffect(()=>{t==="rtl"?r?.documentElement.setAttribute("dir","rtl"):r?.documentElement.removeAttribute("dir");},[t,r]),null},K=({children:t,locale:r,theme:n,themeStyle:s,disableThemeProvider:a=false,disableFrameProvider:i=false,messages:m})=>{let[l,u]=c.useState(null),e=l?.ownerDocument.body,o=t;return i||(o=jsxRuntime.jsx(core.FrameProvider,{container:e,portalContainer:l,children:o})),a||(o=jsxRuntime.jsx(core.ThemeProvider,{theme:n,style:s,children:o})),jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsxs(core.I18nProvider,{locale:r,messages:m,children:[jsxRuntime.jsx(H,{}),o]}),jsxRuntime.jsx("div",{id:"portal-container",ref:u})]})};exports.IFrame=b;exports.ProvidersContainer=K;exports.ShadowRoot=E;exports.getRootStyles=R;
|
package/client/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useObjectRef,PortalContainerProvider,FrameProvider,ThemeProvider,I18nProvider}from'@baseline-ui/core';import
|
|
1
|
+
import {useObjectRef,PortalContainerProvider,FrameProvider,ThemeProvider,I18nProvider,useLocale}from'@baseline-ui/core';import c,{useEffect,useState,useLayoutEffect}from'react';import F from'react-dom/client';import w,{useFrame}from'react-frame-component';import {jsx,jsxs,Fragment}from'react/jsx-runtime';/**
|
|
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,5 +7,5 @@ import {useObjectRef,PortalContainerProvider,FrameProvider,ThemeProvider,I18nPro
|
|
|
7
7
|
* This notice may not be removed from this file.
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
|
-
function R(){let
|
|
11
|
-
`)}var b=
|
|
10
|
+
function R(){let t=[],r=document;for(let n=0;n<r.styleSheets.length;n++){let s=r.styleSheets[n];try{for(let a of s.cssRules)t.push(a.cssText);}catch{}}return t.join(`
|
|
11
|
+
`)}var b=c.forwardRef(({head:t=jsx("style",{children:R()}),...r},n)=>jsx(w,{style:{width:"100vw",height:"100vh",border:"none"},ref:n,...r,head:t}));b.displayName="IFrame";var E=c.forwardRef(({children:t,style:r},n)=>{let s=useObjectRef(n),a=c.useRef(null),i=c.useRef(null),m=c.useRef(null),l=c.useRef(false),u=c.useMemo(()=>R(),[]);return useEffect(()=>{if(l.current=false,s.current&&!a.current){let e=s.current.shadowRoot??s.current.attachShadow({mode:"open"});e.replaceChildren(),a.current=e;let o=document.createElement("style");o.textContent=u,e.append(o);let f=document.createElement("div");f.className="content-container",e.append(f);let P=document.createElement("div");P.className="overlay-portal",e.append(P),m.current=P;let y=F.createRoot(f);i.current=y;}return ()=>{l.current=true;let e=i.current;e&&(queueMicrotask(()=>{e.unmount();}),i.current=null),a.current=null,m.current=null;}},[u,s]),useEffect(()=>{if(l.current)return;let e=i.current,o=m.current;e&&o&&e.render(jsx(PortalContainerProvider,{portalContainer:o,children:t}));},[t]),jsx("div",{style:r,ref:s})});E.displayName="ShadowRoot";var H=()=>{let{direction:t}=useLocale(),{document:r}=useFrame();return useLayoutEffect(()=>{t==="rtl"?r?.documentElement.setAttribute("dir","rtl"):r?.documentElement.removeAttribute("dir");},[t,r]),null},K=({children:t,locale:r,theme:n,themeStyle:s,disableThemeProvider:a=false,disableFrameProvider:i=false,messages:m})=>{let[l,u]=useState(null),e=l?.ownerDocument.body,o=t;return i||(o=jsx(FrameProvider,{container:e,portalContainer:l,children:o})),a||(o=jsx(ThemeProvider,{theme:n,style:s,children:o})),jsxs(Fragment,{children:[jsxs(I18nProvider,{locale:r,messages:m,children:[jsx(H,{}),o]}),jsx("div",{id:"portal-container",ref:u})]})};export{b as IFrame,K as ProvidersContainer,E as ShadowRoot,R as getRootStyles};
|
package/dist/index.d.mts
CHANGED
|
@@ -101,9 +101,15 @@ declare const expect: playwright_test.Expect<{
|
|
|
101
101
|
}): Promise<{
|
|
102
102
|
message: () => string;
|
|
103
103
|
pass: false;
|
|
104
|
+
shouldNotRetryTest?: undefined;
|
|
104
105
|
} | {
|
|
105
106
|
message: () => string;
|
|
106
107
|
pass: true;
|
|
108
|
+
shouldNotRetryTest?: undefined;
|
|
109
|
+
} | {
|
|
110
|
+
message: () => string;
|
|
111
|
+
pass: false;
|
|
112
|
+
shouldNotRetryTest: boolean;
|
|
107
113
|
}>;
|
|
108
114
|
}>;
|
|
109
115
|
|
|
@@ -337,7 +343,10 @@ type MenuTesterOptions = BaseTesterOptions;
|
|
|
337
343
|
interface MenuLevelTester {
|
|
338
344
|
/** The `role="menu"` element for this level. Only attached while open. */
|
|
339
345
|
readonly menu: Locator;
|
|
340
|
-
/**
|
|
346
|
+
/**
|
|
347
|
+
* A `menuitem`, `menuitemradio` or `menuitemcheckbox` within this level, by
|
|
348
|
+
* accessible name. Selection menus render radio/checkbox items.
|
|
349
|
+
*/
|
|
341
350
|
item: (name: string | RegExp) => Locator;
|
|
342
351
|
/** A `role="group"` section within this level, by accessible name. */
|
|
343
352
|
section: (name: string | RegExp) => Locator;
|
package/dist/index.d.ts
CHANGED
|
@@ -101,9 +101,15 @@ declare const expect: playwright_test.Expect<{
|
|
|
101
101
|
}): Promise<{
|
|
102
102
|
message: () => string;
|
|
103
103
|
pass: false;
|
|
104
|
+
shouldNotRetryTest?: undefined;
|
|
104
105
|
} | {
|
|
105
106
|
message: () => string;
|
|
106
107
|
pass: true;
|
|
108
|
+
shouldNotRetryTest?: undefined;
|
|
109
|
+
} | {
|
|
110
|
+
message: () => string;
|
|
111
|
+
pass: false;
|
|
112
|
+
shouldNotRetryTest: boolean;
|
|
107
113
|
}>;
|
|
108
114
|
}>;
|
|
109
115
|
|
|
@@ -337,7 +343,10 @@ type MenuTesterOptions = BaseTesterOptions;
|
|
|
337
343
|
interface MenuLevelTester {
|
|
338
344
|
/** The `role="menu"` element for this level. Only attached while open. */
|
|
339
345
|
readonly menu: Locator;
|
|
340
|
-
/**
|
|
346
|
+
/**
|
|
347
|
+
* A `menuitem`, `menuitemradio` or `menuitemcheckbox` within this level, by
|
|
348
|
+
* accessible name. Selection menus render radio/checkbox items.
|
|
349
|
+
*/
|
|
341
350
|
item: (name: string | RegExp) => Locator;
|
|
342
351
|
/** A `role="group"` section within this level, by accessible name. */
|
|
343
352
|
section: (name: string | RegExp) => Locator;
|
package/dist/index.js
CHANGED
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
* This notice may not be removed from this file.
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
|
-
var w=["wcag2a","wcag2aa","wcag21a","wcag21aa","best-practice"],L=["landmark-one-main","page-has-heading-one","region","aria-allowed-role","duplicate-id","scrollable-region-focusable"],O=["aria-valid-attr-value","aria-valid-attr","aria-required-attr"];function A(e,
|
|
10
|
+
var w=["wcag2a","wcag2aa","wcag21a","wcag21aa","best-practice"],L=["landmark-one-main","page-has-heading-one","region","aria-allowed-role","duplicate-id","scrollable-region-focusable"],O=["aria-valid-attr-value","aria-valid-attr","aria-required-attr"];function A(e,t={}){let{prefix:o="BaselineUI-",ignoreHashed:n=false}=t,i=Array.isArray(o)?o:[o];function r(u){return u.split(/[-_]/).some(p=>p.length>=5&&/^[a-z\d]+$/.test(p)&&/[a-z]/.test(p)&&/\d/.test(p))}function s(u){return [...u.classList].filter(p=>i.some(m=>p.startsWith(m))&&(!n||!r(p)))}function l(u,p=false){let m=[],h=u.shadowRoot?u.shadowRoot.children:u.children;for(let T of h){let x=p||T.getAttribute("aria-hidden")==="true",B=s(T),b=l(T,x);B.length>0?m.push({classes:B,children:b,hidden:x}):m.push(...b);}return m}function d(u,p="",m=false){let h=[];for(let T=0;T<u.length;T++){let x=u[T],B=T===u.length-1,b=m?"":B?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 ",_=m?"":p+(B?" ":"\u2502 "),$=x.classes.join(", ");h.push(p+b+$+(x.hidden?" [aria-hidden]":""),...d(x.children,_));}return h}let a=s(e),c=l(e);return a.length===0&&c.length===0?"":a.length>0?d([{classes:a,children:c,hidden:false}],"",true).join(`
|
|
11
11
|
`):d(c,"",true).join(`
|
|
12
|
-
`)}var f=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"}},H=async e=>{let
|
|
13
|
-
${f(
|
|
12
|
+
`)}var f=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"}},H=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},z=async e=>{await e.first().evaluate((t,o)=>{if(window.axe)return;let i=document.createElement("script");i.textContent=o,document.head.append(i);},W__default.default.source);},V=["document-title","html-has-lang","html-lang-valid","html-xml-lang-mismatch"],G=(e,t=[])=>{let o=new Set([...L,...t,...e.disableRules??[]]);return {runOnly:{type:"tag",values:[...e.tags??w]},rules:Object.fromEntries([...o].map(n=>[n,{enabled:false}]))}},k=e=>{if(e!==void 0)return typeof e=="string"?[e]:[...e]},q=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 z(e);let i=k(t.include),r=k(t.exclude);return n===1?await e.evaluate(async(s,l)=>{let d=window,a={include:[s,...l.userInclude??[]],exclude:l.userExclude??[]};return await d.axe.run(a,l.runOptions)},{userInclude:i,userExclude:r,runOptions:o}):await e.evaluateAll(async(s,l)=>{let d=window,a={include:[...s,...l.userInclude??[]],exclude:l.userExclude??[]};return await d.axe.run(a,l.runOptions)},{userInclude:i,userExclude:r,runOptions:o})},S="BaselineUI-";function E(e){return e.replaceAll(/\W+/g,"-").replaceAll(/^-|-$/g,"")}function J(e){return Array.isArray(e)?e.length===1&&e[0]===S:e===S}function K(e){return (Array.isArray(e)?e:[e]).join("+")}function X(e,t,o,n,i){let r=t.slice(1).map(s=>E(s));return o&&r.push(E(o)),n!==void 0&&!J(n)&&r.push(E(K(n))),i&&r.push("ignoreHashed"),I__default.default.join(e,`${r.join("--")}.txt`)}var Q=test.expect.extend({toBeWithinDelta(e,t,o){return Math.abs(e-t)<=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){try{return await test.expect.poll(()=>e.callCount).toBe(1),{message:()=>"Expected function to be called once.",pass:!0}}catch(t){return {message:()=>f(t),pass:false}}},async toBeCalledTwice(e){try{return await test.expect.poll(()=>e.callCount).toBe(2),{message:()=>"Expected function to be called twice.",pass:!0}}catch(t){return {message:()=>f(t),pass:false}}},async toBeCalledThrice(e){try{return await test.expect.poll(()=>e.callCount).toBe(3),{message:()=>"Expected function to be called thrice.",pass:!0}}catch(t){return {message:()=>f(t),pass:false}}},async toBeCalledWith(e,...t){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:()=>f(o),pass:false}}},async toBeCalledWithExactly(e,...t){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:()=>f(o),pass:false}}},toBeCalled(e){try{return test.expect(e.called).toBeTruthy(),{message:()=>"Expected function to be called.",pass:!0}}catch(t){return {message:()=>f(t),pass:false}}},async toBeCalledTimes(e,t){try{return await test.expect.poll(()=>e.callCount).toBe(t),{message:()=>`Expected function to be called ${t} times.`,pass:!0}}catch(o){return {message:()=>f(o),pass:false}}},async toBeAccessible(e,t={}){let o=await H(e),n=await o.first().evaluate(c=>{let u=c.ownerDocument?.defaultView;return u!==null&&u!==window.top}).catch(()=>false),i=G(t,n?V:[]),r,s=async()=>{let c=await q(o,t,i),u=t.treatIncompleteAsViolationIds??O,p=[...c.violations,...c.incomplete.filter(m=>u.includes(m.id))];return r={result:c,violations:p},p},l;try{if(t.timeout&&t.timeout>0){let c=test.expect.poll(s,{timeout:t.timeout});await(this.isNot?c.not.toHaveLength(0):c.toHaveLength(0));}else await s();}catch(c){l=c;}if(!r)throw new Error(`toBeAccessible: axe scan never completed.
|
|
13
|
+
${f(l)}`);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(c=>{let u=c.nodes.map(p=>Array.isArray(p.target)?p.target.join(" "):String(p.target)).join(`
|
|
14
14
|
`);return ` \u2022 [${c.id}] ${c.help}
|
|
15
15
|
${u}
|
|
16
16
|
${c.helpUrl??""}`}).join(`
|
|
17
|
-
`),
|
|
18
|
-
${d}`,pass:false}},async toMatchClassTree(e,o
|
|
19
|
-
`,
|
|
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=S,ignoreHashed:i=false}=o,r=await e.evaluate(A,{prefix:n,ignoreHashed:i});if(!r){let s=Array.isArray(n)?n.map(l=>`${l}*`).join(" / "):`${n}*`;return {message:()=>`No ${s} classes found in the DOM tree.`,pass:false}}try{let s=`${r}
|
|
19
|
+
`,l=test.test.info();if(l.config.ignoreSnapshots)return {message:()=>"Class tree snapshot check skipped.",pass:!0};let a=X(l.project.snapshotDir,l.titlePath,t,n,i),c=l.config.updateSnapshots;await promises.mkdir(I__default.default.dirname(a),{recursive:!0});let u;try{u=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:!0};if(m==="ENOENT"&&c!=="none")return await promises.writeFile(a,s),c==="missing"?{message:()=>`A class tree snapshot doesn't exist at ${a}, writing actual.`,pass:!1,shouldNotRetryTest:!0}:{message:()=>"Class tree snapshot created.",pass:!0};throw p}if(u!==s){if(c==="all"||c==="changed")return await promises.writeFile(a,s),{message:()=>"Class tree snapshot updated.",pass:!0};try{test.expect(s).toBe(u);}catch(p){return {message:()=>`Class tree snapshot mismatch at ${a}
|
|
20
20
|
|
|
21
|
-
${p.message}`,pass:!1}}}return {message:()=>"Class tree matched snapshot.",pass:!0}}catch(
|
|
21
|
+
${p.message}`,pass:!1}}}return {message:()=>"Class tree matched snapshot.",pass:!0}}catch(s){return {message:()=>f(s),pass:false}}}});function g(e){let t=async()=>{if(await e.getAttribute("aria-expanded")!==null)return e;let i=e.locator("[aria-expanded]").first();return await i.count()>0?i:e},o=async()=>await(await t()).getAttribute("aria-expanded")==="true";return {isOpen:o,open:async(i="click")=>{if(await o())return;if(i==="click"){await e.click();return}await(await t()).press(i);}}}function F({frame:e,trigger:t,toggleButton:o}){let n=t??e.getByRole("combobox",{includeHidden:true}),i=o??e.getByTestId("combobox-toggle-button"),r=a=>e.getByRole("option",{name:a,exact:typeof a=="string"}),{isOpen:s,open:l}=g(n),d=async(a="click")=>{if(!await s()){if(a==="click"){await i.click();return}await l(a);}};return {input:n,toggleButton:i,listbox:e.getByRole("listbox"),option:r,isOpen:s,open:d,async selectOption(a){await d(),await r(a).click();},async filter(a){await n.fill(a);}}}function v({frame:e,root:t}){let o=t??e.locator(".BaselineUI-Editor"),n=o.getByTestId("editor-font-color-button"),i=o.getByTestId("editor-background-color-button"),r=o.getByTestId("editor-add-link-button"),s=o.getByTestId("editor-help-button"),l=o.getByTestId("editor-mention-button"),d=o.getByRole("combobox"),a=e.getByTestId("editor-mention-listbox"),c=e.getByTestId("editor-help-dialog-close-button"),u=e.getByRole("dialog").filter({has:c}),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:i,openFontColorPicker:g(n).open,openBackgroundColorPicker:g(i).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:l,helpButton:s,helpDialog:u,helpDialogCloseButton:c,async openHelpDialog(){await u.isVisible()||await s.click();},mentionInput:d,mentionListbox:a,mentionOption:m=>a.getByRole("option",{name:m}),async startMention(){await d.isVisible()||await l.click();}}}function D({frame:e,trigger:t,variant:o="dropzone"}){let n=e.getByTestId("file-uploader"),i=n.locator('input[type="file"]'),r=t??(o==="button"?n.getByRole("button"):e.getByTestId("file-uploader-dropzone"));return {root:n,button:r,input:i,dropzone:e.getByTestId("file-uploader-dropzone"),errorMessage:n.getByRole("alert"),async upload(s){await i.setInputFiles(s);}}}function P(e,t,o){let n=s=>t.getByRole("menuitem",{name:s}).or(t.getByRole("menuitemradio",{name:s})).or(t.getByRole("menuitemcheckbox",{name:s})),i=s=>t.getByRole("group",{name:s}),{isOpen:r}=g(o);return {menu:t,item:n,section:i,isOpen:r,submenu(s){let l=n(s),d=e.getByRole("menu",{name:s}),a=P(e,d,l),{open:c}=g(l);return {...a,trigger:l,open:async(u="Enter")=>c(u)}}}}function U({frame:e,trigger:t}){let o=t??e.getByTestId("menu-trigger"),n=e.getByRole("menu").first(),i=P(e,n,o),{open:r,isOpen:s}=g(o);return {...i,trigger:o,open:r,isOpen:s,async close(){await s()&&await o.press("Escape");},async selectItem(l){await r(),await i.item(l).click();}}}var M=(e=>{let o=Y(e);return e.multiple&&(o=Z(o,e.frame)),e.autocomplete&&(o=ee(o,e.frame)),o});function Y({frame:e,trigger:t}){let o=t??e.getByTestId("select-button"),n=r=>e.getByRole("option",{name:r,exact:typeof r=="string"}),{open:i}=g(o);return {trigger:o,listbox:e.getByRole("listbox"),option:n,open:i,async selectOption(r){await i(),await n(r).click();}}}function Z(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 ee(e,t){let o=t.getByRole("searchbox");return {...e,searchbox:o,async filter(n){await o.fill(n);}}}function te(e){return {select:((t={})=>M({frame:e,...t})),comboBox:(t={})=>F({frame:e,...t}),editor:(t={})=>v({frame:e,...t}),fileUploader:(t={})=>D({frame:e,...t}),menu:(t={})=>U({frame:e,...t})}}exports.DEFAULT_CRITICAL_INCOMPLETE_RULES=O;exports.DEFAULT_DISABLED_RULES=L;exports.DEFAULT_WCAG_TAGS=w;exports.expect=Q;exports.testers=te;
|
package/dist/index.mjs
CHANGED
|
@@ -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 w=["wcag2a","wcag2aa","wcag21a","wcag21aa","best-practice"],L=["landmark-one-main","page-has-heading-one","region","aria-allowed-role","duplicate-id","scrollable-region-focusable"],O=["aria-valid-attr-value","aria-valid-attr","aria-required-attr"];function A(e,
|
|
10
|
+
var w=["wcag2a","wcag2aa","wcag21a","wcag21aa","best-practice"],L=["landmark-one-main","page-has-heading-one","region","aria-allowed-role","duplicate-id","scrollable-region-focusable"],O=["aria-valid-attr-value","aria-valid-attr","aria-required-attr"];function A(e,t={}){let{prefix:o="BaselineUI-",ignoreHashed:n=false}=t,i=Array.isArray(o)?o:[o];function r(u){return u.split(/[-_]/).some(p=>p.length>=5&&/^[a-z\d]+$/.test(p)&&/[a-z]/.test(p)&&/\d/.test(p))}function s(u){return [...u.classList].filter(p=>i.some(m=>p.startsWith(m))&&(!n||!r(p)))}function l(u,p=false){let m=[],h=u.shadowRoot?u.shadowRoot.children:u.children;for(let T of h){let x=p||T.getAttribute("aria-hidden")==="true",B=s(T),b=l(T,x);B.length>0?m.push({classes:B,children:b,hidden:x}):m.push(...b);}return m}function d(u,p="",m=false){let h=[];for(let T=0;T<u.length;T++){let x=u[T],B=T===u.length-1,b=m?"":B?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 ",_=m?"":p+(B?" ":"\u2502 "),$=x.classes.join(", ");h.push(p+b+$+(x.hidden?" [aria-hidden]":""),...d(x.children,_));}return h}let a=s(e),c=l(e);return a.length===0&&c.length===0?"":a.length>0?d([{classes:a,children:c,hidden:false}],"",true).join(`
|
|
11
11
|
`):d(c,"",true).join(`
|
|
12
|
-
`)}var f=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"}},H=async e=>{let
|
|
13
|
-
${f(
|
|
12
|
+
`)}var f=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"}},H=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},z=async e=>{await e.first().evaluate((t,o)=>{if(window.axe)return;let i=document.createElement("script");i.textContent=o,document.head.append(i);},W.source);},V=["document-title","html-has-lang","html-lang-valid","html-xml-lang-mismatch"],G=(e,t=[])=>{let o=new Set([...L,...t,...e.disableRules??[]]);return {runOnly:{type:"tag",values:[...e.tags??w]},rules:Object.fromEntries([...o].map(n=>[n,{enabled:false}]))}},k=e=>{if(e!==void 0)return typeof e=="string"?[e]:[...e]},q=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 z(e);let i=k(t.include),r=k(t.exclude);return n===1?await e.evaluate(async(s,l)=>{let d=window,a={include:[s,...l.userInclude??[]],exclude:l.userExclude??[]};return await d.axe.run(a,l.runOptions)},{userInclude:i,userExclude:r,runOptions:o}):await e.evaluateAll(async(s,l)=>{let d=window,a={include:[...s,...l.userInclude??[]],exclude:l.userExclude??[]};return await d.axe.run(a,l.runOptions)},{userInclude:i,userExclude:r,runOptions:o})},S="BaselineUI-";function E(e){return e.replaceAll(/\W+/g,"-").replaceAll(/^-|-$/g,"")}function J(e){return Array.isArray(e)?e.length===1&&e[0]===S:e===S}function K(e){return (Array.isArray(e)?e:[e]).join("+")}function X(e,t,o,n,i){let r=t.slice(1).map(s=>E(s));return o&&r.push(E(o)),n!==void 0&&!J(n)&&r.push(E(K(n))),i&&r.push("ignoreHashed"),I.join(e,`${r.join("--")}.txt`)}var Q=expect.extend({toBeWithinDelta(e,t,o){return Math.abs(e-t)<=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){try{return await expect.poll(()=>e.callCount).toBe(1),{message:()=>"Expected function to be called once.",pass:!0}}catch(t){return {message:()=>f(t),pass:false}}},async toBeCalledTwice(e){try{return await expect.poll(()=>e.callCount).toBe(2),{message:()=>"Expected function to be called twice.",pass:!0}}catch(t){return {message:()=>f(t),pass:false}}},async toBeCalledThrice(e){try{return await expect.poll(()=>e.callCount).toBe(3),{message:()=>"Expected function to be called thrice.",pass:!0}}catch(t){return {message:()=>f(t),pass:false}}},async toBeCalledWith(e,...t){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:()=>f(o),pass:false}}},async toBeCalledWithExactly(e,...t){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:()=>f(o),pass:false}}},toBeCalled(e){try{return expect(e.called).toBeTruthy(),{message:()=>"Expected function to be called.",pass:!0}}catch(t){return {message:()=>f(t),pass:false}}},async toBeCalledTimes(e,t){try{return await expect.poll(()=>e.callCount).toBe(t),{message:()=>`Expected function to be called ${t} times.`,pass:!0}}catch(o){return {message:()=>f(o),pass:false}}},async toBeAccessible(e,t={}){let o=await H(e),n=await o.first().evaluate(c=>{let u=c.ownerDocument?.defaultView;return u!==null&&u!==window.top}).catch(()=>false),i=G(t,n?V:[]),r,s=async()=>{let c=await q(o,t,i),u=t.treatIncompleteAsViolationIds??O,p=[...c.violations,...c.incomplete.filter(m=>u.includes(m.id))];return r={result:c,violations:p},p},l;try{if(t.timeout&&t.timeout>0){let c=expect.poll(s,{timeout:t.timeout});await(this.isNot?c.not.toHaveLength(0):c.toHaveLength(0));}else await s();}catch(c){l=c;}if(!r)throw new Error(`toBeAccessible: axe scan never completed.
|
|
13
|
+
${f(l)}`);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(c=>{let u=c.nodes.map(p=>Array.isArray(p.target)?p.target.join(" "):String(p.target)).join(`
|
|
14
14
|
`);return ` \u2022 [${c.id}] ${c.help}
|
|
15
15
|
${u}
|
|
16
16
|
${c.helpUrl??""}`}).join(`
|
|
17
|
-
`),
|
|
18
|
-
${d}`,pass:false}},async toMatchClassTree(e,o
|
|
19
|
-
`,
|
|
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=S,ignoreHashed:i=false}=o,r=await e.evaluate(A,{prefix:n,ignoreHashed:i});if(!r){let s=Array.isArray(n)?n.map(l=>`${l}*`).join(" / "):`${n}*`;return {message:()=>`No ${s} classes found in the DOM tree.`,pass:false}}try{let s=`${r}
|
|
19
|
+
`,l=test.info();if(l.config.ignoreSnapshots)return {message:()=>"Class tree snapshot check skipped.",pass:!0};let a=X(l.project.snapshotDir,l.titlePath,t,n,i),c=l.config.updateSnapshots;await mkdir(I.dirname(a),{recursive:!0});let u;try{u=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:!0};if(m==="ENOENT"&&c!=="none")return await writeFile(a,s),c==="missing"?{message:()=>`A class tree snapshot doesn't exist at ${a}, writing actual.`,pass:!1,shouldNotRetryTest:!0}:{message:()=>"Class tree snapshot created.",pass:!0};throw p}if(u!==s){if(c==="all"||c==="changed")return await writeFile(a,s),{message:()=>"Class tree snapshot updated.",pass:!0};try{expect(s).toBe(u);}catch(p){return {message:()=>`Class tree snapshot mismatch at ${a}
|
|
20
20
|
|
|
21
|
-
${p.message}`,pass:!1}}}return {message:()=>"Class tree matched snapshot.",pass:!0}}catch(
|
|
21
|
+
${p.message}`,pass:!1}}}return {message:()=>"Class tree matched snapshot.",pass:!0}}catch(s){return {message:()=>f(s),pass:false}}}});function g(e){let t=async()=>{if(await e.getAttribute("aria-expanded")!==null)return e;let i=e.locator("[aria-expanded]").first();return await i.count()>0?i:e},o=async()=>await(await t()).getAttribute("aria-expanded")==="true";return {isOpen:o,open:async(i="click")=>{if(await o())return;if(i==="click"){await e.click();return}await(await t()).press(i);}}}function F({frame:e,trigger:t,toggleButton:o}){let n=t??e.getByRole("combobox",{includeHidden:true}),i=o??e.getByTestId("combobox-toggle-button"),r=a=>e.getByRole("option",{name:a,exact:typeof a=="string"}),{isOpen:s,open:l}=g(n),d=async(a="click")=>{if(!await s()){if(a==="click"){await i.click();return}await l(a);}};return {input:n,toggleButton:i,listbox:e.getByRole("listbox"),option:r,isOpen:s,open:d,async selectOption(a){await d(),await r(a).click();},async filter(a){await n.fill(a);}}}function v({frame:e,root:t}){let o=t??e.locator(".BaselineUI-Editor"),n=o.getByTestId("editor-font-color-button"),i=o.getByTestId("editor-background-color-button"),r=o.getByTestId("editor-add-link-button"),s=o.getByTestId("editor-help-button"),l=o.getByTestId("editor-mention-button"),d=o.getByRole("combobox"),a=e.getByTestId("editor-mention-listbox"),c=e.getByTestId("editor-help-dialog-close-button"),u=e.getByRole("dialog").filter({has:c}),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:i,openFontColorPicker:g(n).open,openBackgroundColorPicker:g(i).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:l,helpButton:s,helpDialog:u,helpDialogCloseButton:c,async openHelpDialog(){await u.isVisible()||await s.click();},mentionInput:d,mentionListbox:a,mentionOption:m=>a.getByRole("option",{name:m}),async startMention(){await d.isVisible()||await l.click();}}}function D({frame:e,trigger:t,variant:o="dropzone"}){let n=e.getByTestId("file-uploader"),i=n.locator('input[type="file"]'),r=t??(o==="button"?n.getByRole("button"):e.getByTestId("file-uploader-dropzone"));return {root:n,button:r,input:i,dropzone:e.getByTestId("file-uploader-dropzone"),errorMessage:n.getByRole("alert"),async upload(s){await i.setInputFiles(s);}}}function P(e,t,o){let n=s=>t.getByRole("menuitem",{name:s}).or(t.getByRole("menuitemradio",{name:s})).or(t.getByRole("menuitemcheckbox",{name:s})),i=s=>t.getByRole("group",{name:s}),{isOpen:r}=g(o);return {menu:t,item:n,section:i,isOpen:r,submenu(s){let l=n(s),d=e.getByRole("menu",{name:s}),a=P(e,d,l),{open:c}=g(l);return {...a,trigger:l,open:async(u="Enter")=>c(u)}}}}function U({frame:e,trigger:t}){let o=t??e.getByTestId("menu-trigger"),n=e.getByRole("menu").first(),i=P(e,n,o),{open:r,isOpen:s}=g(o);return {...i,trigger:o,open:r,isOpen:s,async close(){await s()&&await o.press("Escape");},async selectItem(l){await r(),await i.item(l).click();}}}var M=(e=>{let o=Y(e);return e.multiple&&(o=Z(o,e.frame)),e.autocomplete&&(o=ee(o,e.frame)),o});function Y({frame:e,trigger:t}){let o=t??e.getByTestId("select-button"),n=r=>e.getByRole("option",{name:r,exact:typeof r=="string"}),{open:i}=g(o);return {trigger:o,listbox:e.getByRole("listbox"),option:n,open:i,async selectOption(r){await i(),await n(r).click();}}}function Z(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 ee(e,t){let o=t.getByRole("searchbox");return {...e,searchbox:o,async filter(n){await o.fill(n);}}}function te(e){return {select:((t={})=>M({frame:e,...t})),comboBox:(t={})=>F({frame:e,...t}),editor:(t={})=>v({frame:e,...t}),fileUploader:(t={})=>D({frame:e,...t}),menu:(t={})=>U({frame:e,...t})}}export{O as DEFAULT_CRITICAL_INCOMPLETE_RULES,L as DEFAULT_DISABLED_RULES,w as DEFAULT_WCAG_TAGS,Q as expect,te 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.4.0",
|
|
4
4
|
"description": "A collection of test utilities for Baseline UI",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@neondatabase/serverless": "^1.0.2",
|
|
39
39
|
"@playwright/test": "^1.58.2",
|
|
40
|
-
"react": "^18.0.0",
|
|
41
|
-
"react-dom": "^18.0.0",
|
|
40
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
41
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
42
42
|
"sinon": ">=19.0.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependenciesMeta": {
|
|
@@ -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.4.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:22c72a5b-c951-4575-adf7-e61a58a4ae3c",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-
|
|
8
|
+
"timestamp": "2026-10-15T12:21:09.558Z",
|
|
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.4.0",
|
|
27
|
+
"purl": "pkg:npm/@baseline-ui/test-utils@3.4.0",
|
|
28
|
+
"bom-ref": "pkg:npm/@baseline-ui/test-utils@3.4.0",
|
|
29
29
|
"licenses": [
|
|
30
30
|
{
|
|
31
31
|
"license": {
|
|
@@ -59,18 +59,18 @@
|
|
|
59
59
|
{
|
|
60
60
|
"type": "library",
|
|
61
61
|
"name": "test",
|
|
62
|
-
"version": "1.
|
|
63
|
-
"purl": "pkg:npm/%40playwright/test@1.
|
|
64
|
-
"bom-ref": "pkg:npm/%40playwright/test@1.
|
|
62
|
+
"version": "1.62.1",
|
|
63
|
+
"purl": "pkg:npm/%40playwright/test@1.62.1",
|
|
64
|
+
"bom-ref": "pkg:npm/%40playwright/test@1.62.1",
|
|
65
65
|
"group": "@playwright",
|
|
66
66
|
"externalReferences": [
|
|
67
67
|
{
|
|
68
68
|
"type": "distribution",
|
|
69
|
-
"url": "https://registry.npmjs.org/@playwright/test/-/test-1.
|
|
69
|
+
"url": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz",
|
|
70
70
|
"hashes": [
|
|
71
71
|
{
|
|
72
72
|
"alg": "SHA-512",
|
|
73
|
-
"content": "
|
|
73
|
+
"content": "0d371473caa28be7291efb4ec2080cb4144c8ca6475d859dc3cb32458bb6bedceeab85b1a61aaae0d7c2b3966de382fa980f2b7c37e3f8f1e7c4573f15e2ba99"
|
|
74
74
|
}
|
|
75
75
|
]
|
|
76
76
|
}
|
|
@@ -79,17 +79,17 @@
|
|
|
79
79
|
{
|
|
80
80
|
"type": "library",
|
|
81
81
|
"name": "playwright",
|
|
82
|
-
"version": "1.
|
|
83
|
-
"purl": "pkg:npm/playwright@1.
|
|
84
|
-
"bom-ref": "pkg:npm/playwright@1.
|
|
82
|
+
"version": "1.62.1",
|
|
83
|
+
"purl": "pkg:npm/playwright@1.62.1",
|
|
84
|
+
"bom-ref": "pkg:npm/playwright@1.62.1",
|
|
85
85
|
"externalReferences": [
|
|
86
86
|
{
|
|
87
87
|
"type": "distribution",
|
|
88
|
-
"url": "https://registry.npmjs.org/playwright/-/playwright-1.
|
|
88
|
+
"url": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz",
|
|
89
89
|
"hashes": [
|
|
90
90
|
{
|
|
91
91
|
"alg": "SHA-512",
|
|
92
|
-
"content": "
|
|
92
|
+
"content": "d0cf8bdcb003f3f9e6e79e0b3a56bd032c748f4b66159d0505ca10ec5d55b951e933f5e9882f11703cc1401f16e7e840f0bdb64c7c442f3785fb659c533bdc2e"
|
|
93
93
|
}
|
|
94
94
|
]
|
|
95
95
|
}
|
|
@@ -98,17 +98,17 @@
|
|
|
98
98
|
{
|
|
99
99
|
"type": "library",
|
|
100
100
|
"name": "playwright-core",
|
|
101
|
-
"version": "1.
|
|
102
|
-
"purl": "pkg:npm/playwright-core@1.
|
|
103
|
-
"bom-ref": "pkg:npm/playwright-core@1.
|
|
101
|
+
"version": "1.62.1",
|
|
102
|
+
"purl": "pkg:npm/playwright-core@1.62.1",
|
|
103
|
+
"bom-ref": "pkg:npm/playwright-core@1.62.1",
|
|
104
104
|
"externalReferences": [
|
|
105
105
|
{
|
|
106
106
|
"type": "distribution",
|
|
107
|
-
"url": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.
|
|
107
|
+
"url": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz",
|
|
108
108
|
"hashes": [
|
|
109
109
|
{
|
|
110
110
|
"alg": "SHA-512",
|
|
111
|
-
"content": "
|
|
111
|
+
"content": "c0f612c0404963d187ada2125eacaab71d276b42e93b75c45fb8cd0e19ed6dec7bb73512ee42e766c3a516bb85241e078bfae10cc8d71aa1dec0367af2761957"
|
|
112
112
|
}
|
|
113
113
|
]
|
|
114
114
|
}
|
|
@@ -155,17 +155,17 @@
|
|
|
155
155
|
{
|
|
156
156
|
"type": "library",
|
|
157
157
|
"name": "react",
|
|
158
|
-
"version": "
|
|
159
|
-
"purl": "pkg:npm/react@
|
|
160
|
-
"bom-ref": "pkg:npm/react@
|
|
158
|
+
"version": "19.2.8",
|
|
159
|
+
"purl": "pkg:npm/react@19.2.8",
|
|
160
|
+
"bom-ref": "pkg:npm/react@19.2.8",
|
|
161
161
|
"externalReferences": [
|
|
162
162
|
{
|
|
163
163
|
"type": "distribution",
|
|
164
|
-
"url": "https://registry.npmjs.org/react/-/react-
|
|
164
|
+
"url": "https://registry.npmjs.org/react/-/react-19.2.8.tgz",
|
|
165
165
|
"hashes": [
|
|
166
166
|
{
|
|
167
167
|
"alg": "SHA-512",
|
|
168
|
-
"content": "
|
|
168
|
+
"content": "3d66980352ffabdbb6bbbc58422f98dcbdd87e789eed7c8b79a248095d4c183e8ba6bb01c5c02a1a3632af4798de9f9076c03ec7f22b92de1089ff03eb7f926f"
|
|
169
169
|
}
|
|
170
170
|
]
|
|
171
171
|
}
|
|
@@ -173,18 +173,18 @@
|
|
|
173
173
|
},
|
|
174
174
|
{
|
|
175
175
|
"type": "library",
|
|
176
|
-
"name": "
|
|
177
|
-
"version": "
|
|
178
|
-
"purl": "pkg:npm/
|
|
179
|
-
"bom-ref": "pkg:npm/
|
|
176
|
+
"name": "react-dom",
|
|
177
|
+
"version": "19.2.8",
|
|
178
|
+
"purl": "pkg:npm/react-dom@19.2.8",
|
|
179
|
+
"bom-ref": "pkg:npm/react-dom@19.2.8",
|
|
180
180
|
"externalReferences": [
|
|
181
181
|
{
|
|
182
182
|
"type": "distribution",
|
|
183
|
-
"url": "https://registry.npmjs.org/
|
|
183
|
+
"url": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz",
|
|
184
184
|
"hashes": [
|
|
185
185
|
{
|
|
186
186
|
"alg": "SHA-512",
|
|
187
|
-
"content": "
|
|
187
|
+
"content": "ad5a6b8a67c6046dc347e4ead08406d834f93f12ad8755881839a3e723e6973af86017bbbb213e0eee2856a4c35d948718619746d4c910649278fc9008c06095"
|
|
188
188
|
}
|
|
189
189
|
]
|
|
190
190
|
}
|
|
@@ -192,18 +192,18 @@
|
|
|
192
192
|
},
|
|
193
193
|
{
|
|
194
194
|
"type": "library",
|
|
195
|
-
"name": "
|
|
196
|
-
"version": "
|
|
197
|
-
"purl": "pkg:npm/
|
|
198
|
-
"bom-ref": "pkg:npm/
|
|
195
|
+
"name": "scheduler",
|
|
196
|
+
"version": "0.27.0",
|
|
197
|
+
"purl": "pkg:npm/scheduler@0.27.0",
|
|
198
|
+
"bom-ref": "pkg:npm/scheduler@0.27.0",
|
|
199
199
|
"externalReferences": [
|
|
200
200
|
{
|
|
201
201
|
"type": "distribution",
|
|
202
|
-
"url": "https://registry.npmjs.org/
|
|
202
|
+
"url": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
|
203
203
|
"hashes": [
|
|
204
204
|
{
|
|
205
205
|
"alg": "SHA-512",
|
|
206
|
-
"content": "
|
|
206
|
+
"content": "78dbfe5ab55b2aed5fdef6d8253ff1b62179b320391cf20cb5ff48818fe72a0d2c5aacc0504bea63fc66ece71973fa9a7cbc7f88ef4580e99e480a78bf9b62fd"
|
|
207
207
|
}
|
|
208
208
|
]
|
|
209
209
|
}
|
|
@@ -211,18 +211,18 @@
|
|
|
211
211
|
},
|
|
212
212
|
{
|
|
213
213
|
"type": "library",
|
|
214
|
-
"name": "react-
|
|
215
|
-
"version": "
|
|
216
|
-
"purl": "pkg:npm/react-
|
|
217
|
-
"bom-ref": "pkg:npm/react-
|
|
214
|
+
"name": "react-frame-component",
|
|
215
|
+
"version": "5.3.2",
|
|
216
|
+
"purl": "pkg:npm/react-frame-component@5.3.2",
|
|
217
|
+
"bom-ref": "pkg:npm/react-frame-component@5.3.2",
|
|
218
218
|
"externalReferences": [
|
|
219
219
|
{
|
|
220
220
|
"type": "distribution",
|
|
221
|
-
"url": "https://registry.npmjs.org/react-
|
|
221
|
+
"url": "https://registry.npmjs.org/react-frame-component/-/react-frame-component-5.3.2.tgz",
|
|
222
222
|
"hashes": [
|
|
223
223
|
{
|
|
224
224
|
"alg": "SHA-512",
|
|
225
|
-
"content": "
|
|
225
|
+
"content": "71ed3ff710279e42c363acf19d37a03f7623721c39cfd6846b3d2a28419e72b7678779c09d0e647a13bce2c35e2e3df040f07964cd0ea155500d08a585a13fe5"
|
|
226
226
|
}
|
|
227
227
|
]
|
|
228
228
|
}
|
|
@@ -230,18 +230,18 @@
|
|
|
230
230
|
},
|
|
231
231
|
{
|
|
232
232
|
"type": "library",
|
|
233
|
-
"name": "
|
|
234
|
-
"version": "
|
|
235
|
-
"purl": "pkg:npm/
|
|
236
|
-
"bom-ref": "pkg:npm/
|
|
233
|
+
"name": "prop-types",
|
|
234
|
+
"version": "15.8.1",
|
|
235
|
+
"purl": "pkg:npm/prop-types@15.8.1",
|
|
236
|
+
"bom-ref": "pkg:npm/prop-types@15.8.1",
|
|
237
237
|
"externalReferences": [
|
|
238
238
|
{
|
|
239
239
|
"type": "distribution",
|
|
240
|
-
"url": "https://registry.npmjs.org/
|
|
240
|
+
"url": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
|
241
241
|
"hashes": [
|
|
242
242
|
{
|
|
243
243
|
"alg": "SHA-512",
|
|
244
|
-
"content": "
|
|
244
|
+
"content": "a23f3b0a064809dba5528868815011ec08e50b4df6ed4e1e9782fa780bcea827ae74c0d553435384d695f9bf437f87578123f58173139cf7617deff6a831f972"
|
|
245
245
|
}
|
|
246
246
|
]
|
|
247
247
|
}
|
|
@@ -249,18 +249,18 @@
|
|
|
249
249
|
},
|
|
250
250
|
{
|
|
251
251
|
"type": "library",
|
|
252
|
-
"name": "
|
|
253
|
-
"version": "
|
|
254
|
-
"purl": "pkg:npm/
|
|
255
|
-
"bom-ref": "pkg:npm/
|
|
252
|
+
"name": "loose-envify",
|
|
253
|
+
"version": "1.4.0",
|
|
254
|
+
"purl": "pkg:npm/loose-envify@1.4.0",
|
|
255
|
+
"bom-ref": "pkg:npm/loose-envify@1.4.0",
|
|
256
256
|
"externalReferences": [
|
|
257
257
|
{
|
|
258
258
|
"type": "distribution",
|
|
259
|
-
"url": "https://registry.npmjs.org/
|
|
259
|
+
"url": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
|
260
260
|
"hashes": [
|
|
261
261
|
{
|
|
262
262
|
"alg": "SHA-512",
|
|
263
|
-
"content": "
|
|
263
|
+
"content": "972bb13c6aff59f86b95e9b608bfd472751cd7372a280226043cee918ed8e45ff242235d928ebe7d12debe5c351e03324b0edfeb5d54218e34f04b71452a0add"
|
|
264
264
|
}
|
|
265
265
|
]
|
|
266
266
|
}
|
|
@@ -268,18 +268,18 @@
|
|
|
268
268
|
},
|
|
269
269
|
{
|
|
270
270
|
"type": "library",
|
|
271
|
-
"name": "
|
|
272
|
-
"version": "
|
|
273
|
-
"purl": "pkg:npm/
|
|
274
|
-
"bom-ref": "pkg:npm/
|
|
271
|
+
"name": "js-tokens",
|
|
272
|
+
"version": "4.0.0",
|
|
273
|
+
"purl": "pkg:npm/js-tokens@4.0.0",
|
|
274
|
+
"bom-ref": "pkg:npm/js-tokens@4.0.0",
|
|
275
275
|
"externalReferences": [
|
|
276
276
|
{
|
|
277
277
|
"type": "distribution",
|
|
278
|
-
"url": "https://registry.npmjs.org/
|
|
278
|
+
"url": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
|
279
279
|
"hashes": [
|
|
280
280
|
{
|
|
281
281
|
"alg": "SHA-512",
|
|
282
|
-
"content": "
|
|
282
|
+
"content": "45d2547e5704ddc5332a232a420b02bb4e853eef5474824ed1b7986cf84737893a6a9809b627dca02b53f5b7313a9601b690f690233a49bce0e026aeb16fcf29"
|
|
283
283
|
}
|
|
284
284
|
]
|
|
285
285
|
}
|
|
@@ -500,13 +500,13 @@
|
|
|
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.4.0",
|
|
504
504
|
"dependsOn": [
|
|
505
505
|
"pkg:npm/%40neondatabase/serverless@1.1.0",
|
|
506
|
-
"pkg:npm/%40playwright/test@1.
|
|
506
|
+
"pkg:npm/%40playwright/test@1.62.1",
|
|
507
507
|
"pkg:npm/axe-core@4.11.4",
|
|
508
|
-
"pkg:npm/react@
|
|
509
|
-
"pkg:npm/react-dom@
|
|
508
|
+
"pkg:npm/react@19.2.8",
|
|
509
|
+
"pkg:npm/react-dom@19.2.8",
|
|
510
510
|
"pkg:npm/react-frame-component@5.3.2",
|
|
511
511
|
"pkg:npm/sinon@21.0.3"
|
|
512
512
|
]
|
|
@@ -516,20 +516,20 @@
|
|
|
516
516
|
"dependsOn": []
|
|
517
517
|
},
|
|
518
518
|
{
|
|
519
|
-
"ref": "pkg:npm/%40playwright/test@1.
|
|
519
|
+
"ref": "pkg:npm/%40playwright/test@1.62.1",
|
|
520
520
|
"dependsOn": [
|
|
521
|
-
"pkg:npm/playwright@1.
|
|
521
|
+
"pkg:npm/playwright@1.62.1"
|
|
522
522
|
]
|
|
523
523
|
},
|
|
524
524
|
{
|
|
525
|
-
"ref": "pkg:npm/playwright@1.
|
|
525
|
+
"ref": "pkg:npm/playwright@1.62.1",
|
|
526
526
|
"dependsOn": [
|
|
527
|
-
"pkg:npm/playwright-core@1.
|
|
527
|
+
"pkg:npm/playwright-core@1.62.1",
|
|
528
528
|
"pkg:npm/fsevents@2.3.2"
|
|
529
529
|
]
|
|
530
530
|
},
|
|
531
531
|
{
|
|
532
|
-
"ref": "pkg:npm/playwright-core@1.
|
|
532
|
+
"ref": "pkg:npm/playwright-core@1.62.1",
|
|
533
533
|
"dependsOn": []
|
|
534
534
|
},
|
|
535
535
|
{
|
|
@@ -541,29 +541,17 @@
|
|
|
541
541
|
"dependsOn": []
|
|
542
542
|
},
|
|
543
543
|
{
|
|
544
|
-
"ref": "pkg:npm/react@
|
|
545
|
-
"dependsOn": [
|
|
546
|
-
"pkg:npm/loose-envify@1.4.0"
|
|
547
|
-
]
|
|
548
|
-
},
|
|
549
|
-
{
|
|
550
|
-
"ref": "pkg:npm/loose-envify@1.4.0",
|
|
551
|
-
"dependsOn": [
|
|
552
|
-
"pkg:npm/js-tokens@4.0.0"
|
|
553
|
-
]
|
|
554
|
-
},
|
|
555
|
-
{
|
|
556
|
-
"ref": "pkg:npm/js-tokens@4.0.0",
|
|
544
|
+
"ref": "pkg:npm/react@19.2.8",
|
|
557
545
|
"dependsOn": []
|
|
558
546
|
},
|
|
559
547
|
{
|
|
560
|
-
"ref": "pkg:npm/react-dom@
|
|
548
|
+
"ref": "pkg:npm/react-dom@19.2.8",
|
|
561
549
|
"dependsOn": [
|
|
562
|
-
"pkg:npm/scheduler@0.
|
|
550
|
+
"pkg:npm/scheduler@0.27.0"
|
|
563
551
|
]
|
|
564
552
|
},
|
|
565
553
|
{
|
|
566
|
-
"ref": "pkg:npm/scheduler@0.
|
|
554
|
+
"ref": "pkg:npm/scheduler@0.27.0",
|
|
567
555
|
"dependsOn": []
|
|
568
556
|
},
|
|
569
557
|
{
|
|
@@ -575,10 +563,21 @@
|
|
|
575
563
|
{
|
|
576
564
|
"ref": "pkg:npm/prop-types@15.8.1",
|
|
577
565
|
"dependsOn": [
|
|
566
|
+
"pkg:npm/loose-envify@1.4.0",
|
|
578
567
|
"pkg:npm/object-assign@4.1.1",
|
|
579
568
|
"pkg:npm/react-is@16.13.1"
|
|
580
569
|
]
|
|
581
570
|
},
|
|
571
|
+
{
|
|
572
|
+
"ref": "pkg:npm/loose-envify@1.4.0",
|
|
573
|
+
"dependsOn": [
|
|
574
|
+
"pkg:npm/js-tokens@4.0.0"
|
|
575
|
+
]
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
"ref": "pkg:npm/js-tokens@4.0.0",
|
|
579
|
+
"dependsOn": []
|
|
580
|
+
},
|
|
582
581
|
{
|
|
583
582
|
"ref": "pkg:npm/object-assign@4.1.1",
|
|
584
583
|
"dependsOn": []
|
|
@@ -2,6 +2,7 @@ import { Reporter, TestCase, TestResult } from '@playwright/test/reporter';
|
|
|
2
2
|
|
|
3
3
|
declare class FlakyReporter implements Reporter {
|
|
4
4
|
private flakyTests;
|
|
5
|
+
private recordedTests;
|
|
5
6
|
private browsers;
|
|
6
7
|
private project;
|
|
7
8
|
private variant;
|
|
@@ -13,7 +14,7 @@ declare class FlakyReporter implements Reporter {
|
|
|
13
14
|
private detectProjectFromGit;
|
|
14
15
|
private formatError;
|
|
15
16
|
private stripAnsi;
|
|
16
|
-
onTestEnd(test: TestCase,
|
|
17
|
+
onTestEnd(test: TestCase, _result: TestResult): void;
|
|
17
18
|
onEnd(): Promise<void>;
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -2,6 +2,7 @@ import { Reporter, TestCase, TestResult } from '@playwright/test/reporter';
|
|
|
2
2
|
|
|
3
3
|
declare class FlakyReporter implements Reporter {
|
|
4
4
|
private flakyTests;
|
|
5
|
+
private recordedTests;
|
|
5
6
|
private browsers;
|
|
6
7
|
private project;
|
|
7
8
|
private variant;
|
|
@@ -13,7 +14,7 @@ declare class FlakyReporter implements Reporter {
|
|
|
13
14
|
private detectProjectFromGit;
|
|
14
15
|
private formatError;
|
|
15
16
|
private stripAnsi;
|
|
16
|
-
onTestEnd(test: TestCase,
|
|
17
|
+
onTestEnd(test: TestCase, _result: TestResult): void;
|
|
17
18
|
onEnd(): Promise<void>;
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* This notice may not be removed from this file.
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
|
-
var
|
|
10
|
+
var f=[`CREATE TABLE IF NOT EXISTS ci_runs (
|
|
11
11
|
id BIGSERIAL PRIMARY KEY,
|
|
12
12
|
project TEXT NOT NULL,
|
|
13
13
|
browser TEXT NOT NULL,
|
|
@@ -28,19 +28,19 @@ var p=[`CREATE TABLE IF NOT EXISTS ci_runs (
|
|
|
28
28
|
UNIQUE (test_name, project, browser, variant, run_date)
|
|
29
29
|
)`,`CREATE INDEX IF NOT EXISTS idx_flaky_tests_date
|
|
30
30
|
ON flaky_tests (run_date)`,`CREATE INDEX IF NOT EXISTS idx_flaky_tests_identity_date
|
|
31
|
-
ON flaky_tests (test_name, project, browser, variant, run_date)`],
|
|
31
|
+
ON flaky_tests (test_name, project, browser, variant, run_date)`],A=`
|
|
32
32
|
INSERT INTO ci_runs (project, browser, variant, run_date, run_count)
|
|
33
33
|
VALUES ($1, $2, $3, CURRENT_DATE, 1)
|
|
34
34
|
ON CONFLICT (project, browser, variant, run_date) DO UPDATE SET
|
|
35
35
|
run_count = ci_runs.run_count + 1
|
|
36
|
-
`,
|
|
36
|
+
`,N=`
|
|
37
37
|
INSERT INTO flaky_tests (test_name, file_path, project, browser, variant, run_date, flaky_count, error_message)
|
|
38
38
|
SELECT unnest($1::text[]), unnest($2::text[]), unnest($3::text[]), unnest($4::text[]), unnest($5::text[]),
|
|
39
|
-
CURRENT_DATE,
|
|
39
|
+
CURRENT_DATE, unnest($7::int[]), unnest($6::text[])
|
|
40
40
|
ON CONFLICT (test_name, project, browser, variant, run_date) DO UPDATE SET
|
|
41
41
|
file_path = EXCLUDED.file_path,
|
|
42
|
-
flaky_count = flaky_tests.flaky_count +
|
|
42
|
+
flaky_count = flaky_tests.flaky_count + EXCLUDED.flaky_count,
|
|
43
43
|
error_message = COALESCE(EXCLUDED.error_message, flaky_tests.error_message)
|
|
44
|
-
`,
|
|
44
|
+
`,u=class{flakyTests=[];recordedTests=new Set;browsers=new Set;project;variant;databaseUrl;rootDir;constructor(){this.databaseUrl=process.env.TEST_DASHBOARD_DATABASE_URL,this.rootDir=this.detectRootDir(),this.project=process.env.PROJECT??this.detectProjectFromGit(),this.variant=this.deriveVariant();}deriveVariant(){return process.env.METADATA_VARIANT?process.env.METADATA_VARIANT:process.env.METADATA_SHADOW_DOM==="true"?"shadow-dom":process.env.METADATA_IFRAME==="true"?"iframe":"default"}detectRootDir(){if(process.env.TEST_ROOT_DIR)return process.env.TEST_ROOT_DIR;try{return child_process.execSync("git rev-parse --show-toplevel",{encoding:"utf8"}).trim()}catch{return console.debug("[flaky-reporter] Could not detect git root, falling back to cwd."),process.cwd()}}detectProjectFromGit(){try{let e=child_process.execSync("git remote get-url origin",{encoding:"utf8"}).trim(),r=/[/:]([\w.-]+)\/([\w.-]+?)(?:\.git)?$/.exec(e);if(r)return `${r[1]}/${r[2]}`}catch{console.debug("[flaky-reporter] Could not detect project from git remote.");}return "unknown"}formatError(e){if(!e)return null;let r=[];if(e.message&&r.push(e.message),e.snippet&&r.push(e.snippet),e.stack){let s=e.message?e.stack.replace(e.message,"").trim():e.stack;s&&r.push(s);}return this.stripAnsi(r.join(`
|
|
45
45
|
|
|
46
|
-
`))||null}stripAnsi(
|
|
46
|
+
`))||null}stripAnsi(e){return e.replaceAll(/\u001B\[[0-9;]*m/g,"")}onTestEnd(e,r){if(!this.databaseUrl)return;let s=e.parent.project()?.name??"unknown";if(this.browsers.add(s),e.outcome()!=="flaky"||this.recordedTests.has(e))return;this.recordedTests.add(e);let o=e.results.find(n=>n.status!==e.expectedStatus&&n.status!=="skipped"&&n.status!=="interrupted");this.flakyTests.push({test_name:e.titlePath().filter(Boolean).join(" > "),file_path:e.location.file.replace(this.rootDir+"/",""),browser:s,error_message:this.formatError(o?.error)});}async onEnd(){if(!this.databaseUrl)return;let e="@neondatabase/serverless",r;try{({neon:r}=await import(e));}catch{console.error("[flaky-reporter] @neondatabase/serverless is not installed. Skipping.");return}let s=r(this.databaseUrl);try{for(let t of f)await s.query(t);}catch(t){console.error("[flaky-reporter] CRITICAL: Failed to initialize database tables. No flaky test data was recorded. Check TEST_DASHBOARD_DATABASE_URL and database permissions.",t);return}try{for(let t of this.browsers)await s.query(A,[this.project,t,this.variant]);}catch(t){console.error("[flaky-reporter] Failed to record CI run:",t);}if(this.flakyTests.length===0){console.log("[flaky-reporter] No flaky tests detected.");return}let o=[],n=[],T=[],l=[],p=[],d=[],_=[],a=new Map;for(let t of this.flakyTests){let i=`${t.test_name}\0${t.browser}`,c=a.get(i);c?(c.count++,c.record.error_message=t.error_message??c.record.error_message):a.set(i,{record:{...t},count:1});}for(let{record:t,count:i}of a.values())o.push(t.test_name),n.push(t.file_path),T.push(this.project),l.push(t.browser),p.push(this.variant),d.push(t.error_message),_.push(i);try{await s.query(N,[o,n,T,l,p,d,_]),console.log(`[flaky-reporter] Reported ${a.size} flaky test(s) to database.`);}catch(t){console.error("[flaky-reporter] Failed to write to database:",t);}}},m=u;module.exports=m;
|
|
@@ -7,7 +7,7 @@ import {execSync}from'child_process';/**
|
|
|
7
7
|
* This notice may not be removed from this file.
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
|
-
var
|
|
10
|
+
var f=[`CREATE TABLE IF NOT EXISTS ci_runs (
|
|
11
11
|
id BIGSERIAL PRIMARY KEY,
|
|
12
12
|
project TEXT NOT NULL,
|
|
13
13
|
browser TEXT NOT NULL,
|
|
@@ -28,19 +28,19 @@ var p=[`CREATE TABLE IF NOT EXISTS ci_runs (
|
|
|
28
28
|
UNIQUE (test_name, project, browser, variant, run_date)
|
|
29
29
|
)`,`CREATE INDEX IF NOT EXISTS idx_flaky_tests_date
|
|
30
30
|
ON flaky_tests (run_date)`,`CREATE INDEX IF NOT EXISTS idx_flaky_tests_identity_date
|
|
31
|
-
ON flaky_tests (test_name, project, browser, variant, run_date)`],
|
|
31
|
+
ON flaky_tests (test_name, project, browser, variant, run_date)`],A=`
|
|
32
32
|
INSERT INTO ci_runs (project, browser, variant, run_date, run_count)
|
|
33
33
|
VALUES ($1, $2, $3, CURRENT_DATE, 1)
|
|
34
34
|
ON CONFLICT (project, browser, variant, run_date) DO UPDATE SET
|
|
35
35
|
run_count = ci_runs.run_count + 1
|
|
36
|
-
`,
|
|
36
|
+
`,N=`
|
|
37
37
|
INSERT INTO flaky_tests (test_name, file_path, project, browser, variant, run_date, flaky_count, error_message)
|
|
38
38
|
SELECT unnest($1::text[]), unnest($2::text[]), unnest($3::text[]), unnest($4::text[]), unnest($5::text[]),
|
|
39
|
-
CURRENT_DATE,
|
|
39
|
+
CURRENT_DATE, unnest($7::int[]), unnest($6::text[])
|
|
40
40
|
ON CONFLICT (test_name, project, browser, variant, run_date) DO UPDATE SET
|
|
41
41
|
file_path = EXCLUDED.file_path,
|
|
42
|
-
flaky_count = flaky_tests.flaky_count +
|
|
42
|
+
flaky_count = flaky_tests.flaky_count + EXCLUDED.flaky_count,
|
|
43
43
|
error_message = COALESCE(EXCLUDED.error_message, flaky_tests.error_message)
|
|
44
|
-
`,
|
|
44
|
+
`,u=class{flakyTests=[];recordedTests=new Set;browsers=new Set;project;variant;databaseUrl;rootDir;constructor(){this.databaseUrl=process.env.TEST_DASHBOARD_DATABASE_URL,this.rootDir=this.detectRootDir(),this.project=process.env.PROJECT??this.detectProjectFromGit(),this.variant=this.deriveVariant();}deriveVariant(){return process.env.METADATA_VARIANT?process.env.METADATA_VARIANT:process.env.METADATA_SHADOW_DOM==="true"?"shadow-dom":process.env.METADATA_IFRAME==="true"?"iframe":"default"}detectRootDir(){if(process.env.TEST_ROOT_DIR)return process.env.TEST_ROOT_DIR;try{return execSync("git rev-parse --show-toplevel",{encoding:"utf8"}).trim()}catch{return console.debug("[flaky-reporter] Could not detect git root, falling back to cwd."),process.cwd()}}detectProjectFromGit(){try{let e=execSync("git remote get-url origin",{encoding:"utf8"}).trim(),r=/[/:]([\w.-]+)\/([\w.-]+?)(?:\.git)?$/.exec(e);if(r)return `${r[1]}/${r[2]}`}catch{console.debug("[flaky-reporter] Could not detect project from git remote.");}return "unknown"}formatError(e){if(!e)return null;let r=[];if(e.message&&r.push(e.message),e.snippet&&r.push(e.snippet),e.stack){let s=e.message?e.stack.replace(e.message,"").trim():e.stack;s&&r.push(s);}return this.stripAnsi(r.join(`
|
|
45
45
|
|
|
46
|
-
`))||null}stripAnsi(
|
|
46
|
+
`))||null}stripAnsi(e){return e.replaceAll(/\u001B\[[0-9;]*m/g,"")}onTestEnd(e,r){if(!this.databaseUrl)return;let s=e.parent.project()?.name??"unknown";if(this.browsers.add(s),e.outcome()!=="flaky"||this.recordedTests.has(e))return;this.recordedTests.add(e);let o=e.results.find(n=>n.status!==e.expectedStatus&&n.status!=="skipped"&&n.status!=="interrupted");this.flakyTests.push({test_name:e.titlePath().filter(Boolean).join(" > "),file_path:e.location.file.replace(this.rootDir+"/",""),browser:s,error_message:this.formatError(o?.error)});}async onEnd(){if(!this.databaseUrl)return;let e="@neondatabase/serverless",r;try{({neon:r}=await import(e));}catch{console.error("[flaky-reporter] @neondatabase/serverless is not installed. Skipping.");return}let s=r(this.databaseUrl);try{for(let t of f)await s.query(t);}catch(t){console.error("[flaky-reporter] CRITICAL: Failed to initialize database tables. No flaky test data was recorded. Check TEST_DASHBOARD_DATABASE_URL and database permissions.",t);return}try{for(let t of this.browsers)await s.query(A,[this.project,t,this.variant]);}catch(t){console.error("[flaky-reporter] Failed to record CI run:",t);}if(this.flakyTests.length===0){console.log("[flaky-reporter] No flaky tests detected.");return}let o=[],n=[],T=[],l=[],p=[],d=[],_=[],a=new Map;for(let t of this.flakyTests){let i=`${t.test_name}\0${t.browser}`,c=a.get(i);c?(c.count++,c.record.error_message=t.error_message??c.record.error_message):a.set(i,{record:{...t},count:1});}for(let{record:t,count:i}of a.values())o.push(t.test_name),n.push(t.file_path),T.push(this.project),l.push(t.browser),p.push(this.variant),d.push(t.error_message),_.push(i);try{await s.query(N,[o,n,T,l,p,d,_]),console.log(`[flaky-reporter] Reported ${a.size} flaky test(s) to database.`);}catch(t){console.error("[flaky-reporter] Failed to write to database:",t);}}},m=u;export{m as default};
|