learn and share notes on atproto (wip) 馃
malfestio.stormlightlabs.org/
readability
solid
axum
atproto
srs
1import { TutorialProvider, useTutorial } from "$lib/TutorialProvider";
2import { cleanup, render, screen } from "@solidjs/testing-library";
3import { createEffect } from "solid-js";
4import { afterEach, describe, expect, it, vi } from "vitest";
5import { TutorialOverlay } from "../TutorialOverlay";
6
7vi.mock("$lib/api", () => ({ api: { updatePreferences: vi.fn().mockResolvedValue({ ok: true }) } }));
8
9vi.mock(
10 "$lib/store",
11 () => ({ prefStore: { prefs: vi.fn(() => ({ tutorial_deck_completed: false })), fetchPrefs: vi.fn() } }),
12);
13
14const TutorialTestHarness = (props: { onTutorial?: (t: ReturnType<typeof useTutorial>) => void }) => {
15 const tutorial = useTutorial();
16
17 createEffect(() => {
18 props.onTutorial?.(tutorial);
19 });
20
21 return (
22 <>
23 <div
24 ref={(el) => tutorial.registerTarget("title", el)}
25 style={{ position: "absolute", top: "100px", left: "100px", width: "200px", height: "50px" }}>
26 Target Element
27 </div>
28 <TutorialOverlay />
29 </>
30 );
31};
32
33describe("TutorialOverlay", () => {
34 afterEach(cleanup);
35
36 it("does not render when tutorial is inactive", () => {
37 render(() => (
38 <TutorialProvider>
39 <TutorialTestHarness />
40 </TutorialProvider>
41 ));
42 expect(screen.queryByText("Name Your Deck")).not.toBeInTheDocument();
43 });
44
45 it("renders when tutorial is active and target exists", () => {
46 let tutorialRef: ReturnType<typeof useTutorial> | undefined;
47
48 render(() => (
49 <TutorialProvider>
50 <TutorialTestHarness
51 onTutorial={(t) => {
52 tutorialRef = t;
53 }} />
54 </TutorialProvider>
55 ));
56
57 tutorialRef!.startTutorial();
58
59 expect(screen.getByText("Name Your Deck")).toBeInTheDocument();
60 });
61
62 it("shows skip button when active", () => {
63 let tutorialRef: ReturnType<typeof useTutorial> | undefined;
64
65 render(() => (
66 <TutorialProvider>
67 <TutorialTestHarness
68 onTutorial={(t) => {
69 tutorialRef = t;
70 }} />
71 </TutorialProvider>
72 ));
73
74 tutorialRef!.startTutorial();
75
76 expect(screen.getByText("Skip tutorial")).toBeInTheDocument();
77 });
78
79 it("shows Next button when not on last step", () => {
80 let tutorialRef: ReturnType<typeof useTutorial> | undefined;
81
82 render(() => (
83 <TutorialProvider>
84 <TutorialTestHarness
85 onTutorial={(t) => {
86 tutorialRef = t;
87 }} />
88 </TutorialProvider>
89 ));
90
91 tutorialRef!.startTutorial();
92
93 expect(screen.getByRole("button", { name: /Next/i })).toBeInTheDocument();
94 });
95
96 it("shows keyboard hint", () => {
97 let tutorialRef: ReturnType<typeof useTutorial> | undefined;
98
99 render(() => (
100 <TutorialProvider>
101 <TutorialTestHarness
102 onTutorial={(t) => {
103 tutorialRef = t;
104 }} />
105 </TutorialProvider>
106 ));
107
108 tutorialRef!.startTutorial();
109
110 expect(screen.getByText(/arrow keys/i)).toBeInTheDocument();
111 });
112});