@baseline-ui/test-utils 0.54.2 → 0.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # @baseline-ui/test-utils
2
+
3
+ A collection of test utilities for Baseline UI, including a Playwright reporter that tracks flaky tests in a Neon database.
4
+
5
+ ## Exports
6
+
7
+ | Entry point | Description |
8
+ | --------------------------------------- | -------------------------------------------------- |
9
+ | `@baseline-ui/test-utils` | Custom Playwright matchers (`expect`) |
10
+ | `@baseline-ui/test-utils/client` | Client-side test helpers and components |
11
+ | `@baseline-ui/test-utils/test-reporter` | Playwright reporter for tracking flaky tests in CI |
12
+
13
+ ## Flaky Test Reporter
14
+
15
+ A custom [Playwright reporter](https://playwright.dev/docs/test-reporters) that automatically detects flaky tests (tests that fail then pass on retry) and records them to a Neon (Postgres) database. It also tracks total CI run counts per project/browser/variant so you can compute flaky rates.
16
+
17
+ ### Setup
18
+
19
+ #### 1. Install the optional peer dependency
20
+
21
+ The reporter uses `@neondatabase/serverless` to write to the database. It's listed as an optional peer dependency — install it in the project that runs the tests:
22
+
23
+ ```bash
24
+ pnpm add -D @neondatabase/serverless
25
+ ```
26
+
27
+ #### 2. Add the reporter to your Playwright config
28
+
29
+ ```ts
30
+ // playwright.config.ts
31
+ import { defineConfig } from "@playwright/test";
32
+
33
+ export default defineConfig({
34
+ reporter: [["list"], ["@baseline-ui/test-utils/test-reporter"]],
35
+ });
36
+ ```
37
+
38
+ #### 3. Set the database URL
39
+
40
+ The reporter reads `TEST_DASHBOARD_DATABASE_URL` from the environment. When the variable is missing, the reporter silently no-ops — safe to leave unconfigured locally.
41
+
42
+ ```bash
43
+ # .env or CI secrets
44
+ TEST_DASHBOARD_DATABASE_URL="postgres://user:pass@host/dbname"
45
+ ```
46
+
47
+ ### Environment Variables
48
+
49
+ | Variable | Required | Description |
50
+ | ----------------------------- | -------- | ------------------------------------------------------------------------------- |
51
+ | `TEST_DASHBOARD_DATABASE_URL` | Yes | Neon/Postgres connection string. Reporter is disabled when absent. |
52
+ | `PROJECT` | No | Project identifier (e.g. `org/repo`). Auto-detected from `git remote` if unset. |
53
+ | `METADATA_SHADOW_DOM` | No | Set to `"true"` to record the variant as `shadow-dom`. |
54
+ | `METADATA_IFRAME` | No | Set to `"true"` to record the variant as `iframe`. |
55
+
56
+ ### Database Schema
57
+
58
+ The reporter auto-creates two tables on first run:
59
+
60
+ **`ci_runs`** — one row per project/browser/variant/day, incremented each run.
61
+
62
+ **`flaky_tests`** — one row per flaky test/project/browser/variant/day, with the error message from the first failure.
63
+
64
+ ### How It Works
65
+
66
+ 1. **`onTestEnd`** — if a test's final status is `passed` but it had prior `failed` results (i.e. it was retried), it's recorded as flaky.
67
+ 2. **`onEnd`** — all collected flaky records are batch-upserted into the database. CI run counts are also upserted so you can compute `flaky_count / run_count` as a flaky rate.
package/client/index.js CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';var l=require('react'),w=require('react-frame-component'),F=require('react-dom/client'),core=require('@baseline-ui/core'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var l__default=/*#__PURE__*/_interopDefault(l);var w__default=/*#__PURE__*/_interopDefault(w);var F__default=/*#__PURE__*/_interopDefault(F);/**
1
+ 'use strict';var core=require('@baseline-ui/core'),l=require('react'),b=require('react-dom/client'),x=require('react-frame-component'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var l__default=/*#__PURE__*/_interopDefault(l);var b__default=/*#__PURE__*/_interopDefault(b);var x__default=/*#__PURE__*/_interopDefault(x);/**
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
@@ -8,4 +8,4 @@
8
8
  *
9
9
  */
10
10
  function h(){let t=[],r=document;for(let o=0;o<r.styleSheets.length;o++){let n=r.styleSheets[o];if(n.cssRules){let i=n.cssRules;for(let s of i)t.push(s.cssText);}}return t.join(`
11
- `)}var E=l__default.default.forwardRef(({head:t=jsxRuntime.jsx("style",{children:h()}),...r},o)=>jsxRuntime.jsx(w__default.default,{style:{width:"100vw",height:"100vh",border:"none"},ref:o,...r,head:t}));E.displayName="IFrame";var T=l__default.default.forwardRef(({children:t,style:r},o)=>{let n=core.useObjectRef(o),i=l__default.default.useRef(null),s=l__default.default.useRef(null),d=l__default.default.useRef(null),m=l__default.default.useRef(false),P=l__default.default.useMemo(()=>h(),[]);return l.useEffect(()=>{if(m.current=false,n.current&&!i.current){let e=n.current.attachShadow({mode:"open"});i.current=e;let c=document.createElement("style");c.textContent=P,e.append(c);let u=document.createElement("div");u.className="content-container",e.append(u);let p=document.createElement("div");p.className="overlay-portal",e.append(p),d.current=p;let y=F__default.default.createRoot(u);s.current=y;}return ()=>{m.current=true;let e=s.current;e&&(queueMicrotask(()=>{e.unmount();}),s.current=null),i.current=null,d.current=null;}},[P,n]),l.useEffect(()=>{if(m.current)return;let e=s.current,c=d.current;e&&c&&e.render(jsxRuntime.jsx(core.PortalContainerProvider,{portalContainer:c,children:t}));},[t]),jsxRuntime.jsx("div",{style:r,ref:n})});T.displayName="ShadowRoot";var Q=({children:t,locale:r,theme:o,themeStyle:n,disableThemeProvider:i=false,disableFrameProvider:s=false,messages:d})=>{let[m,P]=l.useState(),{document:e}=w.useFrame(),c=r==="ar"||r==="he";l.useLayoutEffect(()=>{c?e?.documentElement.setAttribute("dir","rtl"):e?.documentElement.removeAttribute("dir");},[c,e]);let u=m?.ownerDocument?.body,p=[i?void 0:({children:a})=>jsxRuntime.jsx(core.ThemeProvider,{theme:o,style:n,children:a}),s?void 0:({children:a})=>jsxRuntime.jsx(core.FrameProvider,{container:u,portalContainer:m,children:a})].filter(Boolean);return jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(core.I18nProvider,{locale:r,messages:d,children:jsxRuntime.jsx(({children:a})=>{let v=a;for(let C of [...p].toReversed())v=C?.({children:v});return v},{children:t})}),jsxRuntime.jsx("div",{id:"portal-container",ref:a=>{P(a??void 0);}})]})};exports.IFrame=E;exports.ProvidersContainer=Q;exports.ShadowRoot=T;exports.getRootStyles=h;
11
+ `)}var E=l__default.default.forwardRef(({head:t=jsxRuntime.jsx("style",{children:h()}),...r},o)=>jsxRuntime.jsx(x__default.default,{style:{width:"100vw",height:"100vh",border:"none"},ref:o,...r,head:t}));E.displayName="IFrame";var T=l__default.default.forwardRef(({children:t,style:r},o)=>{let n=core.useObjectRef(o),i=l__default.default.useRef(null),s=l__default.default.useRef(null),d=l__default.default.useRef(null),m=l__default.default.useRef(false),P=l__default.default.useMemo(()=>h(),[]);return l.useEffect(()=>{if(m.current=false,n.current&&!i.current){let e=n.current.attachShadow({mode:"open"});i.current=e;let c=document.createElement("style");c.textContent=P,e.append(c);let u=document.createElement("div");u.className="content-container",e.append(u);let p=document.createElement("div");p.className="overlay-portal",e.append(p),d.current=p;let y=b__default.default.createRoot(u);s.current=y;}return ()=>{m.current=true;let e=s.current;e&&(queueMicrotask(()=>{e.unmount();}),s.current=null),i.current=null,d.current=null;}},[P,n]),l.useEffect(()=>{if(m.current)return;let e=s.current,c=d.current;e&&c&&e.render(jsxRuntime.jsx(core.PortalContainerProvider,{portalContainer:c,children:t}));},[t]),jsxRuntime.jsx("div",{style:r,ref:n})});T.displayName="ShadowRoot";var Q=({children:t,locale:r,theme:o,themeStyle:n,disableThemeProvider:i=false,disableFrameProvider:s=false,messages:d})=>{let[m,P]=l.useState(),{document:e}=x.useFrame(),c=r==="ar"||r==="he";l.useLayoutEffect(()=>{c?e?.documentElement.setAttribute("dir","rtl"):e?.documentElement.removeAttribute("dir");},[c,e]);let u=m?.ownerDocument?.body,p=[i?void 0:({children:a})=>jsxRuntime.jsx(core.ThemeProvider,{theme:o,style:n,children:a}),s?void 0:({children:a})=>jsxRuntime.jsx(core.FrameProvider,{container:u,portalContainer:m,children:a})].filter(Boolean);return jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(core.I18nProvider,{locale:r,messages:d,children:jsxRuntime.jsx(({children:a})=>{let v=a;for(let C of [...p].toReversed())v=C?.({children:v});return v},{children:t})}),jsxRuntime.jsx("div",{id:"portal-container",ref:a=>{P(a??void 0);}})]})};exports.IFrame=E;exports.ProvidersContainer=Q;exports.ShadowRoot=T;exports.getRootStyles=h;
package/client/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import l,{useEffect,useState,useLayoutEffect}from'react';import w,{useFrame}from'react-frame-component';import F from'react-dom/client';import {useObjectRef,PortalContainerProvider,I18nProvider,ThemeProvider,FrameProvider}from'@baseline-ui/core';import {jsx,jsxs,Fragment}from'react/jsx-runtime';/**
1
+ import {useObjectRef,PortalContainerProvider,I18nProvider,ThemeProvider,FrameProvider}from'@baseline-ui/core';import l,{useEffect,useState,useLayoutEffect}from'react';import b from'react-dom/client';import x,{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
@@ -8,4 +8,4 @@ import l,{useEffect,useState,useLayoutEffect}from'react';import w,{useFrame}from
8
8
  *
9
9
  */
10
10
  function h(){let t=[],r=document;for(let o=0;o<r.styleSheets.length;o++){let n=r.styleSheets[o];if(n.cssRules){let i=n.cssRules;for(let s of i)t.push(s.cssText);}}return t.join(`
11
- `)}var E=l.forwardRef(({head:t=jsx("style",{children:h()}),...r},o)=>jsx(w,{style:{width:"100vw",height:"100vh",border:"none"},ref:o,...r,head:t}));E.displayName="IFrame";var T=l.forwardRef(({children:t,style:r},o)=>{let n=useObjectRef(o),i=l.useRef(null),s=l.useRef(null),d=l.useRef(null),m=l.useRef(false),P=l.useMemo(()=>h(),[]);return useEffect(()=>{if(m.current=false,n.current&&!i.current){let e=n.current.attachShadow({mode:"open"});i.current=e;let c=document.createElement("style");c.textContent=P,e.append(c);let u=document.createElement("div");u.className="content-container",e.append(u);let p=document.createElement("div");p.className="overlay-portal",e.append(p),d.current=p;let y=F.createRoot(u);s.current=y;}return ()=>{m.current=true;let e=s.current;e&&(queueMicrotask(()=>{e.unmount();}),s.current=null),i.current=null,d.current=null;}},[P,n]),useEffect(()=>{if(m.current)return;let e=s.current,c=d.current;e&&c&&e.render(jsx(PortalContainerProvider,{portalContainer:c,children:t}));},[t]),jsx("div",{style:r,ref:n})});T.displayName="ShadowRoot";var Q=({children:t,locale:r,theme:o,themeStyle:n,disableThemeProvider:i=false,disableFrameProvider:s=false,messages:d})=>{let[m,P]=useState(),{document:e}=useFrame(),c=r==="ar"||r==="he";useLayoutEffect(()=>{c?e?.documentElement.setAttribute("dir","rtl"):e?.documentElement.removeAttribute("dir");},[c,e]);let u=m?.ownerDocument?.body,p=[i?void 0:({children:a})=>jsx(ThemeProvider,{theme:o,style:n,children:a}),s?void 0:({children:a})=>jsx(FrameProvider,{container:u,portalContainer:m,children:a})].filter(Boolean);return jsxs(Fragment,{children:[jsx(I18nProvider,{locale:r,messages:d,children:jsx(({children:a})=>{let v=a;for(let C of [...p].toReversed())v=C?.({children:v});return v},{children:t})}),jsx("div",{id:"portal-container",ref:a=>{P(a??void 0);}})]})};export{E as IFrame,Q as ProvidersContainer,T as ShadowRoot,h as getRootStyles};
11
+ `)}var E=l.forwardRef(({head:t=jsx("style",{children:h()}),...r},o)=>jsx(x,{style:{width:"100vw",height:"100vh",border:"none"},ref:o,...r,head:t}));E.displayName="IFrame";var T=l.forwardRef(({children:t,style:r},o)=>{let n=useObjectRef(o),i=l.useRef(null),s=l.useRef(null),d=l.useRef(null),m=l.useRef(false),P=l.useMemo(()=>h(),[]);return useEffect(()=>{if(m.current=false,n.current&&!i.current){let e=n.current.attachShadow({mode:"open"});i.current=e;let c=document.createElement("style");c.textContent=P,e.append(c);let u=document.createElement("div");u.className="content-container",e.append(u);let p=document.createElement("div");p.className="overlay-portal",e.append(p),d.current=p;let y=b.createRoot(u);s.current=y;}return ()=>{m.current=true;let e=s.current;e&&(queueMicrotask(()=>{e.unmount();}),s.current=null),i.current=null,d.current=null;}},[P,n]),useEffect(()=>{if(m.current)return;let e=s.current,c=d.current;e&&c&&e.render(jsx(PortalContainerProvider,{portalContainer:c,children:t}));},[t]),jsx("div",{style:r,ref:n})});T.displayName="ShadowRoot";var Q=({children:t,locale:r,theme:o,themeStyle:n,disableThemeProvider:i=false,disableFrameProvider:s=false,messages:d})=>{let[m,P]=useState(),{document:e}=useFrame(),c=r==="ar"||r==="he";useLayoutEffect(()=>{c?e?.documentElement.setAttribute("dir","rtl"):e?.documentElement.removeAttribute("dir");},[c,e]);let u=m?.ownerDocument?.body,p=[i?void 0:({children:a})=>jsx(ThemeProvider,{theme:o,style:n,children:a}),s?void 0:({children:a})=>jsx(FrameProvider,{container:u,portalContainer:m,children:a})].filter(Boolean);return jsxs(Fragment,{children:[jsx(I18nProvider,{locale:r,messages:d,children:jsx(({children:a})=>{let v=a;for(let C of [...p].toReversed())v=C?.({children:v});return v},{children:t})}),jsx("div",{id:"portal-container",ref:a=>{P(a??void 0);}})]})};export{E as IFrame,Q as ProvidersContainer,T as ShadowRoot,h as getRootStyles};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baseline-ui/test-utils",
3
- "version": "0.54.2",
3
+ "version": "0.56.0",
4
4
  "description": "A collection of test utilities for Baseline UI",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -11,23 +11,47 @@
11
11
  "License.md",
12
12
  "Acknowledgements.md",
13
13
  "client",
14
+ "test-reporter",
14
15
  "sbom.json"
15
16
  ],
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.mjs",
21
+ "require": "./dist/index.js"
22
+ },
23
+ "./client": {
24
+ "types": "./client/index.d.ts",
25
+ "import": "./client/index.mjs",
26
+ "require": "./client/index.js"
27
+ },
28
+ "./test-reporter": {
29
+ "types": "./test-reporter/test-reporter.d.ts",
30
+ "import": "./test-reporter/test-reporter.mjs",
31
+ "require": "./test-reporter/test-reporter.js"
32
+ }
33
+ },
16
34
  "source": "src/index.ts",
17
35
  "author": "",
18
36
  "license": "SEE LICENSE IN https://pspdfkit.com/legal/License.pdf",
19
37
  "peerDependencies": {
38
+ "@neondatabase/serverless": "^1.0.2",
39
+ "@playwright/test": "^1.58.2",
20
40
  "react": "^18.0.0",
21
41
  "react-dom": "^18.0.0",
22
- "sinon": ">=19.0.0",
23
- "@playwright/test": "^1.58.2"
42
+ "sinon": ">=19.0.0"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "@neondatabase/serverless": {
46
+ "optional": true
47
+ }
24
48
  },
25
49
  "publishConfig": {
26
50
  "access": "public"
27
51
  },
28
52
  "dependencies": {
29
53
  "react-frame-component": "^5.2.7",
30
- "@baseline-ui/core": "0.54.2"
54
+ "@baseline-ui/core": "0.56.0"
31
55
  },
32
56
  "scripts": {
33
57
  "build": "tsup",