Skip to content

Commit 7093b99

Browse files
committed
➕ add @testing-library/preact. ➕ add @testing-library/react. ➕ add
@testing-library/user-event. 🔧 ignore env instead of allowing it. 🎨 simplify test setup. ✅ simplify react and preact tests.
1 parent e31127f commit 7093b99

7 files changed

Lines changed: 72 additions & 161 deletions

File tree

@simulcast/preact/tests/useBroadcast.test.tsx

Lines changed: 26 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22
import { broadcast, type EventRegistry } from "@simulcast/core";
33
import { useBroadcast } from "@simulcast/preact";
44
import { assertStrictEquals } from "@std/assert";
5-
import { mockDOM } from "@test/mockDOM.ts";
6-
import { timeout } from "@test/timeout.ts";
7-
import { type ComponentProps, render, type TargetedMouseEvent } from "preact";
5+
import { cleanup, render, screen } from "@testing-library/preact";
6+
import { userEvent } from "@testing-library/user-event";
7+
import type { ComponentProps, TargetedMouseEvent } from "preact";
88
import { useState } from "preact/hooks";
99

1010
const CountComponent = (properties: ComponentProps<"button">) => {
1111
const [count, setCount] = useState(0);
1212

1313
return (
1414
<button
15-
className="add"
1615
onClick={() => setCount(count + 1)}
16+
title="Add"
1717
{...properties}
1818
>
1919
{count}
@@ -22,7 +22,6 @@ const CountComponent = (properties: ComponentProps<"button">) => {
2222
};
2323

2424
const BroadcastComponent = ({
25-
className,
2625
registry,
2726
state,
2827
...properties
@@ -40,22 +39,21 @@ const BroadcastComponent = ({
4039

4140
return (
4241
<button
43-
className={className ?? "broadcast"}
4442
onClick={emitClick}
43+
title="Broadcast"
4544
{...properties}
4645
>
4746
Click me!
4847
</button>
4948
);
5049
};
5150

51+
Deno.test.afterEach(cleanup);
52+
5253
Deno.test(
5354
"Broadcast's on handler is called once even when it re-renders",
5455
async () => {
55-
mockDOM({ fakeTimers: true });
56-
5756
const state = { calledTimes: 0 };
58-
const root = document.querySelector("#root") as HTMLDivElement;
5957
const { registry } = broadcast<{
6058
click: TargetedMouseEvent<HTMLButtonElement>;
6159
}>();
@@ -67,31 +65,24 @@ Deno.test(
6765
<CountComponent />
6866
</>
6967
),
70-
root,
7168
);
7269

73-
const addButton = document.querySelector<HTMLButtonElement>(
74-
"button.add",
75-
) as HTMLButtonElement;
76-
const broadcastButton = document.querySelector<HTMLButtonElement>(
77-
"button.broadcast",
78-
) as HTMLButtonElement;
79-
80-
addButton.click(); // Click button that will re-render once
81-
await timeout(10);
82-
addButton.click(); // Click button that will re-render twice
83-
await timeout(10);
84-
broadcastButton.click(); // Click broadcast button once
70+
const addButton = screen.getByTitle<HTMLButtonElement>("Add");
71+
const broadcastButton = screen.getByTitle<HTMLButtonElement>(
72+
"Broadcast",
73+
);
74+
75+
await userEvent.click(addButton); // Click button that will re-render once
76+
await userEvent.click(addButton); // Click button that will re-render twice
77+
await userEvent.click(broadcastButton); // Click broadcast button once
8578
assertStrictEquals(state.calledTimes, 1); // State should be updated once
8679
assertStrictEquals(addButton.textContent, "2"); // Even when it re-rendered twice
8780
},
8881
);
8982

9083
Deno.test("Broadcast's on handler is removed when unmounted", async () => {
91-
mockDOM({ fakeTimers: true });
9284
const state1 = { calledTimes: 0 };
9385
const state2 = { calledTimes: 0 };
94-
const root = document.querySelector("#root") as HTMLDivElement;
9586
const { registry } = broadcast<{
9687
click: TargetedMouseEvent<HTMLButtonElement>;
9788
}>();
@@ -111,13 +102,13 @@ Deno.test("Broadcast's on handler is removed when unmounted", async () => {
111102
)
112103
: undefined}
113104
<BroadcastComponent
114-
className="always-visible-broadcast"
115105
state={state2}
106+
title="Always visible Broadcast"
116107
{...{ registry }}
117108
/>
118109
<button
119-
className="toggle"
120110
onClick={() => setVisible(!visible)}
111+
title="Toggle"
121112
type="button"
122113
>
123114
Toggle Visibility
@@ -126,32 +117,22 @@ Deno.test("Broadcast's on handler is removed when unmounted", async () => {
126117
);
127118
};
128119

129-
render(<App />, root);
130-
131-
await timeout();
120+
render(<App />);
132121

133-
const toggleButton = document.querySelector<HTMLButtonElement>(
134-
"button.toggle",
135-
) as HTMLButtonElement;
136-
const broadcastButton = document.querySelector<HTMLButtonElement>(
137-
"button.broadcast",
138-
) as HTMLButtonElement;
139-
const alwaysVisibleBroadcastButton = document.querySelector<
122+
const toggleButton = screen.getByTitle<HTMLButtonElement>("Toggle");
123+
const broadcastButton = screen.getByTitle<HTMLButtonElement>("Broadcast");
124+
const alwaysVisibleBroadcastButton = screen.getByTitle<
140125
HTMLButtonElement
141-
>(
142-
"button.always-visible-broadcast",
143-
) as HTMLButtonElement;
126+
>("Always visible Broadcast");
144127

145-
await timeout();
146128
// Click broadcast button that will be removed from the DOM
147-
broadcastButton.click();
129+
await userEvent.click(broadcastButton);
148130
// Click broadcast button that will stay in the DOM
149-
alwaysVisibleBroadcastButton.click();
131+
await userEvent.click(alwaysVisibleBroadcastButton);
150132
// Click toggle button (removes the first broadcast button)
151-
toggleButton.click();
152-
await timeout();
133+
await userEvent.click(toggleButton);
153134
// Click broadcast button that stayed again
154-
alwaysVisibleBroadcastButton.click();
135+
await userEvent.click(alwaysVisibleBroadcastButton);
155136
assertStrictEquals(state1.calledTimes, 2); // State 1 should have registered events until removed
156137
assertStrictEquals(state2.calledTimes, 3); // State 2 should have registered all events
157138
});

@simulcast/react/tests/useBroadcast.test.tsx

Lines changed: 29 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,17 @@
22
import { broadcast, type EventRegistry } from "@simulcast/core";
33
import { useBroadcast } from "@simulcast/react";
44
import { assertStrictEquals } from "@std/assert";
5-
import { mockDOM } from "@test/mockDOM.ts";
6-
import { timeout } from "@test/timeout.ts";
5+
import { cleanup, render, screen } from "@testing-library/react";
6+
import { userEvent } from "@testing-library/user-event";
77
import { type ComponentProps, type MouseEvent, useState } from "react";
8-
import { createRoot } from "react-dom/client";
98

109
const CountComponent = (properties: ComponentProps<"button">) => {
1110
const [count, setCount] = useState(0);
1211

1312
return (
1413
<button
15-
className="add"
1614
onClick={() => setCount(count + 1)}
15+
title="Add"
1716
{...properties}
1817
>
1918
{count}
@@ -22,7 +21,6 @@ const CountComponent = (properties: ComponentProps<"button">) => {
2221
};
2322

2423
const BroadcastComponent = ({
25-
className,
2624
registry,
2725
state,
2826
...properties
@@ -38,29 +36,26 @@ const BroadcastComponent = ({
3836

3937
return (
4038
<button
41-
className={className ?? "broadcast"}
4239
onClick={emitClick}
40+
title="Broadcast"
4341
{...properties}
4442
>
4543
Click me!
4644
</button>
4745
);
4846
};
4947

48+
Deno.test.afterEach(cleanup);
49+
5050
Deno.test(
5151
"Broadcast's on handler is called once even when it re-renders",
5252
async () => {
53-
mockDOM();
54-
const root = createRoot(
55-
document.querySelector("#root") as HTMLDivElement,
56-
);
57-
5853
const state = { calledTimes: 0 };
5954
const { registry } = broadcast<{
6055
click: MouseEvent<HTMLButtonElement>;
6156
}>();
6257

63-
root.render(
58+
render(
6459
(
6560
<>
6661
<BroadcastComponent {...{ registry, state }} />
@@ -69,30 +64,20 @@ Deno.test(
6964
),
7065
);
7166

72-
await timeout(10);
73-
74-
const addButton = document.querySelector<HTMLButtonElement>(
75-
"button.add",
76-
) as HTMLButtonElement;
77-
const broadcastButton = document.querySelector<HTMLButtonElement>(
78-
"button.broadcast",
79-
) as HTMLButtonElement;
80-
81-
await timeout();
82-
addButton.click(); // Click button that will re-render once
83-
await timeout();
84-
addButton.click(); // Click button that will re-render twice
85-
await timeout();
86-
broadcastButton.click(); // Click broadcast button once
67+
const addButton = screen.getByTitle<HTMLButtonElement>("Add");
68+
const broadcastButton = screen.getByTitle<HTMLButtonElement>(
69+
"Broadcast",
70+
);
71+
72+
await userEvent.click(addButton); // Click button that will re-render once
73+
await userEvent.click(addButton); // Click button that will re-render twice
74+
await userEvent.click(broadcastButton); // Click broadcast button once
8775
assertStrictEquals(state.calledTimes, 1); // State should be updated once
8876
assertStrictEquals(addButton.textContent, "2"); // Even when it re-rendered twice
8977
},
9078
);
9179

9280
Deno.test("Broadcast's on handler is removed when unmounted", async () => {
93-
mockDOM();
94-
const root = createRoot(document.querySelector("#root") as HTMLDivElement);
95-
9681
const state1 = { calledTimes: 0 };
9782
const state2 = { calledTimes: 0 };
9883
const { registry } = broadcast<{
@@ -108,19 +93,19 @@ Deno.test("Broadcast's on handler is removed when unmounted", async () => {
10893
{visible
10994
? (
11095
<BroadcastComponent
111-
{...{ registry }}
11296
state={state1}
97+
{...{ registry }}
11398
/>
11499
)
115100
: undefined}
116101
<BroadcastComponent
117-
className="always-visible-broadcast"
118-
{...{ registry }}
119102
state={state2}
103+
title="Always visible Broadcast"
104+
{...{ registry }}
120105
/>
121106
<button
122-
className="toggle"
123107
onClick={() => setVisible(!visible)}
108+
title="Toggle"
124109
type="button"
125110
>
126111
Toggle Visibility
@@ -129,35 +114,23 @@ Deno.test("Broadcast's on handler is removed when unmounted", async () => {
129114
);
130115
};
131116

132-
root.render(<App />);
133-
134-
await timeout(10);
117+
render(<App />);
135118

136-
const toggleButton = document.querySelector<HTMLButtonElement>(
137-
"button.toggle",
138-
) as HTMLButtonElement;
139-
const broadcastButton = document.querySelector<HTMLButtonElement>(
140-
"button.broadcast",
141-
) as HTMLButtonElement;
142-
const alwaysVisibleBroadcastButton = document.querySelector<
119+
const toggleButton = screen.getByTitle<HTMLButtonElement>("Toggle");
120+
const broadcastButton = screen.getByTitle<HTMLButtonElement>("Broadcast");
121+
const alwaysVisibleBroadcastButton = screen.getByTitle<
143122
HTMLButtonElement
144-
>(
145-
"button.always-visible-broadcast",
146-
) as HTMLButtonElement;
123+
>("Always visible Broadcast");
147124

148125
// Click broadcast button that will be removed from the DOM
149-
await timeout();
150-
broadcastButton.click();
151-
await timeout();
126+
127+
await userEvent.click(broadcastButton);
152128
// Click broadcast button that will stay in the DOM
153-
alwaysVisibleBroadcastButton.click();
154-
await timeout();
129+
await userEvent.click(alwaysVisibleBroadcastButton);
155130
// Click toggle button (removes the first broadcast button)
156-
toggleButton.click();
157-
await timeout();
131+
await userEvent.click(toggleButton);
158132
// Click broadcast button that stayed again
159-
alwaysVisibleBroadcastButton.click();
160-
await timeout();
133+
await userEvent.click(alwaysVisibleBroadcastButton);
161134
assertStrictEquals(state1.calledTimes, 2); // State 1 should have registered events until removed
162135
assertStrictEquals(state2.calledTimes, 3); // State 2 should have registered all events
163136
});

deno.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@
4040
},
4141
"imports": {
4242
"@std/assert": "jsr:@std/assert@^1.0.16",
43-
"@test/": "./test/",
43+
"@testing-library/preact": "npm:@testing-library/preact@^3.2.4",
44+
"@testing-library/react": "npm:@testing-library/react@^16.3.1",
45+
"@testing-library/user-event": "npm:@testing-library/user-event@^14.6.1",
4446
"@types/react": "npm:@types/react@^19.2.7",
4547
"@types/react-dom": "npm:@types/react-dom@^19.2.3",
4648
"@vue/test-utils": "npm:@vue/test-utils@^2.4.6",
@@ -95,8 +97,8 @@
9597
"nodeModulesDir": "auto",
9698
"tasks": {
9799
"lint-docs": "deno doc --lint ./@coven/**/*.ts ./@simulcast/**/*.ts",
98-
"test": "deno test --allow-env=NODE_ENV --import=./test/setupTests.ts --parallel",
99-
"test-coverage": "deno test --allow-env=NODE_ENV --coverage --doc --import=./test/setupTests.ts --parallel --quiet"
100+
"test": "deno test --ignore-env --import=./setupTests.ts --parallel",
101+
"test-coverage": "deno test --ignore-env --coverage --doc --import=./setupTests.ts --parallel --quiet"
100102
},
101103
"workspace": [
102104
"./@coven/compare",

setupTests.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// deno-coverage-ignore-file
2+
import { Window } from "happy-dom";
3+
4+
const {
5+
Element, // Required by Vue
6+
Node, // Required by Vue
7+
SVGElement, // Required by Vue
8+
document,
9+
window,
10+
} = new Window();
11+
12+
Object.assign(globalThis, { window, Element, Node, SVGElement, document });

0 commit comments

Comments
 (0)