sibujs 1.1.0 → 1.3.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 +29 -25
- package/dist/browser.cjs +804 -2
- package/dist/browser.d.cts +591 -1
- package/dist/browser.d.ts +591 -1
- package/dist/browser.js +50 -8
- package/dist/build.cjs +706 -161
- package/dist/build.js +21 -12
- package/dist/cdn.global.js +188 -7
- package/dist/chunk-2BYQDGN3.js +742 -0
- package/dist/chunk-32DY64NT.js +282 -0
- package/dist/chunk-3AIRKM3B.js +1263 -0
- package/dist/chunk-3X2YG6YM.js +505 -0
- package/dist/chunk-5X6PP2UK.js +28 -0
- package/dist/chunk-77L6NL3X.js +1097 -0
- package/dist/chunk-B7SWRFUT.js +332 -0
- package/dist/chunk-BGN5ZMP4.js +26 -0
- package/dist/chunk-BTU3TJDS.js +365 -0
- package/dist/chunk-CHF5OHIA.js +61 -0
- package/dist/chunk-CMBFNA7L.js +27 -0
- package/dist/chunk-DAHRH4ON.js +331 -0
- package/dist/chunk-EBGIRKQY.js +616 -0
- package/dist/chunk-EUZND3CB.js +27 -0
- package/dist/chunk-F3FA4F32.js +292 -0
- package/dist/chunk-GCOK2LC3.js +282 -0
- package/dist/chunk-JAKHTMQU.js +1000 -0
- package/dist/chunk-JCI5M6U6.js +956 -0
- package/dist/chunk-KQPDEVVS.js +398 -0
- package/dist/chunk-NEKUBFPT.js +60 -0
- package/dist/chunk-NYVAC6P5.js +37 -0
- package/dist/chunk-OUZZEE4S.js +365 -0
- package/dist/chunk-P6W3STU4.js +2249 -0
- package/dist/chunk-PTQJDMRT.js +146 -0
- package/dist/chunk-QWZG56ET.js +2744 -0
- package/dist/chunk-TSOKIX5Z.js +654 -0
- package/dist/chunk-VMVDTCXB.js +712 -0
- package/dist/chunk-VRW3FULF.js +725 -0
- package/dist/chunk-WZSPOOER.js +84 -0
- package/dist/chunk-YT6HQ6AM.js +14 -0
- package/dist/chunk-ZD6OAMTH.js +277 -0
- package/dist/contracts-DDrwxvJ-.d.cts +245 -0
- package/dist/contracts-DDrwxvJ-.d.ts +245 -0
- package/dist/data.cjs +35 -2
- package/dist/data.d.cts +7 -0
- package/dist/data.d.ts +7 -0
- package/dist/data.js +9 -8
- package/dist/devtools.cjs +122 -0
- package/dist/devtools.d.cts +69 -461
- package/dist/devtools.d.ts +69 -461
- package/dist/devtools.js +127 -6
- package/dist/ecosystem.cjs +68 -23
- package/dist/ecosystem.d.cts +1 -1
- package/dist/ecosystem.d.ts +1 -1
- package/dist/ecosystem.js +10 -9
- package/dist/extras.cjs +1252 -82
- package/dist/extras.d.cts +5 -5
- package/dist/extras.d.ts +5 -5
- package/dist/extras.js +69 -24
- package/dist/index.cjs +708 -161
- package/dist/index.d.cts +397 -17
- package/dist/index.d.ts +397 -17
- package/dist/index.js +39 -17
- package/dist/introspect-BumjnBKr.d.cts +477 -0
- package/dist/introspect-CZrlcaYy.d.ts +477 -0
- package/dist/introspect-Cb0zgpi2.d.cts +477 -0
- package/dist/introspect-Y2xNXGSf.d.ts +477 -0
- package/dist/motion.js +4 -4
- package/dist/patterns.cjs +51 -2
- package/dist/patterns.d.cts +18 -8
- package/dist/patterns.d.ts +18 -8
- package/dist/patterns.js +7 -7
- package/dist/performance.js +4 -4
- package/dist/plugins.cjs +473 -98
- package/dist/plugins.d.cts +27 -4
- package/dist/plugins.d.ts +27 -4
- package/dist/plugins.js +156 -37
- package/dist/ssr-4PBXAOO3.js +40 -0
- package/dist/ssr-Do_SiVoL.d.cts +201 -0
- package/dist/ssr-Do_SiVoL.d.ts +201 -0
- package/dist/ssr.cjs +357 -77
- package/dist/ssr.d.cts +10 -1
- package/dist/ssr.d.ts +10 -1
- package/dist/ssr.js +13 -10
- package/dist/tagFactory-DaJ0YWX6.d.cts +47 -0
- package/dist/tagFactory-DaJ0YWX6.d.ts +47 -0
- package/dist/testing.cjs +233 -2
- package/dist/testing.d.cts +42 -1
- package/dist/testing.d.ts +42 -1
- package/dist/testing.js +129 -2
- package/dist/ui.cjs +374 -3
- package/dist/ui.d.cts +252 -2
- package/dist/ui.d.ts +252 -2
- package/dist/ui.js +328 -8
- package/dist/widgets.js +7 -7
- package/package.json +1 -1
package/dist/ssr.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { T as TrustedHTML, c as collectStream, d as deserializeState, h as hydrate,
|
|
1
|
+
export { H as HydrateOptions, a as HydrationMismatch, T as TrustedHTML, c as collectStream, d as deserializeState, e as escapeScriptJson, h as hydrate, b as hydrateIslands, f as hydrateProgressively, i as island, r as renderToDocument, g as renderToReadableStream, j as renderToStream, k as renderToString, l as renderToSuspenseStream, m as resetSSRState, s as serializeState, n as ssrSuspense, o as suspenseSwapScript, t as trustHTML } from './ssr-Do_SiVoL.cjs';
|
|
2
2
|
|
|
3
3
|
interface HeadProps {
|
|
4
4
|
title?: string | (() => string);
|
|
@@ -18,6 +18,15 @@ interface HeadProps {
|
|
|
18
18
|
declare function Head(props: HeadProps): Comment;
|
|
19
19
|
/**
|
|
20
20
|
* Sets structured data (JSON-LD) for SEO.
|
|
21
|
+
*
|
|
22
|
+
* Security: the serialized JSON is passed through `escapeScriptJsonLocal`
|
|
23
|
+
* which unicode-escapes `<`, `>`, `&`, `U+2028`, and `U+2029`. This is
|
|
24
|
+
* defense-in-depth: when the element is inserted via `document.createElement`
|
|
25
|
+
* + `textContent` the browser will NOT re-parse the body, so `</script>`
|
|
26
|
+
* cannot break out of the tag at insertion time. However, tools that
|
|
27
|
+
* later serialize `document.head.innerHTML` DO re-parse, and the server
|
|
28
|
+
* side of any SSR roundtrip would see the raw text. Escaping here makes
|
|
29
|
+
* both paths safe.
|
|
21
30
|
*/
|
|
22
31
|
declare function setStructuredData(data: Record<string, unknown>): void;
|
|
23
32
|
/**
|
package/dist/ssr.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { T as TrustedHTML, c as collectStream, d as deserializeState, h as hydrate,
|
|
1
|
+
export { H as HydrateOptions, a as HydrationMismatch, T as TrustedHTML, c as collectStream, d as deserializeState, e as escapeScriptJson, h as hydrate, b as hydrateIslands, f as hydrateProgressively, i as island, r as renderToDocument, g as renderToReadableStream, j as renderToStream, k as renderToString, l as renderToSuspenseStream, m as resetSSRState, s as serializeState, n as ssrSuspense, o as suspenseSwapScript, t as trustHTML } from './ssr-Do_SiVoL.js';
|
|
2
2
|
|
|
3
3
|
interface HeadProps {
|
|
4
4
|
title?: string | (() => string);
|
|
@@ -18,6 +18,15 @@ interface HeadProps {
|
|
|
18
18
|
declare function Head(props: HeadProps): Comment;
|
|
19
19
|
/**
|
|
20
20
|
* Sets structured data (JSON-LD) for SEO.
|
|
21
|
+
*
|
|
22
|
+
* Security: the serialized JSON is passed through `escapeScriptJsonLocal`
|
|
23
|
+
* which unicode-escapes `<`, `>`, `&`, `U+2028`, and `U+2029`. This is
|
|
24
|
+
* defense-in-depth: when the element is inserted via `document.createElement`
|
|
25
|
+
* + `textContent` the browser will NOT re-parse the body, so `</script>`
|
|
26
|
+
* cannot break out of the tag at insertion time. However, tools that
|
|
27
|
+
* later serialize `document.head.innerHTML` DO re-parse, and the server
|
|
28
|
+
* side of any SSR roundtrip would see the raw text. Escaping here makes
|
|
29
|
+
* both paths safe.
|
|
21
30
|
*/
|
|
22
31
|
declare function setStructuredData(data: Record<string, unknown>): void;
|
|
23
32
|
/**
|
package/dist/ssr.js
CHANGED
|
@@ -22,10 +22,11 @@ import {
|
|
|
22
22
|
wasm,
|
|
23
23
|
worker,
|
|
24
24
|
workerFn
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-2BYQDGN3.js";
|
|
26
26
|
import {
|
|
27
27
|
collectStream,
|
|
28
28
|
deserializeState,
|
|
29
|
+
escapeScriptJson,
|
|
29
30
|
hydrate,
|
|
30
31
|
hydrateIslands,
|
|
31
32
|
hydrateProgressively,
|
|
@@ -40,15 +41,16 @@ import {
|
|
|
40
41
|
ssrSuspense,
|
|
41
42
|
suspenseSwapScript,
|
|
42
43
|
trustHTML
|
|
43
|
-
} from "./chunk-
|
|
44
|
-
import "./chunk-
|
|
45
|
-
import "./chunk-
|
|
46
|
-
import "./chunk-
|
|
47
|
-
import "./chunk-
|
|
48
|
-
import "./chunk-
|
|
49
|
-
import "./chunk-
|
|
50
|
-
import "./chunk-
|
|
51
|
-
import "./chunk-
|
|
44
|
+
} from "./chunk-3X2YG6YM.js";
|
|
45
|
+
import "./chunk-32DY64NT.js";
|
|
46
|
+
import "./chunk-F3FA4F32.js";
|
|
47
|
+
import "./chunk-PTQJDMRT.js";
|
|
48
|
+
import "./chunk-CMBFNA7L.js";
|
|
49
|
+
import "./chunk-CHF5OHIA.js";
|
|
50
|
+
import "./chunk-EUZND3CB.js";
|
|
51
|
+
import "./chunk-WZSPOOER.js";
|
|
52
|
+
import "./chunk-ZD6OAMTH.js";
|
|
53
|
+
import "./chunk-5X6PP2UK.js";
|
|
52
54
|
export {
|
|
53
55
|
Head,
|
|
54
56
|
clearWasmCache,
|
|
@@ -63,6 +65,7 @@ export {
|
|
|
63
65
|
createWorkerPool,
|
|
64
66
|
defineRemoteComponent,
|
|
65
67
|
deserializeState,
|
|
68
|
+
escapeScriptJson,
|
|
66
69
|
generateStaticSite,
|
|
67
70
|
hydrate,
|
|
68
71
|
hydrateIslands,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
type NodeChild = Node | Element | Text | Comment | string | number | boolean | (() => NodeChild) | null | undefined;
|
|
2
|
+
type NodeChildren = NodeChild | NodeChild[] | NodeChild[][] | (() => NodeChild | NodeChild[]);
|
|
3
|
+
|
|
4
|
+
declare const SVG_NS = "http://www.w3.org/2000/svg";
|
|
5
|
+
interface TagProps {
|
|
6
|
+
id?: string;
|
|
7
|
+
class?: string | (() => string) | Record<string, boolean | (() => boolean)>;
|
|
8
|
+
style?: Record<string, string | number | (() => string | number)> | string | (() => string);
|
|
9
|
+
ref?: {
|
|
10
|
+
current: Element | null;
|
|
11
|
+
};
|
|
12
|
+
nodes?: NodeChildren;
|
|
13
|
+
on?: Record<string, (ev: Event) => void>;
|
|
14
|
+
/** Called with the element after creation — useful for imperative bindings */
|
|
15
|
+
onElement?: (el: HTMLElement) => void;
|
|
16
|
+
[attr: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Factory for creating HTML or SVG elements with reactive props and nodes.
|
|
20
|
+
*
|
|
21
|
+
* Calling conventions:
|
|
22
|
+
*
|
|
23
|
+
* tag() empty element
|
|
24
|
+
* tag("text") element with text content
|
|
25
|
+
* tag(42) element with numeric text content
|
|
26
|
+
* tag([childA, childB]) element with children (array)
|
|
27
|
+
* tag(node) element wrapping a single existing node
|
|
28
|
+
* tag(getter) element with a reactive child
|
|
29
|
+
* tag("className", children) positional: class + children
|
|
30
|
+
* tag({ ...props }) full props object (children via props.nodes)
|
|
31
|
+
* tag({ ...props }, children) props + children (no need for `nodes:` key!)
|
|
32
|
+
*
|
|
33
|
+
* The last form is the "deeply-nested shorthand" the codebase favours:
|
|
34
|
+
*
|
|
35
|
+
* div({ class: "card" }, [
|
|
36
|
+
* h1({ class: "title" }, "Hello"),
|
|
37
|
+
* p({ class: "body" }, "World"),
|
|
38
|
+
* div({ class: "row" }, [
|
|
39
|
+
* span({ id: "x" }, "child"),
|
|
40
|
+
* ]),
|
|
41
|
+
* ])
|
|
42
|
+
*
|
|
43
|
+
* `children` overrides `props.nodes` when both are present.
|
|
44
|
+
*/
|
|
45
|
+
declare const tagFactory: (tag: string, ns?: string) => (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
|
|
46
|
+
|
|
47
|
+
export { type NodeChildren as N, SVG_NS as S, type TagProps as T, type NodeChild as a, tagFactory as t };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
type NodeChild = Node | Element | Text | Comment | string | number | boolean | (() => NodeChild) | null | undefined;
|
|
2
|
+
type NodeChildren = NodeChild | NodeChild[] | NodeChild[][] | (() => NodeChild | NodeChild[]);
|
|
3
|
+
|
|
4
|
+
declare const SVG_NS = "http://www.w3.org/2000/svg";
|
|
5
|
+
interface TagProps {
|
|
6
|
+
id?: string;
|
|
7
|
+
class?: string | (() => string) | Record<string, boolean | (() => boolean)>;
|
|
8
|
+
style?: Record<string, string | number | (() => string | number)> | string | (() => string);
|
|
9
|
+
ref?: {
|
|
10
|
+
current: Element | null;
|
|
11
|
+
};
|
|
12
|
+
nodes?: NodeChildren;
|
|
13
|
+
on?: Record<string, (ev: Event) => void>;
|
|
14
|
+
/** Called with the element after creation — useful for imperative bindings */
|
|
15
|
+
onElement?: (el: HTMLElement) => void;
|
|
16
|
+
[attr: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Factory for creating HTML or SVG elements with reactive props and nodes.
|
|
20
|
+
*
|
|
21
|
+
* Calling conventions:
|
|
22
|
+
*
|
|
23
|
+
* tag() empty element
|
|
24
|
+
* tag("text") element with text content
|
|
25
|
+
* tag(42) element with numeric text content
|
|
26
|
+
* tag([childA, childB]) element with children (array)
|
|
27
|
+
* tag(node) element wrapping a single existing node
|
|
28
|
+
* tag(getter) element with a reactive child
|
|
29
|
+
* tag("className", children) positional: class + children
|
|
30
|
+
* tag({ ...props }) full props object (children via props.nodes)
|
|
31
|
+
* tag({ ...props }, children) props + children (no need for `nodes:` key!)
|
|
32
|
+
*
|
|
33
|
+
* The last form is the "deeply-nested shorthand" the codebase favours:
|
|
34
|
+
*
|
|
35
|
+
* div({ class: "card" }, [
|
|
36
|
+
* h1({ class: "title" }, "Hello"),
|
|
37
|
+
* p({ class: "body" }, "World"),
|
|
38
|
+
* div({ class: "row" }, [
|
|
39
|
+
* span({ id: "x" }, "child"),
|
|
40
|
+
* ]),
|
|
41
|
+
* ])
|
|
42
|
+
*
|
|
43
|
+
* `children` overrides `props.nodes` when both are present.
|
|
44
|
+
*/
|
|
45
|
+
declare const tagFactory: (tag: string, ns?: string) => (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
|
|
46
|
+
|
|
47
|
+
export { type NodeChildren as N, SVG_NS as S, type TagProps as T, type NodeChild as a, tagFactory as t };
|
package/dist/testing.cjs
CHANGED
|
@@ -45,14 +45,23 @@ __export(testing_exports, {
|
|
|
45
45
|
createTimerMock: () => createTimerMock,
|
|
46
46
|
createUniversalAdapter: () => createUniversalAdapter,
|
|
47
47
|
createVisualSuite: () => createVisualSuite,
|
|
48
|
+
findByRole: () => findByRole,
|
|
49
|
+
findByTestId: () => findByTestId,
|
|
50
|
+
findByText: () => findByText,
|
|
48
51
|
fireEvent: () => fireEvent,
|
|
49
52
|
matchSnapshot: () => matchSnapshot,
|
|
50
53
|
mockRouter: () => mockRouter,
|
|
51
54
|
mockStore: () => mockStore,
|
|
55
|
+
queryByLabel: () => queryByLabel,
|
|
56
|
+
queryByRole: () => queryByRole,
|
|
57
|
+
queryByTestId: () => queryByTestId,
|
|
58
|
+
queryByText: () => queryByText,
|
|
52
59
|
render: () => render,
|
|
53
60
|
snapshotComponent: () => snapshotComponent,
|
|
54
61
|
testComponent: () => testComponent,
|
|
55
|
-
|
|
62
|
+
type: () => type,
|
|
63
|
+
waitFor: () => waitFor,
|
|
64
|
+
waitForSignal: () => waitForSignal
|
|
56
65
|
});
|
|
57
66
|
module.exports = __toCommonJS(testing_exports);
|
|
58
67
|
|
|
@@ -1445,6 +1454,219 @@ function testComponent(component, options = {}) {
|
|
|
1445
1454
|
};
|
|
1446
1455
|
}
|
|
1447
1456
|
|
|
1457
|
+
// src/core/dev.ts
|
|
1458
|
+
function isDev() {
|
|
1459
|
+
return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
1460
|
+
}
|
|
1461
|
+
var _isDev = isDev();
|
|
1462
|
+
function devAssert(condition, message) {
|
|
1463
|
+
if (_isDev && !condition) {
|
|
1464
|
+
throw new Error(`[Sibu] ${message}`);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
// src/reactivity/track.ts
|
|
1469
|
+
var _isDev2 = isDev();
|
|
1470
|
+
var subscriberStack = new Array(32);
|
|
1471
|
+
var stackCapacity = 32;
|
|
1472
|
+
var stackTop = -1;
|
|
1473
|
+
var currentSubscriber = null;
|
|
1474
|
+
var SUBS = "__s";
|
|
1475
|
+
function track(effectFn, subscriber) {
|
|
1476
|
+
if (!subscriber) subscriber = effectFn;
|
|
1477
|
+
cleanup(subscriber);
|
|
1478
|
+
++stackTop;
|
|
1479
|
+
if (stackTop >= stackCapacity) {
|
|
1480
|
+
stackCapacity *= 2;
|
|
1481
|
+
subscriberStack.length = stackCapacity;
|
|
1482
|
+
}
|
|
1483
|
+
subscriberStack[stackTop] = subscriber;
|
|
1484
|
+
currentSubscriber = subscriber;
|
|
1485
|
+
try {
|
|
1486
|
+
effectFn();
|
|
1487
|
+
} finally {
|
|
1488
|
+
stackTop--;
|
|
1489
|
+
currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
|
|
1490
|
+
}
|
|
1491
|
+
return () => cleanup(subscriber);
|
|
1492
|
+
}
|
|
1493
|
+
function cleanup(subscriber) {
|
|
1494
|
+
const sub = subscriber;
|
|
1495
|
+
const singleDep = sub._dep;
|
|
1496
|
+
if (singleDep !== void 0) {
|
|
1497
|
+
const subs = singleDep[SUBS];
|
|
1498
|
+
if (subs) {
|
|
1499
|
+
subs.delete(subscriber);
|
|
1500
|
+
if (singleDep.__f === subscriber) {
|
|
1501
|
+
singleDep.__f = void 0;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
sub._dep = void 0;
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
const deps = sub._deps;
|
|
1508
|
+
if (!deps || deps.size === 0) return;
|
|
1509
|
+
for (const signal of deps) {
|
|
1510
|
+
const subs = signal[SUBS];
|
|
1511
|
+
if (subs) {
|
|
1512
|
+
subs.delete(subscriber);
|
|
1513
|
+
if (signal.__f === subscriber) {
|
|
1514
|
+
signal.__f = void 0;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
deps.clear();
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// src/core/ssr-context.ts
|
|
1522
|
+
var ssrMode = false;
|
|
1523
|
+
function isSSR() {
|
|
1524
|
+
return ssrMode;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
// src/core/signals/effect.ts
|
|
1528
|
+
var _g = globalThis;
|
|
1529
|
+
function effect(effectFn, options) {
|
|
1530
|
+
devAssert(typeof effectFn === "function", "effect: argument must be a function.");
|
|
1531
|
+
if (isSSR()) return () => {
|
|
1532
|
+
};
|
|
1533
|
+
const onError = options?.onError;
|
|
1534
|
+
const wrappedFn = onError ? () => {
|
|
1535
|
+
try {
|
|
1536
|
+
effectFn();
|
|
1537
|
+
} catch (err) {
|
|
1538
|
+
onError(err);
|
|
1539
|
+
}
|
|
1540
|
+
} : effectFn;
|
|
1541
|
+
let cleanupHandle = () => {
|
|
1542
|
+
};
|
|
1543
|
+
const subscriber = () => {
|
|
1544
|
+
cleanupHandle();
|
|
1545
|
+
cleanupHandle = track(wrappedFn, subscriber);
|
|
1546
|
+
};
|
|
1547
|
+
cleanupHandle = track(wrappedFn, subscriber);
|
|
1548
|
+
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1549
|
+
if (hook) hook.emit("effect:create", { effectFn });
|
|
1550
|
+
return () => {
|
|
1551
|
+
const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1552
|
+
if (h) h.emit("effect:destroy", { effectFn });
|
|
1553
|
+
cleanupHandle();
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
// src/testing/queries.ts
|
|
1558
|
+
function queryByText(container, text) {
|
|
1559
|
+
const walk = (node) => {
|
|
1560
|
+
if (node.childNodes.length === 1 && node.childNodes[0].nodeType === 3) {
|
|
1561
|
+
if (node.textContent?.includes(text)) return node;
|
|
1562
|
+
}
|
|
1563
|
+
for (const child of Array.from(node.children)) {
|
|
1564
|
+
const found = walk(child);
|
|
1565
|
+
if (found) return found;
|
|
1566
|
+
}
|
|
1567
|
+
return null;
|
|
1568
|
+
};
|
|
1569
|
+
return walk(container);
|
|
1570
|
+
}
|
|
1571
|
+
function queryByTestId(container, testId) {
|
|
1572
|
+
return container.querySelector(`[data-testid="${testId}"]`);
|
|
1573
|
+
}
|
|
1574
|
+
function queryByRole(container, role) {
|
|
1575
|
+
return container.querySelector(`[role="${role}"]`);
|
|
1576
|
+
}
|
|
1577
|
+
function cssEscape(value) {
|
|
1578
|
+
const g = globalThis;
|
|
1579
|
+
if (g.CSS && typeof g.CSS.escape === "function") return g.CSS.escape(value);
|
|
1580
|
+
return value.replace(/[^\w-]/g, (m) => `\\${m.charCodeAt(0).toString(16)} `);
|
|
1581
|
+
}
|
|
1582
|
+
function queryByLabel(container, labelText) {
|
|
1583
|
+
const labels = Array.from(container.querySelectorAll("label"));
|
|
1584
|
+
for (const label of labels) {
|
|
1585
|
+
if (label.textContent?.trim() === labelText) {
|
|
1586
|
+
const forId = label.getAttribute("for");
|
|
1587
|
+
if (forId) {
|
|
1588
|
+
const target = container.querySelector(`#${cssEscape(forId)}`);
|
|
1589
|
+
if (target) return target;
|
|
1590
|
+
}
|
|
1591
|
+
const child = label.querySelector("input, select, textarea, button");
|
|
1592
|
+
if (child) return child;
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
return container.querySelector(`[aria-label="${labelText}"]`);
|
|
1596
|
+
}
|
|
1597
|
+
async function pollUntil(fn, timeout, interval, errorIfTimeout) {
|
|
1598
|
+
const start = Date.now();
|
|
1599
|
+
return new Promise((resolve, reject) => {
|
|
1600
|
+
const check = () => {
|
|
1601
|
+
const result = fn();
|
|
1602
|
+
if (result !== null) {
|
|
1603
|
+
resolve(result);
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
if (Date.now() - start >= timeout) {
|
|
1607
|
+
reject(new Error(errorIfTimeout));
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
setTimeout(check, interval);
|
|
1611
|
+
};
|
|
1612
|
+
check();
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
function findByText(container, text, options = {}) {
|
|
1616
|
+
return pollUntil(
|
|
1617
|
+
() => queryByText(container, text),
|
|
1618
|
+
options.timeout ?? 1e3,
|
|
1619
|
+
options.interval ?? 50,
|
|
1620
|
+
`findByText: no element with text "${text}" after ${options.timeout ?? 1e3}ms`
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
function findByTestId(container, testId, options = {}) {
|
|
1624
|
+
return pollUntil(
|
|
1625
|
+
() => queryByTestId(container, testId),
|
|
1626
|
+
options.timeout ?? 1e3,
|
|
1627
|
+
options.interval ?? 50,
|
|
1628
|
+
`findByTestId: no element with data-testid="${testId}" after ${options.timeout ?? 1e3}ms`
|
|
1629
|
+
);
|
|
1630
|
+
}
|
|
1631
|
+
function findByRole(container, role, options = {}) {
|
|
1632
|
+
return pollUntil(
|
|
1633
|
+
() => queryByRole(container, role),
|
|
1634
|
+
options.timeout ?? 1e3,
|
|
1635
|
+
options.interval ?? 50,
|
|
1636
|
+
`findByRole: no element with role="${role}" after ${options.timeout ?? 1e3}ms`
|
|
1637
|
+
);
|
|
1638
|
+
}
|
|
1639
|
+
function waitForSignal(getter, predicate, options = {}) {
|
|
1640
|
+
const timeoutMs = options.timeout ?? 1e3;
|
|
1641
|
+
return new Promise((resolve, reject) => {
|
|
1642
|
+
let resolved = false;
|
|
1643
|
+
const timer = setTimeout(() => {
|
|
1644
|
+
if (!resolved) {
|
|
1645
|
+
resolved = true;
|
|
1646
|
+
teardown();
|
|
1647
|
+
reject(new Error(`waitForSignal: predicate did not match within ${timeoutMs}ms`));
|
|
1648
|
+
}
|
|
1649
|
+
}, timeoutMs);
|
|
1650
|
+
const teardown = effect(() => {
|
|
1651
|
+
if (resolved) return;
|
|
1652
|
+
const value = getter();
|
|
1653
|
+
if (predicate(value)) {
|
|
1654
|
+
resolved = true;
|
|
1655
|
+
clearTimeout(timer);
|
|
1656
|
+
queueMicrotask(() => teardown());
|
|
1657
|
+
resolve(value);
|
|
1658
|
+
}
|
|
1659
|
+
});
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
function type(element, text) {
|
|
1663
|
+
for (const char of text) {
|
|
1664
|
+
element.value += char;
|
|
1665
|
+
element.dispatchEvent(new InputEvent("input", { bubbles: true, data: char }));
|
|
1666
|
+
}
|
|
1667
|
+
element.dispatchEvent(new Event("change", { bubbles: true }));
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1448
1670
|
// src/testing/snapshot.ts
|
|
1449
1671
|
function serializeElement2(el, indent) {
|
|
1450
1672
|
const pad = " ".repeat(indent);
|
|
@@ -1908,12 +2130,21 @@ function mockStore(initialState) {
|
|
|
1908
2130
|
createTimerMock,
|
|
1909
2131
|
createUniversalAdapter,
|
|
1910
2132
|
createVisualSuite,
|
|
2133
|
+
findByRole,
|
|
2134
|
+
findByTestId,
|
|
2135
|
+
findByText,
|
|
1911
2136
|
fireEvent,
|
|
1912
2137
|
matchSnapshot,
|
|
1913
2138
|
mockRouter,
|
|
1914
2139
|
mockStore,
|
|
2140
|
+
queryByLabel,
|
|
2141
|
+
queryByRole,
|
|
2142
|
+
queryByTestId,
|
|
2143
|
+
queryByText,
|
|
1915
2144
|
render,
|
|
1916
2145
|
snapshotComponent,
|
|
1917
2146
|
testComponent,
|
|
1918
|
-
|
|
2147
|
+
type,
|
|
2148
|
+
waitFor,
|
|
2149
|
+
waitForSignal
|
|
1919
2150
|
});
|
package/dist/testing.d.cts
CHANGED
|
@@ -315,6 +315,47 @@ declare function testComponent(component: (() => HTMLElement) | HTMLElement, opt
|
|
|
315
315
|
destroy: () => void;
|
|
316
316
|
};
|
|
317
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Find an element by its exact or substring text content. Returns
|
|
320
|
+
* `null` if no match is found — unlike `getByText`, does not throw.
|
|
321
|
+
*/
|
|
322
|
+
declare function queryByText(container: HTMLElement, text: string): HTMLElement | null;
|
|
323
|
+
declare function queryByTestId(container: HTMLElement, testId: string): HTMLElement | null;
|
|
324
|
+
declare function queryByRole(container: HTMLElement, role: string): HTMLElement | null;
|
|
325
|
+
declare function queryByLabel(container: HTMLElement, labelText: string): HTMLElement | null;
|
|
326
|
+
interface FindOptions {
|
|
327
|
+
timeout?: number;
|
|
328
|
+
interval?: number;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Resolve with the first element whose text matches, polling until
|
|
332
|
+
* `timeout` ms elapse. Useful for async content (data fetching,
|
|
333
|
+
* transitions, etc.) that appears after the initial render.
|
|
334
|
+
*/
|
|
335
|
+
declare function findByText(container: HTMLElement, text: string, options?: FindOptions): Promise<HTMLElement>;
|
|
336
|
+
declare function findByTestId(container: HTMLElement, testId: string, options?: FindOptions): Promise<HTMLElement>;
|
|
337
|
+
declare function findByRole(container: HTMLElement, role: string, options?: FindOptions): Promise<HTMLElement>;
|
|
338
|
+
/**
|
|
339
|
+
* Wait until a reactive getter satisfies a predicate. Unlike `waitFor`,
|
|
340
|
+
* this subscribes to the getter so it reacts immediately on signal
|
|
341
|
+
* updates rather than polling. Falls back to a `timeout` rejection.
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```ts
|
|
345
|
+
* await waitForSignal(() => loading(), (v) => v === false);
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
declare function waitForSignal<T>(getter: () => T, predicate: (value: T) => boolean, options?: {
|
|
349
|
+
timeout?: number;
|
|
350
|
+
}): Promise<T>;
|
|
351
|
+
/**
|
|
352
|
+
* Type a full string into an input, dispatching an input event after
|
|
353
|
+
* each character. This is closer to real user input than a single
|
|
354
|
+
* `fireEvent.input(el, value)` call and catches handlers that only
|
|
355
|
+
* run on specific event shapes.
|
|
356
|
+
*/
|
|
357
|
+
declare function type(element: HTMLInputElement | HTMLTextAreaElement, text: string): void;
|
|
358
|
+
|
|
318
359
|
/**
|
|
319
360
|
* Snapshot testing utilities for SibuJS components.
|
|
320
361
|
* Capture and compare component output over time.
|
|
@@ -488,4 +529,4 @@ declare function mockStore<T extends Record<string, unknown>>(initialState: T):
|
|
|
488
529
|
reset: () => void;
|
|
489
530
|
};
|
|
490
531
|
|
|
491
|
-
export { type A11yCheckResult, type A11yViolation, type A11yViolationLevel, type FingerprintChange, type MockResponse, type MockRoute, type VisualFingerprint, assertA11y, assertDOMEquals, captureFingerprint, checkA11y, checkAriaAttributes, checkColorContrast, checkFormLabels, checkHeadingHierarchy, checkImageAlt, checkKeyboardAccess, checkLandmarks, checkLinksAndButtons, checkListSemantics, checkTabOrder, compareFingerprints, createCypressAdapter, createDOMSnapshot, createHttpMock, createJestAdapter, createPlaywrightAdapter, createSnapshotMatcher, createSnapshotStore, createTimerMock, createUniversalAdapter, createVisualSuite, fireEvent, matchSnapshot, mockRouter, mockStore, render, snapshotComponent, testComponent, waitFor };
|
|
532
|
+
export { type A11yCheckResult, type A11yViolation, type A11yViolationLevel, type FindOptions, type FingerprintChange, type MockResponse, type MockRoute, type VisualFingerprint, assertA11y, assertDOMEquals, captureFingerprint, checkA11y, checkAriaAttributes, checkColorContrast, checkFormLabels, checkHeadingHierarchy, checkImageAlt, checkKeyboardAccess, checkLandmarks, checkLinksAndButtons, checkListSemantics, checkTabOrder, compareFingerprints, createCypressAdapter, createDOMSnapshot, createHttpMock, createJestAdapter, createPlaywrightAdapter, createSnapshotMatcher, createSnapshotStore, createTimerMock, createUniversalAdapter, createVisualSuite, findByRole, findByTestId, findByText, fireEvent, matchSnapshot, mockRouter, mockStore, queryByLabel, queryByRole, queryByTestId, queryByText, render, snapshotComponent, testComponent, type, waitFor, waitForSignal };
|
package/dist/testing.d.ts
CHANGED
|
@@ -315,6 +315,47 @@ declare function testComponent(component: (() => HTMLElement) | HTMLElement, opt
|
|
|
315
315
|
destroy: () => void;
|
|
316
316
|
};
|
|
317
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Find an element by its exact or substring text content. Returns
|
|
320
|
+
* `null` if no match is found — unlike `getByText`, does not throw.
|
|
321
|
+
*/
|
|
322
|
+
declare function queryByText(container: HTMLElement, text: string): HTMLElement | null;
|
|
323
|
+
declare function queryByTestId(container: HTMLElement, testId: string): HTMLElement | null;
|
|
324
|
+
declare function queryByRole(container: HTMLElement, role: string): HTMLElement | null;
|
|
325
|
+
declare function queryByLabel(container: HTMLElement, labelText: string): HTMLElement | null;
|
|
326
|
+
interface FindOptions {
|
|
327
|
+
timeout?: number;
|
|
328
|
+
interval?: number;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Resolve with the first element whose text matches, polling until
|
|
332
|
+
* `timeout` ms elapse. Useful for async content (data fetching,
|
|
333
|
+
* transitions, etc.) that appears after the initial render.
|
|
334
|
+
*/
|
|
335
|
+
declare function findByText(container: HTMLElement, text: string, options?: FindOptions): Promise<HTMLElement>;
|
|
336
|
+
declare function findByTestId(container: HTMLElement, testId: string, options?: FindOptions): Promise<HTMLElement>;
|
|
337
|
+
declare function findByRole(container: HTMLElement, role: string, options?: FindOptions): Promise<HTMLElement>;
|
|
338
|
+
/**
|
|
339
|
+
* Wait until a reactive getter satisfies a predicate. Unlike `waitFor`,
|
|
340
|
+
* this subscribes to the getter so it reacts immediately on signal
|
|
341
|
+
* updates rather than polling. Falls back to a `timeout` rejection.
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```ts
|
|
345
|
+
* await waitForSignal(() => loading(), (v) => v === false);
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
declare function waitForSignal<T>(getter: () => T, predicate: (value: T) => boolean, options?: {
|
|
349
|
+
timeout?: number;
|
|
350
|
+
}): Promise<T>;
|
|
351
|
+
/**
|
|
352
|
+
* Type a full string into an input, dispatching an input event after
|
|
353
|
+
* each character. This is closer to real user input than a single
|
|
354
|
+
* `fireEvent.input(el, value)` call and catches handlers that only
|
|
355
|
+
* run on specific event shapes.
|
|
356
|
+
*/
|
|
357
|
+
declare function type(element: HTMLInputElement | HTMLTextAreaElement, text: string): void;
|
|
358
|
+
|
|
318
359
|
/**
|
|
319
360
|
* Snapshot testing utilities for SibuJS components.
|
|
320
361
|
* Capture and compare component output over time.
|
|
@@ -488,4 +529,4 @@ declare function mockStore<T extends Record<string, unknown>>(initialState: T):
|
|
|
488
529
|
reset: () => void;
|
|
489
530
|
};
|
|
490
531
|
|
|
491
|
-
export { type A11yCheckResult, type A11yViolation, type A11yViolationLevel, type FingerprintChange, type MockResponse, type MockRoute, type VisualFingerprint, assertA11y, assertDOMEquals, captureFingerprint, checkA11y, checkAriaAttributes, checkColorContrast, checkFormLabels, checkHeadingHierarchy, checkImageAlt, checkKeyboardAccess, checkLandmarks, checkLinksAndButtons, checkListSemantics, checkTabOrder, compareFingerprints, createCypressAdapter, createDOMSnapshot, createHttpMock, createJestAdapter, createPlaywrightAdapter, createSnapshotMatcher, createSnapshotStore, createTimerMock, createUniversalAdapter, createVisualSuite, fireEvent, matchSnapshot, mockRouter, mockStore, render, snapshotComponent, testComponent, waitFor };
|
|
532
|
+
export { type A11yCheckResult, type A11yViolation, type A11yViolationLevel, type FindOptions, type FingerprintChange, type MockResponse, type MockRoute, type VisualFingerprint, assertA11y, assertDOMEquals, captureFingerprint, checkA11y, checkAriaAttributes, checkColorContrast, checkFormLabels, checkHeadingHierarchy, checkImageAlt, checkKeyboardAccess, checkLandmarks, checkLinksAndButtons, checkListSemantics, checkTabOrder, compareFingerprints, createCypressAdapter, createDOMSnapshot, createHttpMock, createJestAdapter, createPlaywrightAdapter, createSnapshotMatcher, createSnapshotStore, createTimerMock, createUniversalAdapter, createVisualSuite, findByRole, findByTestId, findByText, fireEvent, matchSnapshot, mockRouter, mockStore, queryByLabel, queryByRole, queryByTestId, queryByText, render, snapshotComponent, testComponent, type, waitFor, waitForSignal };
|