sibujs 1.2.0 → 1.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/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 +655 -237
- package/dist/build.js +15 -93
- 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-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-CNZ35WI2.js +178 -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-JAKHTMQU.js +1000 -0
- package/dist/chunk-JCI5M6U6.js +956 -0
- package/dist/chunk-KQPDEVVS.js +398 -0
- package/dist/chunk-M4NLBH4I.js +725 -0
- package/dist/chunk-NEKUBFPT.js +60 -0
- package/dist/chunk-NYVAC6P5.js +37 -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-UHNL42EF.js +2730 -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/chunk-ZWKZCBO6.js +317 -0
- package/dist/contracts-DDrwxvJ-.d.cts +245 -0
- package/dist/contracts-DDrwxvJ-.d.ts +245 -0
- package/dist/contracts-xo5ckdRP.d.cts +240 -0
- package/dist/contracts-xo5ckdRP.d.ts +240 -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 +23 -6
- package/dist/ecosystem.d.cts +1 -1
- package/dist/ecosystem.d.ts +1 -1
- package/dist/ecosystem.js +10 -9
- package/dist/extras.cjs +1208 -88
- package/dist/extras.d.cts +6 -6
- package/dist/extras.d.ts +6 -6
- package/dist/extras.js +70 -33
- package/dist/index.cjs +663 -158
- package/dist/index.d.cts +398 -40
- package/dist/index.d.ts +398 -40
- package/dist/index.js +39 -21
- 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 -24
- package/dist/patterns.d.cts +19 -57
- package/dist/patterns.d.ts +19 -57
- package/dist/patterns.js +8 -16
- package/dist/performance.js +4 -4
- package/dist/plugins.cjs +429 -82
- 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 +312 -60
- 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 -8
- package/dist/ui.d.cts +252 -2
- package/dist/ui.d.ts +252 -2
- package/dist/ui.js +329 -11
- package/dist/widgets.js +7 -7
- package/package.json +1 -1
|
@@ -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 };
|
package/dist/testing.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
effect
|
|
3
|
+
} from "./chunk-CHF5OHIA.js";
|
|
4
|
+
import "./chunk-EUZND3CB.js";
|
|
5
|
+
import "./chunk-ZD6OAMTH.js";
|
|
6
|
+
import "./chunk-5X6PP2UK.js";
|
|
2
7
|
|
|
3
8
|
// src/testing/a11y.ts
|
|
4
9
|
var VALID_ARIA_ROLES = /* @__PURE__ */ new Set([
|
|
@@ -1389,6 +1394,119 @@ function testComponent(component, options = {}) {
|
|
|
1389
1394
|
};
|
|
1390
1395
|
}
|
|
1391
1396
|
|
|
1397
|
+
// src/testing/queries.ts
|
|
1398
|
+
function queryByText(container, text) {
|
|
1399
|
+
const walk = (node) => {
|
|
1400
|
+
if (node.childNodes.length === 1 && node.childNodes[0].nodeType === 3) {
|
|
1401
|
+
if (node.textContent?.includes(text)) return node;
|
|
1402
|
+
}
|
|
1403
|
+
for (const child of Array.from(node.children)) {
|
|
1404
|
+
const found = walk(child);
|
|
1405
|
+
if (found) return found;
|
|
1406
|
+
}
|
|
1407
|
+
return null;
|
|
1408
|
+
};
|
|
1409
|
+
return walk(container);
|
|
1410
|
+
}
|
|
1411
|
+
function queryByTestId(container, testId) {
|
|
1412
|
+
return container.querySelector(`[data-testid="${testId}"]`);
|
|
1413
|
+
}
|
|
1414
|
+
function queryByRole(container, role) {
|
|
1415
|
+
return container.querySelector(`[role="${role}"]`);
|
|
1416
|
+
}
|
|
1417
|
+
function cssEscape(value) {
|
|
1418
|
+
const g = globalThis;
|
|
1419
|
+
if (g.CSS && typeof g.CSS.escape === "function") return g.CSS.escape(value);
|
|
1420
|
+
return value.replace(/[^\w-]/g, (m) => `\\${m.charCodeAt(0).toString(16)} `);
|
|
1421
|
+
}
|
|
1422
|
+
function queryByLabel(container, labelText) {
|
|
1423
|
+
const labels = Array.from(container.querySelectorAll("label"));
|
|
1424
|
+
for (const label of labels) {
|
|
1425
|
+
if (label.textContent?.trim() === labelText) {
|
|
1426
|
+
const forId = label.getAttribute("for");
|
|
1427
|
+
if (forId) {
|
|
1428
|
+
const target = container.querySelector(`#${cssEscape(forId)}`);
|
|
1429
|
+
if (target) return target;
|
|
1430
|
+
}
|
|
1431
|
+
const child = label.querySelector("input, select, textarea, button");
|
|
1432
|
+
if (child) return child;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
return container.querySelector(`[aria-label="${labelText}"]`);
|
|
1436
|
+
}
|
|
1437
|
+
async function pollUntil(fn, timeout, interval, errorIfTimeout) {
|
|
1438
|
+
const start = Date.now();
|
|
1439
|
+
return new Promise((resolve, reject) => {
|
|
1440
|
+
const check = () => {
|
|
1441
|
+
const result = fn();
|
|
1442
|
+
if (result !== null) {
|
|
1443
|
+
resolve(result);
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
if (Date.now() - start >= timeout) {
|
|
1447
|
+
reject(new Error(errorIfTimeout));
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
setTimeout(check, interval);
|
|
1451
|
+
};
|
|
1452
|
+
check();
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
function findByText(container, text, options = {}) {
|
|
1456
|
+
return pollUntil(
|
|
1457
|
+
() => queryByText(container, text),
|
|
1458
|
+
options.timeout ?? 1e3,
|
|
1459
|
+
options.interval ?? 50,
|
|
1460
|
+
`findByText: no element with text "${text}" after ${options.timeout ?? 1e3}ms`
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1463
|
+
function findByTestId(container, testId, options = {}) {
|
|
1464
|
+
return pollUntil(
|
|
1465
|
+
() => queryByTestId(container, testId),
|
|
1466
|
+
options.timeout ?? 1e3,
|
|
1467
|
+
options.interval ?? 50,
|
|
1468
|
+
`findByTestId: no element with data-testid="${testId}" after ${options.timeout ?? 1e3}ms`
|
|
1469
|
+
);
|
|
1470
|
+
}
|
|
1471
|
+
function findByRole(container, role, options = {}) {
|
|
1472
|
+
return pollUntil(
|
|
1473
|
+
() => queryByRole(container, role),
|
|
1474
|
+
options.timeout ?? 1e3,
|
|
1475
|
+
options.interval ?? 50,
|
|
1476
|
+
`findByRole: no element with role="${role}" after ${options.timeout ?? 1e3}ms`
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
function waitForSignal(getter, predicate, options = {}) {
|
|
1480
|
+
const timeoutMs = options.timeout ?? 1e3;
|
|
1481
|
+
return new Promise((resolve, reject) => {
|
|
1482
|
+
let resolved = false;
|
|
1483
|
+
const timer = setTimeout(() => {
|
|
1484
|
+
if (!resolved) {
|
|
1485
|
+
resolved = true;
|
|
1486
|
+
teardown();
|
|
1487
|
+
reject(new Error(`waitForSignal: predicate did not match within ${timeoutMs}ms`));
|
|
1488
|
+
}
|
|
1489
|
+
}, timeoutMs);
|
|
1490
|
+
const teardown = effect(() => {
|
|
1491
|
+
if (resolved) return;
|
|
1492
|
+
const value = getter();
|
|
1493
|
+
if (predicate(value)) {
|
|
1494
|
+
resolved = true;
|
|
1495
|
+
clearTimeout(timer);
|
|
1496
|
+
queueMicrotask(() => teardown());
|
|
1497
|
+
resolve(value);
|
|
1498
|
+
}
|
|
1499
|
+
});
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
function type(element, text) {
|
|
1503
|
+
for (const char of text) {
|
|
1504
|
+
element.value += char;
|
|
1505
|
+
element.dispatchEvent(new InputEvent("input", { bubbles: true, data: char }));
|
|
1506
|
+
}
|
|
1507
|
+
element.dispatchEvent(new Event("change", { bubbles: true }));
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1392
1510
|
// src/testing/snapshot.ts
|
|
1393
1511
|
function serializeElement2(el, indent) {
|
|
1394
1512
|
const pad = " ".repeat(indent);
|
|
@@ -1851,12 +1969,21 @@ export {
|
|
|
1851
1969
|
createTimerMock,
|
|
1852
1970
|
createUniversalAdapter,
|
|
1853
1971
|
createVisualSuite,
|
|
1972
|
+
findByRole,
|
|
1973
|
+
findByTestId,
|
|
1974
|
+
findByText,
|
|
1854
1975
|
fireEvent,
|
|
1855
1976
|
matchSnapshot,
|
|
1856
1977
|
mockRouter,
|
|
1857
1978
|
mockStore,
|
|
1979
|
+
queryByLabel,
|
|
1980
|
+
queryByRole,
|
|
1981
|
+
queryByTestId,
|
|
1982
|
+
queryByText,
|
|
1858
1983
|
render,
|
|
1859
1984
|
snapshotComponent,
|
|
1860
1985
|
testComponent,
|
|
1861
|
-
|
|
1986
|
+
type,
|
|
1987
|
+
waitFor,
|
|
1988
|
+
waitForSignal
|
|
1862
1989
|
};
|