Skip to content

Commit 62ec41a

Browse files
authored
Merge pull request #12 from morishxt/refactor-tests
Refactoring
2 parents f83eb78 + 2915040 commit 62ec41a

File tree

12 files changed

+1003
-973
lines changed

12 files changed

+1003
-973
lines changed

src/__tests__/base.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { describe, it, expect } from "vitest";
2+
import { windctrl } from "../";
3+
4+
describe("windctrl", () => {
5+
describe("Base classes", () => {
6+
it("should apply base classes when provided", () => {
7+
const button = windctrl({
8+
base: "rounded px-4 py-2",
9+
});
10+
11+
const result = button();
12+
expect(result.className).toContain("rounded");
13+
expect(result.className).toContain("px-4");
14+
expect(result.className).toContain("py-2");
15+
expect(result.style).toEqual(undefined);
16+
});
17+
18+
it("should work without base classes", () => {
19+
const button = windctrl({});
20+
21+
const result = button({});
22+
expect(result.className).toBe("");
23+
expect(result.style).toEqual(undefined);
24+
});
25+
});
26+
});

src/__tests__/dynamic.test.ts

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
import { describe, it, expect } from "vitest";
2+
import { windctrl, dynamic as d } from "../";
3+
4+
describe("windctrl", () => {
5+
describe("Dynamic (Interpolated Variants)", () => {
6+
it("should apply className when dynamic resolver returns string", () => {
7+
const button = windctrl({
8+
dynamic: {
9+
w: (val) => (typeof val === "number" ? `w-[${val}px]` : `w-${val}`),
10+
},
11+
});
12+
13+
const result = button({ w: "full" });
14+
expect(result.className).toContain("w-full");
15+
expect(result.style).toEqual(undefined);
16+
});
17+
18+
it("should apply style when dynamic resolver returns object with style", () => {
19+
const button = windctrl({
20+
dynamic: {
21+
w: (val) =>
22+
typeof val === "number"
23+
? { style: { width: `${val}px` } }
24+
: `w-${val}`,
25+
},
26+
});
27+
28+
const result = button({ w: 200 });
29+
expect(result.style).toEqual({ width: "200px" });
30+
});
31+
32+
it("should merge className and style when dynamic resolver returns both", () => {
33+
const button = windctrl({
34+
base: "rounded",
35+
dynamic: {
36+
color: (val) => ({
37+
className: `text-${val}`,
38+
style: { color: val },
39+
}),
40+
},
41+
});
42+
43+
const result = button({ color: "red" });
44+
expect(result.className).toContain("text-red");
45+
expect(result.style).toEqual({ color: "red" });
46+
});
47+
48+
it("should handle multiple dynamic props", () => {
49+
const button = windctrl({
50+
dynamic: {
51+
w: (val) =>
52+
typeof val === "number"
53+
? { style: { width: `${val}px` } }
54+
: `w-${val}`,
55+
h: (val) =>
56+
typeof val === "number"
57+
? { style: { height: `${val}px` } }
58+
: `h-${val}`,
59+
},
60+
});
61+
62+
const result = button({ w: 100, h: 200 });
63+
expect(result.style).toEqual({ width: "100px", height: "200px" });
64+
});
65+
66+
it("should handle mixed dynamic props (string and number)", () => {
67+
const button = windctrl({
68+
dynamic: {
69+
w: (val) =>
70+
typeof val === "number"
71+
? { style: { width: `${val}px` } }
72+
: `w-${val}`,
73+
},
74+
});
75+
76+
const stringResult = button({ w: "full" });
77+
expect(stringResult.className).toContain("w-full");
78+
expect(stringResult.style).toEqual(undefined);
79+
80+
const numberResult = button({ w: 300 });
81+
expect(numberResult.style).toEqual({ width: "300px" });
82+
});
83+
84+
it("should resolve style conflicts with last one wins for dynamic styles", () => {
85+
const box = windctrl({
86+
dynamic: {
87+
w1: () => ({ style: { width: "100px" } }),
88+
w2: () => ({ style: { width: "200px" } }),
89+
},
90+
});
91+
92+
const result = box({ w1: true as any, w2: true as any });
93+
94+
expect(result.style).toEqual({ width: "200px" });
95+
});
96+
});
97+
98+
describe("Dynamic presets", () => {
99+
describe("d.px()", () => {
100+
it("should output inline style for number (px) and keep className empty", () => {
101+
const box = windctrl({
102+
dynamic: {
103+
w: d.px("width"),
104+
},
105+
});
106+
107+
const result = box({ w: 123 });
108+
expect(result.style).toEqual({ width: "123px" });
109+
expect(result.className).toBe("");
110+
});
111+
112+
it("should pass through className string for string input (Unified API)", () => {
113+
const box = windctrl({
114+
dynamic: {
115+
w: d.px("width"),
116+
},
117+
});
118+
119+
const result = box({ w: "w-full" });
120+
expect(result.className).toContain("w-full");
121+
expect(result.style).toEqual(undefined);
122+
});
123+
});
124+
125+
describe("d.num()", () => {
126+
it("should output inline style for number (unitless) and keep className empty", () => {
127+
const layer = windctrl({
128+
dynamic: {
129+
z: d.num("zIndex"),
130+
},
131+
});
132+
133+
const result = layer({ z: 999 });
134+
expect(result.style).toEqual({ zIndex: 999 });
135+
expect(result.className).toBe("");
136+
});
137+
138+
it("should pass through className string for string input (Unified API)", () => {
139+
const layer = windctrl({
140+
dynamic: {
141+
z: d.num("zIndex"),
142+
},
143+
});
144+
145+
const result = layer({ z: "z-50" });
146+
expect(result.className).toContain("z-50");
147+
expect(result.style).toEqual(undefined);
148+
});
149+
});
150+
151+
describe("d.opacity()", () => {
152+
it("should output inline style for number and keep className empty", () => {
153+
const fade = windctrl({
154+
dynamic: {
155+
opacity: d.opacity(),
156+
},
157+
});
158+
159+
const result = fade({ opacity: 0.4 });
160+
expect(result.style).toEqual({ opacity: 0.4 });
161+
expect(result.className).toBe("");
162+
});
163+
164+
it("should pass through className string for string input (Unified API)", () => {
165+
const fade = windctrl({
166+
dynamic: {
167+
opacity: d.opacity(),
168+
},
169+
});
170+
171+
const result = fade({ opacity: "opacity-50" });
172+
expect(result.className).toContain("opacity-50");
173+
expect(result.style).toEqual(undefined);
174+
});
175+
});
176+
177+
describe("d.var()", () => {
178+
it("should set CSS variable as inline style for number with unit (no className output)", () => {
179+
const card = windctrl({
180+
dynamic: {
181+
x: d.var("--x", { unit: "px" }),
182+
},
183+
});
184+
185+
const result = card({ x: 12 });
186+
187+
// NOTE: CSS custom properties are stored as object keys.
188+
expect(result.style).toEqual({ "--x": "12px" });
189+
expect(result.className).toBe("");
190+
});
191+
192+
it("should set CSS variable as inline style for string value (no className output)", () => {
193+
const card = windctrl({
194+
dynamic: {
195+
x: d.var("--x"),
196+
},
197+
});
198+
199+
const result = card({ x: "10%" });
200+
expect(result.style).toEqual({ "--x": "10%" });
201+
expect(result.className).toBe("");
202+
});
203+
204+
it("should merge multiple CSS variables via last-one-wins when same variable is set twice", () => {
205+
const card = windctrl({
206+
dynamic: {
207+
x1: d.var("--x", { unit: "px" }),
208+
x2: d.var("--x", { unit: "px" }),
209+
},
210+
});
211+
212+
const result = card({ x1: 10, x2: 20 });
213+
214+
// last one wins
215+
expect(result.style).toEqual({ "--x": "20px" });
216+
});
217+
});
218+
219+
it("should coexist with other dynamic resolvers (className + style merge)", () => {
220+
const box = windctrl({
221+
dynamic: {
222+
w: d.px("width"),
223+
opacity: d.opacity(),
224+
// keep an existing custom resolver in the same config
225+
custom: (v) => (v ? "ring-2" : ""),
226+
},
227+
});
228+
229+
const result = box({ w: 100, opacity: 0.5, custom: true });
230+
231+
expect(result.style).toEqual({ width: "100px", opacity: 0.5 });
232+
expect(result.className).toContain("ring-2");
233+
});
234+
});
235+
});

0 commit comments

Comments
 (0)