@sharadox/test-web-library 0.1.0 → 0.2.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 +2 -0
- package/distribution/browser-environment/index.d.ts +22 -0
- package/distribution/browser-environment/index.d.ts.map +1 -0
- package/distribution/browser-environment/index.js +68 -0
- package/distribution/browser-environment/index.js.map +1 -0
- package/distribution/browser-environment/type.d.ts +15 -0
- package/distribution/browser-environment/type.d.ts.map +1 -0
- package/distribution/browser-environment/type.js +2 -0
- package/distribution/browser-environment/type.js.map +1 -0
- package/llms.txt +1 -0
- package/package.json +18 -3
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
- **`describeStyleMemory`:** a style group starts on its default and remembers the visitor's choice.
|
|
10
10
|
- **`describeConsent`:** Deny lasts for the visit, manual choices are kept, and the preferences link reopens the panel.
|
|
11
11
|
|
|
12
|
+
For Vitest component tests, `installBrowserEnvironment()` adds what happy-dom lacks: intersection and resize observers, media queries, `scrollTo`, and Motion animations that finish at once. `reportIntersection()` marks watched elements as visible.
|
|
13
|
+
|
|
12
14
|
## Install
|
|
13
15
|
|
|
14
16
|
```sh
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { BrowserEnvironmentOptions, ObserverRecord } from "@sharadox/test-web-library/browser-environment/type";
|
|
2
|
+
/** Every IntersectionObserver created since `installBrowserEnvironment`, so tests can report elements as visible. */
|
|
3
|
+
export declare const observers: ObserverRecord[];
|
|
4
|
+
/**
|
|
5
|
+
* Gives a Vitest happy-dom environment the browser features components use and the DOM lacks: intersection and
|
|
6
|
+
* resize observers, media queries (reduced motion, fine pointer, colour scheme), `scrollTo`, and Motion animations
|
|
7
|
+
* that finish at once (so unmounting never cancels one mid-way). Call it in `beforeEach`, and restore with
|
|
8
|
+
* `vi.restoreAllMocks()` and `vi.unstubAllGlobals()`.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* beforeEach(() => installBrowserEnvironment({ reducedMotion: true }));
|
|
12
|
+
*/
|
|
13
|
+
export declare function installBrowserEnvironment({ reducedMotion, finePointer, dark, }?: BrowserEnvironmentOptions): void;
|
|
14
|
+
/**
|
|
15
|
+
* Reports elements as visible (or not) to every observer created so far: all watched elements by default, or only
|
|
16
|
+
* those matching `filter`.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* act(() => reportIntersection());
|
|
20
|
+
*/
|
|
21
|
+
export declare function reportIntersection(isIntersecting?: boolean, filter?: (element: Element) => boolean): void;
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/browser-environment/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,qDAAqD,CAAC;AAErH,qHAAqH;AACrH,eAAO,MAAM,SAAS,EAAE,cAAc,EAAO,CAAC;AAE9C;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,aAAqB,EACrB,WAAkB,EAClB,IAAW,GACZ,GAAE,yBAA8B,GAAG,IAAI,CA8CvC;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,cAAc,UAAO,EAAE,MAAM,GAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAoB,GAAG,IAAI,CAYlH"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { MotionGlobalConfig } from "motion";
|
|
2
|
+
import { vi } from "vitest";
|
|
3
|
+
/** Every IntersectionObserver created since `installBrowserEnvironment`, so tests can report elements as visible. */
|
|
4
|
+
export const observers = [];
|
|
5
|
+
/**
|
|
6
|
+
* Gives a Vitest happy-dom environment the browser features components use and the DOM lacks: intersection and
|
|
7
|
+
* resize observers, media queries (reduced motion, fine pointer, colour scheme), `scrollTo`, and Motion animations
|
|
8
|
+
* that finish at once (so unmounting never cancels one mid-way). Call it in `beforeEach`, and restore with
|
|
9
|
+
* `vi.restoreAllMocks()` and `vi.unstubAllGlobals()`.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* beforeEach(() => installBrowserEnvironment({ reducedMotion: true }));
|
|
13
|
+
*/
|
|
14
|
+
export function installBrowserEnvironment({ reducedMotion = false, finePointer = true, dark = true, } = {}) {
|
|
15
|
+
MotionGlobalConfig.skipAnimations = true;
|
|
16
|
+
observers.length = 0;
|
|
17
|
+
vi.stubGlobal("IntersectionObserver", class {
|
|
18
|
+
record;
|
|
19
|
+
constructor(callback, options) {
|
|
20
|
+
this.record = { callback, options, elements: [] };
|
|
21
|
+
observers.push(this.record);
|
|
22
|
+
}
|
|
23
|
+
observe(element) {
|
|
24
|
+
this.record.elements.push(element);
|
|
25
|
+
}
|
|
26
|
+
unobserve() { }
|
|
27
|
+
disconnect() { }
|
|
28
|
+
takeRecords() {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
vi.stubGlobal("ResizeObserver", class {
|
|
33
|
+
observe() { }
|
|
34
|
+
unobserve() { }
|
|
35
|
+
disconnect() { }
|
|
36
|
+
});
|
|
37
|
+
const matches = (query) => (query.includes("reduce") && reducedMotion) ||
|
|
38
|
+
(query.includes("pointer: fine") && finePointer) ||
|
|
39
|
+
(query.includes("dark") && dark) ||
|
|
40
|
+
(query.includes("light") && !dark);
|
|
41
|
+
vi.spyOn(window, "matchMedia").mockImplementation((query) => ({
|
|
42
|
+
matches: matches(query),
|
|
43
|
+
media: query,
|
|
44
|
+
addEventListener: () => { },
|
|
45
|
+
removeEventListener: () => { },
|
|
46
|
+
addListener: () => { },
|
|
47
|
+
removeListener: () => { },
|
|
48
|
+
}));
|
|
49
|
+
vi.stubGlobal("scrollTo", vi.fn());
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Reports elements as visible (or not) to every observer created so far: all watched elements by default, or only
|
|
53
|
+
* those matching `filter`.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* act(() => reportIntersection());
|
|
57
|
+
*/
|
|
58
|
+
export function reportIntersection(isIntersecting = true, filter = () => true) {
|
|
59
|
+
for (const observer of observers) {
|
|
60
|
+
const entries = observer.elements.filter(filter).map((target) => ({
|
|
61
|
+
isIntersecting,
|
|
62
|
+
target,
|
|
63
|
+
intersectionRatio: isIntersecting ? 1 : 0,
|
|
64
|
+
}));
|
|
65
|
+
observer.callback(entries, {});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../source/browser-environment/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAG5B,qHAAqH;AACrH,MAAM,CAAC,MAAM,SAAS,GAAqB,EAAE,CAAC;AAE9C;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CAAC,EACxC,aAAa,GAAG,KAAK,EACrB,WAAW,GAAG,IAAI,EAClB,IAAI,GAAG,IAAI,MACkB,EAAE;IAC/B,kBAAkB,CAAC,cAAc,GAAG,IAAI,CAAC;IACzC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;IACrB,EAAE,CAAC,UAAU,CACX,sBAAsB,EACtB;QACE,MAAM,CAAiB;QACvB,YAAY,QAAsC,EAAE,OAAkC;YACpF,IAAI,CAAC,MAAM,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YAClD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,CAAC,OAAgB;YACtB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;QACD,SAAS,KAAI,CAAC;QACd,UAAU,KAAI,CAAC;QACf,WAAW;YACT,OAAO,EAAE,CAAC;QACZ,CAAC;KACF,CACF,CAAC;IACF,EAAE,CAAC,UAAU,CACX,gBAAgB,EAChB;QACE,OAAO,KAAI,CAAC;QACZ,SAAS,KAAI,CAAC;QACd,UAAU,KAAI,CAAC;KAChB,CACF,CAAC;IACF,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,EAAE,CAChC,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,aAAa,CAAC;QAC3C,CAAC,KAAK,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,WAAW,CAAC;QAChD,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;QAChC,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,kBAAkB,CAC/C,CAAC,KAAa,EAAE,EAAE,CAChB,CAAC;QACC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC;QACvB,KAAK,EAAE,KAAK;QACZ,gBAAgB,EAAE,GAAG,EAAE,GAAE,CAAC;QAC1B,mBAAmB,EAAE,GAAG,EAAE,GAAE,CAAC;QAC7B,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC;QACrB,cAAc,EAAE,GAAG,EAAE,GAAE,CAAC;KACzB,CAA8B,CAClC,CAAC;IACF,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACrC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,cAAc,GAAG,IAAI,EAAE,SAAwC,GAAG,EAAE,CAAC,IAAI;IAC1G,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAClD,CAAC,MAAM,EAAE,EAAE,CACT,CAAC;YACC,cAAc;YACd,MAAM;YACN,iBAAiB,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAC1C,CAAyC,CAC7C,CAAC;QACF,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,EAA0B,CAAC,CAAC;IACzD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface BrowserEnvironmentOptions {
|
|
2
|
+
/** Whether `prefers-reduced-motion: reduce` matches; false by default. */
|
|
3
|
+
reducedMotion?: boolean;
|
|
4
|
+
/** Whether `pointer: fine` matches (mouse or trackpad); true by default. */
|
|
5
|
+
finePointer?: boolean;
|
|
6
|
+
/** Whether `prefers-color-scheme: dark` matches; true by default (light matches otherwise). */
|
|
7
|
+
dark?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** An IntersectionObserver created during a test, with the elements it watches. */
|
|
10
|
+
export interface ObserverRecord {
|
|
11
|
+
callback: IntersectionObserverCallback;
|
|
12
|
+
options?: IntersectionObserverInit;
|
|
13
|
+
elements: Element[];
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../source/browser-environment/type.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,yBAAyB;IACxC,0EAA0E;IAC1E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+FAA+F;IAC/F,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,mFAAmF;AACnF,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,4BAA4B,CAAC;IACvC,OAAO,CAAC,EAAE,wBAAwB,CAAC;IACnC,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.js","sourceRoot":"","sources":["../../source/browser-environment/type.ts"],"names":[],"mappings":""}
|
package/llms.txt
CHANGED
|
@@ -7,3 +7,4 @@
|
|
|
7
7
|
- /installability-suite describeInstallability(path) (Chromium CDP Page.getInstallabilityErrors)
|
|
8
8
|
- /preference-suite describeLocaleMemory({ browserLocale, automaticPath, switchName, pick: { link, path } }), describeStyleMemory({ path, attribute, switchName, colorScheme, initial, pick: { radio, style } }), describeConsent({ path, dialogName, deny, customize, save, preferences, category, storageKey, expectedGranted })
|
|
9
9
|
- Peer: @playwright/test. Licence: Sharadox Open Source Licence 0.1.0
|
|
10
|
+
- /browser-environment installBrowserEnvironment({ reducedMotion=false, finePointer=true, dark=true }) (Vitest happy-dom: IntersectionObserver/ResizeObserver stubs, matchMedia, scrollTo, Motion skipAnimations); reportIntersection(isIntersecting=true, filter?); observers
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sharadox/test-web-library",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Test helpers for websites: Playwright visual snapshots and shared end-to-end suites (search engines, installability, remembered language and style, cookie consent), and a browser environment for Vitest component tests.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "SEE LICENSE IN LICENCA.md",
|
|
7
7
|
"author": "Sharadox <contact@sharadox.com>",
|
|
@@ -56,8 +56,10 @@
|
|
|
56
56
|
"@types/node": "^24.19.2",
|
|
57
57
|
"@vitest/coverage-v8": "^5.1.0",
|
|
58
58
|
"eslint": "9.39.5",
|
|
59
|
+
"happy-dom": "^21.0.4",
|
|
59
60
|
"husky": "^9.1.7",
|
|
60
61
|
"lint-staged": "^17.6.0",
|
|
62
|
+
"motion": "^12.43.0",
|
|
61
63
|
"prettier": "^3.10.0",
|
|
62
64
|
"turbo": "^2.11.10",
|
|
63
65
|
"typedoc": "^0.28.20",
|
|
@@ -68,7 +70,20 @@
|
|
|
68
70
|
"@sharadox/web-library": "^0.5.0"
|
|
69
71
|
},
|
|
70
72
|
"peerDependencies": {
|
|
71
|
-
"@playwright/test": ">=1.50"
|
|
73
|
+
"@playwright/test": ">=1.50",
|
|
74
|
+
"vitest": ">=5",
|
|
75
|
+
"motion": ">=12"
|
|
76
|
+
},
|
|
77
|
+
"peerDependenciesMeta": {
|
|
78
|
+
"@playwright/test": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"vitest": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"motion": {
|
|
85
|
+
"optional": true
|
|
86
|
+
}
|
|
72
87
|
},
|
|
73
88
|
"scripts": {
|
|
74
89
|
"build": "rm -rf distribution && tsc -p tsconfig.build.json",
|