@blotch/dolan 22.2.3 → 22.2.5
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/commands/add.js +9 -1
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/add.test.js +38 -3
- package/dist/commands/add.test.js.map +1 -1
- package/dist/commands/doctor.js +258 -21
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/doctor.test.js +425 -11
- package/dist/commands/doctor.test.js.map +1 -1
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.js +38 -18
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/init.test.js +105 -2
- package/dist/commands/init.test.js.map +1 -1
- package/dist/commands/theme.js +1 -1
- package/dist/commands/theme.js.map +1 -1
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.js +194 -50
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/update.test.js +570 -3
- package/dist/commands/update.test.js.map +1 -1
- package/dist/lockfile.d.ts +17 -0
- package/dist/lockfile.js +56 -1
- package/dist/lockfile.js.map +1 -1
- package/dist/lockfile.test.d.ts +1 -0
- package/dist/lockfile.test.js +65 -0
- package/dist/lockfile.test.js.map +1 -0
- package/dist/registry.d.ts +6 -0
- package/dist/registry.js +119 -11
- package/dist/registry.js.map +1 -1
- package/dist/registry.test.js +61 -6
- package/dist/registry.test.js.map +1 -1
- package/dist/test-utils.d.ts +27 -0
- package/dist/test-utils.js +45 -1
- package/dist/test-utils.js.map +1 -1
- package/dist/testing/fire-lynx-event.d.ts +14 -0
- package/dist/testing/fire-lynx-event.js +19 -0
- package/dist/testing/fire-lynx-event.js.map +1 -0
- package/dist/testing/fire-lynx-event.test.d.ts +1 -0
- package/dist/testing/fire-lynx-event.test.js +44 -0
- package/dist/testing/fire-lynx-event.test.js.map +1 -0
- package/dist/testing/lynx-main-thread.d.ts +22 -0
- package/dist/testing/lynx-main-thread.js +11 -0
- package/dist/testing/lynx-main-thread.js.map +1 -0
- package/dist/testing/lynx-main-thread.test.d.ts +1 -0
- package/dist/testing/lynx-main-thread.test.js +24 -0
- package/dist/testing/lynx-main-thread.test.js.map +1 -0
- package/dist/testing/set-input-signal.d.ts +5 -4
- package/dist/testing/set-input-signal.js +5 -4
- package/dist/testing/set-input-signal.js.map +1 -1
- package/dist/ui/boolean-inputs.test.ts +76 -0
- package/dist/ui/components/accordion/accordion.test.ts +158 -144
- package/dist/ui/components/accordion/accordion.ts +19 -14
- package/dist/ui/components/action-sheet/action-sheet.test.ts +637 -183
- package/dist/ui/components/action-sheet/action-sheet.ts +339 -55
- package/dist/ui/components/action-sheet/index.ts +1 -0
- package/dist/ui/components/alert/alert.test.ts +67 -19
- package/dist/ui/components/alert/alert.ts +14 -16
- package/dist/ui/components/alert-dialog/alert-dialog.test.ts +740 -129
- package/dist/ui/components/alert-dialog/alert-dialog.ts +672 -58
- package/dist/ui/components/alert-dialog/index.ts +5 -0
- package/dist/ui/components/area-chart/area-chart.test.ts +81 -1
- package/dist/ui/components/area-chart/area-chart.ts +3 -2
- package/dist/ui/components/avatar/avatar.test.ts +89 -45
- package/dist/ui/components/badge/badge.test.ts +57 -23
- package/dist/ui/components/badge/badge.ts +14 -15
- package/dist/ui/components/bar-chart/bar-chart.test.ts +82 -1
- package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +1314 -273
- package/dist/ui/components/bottom-sheet/bottom-sheet.ts +1046 -119
- package/dist/ui/components/bottom-sheet/index.ts +7 -1
- package/dist/ui/components/button/button.test.ts +646 -2
- package/dist/ui/components/button/button.ts +443 -64
- package/dist/ui/components/button/index.ts +2 -0
- package/dist/ui/components/button-group/button-group.test.ts +528 -2
- package/dist/ui/components/button-group/button-group.ts +428 -70
- package/dist/ui/components/button-group/index.ts +3 -0
- package/dist/ui/components/candlestick-chart/candlestick-chart.test.ts +82 -1
- package/dist/ui/components/card/card.test.ts +97 -61
- package/dist/ui/components/card/card.ts +2 -1
- package/dist/ui/components/chart/chart-presets.test.ts +197 -1
- package/dist/ui/components/chart/chart-presets.ts +14 -7
- package/dist/ui/components/chart/chart-theme.ts +12 -0
- package/dist/ui/components/chart/chart.test.ts +1031 -0
- package/dist/ui/components/chart/chart.ts +66 -46
- package/dist/ui/components/checkbox/checkbox.test.ts +124 -42
- package/dist/ui/components/checkbox/checkbox.ts +25 -24
- package/dist/ui/components/collapsible/collapsible.test.ts +158 -81
- package/dist/ui/components/collapsible/collapsible.ts +21 -15
- package/dist/ui/components/context-menu/context-menu.test.ts +224 -0
- package/dist/ui/components/context-menu/context-menu.ts +110 -0
- package/dist/ui/components/context-menu/index.ts +1 -0
- package/dist/ui/components/dialog/dialog.test.ts +210 -152
- package/dist/ui/components/dialog/dialog.ts +35 -29
- package/dist/ui/components/dock/dock-scroll.test.ts +130 -0
- package/dist/ui/components/dock/dock-scroll.ts +142 -0
- package/dist/ui/components/dock/dock-search.test.ts +594 -0
- package/dist/ui/components/dock/dock.test.ts +752 -0
- package/dist/ui/components/dock/dock.ts +1438 -0
- package/dist/ui/components/dock/index.ts +2 -0
- package/dist/ui/components/dynamic-color/dynamic-color.test.ts +144 -0
- package/dist/ui/components/dynamic-color/dynamic-color.ts +146 -0
- package/dist/ui/components/dynamic-color/index.ts +5 -0
- package/dist/ui/components/empty-state/empty-state.test.ts +28 -39
- package/dist/ui/components/glass/concentric-radius.test.ts +13 -0
- package/dist/ui/components/glass/concentric-radius.ts +17 -0
- package/dist/ui/components/glass/glass-design.test.ts +78 -0
- package/dist/ui/components/glass/glass-design.ts +119 -0
- package/dist/ui/components/glass/glass.test.ts +376 -0
- package/dist/ui/components/glass/glass.ts +430 -0
- package/dist/ui/components/glass/index.ts +19 -0
- package/dist/ui/components/icon/icon-theme.test.ts +64 -0
- package/dist/ui/components/icon/icon-theme.ts +70 -16
- package/dist/ui/components/icon/icon.test.ts +52 -41
- package/dist/ui/components/input/input.test.ts +178 -101
- package/dist/ui/components/input/input.ts +17 -13
- package/dist/ui/components/label/label.test.ts +11 -24
- package/dist/ui/components/label/label.ts +8 -2
- package/dist/ui/components/line-chart/line-chart.test.ts +76 -1
- package/dist/ui/components/line-chart/line-chart.ts +3 -2
- package/dist/ui/components/list/list.test.ts +80 -49
- package/dist/ui/components/list/list.ts +5 -4
- package/dist/ui/components/menu/index.ts +25 -0
- package/dist/ui/components/menu/menu-placement.test.ts +153 -0
- package/dist/ui/components/menu/menu-placement.ts +159 -0
- package/dist/ui/components/menu/menu-popup.test.ts +636 -0
- package/dist/ui/components/menu/menu-popup.ts +565 -0
- package/dist/ui/components/menu/menu.test.ts +523 -0
- package/dist/ui/components/menu/menu.ts +455 -0
- package/dist/ui/components/nav-drawer/nav-drawer.test.ts +425 -260
- package/dist/ui/components/nav-drawer/nav-drawer.ts +75 -58
- package/dist/ui/components/navigation-bar/index.ts +1 -0
- package/dist/ui/components/navigation-bar/navigation-bar.test.ts +425 -0
- package/dist/ui/components/navigation-bar/navigation-bar.ts +411 -0
- package/dist/ui/components/pagination/pagination.test.ts +136 -86
- package/dist/ui/components/popover/index.ts +9 -0
- package/dist/ui/components/popover/popover-panel.test.ts +735 -0
- package/dist/ui/components/popover/popover-panel.ts +603 -0
- package/dist/ui/components/popover/popover.test.ts +286 -0
- package/dist/ui/components/popover/popover.ts +207 -0
- package/dist/ui/components/progress/progress.test.ts +102 -46
- package/dist/ui/components/progress/progress.ts +54 -55
- package/dist/ui/components/radio-group/radio-group.test.ts +128 -83
- package/dist/ui/components/radio-group/radio-group.ts +26 -25
- package/dist/ui/components/scatter-chart/scatter-chart.test.ts +74 -1
- package/dist/ui/components/scroll-area/scroll-area.test.ts +72 -36
- package/dist/ui/components/scroll-area/scroll-area.ts +4 -3
- package/dist/ui/components/search-field/index.ts +15 -0
- package/dist/ui/components/search-field/keyboard.test.ts +147 -0
- package/dist/ui/components/search-field/keyboard.ts +123 -0
- package/dist/ui/components/search-field/search-field.test.ts +549 -0
- package/dist/ui/components/search-field/search-field.ts +628 -0
- package/dist/ui/components/segmented-control/index.ts +8 -0
- package/dist/ui/components/segmented-control/segmented-control.test.ts +861 -0
- package/dist/ui/components/segmented-control/segmented-control.ts +884 -0
- package/dist/ui/components/select/select.test.ts +138 -78
- package/dist/ui/components/select/select.ts +19 -12
- package/dist/ui/components/separator/separator.test.ts +18 -17
- package/dist/ui/components/sheet/sheet.test.ts +151 -11
- package/dist/ui/components/sheet/sheet.ts +68 -5
- package/dist/ui/components/skeleton/skeleton.test.ts +24 -7
- package/dist/ui/components/skeleton/skeleton.ts +10 -16
- package/dist/ui/components/slider/index.ts +1 -0
- package/dist/ui/components/slider/slider.test.ts +695 -0
- package/dist/ui/components/slider/slider.ts +901 -0
- package/dist/ui/components/spinner/spinner.test.ts +64 -46
- package/dist/ui/components/spinner/spinner.ts +16 -19
- package/dist/ui/components/switch/index.ts +1 -1
- package/dist/ui/components/switch/switch.test.ts +534 -48
- package/dist/ui/components/switch/switch.ts +613 -48
- package/dist/ui/components/tab-bar/index.ts +1 -0
- package/dist/ui/components/tab-bar/tab-bar.test.ts +280 -0
- package/dist/ui/components/tab-bar/tab-bar.ts +230 -0
- package/dist/ui/components/tabs/index.ts +7 -1
- package/dist/ui/components/tabs/tabs.test.ts +484 -150
- package/dist/ui/components/tabs/tabs.ts +344 -149
- package/dist/ui/components/textarea/textarea.test.ts +206 -100
- package/dist/ui/components/textarea/textarea.ts +16 -13
- package/dist/ui/components/toast/toast.test.ts +434 -350
- package/dist/ui/components/toast/toast.ts +42 -51
- package/dist/ui/components/toggle/index.ts +1 -0
- package/dist/ui/components/toggle/toggle.test.ts +559 -146
- package/dist/ui/components/toggle/toggle.ts +241 -26
- package/dist/ui/components/toolbar/bar-scroll.test.ts +310 -0
- package/dist/ui/components/toolbar/bar-scroll.ts +372 -0
- package/dist/ui/components/toolbar/index.ts +12 -0
- package/dist/ui/components/toolbar/toolbar.test.ts +438 -0
- package/dist/ui/components/toolbar/toolbar.ts +535 -0
- package/dist/ui/theme/chart-colors.test.ts +8 -3
- package/dist/ui/theme/dark.css +38 -2
- package/dist/ui/theme/default.css +55 -7
- package/dist/ui/theme/icon-colors.test.ts +8 -3
- package/dist/ui/theme/rgb-channels.test.ts +84 -0
- package/dist/ui/theme/tailwind-build.test.ts +114 -0
- package/dist/ui/theme/tailwind-css.test.ts +83 -0
- package/dist/ui/theme/tailwind-plugin.test.ts +42 -2
- package/dist/ui/theme/tailwind-plugin.ts +93 -29
- package/dist/ui/theme/tailwind.css +113 -0
- package/dist/ui/utils/animate.test.ts +13 -1
- package/dist/utils/analyze.d.ts +19 -0
- package/dist/utils/analyze.js +16 -0
- package/dist/utils/analyze.js.map +1 -1
- package/dist/utils/analyze.test.js +14 -1
- package/dist/utils/analyze.test.js.map +1 -1
- package/dist/utils/installed.d.ts +25 -1
- package/dist/utils/installed.js +33 -0
- package/dist/utils/installed.js.map +1 -1
- package/dist/utils/installed.test.js +55 -1
- package/dist/utils/installed.test.js.map +1 -1
- package/dist/utils/resolve-paths.d.ts +4 -4
- package/dist/utils/resolve-paths.js +5 -7
- package/dist/utils/resolve-paths.js.map +1 -1
- package/dist/utils/resolve-paths.test.js +9 -5
- package/dist/utils/resolve-paths.test.js.map +1 -1
- package/dist/utils/tailwind.d.ts +45 -0
- package/dist/utils/tailwind.js +90 -0
- package/dist/utils/tailwind.js.map +1 -0
- package/dist/utils/tailwind.test.d.ts +1 -0
- package/dist/utils/tailwind.test.js +134 -0
- package/dist/utils/tailwind.test.js.map +1 -0
- package/package.json +9 -1
- package/dist/ui/components/area-chart/area-chart-render.test.ts +0 -63
- package/dist/ui/components/bar-chart/bar-chart-render.test.ts +0 -58
- package/dist/ui/components/button/button-render.test.ts +0 -132
- package/dist/ui/components/button-group/button-group-render.test.ts +0 -146
- package/dist/ui/components/candlestick-chart/candlestick-chart-render.test.ts +0 -59
- package/dist/ui/components/chart/chart-render.test.ts +0 -730
- package/dist/ui/components/line-chart/line-chart-render.test.ts +0 -83
- package/dist/ui/components/scatter-chart/scatter-chart-render.test.ts +0 -64
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import {
|
|
4
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
5
|
+
import { hashContent } from '../lockfile';
|
|
6
|
+
import { createFixture, DEFAULT_CONFIG, installPackage, onTailwind, } from '../test-utils';
|
|
5
7
|
vi.mock('@clack/prompts', () => ({
|
|
6
8
|
intro: vi.fn(),
|
|
7
9
|
outro: vi.fn(),
|
|
@@ -22,6 +24,8 @@ vi.mock('../utils/resolve-paths.js', () => ({
|
|
|
22
24
|
return ['card.ts', 'index.ts'];
|
|
23
25
|
return [`${name}.ts`];
|
|
24
26
|
},
|
|
27
|
+
// dolan's own theme, which the fixture's `themeFiles` write.
|
|
28
|
+
getUiSourceDir: () => fixture.uiDir,
|
|
25
29
|
}));
|
|
26
30
|
beforeEach(() => {
|
|
27
31
|
vi.clearAllMocks();
|
|
@@ -35,13 +39,22 @@ afterEach(() => {
|
|
|
35
39
|
});
|
|
36
40
|
const writePackageJson = (dir, content) => writeFileSync(join(dir, 'package.json'), JSON.stringify(content));
|
|
37
41
|
// What doctor requires (no router: no component imports it), plus the
|
|
38
|
-
// package button imports.
|
|
42
|
+
// package button imports. Not Tailwind: doctor reads the version installed
|
|
43
|
+
// (see installPackage), not the one package.json lists.
|
|
39
44
|
const FULL_ANGULAR_DEPS = {
|
|
40
45
|
'@angular/core': '^20.0.0',
|
|
41
46
|
'@blotch/angular-lynx': '^0.0.1',
|
|
42
47
|
'@blotch/dolan': '^0.0.1',
|
|
43
48
|
'class-variance-authority': '^0.7.1',
|
|
44
|
-
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Every message logged at a level, without the colors picocolors adds in CI.
|
|
52
|
+
*/
|
|
53
|
+
const logged = async (level) => {
|
|
54
|
+
const p = await import('@clack/prompts');
|
|
55
|
+
return vi
|
|
56
|
+
.mocked(p.log[level])
|
|
57
|
+
.mock.calls.map(([message]) => stripVTControlCharacters(String(message)));
|
|
45
58
|
};
|
|
46
59
|
/**
|
|
47
60
|
* A lockfile that tracks the given components, as `dolan add` writes it.
|
|
@@ -84,6 +97,7 @@ describe('doctorCommand', () => {
|
|
|
84
97
|
components: { card: { 'card.ts': '', 'index.ts': '' } },
|
|
85
98
|
});
|
|
86
99
|
writePackageJson(fixture.dir, { dependencies: FULL_ANGULAR_DEPS });
|
|
100
|
+
installPackage(fixture.dir, 'tailwindcss', '3.4.19');
|
|
87
101
|
writeFileSync(join(fixture.dir, DEFAULT_CONFIG.aliases.theme, 'tailwind-plugin.ts'), '');
|
|
88
102
|
writeFileSync(join(fixture.dir, DEFAULT_CONFIG.aliases.theme, 'default.css'), '');
|
|
89
103
|
writeFileSync(join(fixture.dir, 'tailwind.config.ts'), "import { blotchPlugin } from './src/styles/tailwind-plugin';\nexport default { plugins: [blotchPlugin] };\n");
|
|
@@ -92,6 +106,8 @@ describe('doctorCommand', () => {
|
|
|
92
106
|
await expect(doctorCommand()).rejects.toThrow('process.exit(0)');
|
|
93
107
|
const p = await import('@clack/prompts');
|
|
94
108
|
expect(p.log.error).not.toHaveBeenCalled();
|
|
109
|
+
expect(p.log.warn).not.toHaveBeenCalled();
|
|
110
|
+
expect(await logged('success')).toContain('tailwindcss 3.4.19 found, set up for Tailwind 3 (tailwind.config.ts)');
|
|
95
111
|
expect(p.log.success).toHaveBeenCalledWith(expect.stringContaining('component(s) healthy'));
|
|
96
112
|
});
|
|
97
113
|
it('fails when the components or theme directories are missing', async () => {
|
|
@@ -115,6 +131,7 @@ describe('doctorCommand', () => {
|
|
|
115
131
|
it('warns when tailwind-plugin.ts and theme CSS are both missing', async () => {
|
|
116
132
|
fixture = createFixture({ config: DEFAULT_CONFIG });
|
|
117
133
|
writePackageJson(fixture.dir, { dependencies: FULL_ANGULAR_DEPS });
|
|
134
|
+
installPackage(fixture.dir, 'tailwindcss', '3.4.19');
|
|
118
135
|
vi.spyOn(process, 'cwd').mockReturnValue(fixture.dir);
|
|
119
136
|
const { doctorCommand } = await import('./doctor');
|
|
120
137
|
await expect(doctorCommand()).rejects.toThrow('process.exit(0)');
|
|
@@ -132,18 +149,50 @@ describe('doctorCommand', () => {
|
|
|
132
149
|
const p = await import('@clack/prompts');
|
|
133
150
|
expect(p.log.success).toHaveBeenCalledWith('Theme CSS found');
|
|
134
151
|
});
|
|
135
|
-
it('warns when no tailwind config is found', async () => {
|
|
152
|
+
it('warns when no tailwind config is found on Tailwind 3', async () => {
|
|
153
|
+
fixture = createFixture({ config: DEFAULT_CONFIG });
|
|
154
|
+
writePackageJson(fixture.dir, { dependencies: FULL_ANGULAR_DEPS });
|
|
155
|
+
installPackage(fixture.dir, 'tailwindcss', '3.4.19');
|
|
156
|
+
vi.spyOn(process, 'cwd').mockReturnValue(fixture.dir);
|
|
157
|
+
const { doctorCommand } = await import('./doctor');
|
|
158
|
+
await expect(doctorCommand()).rejects.toThrow('process.exit(0)');
|
|
159
|
+
// The build runs Tailwind 3 only from a config.
|
|
160
|
+
expect(await logged('warn')).toContain('tailwindcss 3.4.19 found, but no Tailwind config: the build runs Tailwind 3 only from a tailwind.config.*. Add one with ng generate @blotch/angular-lynx:add-tailwind --tailwind-version=3');
|
|
161
|
+
expect(await logged('success')).not.toContainEqual(expect.stringContaining('set up for'));
|
|
162
|
+
});
|
|
163
|
+
it("passes when a Tailwind 4 stylesheet imports the theme's tailwind.css", async () => {
|
|
164
|
+
fixture = createFixture({ config: DEFAULT_CONFIG });
|
|
165
|
+
writePackageJson(fixture.dir, { dependencies: FULL_ANGULAR_DEPS });
|
|
166
|
+
installPackage(fixture.dir, 'tailwindcss', '4.3.3');
|
|
167
|
+
writeFileSync(join(fixture.dir, DEFAULT_CONFIG.aliases.theme, 'tailwind.css'), '');
|
|
168
|
+
writeFileSync(join(fixture.dir, 'src/styles.css'), "@import 'tailwindcss/utilities.css';\n@import './styles/tailwind.css';\n");
|
|
169
|
+
vi.spyOn(process, 'cwd').mockReturnValue(fixture.dir);
|
|
170
|
+
const { doctorCommand } = await import('./doctor');
|
|
171
|
+
await expect(doctorCommand()).rejects.toThrow('process.exit(0)');
|
|
172
|
+
const p = await import('@clack/prompts');
|
|
173
|
+
expect(await logged('success')).toContain('tailwindcss 4.3.3 found, set up for Tailwind 4');
|
|
174
|
+
expect(p.log.success).toHaveBeenCalledWith(expect.stringContaining('imported in'));
|
|
175
|
+
expect(p.log.success).toHaveBeenCalledWith(expect.stringContaining('found in theme'));
|
|
176
|
+
});
|
|
177
|
+
it("warns when a Tailwind 4 stylesheet, or its absence, doesn't import tailwind.css", async () => {
|
|
136
178
|
fixture = createFixture({ config: DEFAULT_CONFIG });
|
|
137
179
|
writePackageJson(fixture.dir, { dependencies: FULL_ANGULAR_DEPS });
|
|
180
|
+
installPackage(fixture.dir, 'tailwindcss', '4.3.3');
|
|
138
181
|
vi.spyOn(process, 'cwd').mockReturnValue(fixture.dir);
|
|
139
182
|
const { doctorCommand } = await import('./doctor');
|
|
140
183
|
await expect(doctorCommand()).rejects.toThrow('process.exit(0)');
|
|
184
|
+
// Tailwind's own imports don't count.
|
|
185
|
+
writeFileSync(join(fixture.dir, 'src/styles.css'), "@import 'tailwindcss/utilities.css';");
|
|
186
|
+
await expect(doctorCommand()).rejects.toThrow('process.exit(0)');
|
|
141
187
|
const p = await import('@clack/prompts');
|
|
142
|
-
expect(
|
|
188
|
+
expect(vi
|
|
189
|
+
.mocked(p.log.warn)
|
|
190
|
+
.mock.calls.filter(([message]) => message.includes('not imported in'))).toHaveLength(2);
|
|
143
191
|
});
|
|
144
192
|
it('warns when a tailwind config exists but does not reference blotchPlugin', async () => {
|
|
145
193
|
fixture = createFixture({ config: DEFAULT_CONFIG });
|
|
146
194
|
writePackageJson(fixture.dir, { dependencies: FULL_ANGULAR_DEPS });
|
|
195
|
+
installPackage(fixture.dir, 'tailwindcss', '3.4.19');
|
|
147
196
|
writeFileSync(join(fixture.dir, 'tailwind.config.js'), 'module.exports = { plugins: [] };');
|
|
148
197
|
vi.spyOn(process, 'cwd').mockReturnValue(fixture.dir);
|
|
149
198
|
const { doctorCommand } = await import('./doctor');
|
|
@@ -177,7 +226,7 @@ describe('doctorCommand', () => {
|
|
|
177
226
|
await expect(doctorCommand()).rejects.toThrow('process.exit(1)');
|
|
178
227
|
const p = await import('@clack/prompts');
|
|
179
228
|
expect(p.log.error).toHaveBeenCalledWith(expect.stringMatching(/Missing packages: .*@angular\/core.*@blotch\/angular-lynx.*@blotch\/dolan/));
|
|
180
|
-
expect(
|
|
229
|
+
expect(await logged('warn')).toContain('tailwindcss not found — required if using dolan UI components. Install it with npm install tailwindcss, or set it up with ng generate @blotch/angular-lynx:add-tailwind');
|
|
181
230
|
});
|
|
182
231
|
it('reads Angular deps from devDependencies too', async () => {
|
|
183
232
|
fixture = createFixture({ config: DEFAULT_CONFIG });
|
|
@@ -267,15 +316,16 @@ describe('doctorCommand', () => {
|
|
|
267
316
|
expect(p.log.warn).toHaveBeenCalledWith(expect.stringContaining('dolan add spinner'));
|
|
268
317
|
});
|
|
269
318
|
it('does not warn about a dependency that is also installed', async () => {
|
|
270
|
-
// button depends on spinner, and spinner
|
|
271
|
-
// installing all
|
|
272
|
-
// checkComponentHealth is false for
|
|
273
|
-
// run dolan add" warning fires.
|
|
319
|
+
// button depends on spinner, glass and icon, and spinner and glass on
|
|
320
|
+
// icon (registry.ts) — installing all four means the
|
|
321
|
+
// `!installed.includes(dep)` check in checkComponentHealth is false for
|
|
322
|
+
// every dependency, so no "requires ... run dolan add" warning fires.
|
|
274
323
|
fixture = createFixture({
|
|
275
324
|
config: DEFAULT_CONFIG,
|
|
276
325
|
components: {
|
|
277
326
|
button: { 'button.ts': '' },
|
|
278
327
|
spinner: { 'spinner.ts': '' },
|
|
328
|
+
glass: { 'glass.ts': '' },
|
|
279
329
|
icon: { 'icon.ts': '' },
|
|
280
330
|
},
|
|
281
331
|
});
|
|
@@ -382,4 +432,368 @@ describe('doctorCommand', () => {
|
|
|
382
432
|
expect(p.log.warn).not.toHaveBeenCalledWith(expect.stringContaining('is missing files'));
|
|
383
433
|
});
|
|
384
434
|
});
|
|
435
|
+
describe('doctorCommand Tailwind', () => {
|
|
436
|
+
/**
|
|
437
|
+
* A project in `dir` with nothing wrong but its Tailwind, which the test
|
|
438
|
+
* installs. Runs doctor.
|
|
439
|
+
*/
|
|
440
|
+
const runDoctor = async (dir, files = {}) => {
|
|
441
|
+
writePackageJson(dir, { dependencies: FULL_ANGULAR_DEPS });
|
|
442
|
+
for (const [file, content] of Object.entries(files)) {
|
|
443
|
+
writeFileSync(join(dir, file), content);
|
|
444
|
+
}
|
|
445
|
+
vi.spyOn(process, 'cwd').mockReturnValue(dir);
|
|
446
|
+
const { doctorCommand } = await import('./doctor');
|
|
447
|
+
await expect(doctorCommand()).rejects.toThrow('process.exit(0)');
|
|
448
|
+
};
|
|
449
|
+
/** The theme's tailwind.css, imported as dolan init shows. */
|
|
450
|
+
const IMPORTED = { 'src/styles.css': "@import './styles/tailwind.css';" };
|
|
451
|
+
it('finds the Tailwind a workspace root installs', async () => {
|
|
452
|
+
// The app's package.json lists no Tailwind: the root's is every package's.
|
|
453
|
+
fixture = createFixture({});
|
|
454
|
+
installPackage(fixture.dir, 'tailwindcss', '4.3.3');
|
|
455
|
+
const app = join(fixture.dir, 'apps', 'app');
|
|
456
|
+
for (const dir of Object.values(DEFAULT_CONFIG.aliases)) {
|
|
457
|
+
mkdirSync(join(app, dir), { recursive: true });
|
|
458
|
+
}
|
|
459
|
+
writeFileSync(join(app, 'dolan.config.json'), JSON.stringify(DEFAULT_CONFIG));
|
|
460
|
+
await runDoctor(app, IMPORTED);
|
|
461
|
+
expect(await logged('success')).toContain('tailwindcss 4.3.3 found, set up for Tailwind 4');
|
|
462
|
+
expect(await logged('warn')).not.toContainEqual(expect.stringContaining('tailwindcss'));
|
|
463
|
+
});
|
|
464
|
+
it('finds the Tailwind @tailwindcss/postcss depends on, hoisted next to it', async () => {
|
|
465
|
+
// npm's layout, for an app that lists only the plugin.
|
|
466
|
+
fixture = createFixture({ config: DEFAULT_CONFIG });
|
|
467
|
+
installPackage(fixture.dir, '@tailwindcss/postcss', '4.3.3');
|
|
468
|
+
installPackage(fixture.dir, 'tailwindcss', '4.3.3');
|
|
469
|
+
await runDoctor(fixture.dir, IMPORTED);
|
|
470
|
+
expect(await logged('success')).toContain('tailwindcss 4.3.3 found, set up for Tailwind 4');
|
|
471
|
+
});
|
|
472
|
+
it("finds the Tailwind @tailwindcss/postcss depends on in the plugin's own folder", async () => {
|
|
473
|
+
// pnpm's layout, out of the app's reach. The build still finds it: pnpm
|
|
474
|
+
// runs `ng` with the packages it hoists on NODE_PATH.
|
|
475
|
+
fixture = createFixture({ config: DEFAULT_CONFIG });
|
|
476
|
+
installPackage(installPackage(fixture.dir, '@tailwindcss/postcss', '4.3.3'), 'tailwindcss', '4.3.3');
|
|
477
|
+
await runDoctor(fixture.dir, IMPORTED);
|
|
478
|
+
expect(await logged('success')).toEqual(expect.arrayContaining([
|
|
479
|
+
'tailwindcss 4.3.3 found, set up for Tailwind 4',
|
|
480
|
+
'tailwind.css imported in src/styles.css',
|
|
481
|
+
]));
|
|
482
|
+
});
|
|
483
|
+
it('warns about a Tailwind config with Tailwind 4 installed, as the build does', async () => {
|
|
484
|
+
fixture = createFixture({ config: DEFAULT_CONFIG });
|
|
485
|
+
installPackage(fixture.dir, 'tailwindcss', '4.3.3');
|
|
486
|
+
await runDoctor(fixture.dir, {
|
|
487
|
+
'tailwind.config.js': 'module.exports = { plugins: [blotchPlugin] };',
|
|
488
|
+
});
|
|
489
|
+
expect(await logged('warn')).toContain("Found tailwind.config.js, but Tailwind CSS 4.3.3 is installed, which doesn't run from a Tailwind config file. While it's there, dolan sets the theme up for Tailwind 3. Move its settings into your stylesheet and delete it, then run dolan update for the theme's tailwind.css.");
|
|
490
|
+
// Tailwind 4 doesn't run the config, so its plugin isn't a pass.
|
|
491
|
+
expect(await logged('success')).not.toContainEqual(expect.stringMatching(/set up for|blotchPlugin/));
|
|
492
|
+
});
|
|
493
|
+
it("names the package manager's command when Tailwind isn't installed", async () => {
|
|
494
|
+
fixture = createFixture({ config: DEFAULT_CONFIG });
|
|
495
|
+
writeFileSync(join(fixture.dir, 'yarn.lock'), '');
|
|
496
|
+
await runDoctor(fixture.dir);
|
|
497
|
+
expect(await logged('warn')).toContainEqual(expect.stringContaining('tailwindcss not found — required if using dolan UI components. Install it with yarn add tailwindcss'));
|
|
498
|
+
// Without a version, there's no setup to check.
|
|
499
|
+
expect(await logged('warn')).not.toContainEqual(expect.stringMatching(/Tailwind config|tailwind\.css not imported/));
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
describe('doctorCommand theme channels', () => {
|
|
503
|
+
/**
|
|
504
|
+
* A plugin that maps primary and border with themeColor(), as dolan's does.
|
|
505
|
+
*/
|
|
506
|
+
const PLUGIN = `colors: {
|
|
507
|
+
primary: { DEFAULT: themeColor('primary') },
|
|
508
|
+
border: themeColor("border"),
|
|
509
|
+
destructive: { subtle: 'var(--destructive-subtle)' },
|
|
510
|
+
}`;
|
|
511
|
+
/**
|
|
512
|
+
* Writes the theme's plugin and stylesheets, then runs doctor.
|
|
513
|
+
*/
|
|
514
|
+
const runDoctor = async (plugin, css) => {
|
|
515
|
+
fixture = createFixture({ config: DEFAULT_CONFIG });
|
|
516
|
+
writePackageJson(fixture.dir, { dependencies: FULL_ANGULAR_DEPS });
|
|
517
|
+
const themeDir = join(fixture.dir, DEFAULT_CONFIG.aliases.theme);
|
|
518
|
+
writeFileSync(join(themeDir, 'tailwind-plugin.ts'), plugin);
|
|
519
|
+
for (const [file, content] of Object.entries(css)) {
|
|
520
|
+
writeFileSync(join(themeDir, file), content);
|
|
521
|
+
}
|
|
522
|
+
vi.spyOn(process, 'cwd').mockReturnValue(fixture.dir);
|
|
523
|
+
const { doctorCommand } = await import('./doctor');
|
|
524
|
+
await expect(doctorCommand()).rejects.toThrow('process.exit(0)');
|
|
525
|
+
return import('@clack/prompts');
|
|
526
|
+
};
|
|
527
|
+
it('passes when every mapped color has matching channels', async () => {
|
|
528
|
+
const p = await runDoctor(PLUGIN, {
|
|
529
|
+
'default.css': `/* --primary: red; */
|
|
530
|
+
page {
|
|
531
|
+
--primary: rgba(24, 24, 27, 1);
|
|
532
|
+
--primary-rgb: 24, 24, 27;
|
|
533
|
+
--border: #e4e4e7;
|
|
534
|
+
--border-rgb: 228,228,231;
|
|
535
|
+
}`,
|
|
536
|
+
// A theme that sets only some colors is checked for those alone.
|
|
537
|
+
'ocean.css': 'page { --primary: rgb(37 99 235); --primary-rgb: 37,99, 235; }',
|
|
538
|
+
});
|
|
539
|
+
expect(p.log.success).toHaveBeenCalledWith('Theme colors have the channels opacity modifiers need');
|
|
540
|
+
expect(p.log.warn).not.toHaveBeenCalledWith(expect.stringContaining('channels'));
|
|
541
|
+
});
|
|
542
|
+
it('warns about colors without channels, with the line to add', async () => {
|
|
543
|
+
// A theme copied before the -rgb variables, kept by dolan update.
|
|
544
|
+
const p = await runDoctor(PLUGIN, {
|
|
545
|
+
'default.css': 'page { --primary: rgba(24, 24, 27, 1); --border: hsl(240, 6%, 90%); }',
|
|
546
|
+
});
|
|
547
|
+
expect(p.log.warn).toHaveBeenCalledWith(expect.stringMatching(/default\.css.*--primary.*--border.*bg-primary\/50.*--primary-rgb: 24, 24, 27;/));
|
|
548
|
+
});
|
|
549
|
+
it("suggests placeholder channels when it can't read the color", async () => {
|
|
550
|
+
const p = await runDoctor(PLUGIN, {
|
|
551
|
+
'dark.css': 'page.dark { --border: hsl(240, 4%, 16%); }',
|
|
552
|
+
});
|
|
553
|
+
expect(p.log.warn).toHaveBeenCalledWith(expect.stringContaining('--border-rgb: r, g, b;'));
|
|
554
|
+
});
|
|
555
|
+
it("warns about channels that don't match their color", async () => {
|
|
556
|
+
// The brand color changed, its channels didn't.
|
|
557
|
+
const p = await runDoctor(PLUGIN, {
|
|
558
|
+
'default.css': `page {
|
|
559
|
+
--primary: #2563eb;
|
|
560
|
+
--primary-rgb: 24, 24, 27;
|
|
561
|
+
--border: #fff;
|
|
562
|
+
--border-rgb: 0, 0, 0;
|
|
563
|
+
}`,
|
|
564
|
+
});
|
|
565
|
+
expect(p.log.warn).toHaveBeenCalledWith(expect.stringMatching(/default\.css.*--primary-rgb.*--border-rgb.*don't/));
|
|
566
|
+
});
|
|
567
|
+
it('warns about space-separated channels, which rgba() reads as one argument', async () => {
|
|
568
|
+
const p = await runDoctor(PLUGIN, {
|
|
569
|
+
'default.css': 'page { --primary: rgba(24, 24, 27, 1); --primary-rgb: 24 24 27; }',
|
|
570
|
+
});
|
|
571
|
+
expect(p.log.warn).toHaveBeenCalledWith(expect.stringMatching(/--primary-rgb.* doesn't hold .*comma-separated/));
|
|
572
|
+
expect(p.log.success).not.toHaveBeenCalledWith(expect.stringContaining('channels'));
|
|
573
|
+
});
|
|
574
|
+
it('says "doesn\'t" for one differing color, and skips colors it can\'t read', async () => {
|
|
575
|
+
const p = await runDoctor(PLUGIN, {
|
|
576
|
+
'default.css': `page {
|
|
577
|
+
--primary: rgba(37, 99, 235, 1);
|
|
578
|
+
--primary-rgb: 24, 24, 27;
|
|
579
|
+
--border: hsl(240, 6%, 90%);
|
|
580
|
+
--border-rgb: 1, 2, 3;
|
|
581
|
+
}`,
|
|
582
|
+
});
|
|
583
|
+
expect(p.log.warn).toHaveBeenCalledWith(expect.stringMatching(/--primary-rgb.* doesn't hold/));
|
|
584
|
+
expect(p.log.warn).not.toHaveBeenCalledWith(expect.stringContaining('--border-rgb'));
|
|
585
|
+
});
|
|
586
|
+
it("doesn't check a plugin that maps no themeColor()", async () => {
|
|
587
|
+
// An older plugin: its modifiers never worked, so nothing needs channels.
|
|
588
|
+
const p = await runDoctor("colors: { primary: 'var(--primary)' }", {
|
|
589
|
+
'default.css': 'page { --primary: rgba(24, 24, 27, 1); }',
|
|
590
|
+
});
|
|
591
|
+
expect(p.log.warn).not.toHaveBeenCalledWith(expect.stringContaining('channels'));
|
|
592
|
+
expect(p.log.success).not.toHaveBeenCalledWith(expect.stringContaining('channels'));
|
|
593
|
+
});
|
|
594
|
+
describe('on Tailwind 4', () => {
|
|
595
|
+
/**
|
|
596
|
+
* Writes the theme's tailwind.css and stylesheets, then runs doctor.
|
|
597
|
+
*/
|
|
598
|
+
const runDoctor4 = async (css) => {
|
|
599
|
+
fixture = createFixture({ config: DEFAULT_CONFIG });
|
|
600
|
+
writePackageJson(fixture.dir, { dependencies: FULL_ANGULAR_DEPS });
|
|
601
|
+
installPackage(fixture.dir, 'tailwindcss', '4.3.3');
|
|
602
|
+
const themeDir = join(fixture.dir, DEFAULT_CONFIG.aliases.theme);
|
|
603
|
+
for (const [file, content] of Object.entries(css)) {
|
|
604
|
+
writeFileSync(join(themeDir, file), content);
|
|
605
|
+
}
|
|
606
|
+
vi.spyOn(process, 'cwd').mockReturnValue(fixture.dir);
|
|
607
|
+
const { doctorCommand } = await import('./doctor');
|
|
608
|
+
await expect(doctorCommand()).rejects.toThrow('process.exit(0)');
|
|
609
|
+
return import('@clack/prompts');
|
|
610
|
+
};
|
|
611
|
+
/**
|
|
612
|
+
* The colors tailwind.css maps, as the theme's does.
|
|
613
|
+
*/
|
|
614
|
+
const TAILWIND_CSS = `@theme inline {
|
|
615
|
+
--color-primary: var(--primary);
|
|
616
|
+
--color-destructive-subtle: var(--destructive-subtle);
|
|
617
|
+
}`;
|
|
618
|
+
it('checks the colors tailwind.css maps, skipping translucent ones', async () => {
|
|
619
|
+
const p = await runDoctor4({
|
|
620
|
+
'tailwind.css': TAILWIND_CSS,
|
|
621
|
+
'default.css': 'page { --primary: rgba(24, 24, 27, 1); --primary-rgb: 24, 24, 27; --destructive-subtle: rgba(239, 68, 68, 0.1); }',
|
|
622
|
+
});
|
|
623
|
+
expect(p.log.success).toHaveBeenCalledWith('Theme colors have the channels opacity modifiers need');
|
|
624
|
+
});
|
|
625
|
+
it('warns about a color tailwind.css maps without channels', async () => {
|
|
626
|
+
const p = await runDoctor4({
|
|
627
|
+
'tailwind.css': TAILWIND_CSS,
|
|
628
|
+
'default.css': 'page { --primary: rgba(24, 24, 27, 1); }',
|
|
629
|
+
});
|
|
630
|
+
expect(p.log.warn).toHaveBeenCalledWith(expect.stringMatching(/default\.css.*--primary-rgb: 24, 24, 27;/));
|
|
631
|
+
});
|
|
632
|
+
it("doesn't check a theme without tailwind.css or the plugin", async () => {
|
|
633
|
+
const p = await runDoctor4({
|
|
634
|
+
'default.css': 'page { --primary: rgba(24, 24, 27, 1); }',
|
|
635
|
+
});
|
|
636
|
+
expect(p.log.success).not.toHaveBeenCalledWith(expect.stringContaining('channels'));
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
});
|
|
640
|
+
describe('doctorCommand theme colors components read', () => {
|
|
641
|
+
/** Every glass color, as dolan's default.css sets them. */
|
|
642
|
+
const GLASS_CSS = `page {
|
|
643
|
+
--glass-tint: rgba(24, 24, 27, 1);
|
|
644
|
+
--glass-tint-rgb: 24, 24, 27;
|
|
645
|
+
--glass-tint-foreground: rgba(250, 250, 250, 1);
|
|
646
|
+
--glass-tint-foreground-rgb: 250, 250, 250;
|
|
647
|
+
--glass-foreground: rgba(9, 9, 11, 1);
|
|
648
|
+
--glass-foreground-rgb: 9, 9, 11;
|
|
649
|
+
--glass-muted-foreground: rgba(63, 63, 70, 1);
|
|
650
|
+
--glass-muted-foreground-rgb: 63, 63, 70;
|
|
651
|
+
--glass-clear-foreground: rgba(255, 255, 255, 1);
|
|
652
|
+
--glass-clear-foreground-rgb: 255, 255, 255;
|
|
653
|
+
--glass-dim: rgba(0, 0, 0, 0.35);
|
|
654
|
+
}`;
|
|
655
|
+
/** The glass colors mapped as dolan's tailwind.css maps them. */
|
|
656
|
+
const GLASS_TAILWIND_CSS = `@theme inline {
|
|
657
|
+
--color-glass-tint: var(--glass-tint);
|
|
658
|
+
--color-glass-tint-foreground: var(--glass-tint-foreground);
|
|
659
|
+
--color-glass-foreground: var(--glass-foreground);
|
|
660
|
+
--color-glass-muted-foreground: var(--glass-muted-foreground);
|
|
661
|
+
--color-glass-clear-foreground: var(--glass-clear-foreground);
|
|
662
|
+
--color-glass-dim: var(--glass-dim);
|
|
663
|
+
}`;
|
|
664
|
+
/**
|
|
665
|
+
* A Tailwind 4 project with glass installed and the given theme files,
|
|
666
|
+
* and dolan's own theme if `bundled` is given. Runs doctor.
|
|
667
|
+
*/
|
|
668
|
+
const runDoctor = async (theme, bundled, root = {}) => {
|
|
669
|
+
fixture = createFixture({
|
|
670
|
+
config: DEFAULT_CONFIG,
|
|
671
|
+
lockfile: tracking('glass', 'icon'),
|
|
672
|
+
components: {
|
|
673
|
+
glass: { 'glass.ts': '' },
|
|
674
|
+
icon: { 'icon.ts': '' },
|
|
675
|
+
},
|
|
676
|
+
themeFiles: bundled,
|
|
677
|
+
});
|
|
678
|
+
writePackageJson(fixture.dir, { dependencies: FULL_ANGULAR_DEPS });
|
|
679
|
+
// A Tailwind config is a Tailwind 3 setup.
|
|
680
|
+
installPackage(fixture.dir, 'tailwindcss', root['tailwind.config.ts'] ? '3.4.19' : '4.3.3');
|
|
681
|
+
const themeDir = join(fixture.dir, DEFAULT_CONFIG.aliases.theme);
|
|
682
|
+
for (const [file, content] of Object.entries(theme)) {
|
|
683
|
+
writeFileSync(join(themeDir, file), content);
|
|
684
|
+
}
|
|
685
|
+
for (const [file, content] of Object.entries(root)) {
|
|
686
|
+
writeFileSync(join(fixture.dir, file), content);
|
|
687
|
+
}
|
|
688
|
+
vi.spyOn(process, 'cwd').mockReturnValue(fixture.dir);
|
|
689
|
+
const { doctorCommand } = await import('./doctor');
|
|
690
|
+
await expect(doctorCommand()).rejects.toThrow('process.exit(0)');
|
|
691
|
+
return import('@clack/prompts');
|
|
692
|
+
};
|
|
693
|
+
it('passes when the theme has and maps every color the components read', async () => {
|
|
694
|
+
const p = await runDoctor({
|
|
695
|
+
'default.css': GLASS_CSS,
|
|
696
|
+
'tailwind.css': GLASS_TAILWIND_CSS,
|
|
697
|
+
});
|
|
698
|
+
expect(p.log.success).toHaveBeenCalledWith('Theme has the colors your components read');
|
|
699
|
+
});
|
|
700
|
+
it("lists the colors a theme lacks, with dolan's lines for them", async () => {
|
|
701
|
+
// A theme copied before glass, and edited, so dolan update kept it.
|
|
702
|
+
const p = await runDoctor({
|
|
703
|
+
'default.css': 'page { --primary: rgba(24, 24, 27, 1); }',
|
|
704
|
+
'dark.css': GLASS_CSS.replace('page', 'page.dark'),
|
|
705
|
+
}, { 'default.css': GLASS_CSS });
|
|
706
|
+
expect(await logged('warn')).toContainEqual(expect.stringMatching(/default\.css lacks theme colors that glass read: --glass-tint, .*--glass-dim\..*dolan update.*\n--glass-tint: rgba\(24, 24, 27, 1\);\n--glass-tint-rgb: 24, 24, 27;\n/s));
|
|
707
|
+
expect(await logged('warn')).not.toContainEqual(expect.stringContaining('dark.css lacks'));
|
|
708
|
+
expect(p.log.success).not.toHaveBeenCalledWith('Theme has the colors your components read');
|
|
709
|
+
});
|
|
710
|
+
it("points at dolan update alone without dolan's theme to quote", async () => {
|
|
711
|
+
await runDoctor({ 'default.css': 'page {}' });
|
|
712
|
+
expect(await logged('warn')).toContainEqual(expect.stringMatching(/default\.css lacks .*haven't edited\.$/));
|
|
713
|
+
});
|
|
714
|
+
it("lists the colors tailwind.css doesn't map", async () => {
|
|
715
|
+
await runDoctor({
|
|
716
|
+
'default.css': GLASS_CSS,
|
|
717
|
+
'tailwind.css': GLASS_TAILWIND_CSS.replace(' --color-glass-dim: var(--glass-dim);\n', ''),
|
|
718
|
+
});
|
|
719
|
+
expect(await logged('warn')).toContainEqual(expect.stringMatching(/tailwind\.css doesn't map theme colors that glass read to classes: glass-dim\./));
|
|
720
|
+
});
|
|
721
|
+
it("accepts the Tailwind 3 plugin's themeColor() and var() mappings", async () => {
|
|
722
|
+
const p = await runDoctor({
|
|
723
|
+
'default.css': GLASS_CSS,
|
|
724
|
+
'tailwind-plugin.ts': `glass: {
|
|
725
|
+
tint: { DEFAULT: themeColor('glass-tint'), foreground: themeColor("glass-tint-foreground") },
|
|
726
|
+
foreground: themeColor('glass-foreground'),
|
|
727
|
+
'muted-foreground': themeColor('glass-muted-foreground'),
|
|
728
|
+
'clear-foreground': themeColor('glass-clear-foreground'),
|
|
729
|
+
dim: 'var(--glass-dim)',
|
|
730
|
+
}`,
|
|
731
|
+
}, undefined, { 'tailwind.config.ts': 'export default { plugins: [blotchPlugin] };' });
|
|
732
|
+
expect(p.log.success).toHaveBeenCalledWith('Theme has the colors your components read');
|
|
733
|
+
});
|
|
734
|
+
});
|
|
735
|
+
// A move between Tailwind versions leaves the other version's theme files,
|
|
736
|
+
// which nothing uses: the plugin is dead code the app's coverage counts.
|
|
737
|
+
describe('doctorCommand — theme files for the Tailwind version the app left', () => {
|
|
738
|
+
/**
|
|
739
|
+
* Runs doctor in an app on `tailwind` whose theme folder has `files`, which
|
|
740
|
+
* the lockfile records unless `tracked` says otherwise, and returns its
|
|
741
|
+
* warnings about them, colors stripped.
|
|
742
|
+
*/
|
|
743
|
+
const leftoverWarnings = async (tailwind, files, tracked = files) => {
|
|
744
|
+
fixture = createFixture({
|
|
745
|
+
config: DEFAULT_CONFIG,
|
|
746
|
+
lockfile: {
|
|
747
|
+
version: 1,
|
|
748
|
+
components: {},
|
|
749
|
+
theme: Object.fromEntries(tracked.map((file) => [file, { hash: hashContent('') }])),
|
|
750
|
+
},
|
|
751
|
+
});
|
|
752
|
+
const dependencies = FULL_ANGULAR_DEPS;
|
|
753
|
+
if (tailwind === null)
|
|
754
|
+
writePackageJson(fixture.dir, { dependencies });
|
|
755
|
+
else
|
|
756
|
+
onTailwind(fixture.dir, tailwind, { dependencies });
|
|
757
|
+
for (const file of files) {
|
|
758
|
+
writeFileSync(join(fixture.dir, DEFAULT_CONFIG.aliases.theme, file), '');
|
|
759
|
+
}
|
|
760
|
+
vi.spyOn(process, 'cwd').mockReturnValue(fixture.dir);
|
|
761
|
+
const { doctorCommand } = await import('./doctor');
|
|
762
|
+
await expect(doctorCommand()).rejects.toThrow('process.exit(0)');
|
|
763
|
+
const p = await import('@clack/prompts');
|
|
764
|
+
return vi
|
|
765
|
+
.mocked(p.log.warn)
|
|
766
|
+
.mock.calls.map(([message]) => stripVTControlCharacters(message))
|
|
767
|
+
.filter((message) => message.startsWith('Found '));
|
|
768
|
+
};
|
|
769
|
+
it('warns about the Tailwind 3 plugin and its test in a Tailwind 4 app, for dolan update to delete', async () => {
|
|
770
|
+
expect(await leftoverWarnings(4, [
|
|
771
|
+
'tailwind.css',
|
|
772
|
+
'tailwind-plugin.ts',
|
|
773
|
+
'tailwind-plugin.test.ts',
|
|
774
|
+
])).toEqual([
|
|
775
|
+
"Found tailwind-plugin.ts, tailwind-plugin.test.ts in src/styles, but the app uses Tailwind 4, which takes the theme's classes from tailwind.css. Run dolan update to delete them, or delete them.",
|
|
776
|
+
]);
|
|
777
|
+
});
|
|
778
|
+
it('warns about tailwind.css in a Tailwind 3 app', async () => {
|
|
779
|
+
expect(await leftoverWarnings(3, ['tailwind.css', 'tailwind-plugin.ts'])).toEqual([
|
|
780
|
+
"Found tailwind.css in src/styles, but the app uses Tailwind 3, which takes the theme's classes from tailwind-plugin.ts. Run dolan update to delete it, or delete it.",
|
|
781
|
+
]);
|
|
782
|
+
});
|
|
783
|
+
it("doesn't send the app to dolan update for a plugin it doesn't delete", async () => {
|
|
784
|
+
// Kept when `update` asked, so the app's own now.
|
|
785
|
+
expect(await leftoverWarnings(4, ['tailwind.css', 'tailwind-plugin.ts'], [])).toEqual([
|
|
786
|
+
"Found tailwind-plugin.ts in src/styles, but the app uses Tailwind 4, which takes the theme's classes from tailwind.css. Delete it, unless your own code uses it.",
|
|
787
|
+
]);
|
|
788
|
+
});
|
|
789
|
+
it.each([
|
|
790
|
+
[4, ['tailwind.css']],
|
|
791
|
+
[3, ['tailwind-plugin.ts', 'tailwind-plugin.test.ts']],
|
|
792
|
+
])('says nothing when a Tailwind %s theme has only the files the app takes', async (tailwind, files) => {
|
|
793
|
+
expect(await leftoverWarnings(tailwind, [...files])).toEqual([]);
|
|
794
|
+
});
|
|
795
|
+
it('says nothing without Tailwind, which has no version to have left', async () => {
|
|
796
|
+
expect(await leftoverWarnings(null, ['tailwind.css'])).toEqual([]);
|
|
797
|
+
});
|
|
798
|
+
});
|
|
385
799
|
//# sourceMappingURL=doctor.test.js.map
|