@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/Acknowledgements.md +2841 -722
- package/README.md +67 -0
- package/client/index.js +2 -2
- package/client/index.mjs +2 -2
- package/package.json +28 -4
- package/sbom.json +1 -1
- package/test-reporter/test-reporter.d.mts +20 -0
- package/test-reporter/test-reporter.d.ts +20 -0
- package/test-reporter/test-reporter.js +46 -0
- package/test-reporter/test-reporter.mjs +46 -0
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
|
|
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(
|
|
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
|
|
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(
|
|
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.
|
|
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
|
-
|
|
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
|
+
"@baseline-ui/core": "0.56.0"
|
|
31
55
|
},
|
|
32
56
|
"scripts": {
|
|
33
57
|
"build": "tsup",
|