@dowel-ui/react 0.1.0 → 0.1.1
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/dist/components/ai-agent-status/ai-agent-status.d.ts +1 -1
- package/dist/components/ai-tool/ai-tool.d.ts +1 -1
- package/dist/components/alert/alert.d.ts +1 -1
- package/dist/components/avatar/avatar.d.ts +1 -1
- package/dist/components/badge/badge.d.ts +1 -1
- package/dist/components/button/button.d.ts +1 -1
- package/dist/components/progress/progress.d.ts +1 -1
- package/dist/components/spinner/spinner.d.ts +1 -1
- package/dist/components/toast/toast.d.ts +1 -1
- package/package.json +7 -4
- package/src/blocks/admin-users/admin-users.stories.tsx +0 -83
- package/src/blocks/admin-users/admin-users.test.tsx +0 -159
- package/src/blocks/ai-chat/ai-chat.stories.tsx +0 -149
- package/src/blocks/ai-chat/ai-chat.test.tsx +0 -166
- package/src/blocks/dashboard/dashboard.stories.tsx +0 -106
- package/src/blocks/dashboard/dashboard.test.tsx +0 -94
- package/src/blocks/forgot-password/forgot-password.stories.tsx +0 -41
- package/src/blocks/forgot-password/forgot-password.test.tsx +0 -62
- package/src/blocks/login/login.stories.tsx +0 -46
- package/src/blocks/login/login.test.tsx +0 -100
- package/src/blocks/pricing/pricing.stories.tsx +0 -62
- package/src/blocks/pricing/pricing.test.tsx +0 -101
- package/src/blocks/settings/settings.stories.tsx +0 -94
- package/src/blocks/settings/settings.test.tsx +0 -167
- package/src/blocks/signup/signup.stories.tsx +0 -27
- package/src/blocks/signup/signup.test.tsx +0 -97
- package/src/components/accordion/accordion.stories.tsx +0 -90
- package/src/components/accordion/accordion.test.tsx +0 -156
- package/src/components/activity-feed/activity-feed.stories.tsx +0 -163
- package/src/components/activity-feed/activity-feed.test.tsx +0 -125
- package/src/components/ai-agent-status/ai-agent-status.stories.tsx +0 -86
- package/src/components/ai-agent-status/ai-agent-status.test.tsx +0 -66
- package/src/components/ai-conversation/ai-conversation.stories.tsx +0 -126
- package/src/components/ai-conversation/ai-conversation.test.tsx +0 -144
- package/src/components/ai-message/ai-message.stories.tsx +0 -106
- package/src/components/ai-message/ai-message.test.tsx +0 -160
- package/src/components/ai-model-selector/ai-model-selector.stories.tsx +0 -66
- package/src/components/ai-model-selector/ai-model-selector.test.tsx +0 -120
- package/src/components/ai-prompt-input/ai-prompt-input.stories.tsx +0 -145
- package/src/components/ai-prompt-input/ai-prompt-input.test.tsx +0 -273
- package/src/components/ai-reasoning/ai-reasoning.stories.tsx +0 -60
- package/src/components/ai-reasoning/ai-reasoning.test.tsx +0 -95
- package/src/components/ai-response/ai-response.stories.tsx +0 -75
- package/src/components/ai-response/ai-response.test.tsx +0 -96
- package/src/components/ai-sources/ai-sources.stories.tsx +0 -93
- package/src/components/ai-sources/ai-sources.test.tsx +0 -102
- package/src/components/ai-token-usage/ai-token-usage.stories.tsx +0 -59
- package/src/components/ai-token-usage/ai-token-usage.test.tsx +0 -83
- package/src/components/ai-tool/ai-tool.stories.tsx +0 -96
- package/src/components/ai-tool/ai-tool.test.tsx +0 -137
- package/src/components/alert/alert.stories.tsx +0 -101
- package/src/components/alert/alert.test.tsx +0 -94
- package/src/components/avatar/avatar.stories.tsx +0 -58
- package/src/components/avatar/avatar.test.tsx +0 -64
- package/src/components/badge/badge.stories.tsx +0 -68
- package/src/components/badge/badge.test.tsx +0 -61
- package/src/components/button/button.stories.tsx +0 -128
- package/src/components/button/button.test.tsx +0 -224
- package/src/components/calendar/calendar.stories.tsx +0 -105
- package/src/components/calendar/calendar.test.tsx +0 -255
- package/src/components/card/card.stories.tsx +0 -61
- package/src/components/card/card.test.tsx +0 -66
- package/src/components/checkbox/checkbox.stories.tsx +0 -124
- package/src/components/checkbox/checkbox.test.tsx +0 -121
- package/src/components/code-block/code-block.stories.tsx +0 -62
- package/src/components/code-block/code-block.test.tsx +0 -139
- package/src/components/combobox/combobox.stories.tsx +0 -171
- package/src/components/combobox/combobox.test.tsx +0 -429
- package/src/components/command/command.stories.tsx +0 -152
- package/src/components/command/command.test.tsx +0 -380
- package/src/components/data-table/data-table.stories.tsx +0 -151
- package/src/components/data-table/data-table.test.tsx +0 -288
- package/src/components/date-picker/date-picker.stories.tsx +0 -103
- package/src/components/date-picker/date-picker.test.tsx +0 -205
- package/src/components/dialog/dialog.stories.tsx +0 -140
- package/src/components/dialog/dialog.test.tsx +0 -186
- package/src/components/drawer/drawer.stories.tsx +0 -111
- package/src/components/drawer/drawer.test.tsx +0 -187
- package/src/components/dropdown-menu/dropdown-menu.stories.tsx +0 -128
- package/src/components/dropdown-menu/dropdown-menu.test.tsx +0 -321
- package/src/components/empty-state/empty-state.stories.tsx +0 -70
- package/src/components/empty-state/empty-state.test.tsx +0 -97
- package/src/components/form/form.stories.tsx +0 -144
- package/src/components/form/form.test.tsx +0 -203
- package/src/components/input/input.stories.tsx +0 -84
- package/src/components/input/input.test.tsx +0 -108
- package/src/components/label/label.stories.tsx +0 -51
- package/src/components/label/label.test.tsx +0 -55
- package/src/components/pagination/pagination.stories.tsx +0 -129
- package/src/components/pagination/pagination.test.tsx +0 -119
- package/src/components/popover/popover.stories.tsx +0 -84
- package/src/components/popover/popover.test.tsx +0 -163
- package/src/components/progress/progress.stories.tsx +0 -90
- package/src/components/progress/progress.test.tsx +0 -78
- package/src/components/radio-group/radio-group.stories.tsx +0 -102
- package/src/components/radio-group/radio-group.test.tsx +0 -152
- package/src/components/select/select.stories.tsx +0 -137
- package/src/components/select/select.test.tsx +0 -209
- package/src/components/separator/separator.stories.tsx +0 -42
- package/src/components/separator/separator.test.tsx +0 -46
- package/src/components/sheet/sheet.stories.tsx +0 -107
- package/src/components/sheet/sheet.test.tsx +0 -152
- package/src/components/skeleton/skeleton.stories.tsx +0 -46
- package/src/components/skeleton/skeleton.test.tsx +0 -57
- package/src/components/slider/slider.stories.tsx +0 -87
- package/src/components/slider/slider.test.tsx +0 -148
- package/src/components/spinner/spinner.stories.tsx +0 -47
- package/src/components/spinner/spinner.test.tsx +0 -41
- package/src/components/switch/switch.stories.tsx +0 -70
- package/src/components/switch/switch.test.tsx +0 -101
- package/src/components/table/table.stories.tsx +0 -131
- package/src/components/table/table.test.tsx +0 -153
- package/src/components/tabs/tabs.stories.tsx +0 -139
- package/src/components/tabs/tabs.test.tsx +0 -148
- package/src/components/toast/toast.stories.tsx +0 -167
- package/src/components/toast/toast.test.tsx +0 -306
- package/src/components/tooltip/tooltip.stories.tsx +0 -89
- package/src/components/tooltip/tooltip.test.tsx +0 -120
- package/src/lib/utils.test.ts +0 -32
- package/src/registry/meta.test.ts +0 -234
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { render, screen, waitFor } from "@testing-library/react";
|
|
2
|
-
import userEvent from "@testing-library/user-event";
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
4
|
-
|
|
5
|
-
import { expectNoA11yViolations } from "../../../test/a11y";
|
|
6
|
-
import { Button } from "../button";
|
|
7
|
-
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip";
|
|
8
|
-
|
|
9
|
-
function Example({ delayDuration = 0 }: { delayDuration?: number } = {}) {
|
|
10
|
-
return (
|
|
11
|
-
<Tooltip delayDuration={delayDuration}>
|
|
12
|
-
<TooltipTrigger asChild>
|
|
13
|
-
<Button size="icon" aria-label="Add item">
|
|
14
|
-
+
|
|
15
|
-
</Button>
|
|
16
|
-
</TooltipTrigger>
|
|
17
|
-
<TooltipContent>Add item</TooltipContent>
|
|
18
|
-
</Tooltip>
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
describe("Tooltip", () => {
|
|
23
|
-
it("is hidden until the trigger is hovered", async () => {
|
|
24
|
-
const user = userEvent.setup();
|
|
25
|
-
render(<Example />);
|
|
26
|
-
|
|
27
|
-
expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
|
|
28
|
-
await user.hover(screen.getByRole("button", { name: "Add item" }));
|
|
29
|
-
expect(await screen.findByRole("tooltip")).toHaveTextContent("Add item");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("opens on keyboard focus, not only on hover", async () => {
|
|
33
|
-
const user = userEvent.setup();
|
|
34
|
-
render(<Example />);
|
|
35
|
-
|
|
36
|
-
await user.tab();
|
|
37
|
-
expect(screen.getByRole("button", { name: "Add item" })).toHaveFocus();
|
|
38
|
-
expect(await screen.findByRole("tooltip")).toBeInTheDocument();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("closes when the pointer leaves", async () => {
|
|
42
|
-
const user = userEvent.setup();
|
|
43
|
-
// disableHoverableContent removes the grace area that normally lets the
|
|
44
|
-
// pointer travel from trigger to tooltip. That grace area is a polygon
|
|
45
|
-
// computed from measured rects, and jsdom reports every rect as zero-sized,
|
|
46
|
-
// so leaving it enabled would exercise the stub rather than the behaviour.
|
|
47
|
-
render(
|
|
48
|
-
<Tooltip delayDuration={0} disableHoverableContent>
|
|
49
|
-
<TooltipTrigger>Trigger</TooltipTrigger>
|
|
50
|
-
<TooltipContent>Hint</TooltipContent>
|
|
51
|
-
</Tooltip>,
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
const trigger = screen.getByText("Trigger");
|
|
55
|
-
await user.hover(trigger);
|
|
56
|
-
await screen.findByRole("tooltip");
|
|
57
|
-
|
|
58
|
-
await user.unhover(trigger);
|
|
59
|
-
await waitFor(() => {
|
|
60
|
-
expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("closes on Escape", async () => {
|
|
65
|
-
const user = userEvent.setup();
|
|
66
|
-
render(<Example />);
|
|
67
|
-
|
|
68
|
-
await user.tab();
|
|
69
|
-
await screen.findByRole("tooltip");
|
|
70
|
-
|
|
71
|
-
await user.keyboard("{Escape}");
|
|
72
|
-
await waitFor(() => {
|
|
73
|
-
expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("works without an explicit provider", async () => {
|
|
78
|
-
const user = userEvent.setup();
|
|
79
|
-
render(<Example />);
|
|
80
|
-
|
|
81
|
-
await user.hover(screen.getByRole("button", { name: "Add item" }));
|
|
82
|
-
expect(await screen.findByRole("tooltip")).toBeInTheDocument();
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("works inside a shared provider", async () => {
|
|
86
|
-
const user = userEvent.setup();
|
|
87
|
-
render(
|
|
88
|
-
<TooltipProvider delayDuration={0}>
|
|
89
|
-
<Example />
|
|
90
|
-
</TooltipProvider>,
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
await user.hover(screen.getByRole("button", { name: "Add item" }));
|
|
94
|
-
expect(await screen.findByRole("tooltip")).toBeInTheDocument();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("merges a consumer className", async () => {
|
|
98
|
-
const user = userEvent.setup();
|
|
99
|
-
render(
|
|
100
|
-
<Tooltip delayDuration={0}>
|
|
101
|
-
<TooltipTrigger>Trigger</TooltipTrigger>
|
|
102
|
-
<TooltipContent className="max-w-xs">Hint</TooltipContent>
|
|
103
|
-
</Tooltip>,
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
await user.hover(screen.getByText("Trigger"));
|
|
107
|
-
const tooltip = await screen.findByRole("tooltip");
|
|
108
|
-
expect(tooltip).toHaveClass("max-w-xs");
|
|
109
|
-
expect(tooltip).not.toHaveClass("max-w-64");
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("has no accessibility violations while open", async () => {
|
|
113
|
-
const user = userEvent.setup();
|
|
114
|
-
const { baseElement } = render(<Example />);
|
|
115
|
-
await user.hover(screen.getByRole("button", { name: "Add item" }));
|
|
116
|
-
await screen.findByRole("tooltip");
|
|
117
|
-
|
|
118
|
-
await expectNoA11yViolations(baseElement);
|
|
119
|
-
});
|
|
120
|
-
});
|
package/src/lib/utils.test.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { cn } from "./utils";
|
|
4
|
-
|
|
5
|
-
describe("cn", () => {
|
|
6
|
-
it("joins class names", () => {
|
|
7
|
-
expect(cn("a", "b")).toBe("a b");
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("drops falsy values", () => {
|
|
11
|
-
expect(cn("a", false, undefined, null, "b")).toBe("a b");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("lets the later Tailwind utility win a conflict", () => {
|
|
15
|
-
expect(cn("px-2", "px-4")).toBe("px-4");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("resolves conflicts across our custom font-size scale", () => {
|
|
19
|
-
// Without the extendTailwindMerge config this returns "text-sm text-2xs",
|
|
20
|
-
// and a consumer's size override would silently do nothing.
|
|
21
|
-
expect(cn("text-sm", "text-2xs")).toBe("text-2xs");
|
|
22
|
-
expect(cn("text-2xs", "text-lg")).toBe("text-lg");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("keeps non-conflicting utilities", () => {
|
|
26
|
-
expect(cn("rounded-md border", "bg-primary")).toBe("rounded-md border bg-primary");
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("accepts arrays and objects", () => {
|
|
30
|
-
expect(cn(["a", { b: true, c: false }])).toBe("a b");
|
|
31
|
-
});
|
|
32
|
-
});
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { blockMetas } from "./blocks";
|
|
4
|
-
import { componentMetas } from "./components";
|
|
5
|
-
import { COMPONENT_CATEGORIES, COMPONENT_STATUSES, type ComponentMeta } from "./schema";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Registry integrity.
|
|
9
|
-
*
|
|
10
|
-
* A registry that lies about a component's dependencies produces a broken
|
|
11
|
-
* install for every user who runs `add`, and the failure surfaces in *their*
|
|
12
|
-
* project rather than ours. So the declared metadata is checked against the
|
|
13
|
-
* imports actually present in the source on every test run.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const metaModules = {
|
|
17
|
-
...import.meta.glob<{ meta: ComponentMeta }>("../components/*/meta.ts", { eager: true }),
|
|
18
|
-
...import.meta.glob<{ meta: ComponentMeta }>("../blocks/*/meta.ts", { eager: true }),
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const sourceFiles = {
|
|
22
|
-
...import.meta.glob<string>("../components/*/*.{ts,tsx}", {
|
|
23
|
-
eager: true,
|
|
24
|
-
query: "?raw",
|
|
25
|
-
import: "default",
|
|
26
|
-
}),
|
|
27
|
-
...import.meta.glob<string>("../blocks/*/*.{ts,tsx}", {
|
|
28
|
-
eager: true,
|
|
29
|
-
query: "?raw",
|
|
30
|
-
import: "default",
|
|
31
|
-
}),
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const packageJson = import.meta.glob<string>("../../package.json", {
|
|
35
|
-
eager: true,
|
|
36
|
-
query: "?raw",
|
|
37
|
-
import: "default",
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const declaredNpmDependencies = new Set(
|
|
41
|
-
Object.keys(
|
|
42
|
-
(
|
|
43
|
-
JSON.parse(Object.values(packageJson)[0] ?? "{}") as {
|
|
44
|
-
dependencies?: Record<string, string>;
|
|
45
|
-
}
|
|
46
|
-
).dependencies ?? {},
|
|
47
|
-
),
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
/** Provided by the consuming app, never installed by the CLI. */
|
|
51
|
-
const PEER_PACKAGES = new Set(["react", "react-dom", "react/jsx-runtime"]);
|
|
52
|
-
|
|
53
|
-
/** Written by `<cli> init`, so components may import it without declaring it. */
|
|
54
|
-
const INSTALLED_BY_INIT = new Set(["@/lib/utils", "@/lib/styles"]);
|
|
55
|
-
|
|
56
|
-
function componentDir(metaPath: string): string {
|
|
57
|
-
return metaPath.replace(/\/meta\.ts$/, "");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function componentName(metaPath: string): string {
|
|
61
|
-
return componentDir(metaPath).split("/").pop() ?? "";
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Collects module specifiers from `import`/`export ... from` and side-effect imports. */
|
|
65
|
-
function extractSpecifiers(source: string): string[] {
|
|
66
|
-
const specifiers: string[] = [];
|
|
67
|
-
for (const match of source.matchAll(/\bfrom\s+["']([^"']+)["']/g)) {
|
|
68
|
-
if (match[1]) specifiers.push(match[1]);
|
|
69
|
-
}
|
|
70
|
-
for (const match of source.matchAll(/\bimport\s+["']([^"']+)["']/g)) {
|
|
71
|
-
if (match[1]) specifiers.push(match[1]);
|
|
72
|
-
}
|
|
73
|
-
return specifiers;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** `@scope/pkg/sub` -> `@scope/pkg`, `pkg/sub` -> `pkg`. */
|
|
77
|
-
function packageNameOf(specifier: string): string {
|
|
78
|
-
const segments = specifier.split("/");
|
|
79
|
-
return specifier.startsWith("@")
|
|
80
|
-
? segments.slice(0, 2).join("/")
|
|
81
|
-
: (segments[0] ?? specifier);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
interface ResolvedImports {
|
|
85
|
-
npm: Set<string>;
|
|
86
|
-
registry: Set<string>;
|
|
87
|
-
internal: Set<string>;
|
|
88
|
-
relative: Set<string>;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function resolveImports(sources: string[]): ResolvedImports {
|
|
92
|
-
const resolved: ResolvedImports = {
|
|
93
|
-
npm: new Set(),
|
|
94
|
-
registry: new Set(),
|
|
95
|
-
internal: new Set(),
|
|
96
|
-
relative: new Set(),
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
for (const source of sources) {
|
|
100
|
-
for (const specifier of extractSpecifiers(source)) {
|
|
101
|
-
if (PEER_PACKAGES.has(specifier)) continue;
|
|
102
|
-
|
|
103
|
-
if (specifier.startsWith("@/components/")) {
|
|
104
|
-
const name = specifier.split("/")[2];
|
|
105
|
-
if (name) resolved.registry.add(name);
|
|
106
|
-
} else if (specifier.startsWith("@/")) {
|
|
107
|
-
resolved.internal.add(specifier);
|
|
108
|
-
} else if (specifier.startsWith(".")) {
|
|
109
|
-
resolved.relative.add(specifier);
|
|
110
|
-
} else {
|
|
111
|
-
resolved.npm.add(packageNameOf(specifier));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return resolved;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const metaEntries = Object.entries(metaModules);
|
|
120
|
-
|
|
121
|
-
describe("registry metadata", () => {
|
|
122
|
-
it("finds at least one component", () => {
|
|
123
|
-
expect(metaEntries.length).toBeGreaterThan(0);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
describe.each(metaEntries)("%s", (metaPath, module) => {
|
|
127
|
-
const meta = module.meta;
|
|
128
|
-
const dir = componentDir(metaPath);
|
|
129
|
-
const dirName = componentName(metaPath);
|
|
130
|
-
|
|
131
|
-
const shippedSources = meta.files
|
|
132
|
-
.map((file) => sourceFiles[`${dir}/${file}`])
|
|
133
|
-
.filter((source): source is string => typeof source === "string");
|
|
134
|
-
|
|
135
|
-
it("exports a well-formed meta object", () => {
|
|
136
|
-
expect(meta.name).toMatch(/^[a-z][a-z0-9-]*$/);
|
|
137
|
-
expect(meta.title.length).toBeGreaterThan(0);
|
|
138
|
-
expect(meta.description.length).toBeGreaterThan(9);
|
|
139
|
-
expect(COMPONENT_CATEGORIES).toContain(meta.category);
|
|
140
|
-
expect(COMPONENT_STATUSES).toContain(meta.status);
|
|
141
|
-
expect(meta.files.length).toBeGreaterThan(0);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("uses its directory name as its registry name", () => {
|
|
145
|
-
expect(meta.name).toBe(dirName);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("lists only files that exist", () => {
|
|
149
|
-
for (const file of meta.files) {
|
|
150
|
-
expect(
|
|
151
|
-
sourceFiles[`${dir}/${file}`],
|
|
152
|
-
`${dir}/${file} is listed but missing`,
|
|
153
|
-
).toBeTypeOf("string");
|
|
154
|
-
}
|
|
155
|
-
expect(shippedSources).toHaveLength(meta.files.length);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("ships an index, a test and a story", () => {
|
|
159
|
-
expect(sourceFiles[`${dir}/index.ts`]).toBeTypeOf("string");
|
|
160
|
-
expect(sourceFiles[`${dir}/${dirName}.test.tsx`]).toBeTypeOf("string");
|
|
161
|
-
expect(sourceFiles[`${dir}/${dirName}.stories.tsx`]).toBeTypeOf("string");
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it("declares exactly the npm packages its source imports", () => {
|
|
165
|
-
const { npm } = resolveImports(shippedSources);
|
|
166
|
-
expect([...npm].sort()).toEqual([...meta.dependencies].sort());
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it("declares exactly the registry entries its source imports", () => {
|
|
170
|
-
const { registry } = resolveImports(shippedSources);
|
|
171
|
-
expect([...registry].sort()).toEqual([...meta.registryDependencies].sort());
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it("does not let a component depend on a block", () => {
|
|
175
|
-
// Blocks are assembled from components; the reverse would make a
|
|
176
|
-
// component drag a whole page section into a project that wanted a button.
|
|
177
|
-
if (meta.kind === "block") return;
|
|
178
|
-
const blockNames = new Set(blockMetas.map((block) => block.name));
|
|
179
|
-
for (const dependency of meta.registryDependencies) {
|
|
180
|
-
expect(
|
|
181
|
-
blockNames.has(dependency),
|
|
182
|
-
`${meta.name} depends on block "${dependency}"`,
|
|
183
|
-
).toBe(false);
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("only imports internal modules that `init` installs", () => {
|
|
188
|
-
const { internal } = resolveImports(shippedSources);
|
|
189
|
-
for (const specifier of internal) {
|
|
190
|
-
expect(
|
|
191
|
-
INSTALLED_BY_INIT.has(specifier),
|
|
192
|
-
`${specifier} is imported but no CLI step installs it`,
|
|
193
|
-
).toBe(true);
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it("only uses relative imports that resolve inside the shipped file set", () => {
|
|
198
|
-
const { relative } = resolveImports(shippedSources);
|
|
199
|
-
for (const specifier of relative) {
|
|
200
|
-
const target = specifier.replace(/^\.\//, "");
|
|
201
|
-
const matches = meta.files.some((file) => file.replace(/\.tsx?$/, "") === target);
|
|
202
|
-
expect(matches, `${specifier} is imported but not in meta.files`).toBe(true);
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it("declares dependencies that are actually installed in this workspace", () => {
|
|
207
|
-
for (const dependency of meta.dependencies) {
|
|
208
|
-
expect(
|
|
209
|
-
declaredNpmDependencies.has(dependency),
|
|
210
|
-
`${dependency} is declared in meta but missing from @dowel-ui/react dependencies`,
|
|
211
|
-
).toBe(true);
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it("lists every entry in the registry barrels", () => {
|
|
217
|
-
// The barrels are what the registry build reads. An entry that exists on
|
|
218
|
-
// disk but is missing from one would simply never be publishable, with no
|
|
219
|
-
// other signal that anything was wrong.
|
|
220
|
-
const onDisk = metaEntries.map(([, module]) => module.meta.name).sort();
|
|
221
|
-
const inBarrels = [...componentMetas, ...blockMetas].map((meta) => meta.name).sort();
|
|
222
|
-
expect(inBarrels).toEqual(onDisk);
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it("keeps components and blocks in separate barrels", () => {
|
|
226
|
-
expect(componentMetas.every((meta) => meta.kind !== "block")).toBe(true);
|
|
227
|
-
expect(blockMetas.every((meta) => meta.kind === "block")).toBe(true);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it("has no duplicate registry names", () => {
|
|
231
|
-
const names = metaEntries.map(([, module]) => module.meta.name);
|
|
232
|
-
expect(new Set(names).size).toBe(names.length);
|
|
233
|
-
});
|
|
234
|
-
});
|