@usefragments/core 1.0.0
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/LICENSE +21 -0
- package/dist/chunk-243QYRUF.js +2145 -0
- package/dist/chunk-243QYRUF.js.map +1 -0
- package/dist/chunk-3LLRNCPX.js +111 -0
- package/dist/chunk-3LLRNCPX.js.map +1 -0
- package/dist/chunk-EIYNNS77.js +48 -0
- package/dist/chunk-EIYNNS77.js.map +1 -0
- package/dist/chunk-JNBFJ34I.js +326 -0
- package/dist/chunk-JNBFJ34I.js.map +1 -0
- package/dist/chunk-R3X7UBHU.js +228 -0
- package/dist/chunk-R3X7UBHU.js.map +1 -0
- package/dist/chunk-SH4KPIYH.js +393 -0
- package/dist/chunk-SH4KPIYH.js.map +1 -0
- package/dist/chunk-V4VQB57N.js +56 -0
- package/dist/chunk-V4VQB57N.js.map +1 -0
- package/dist/chunk-X34IA4LR.js +319 -0
- package/dist/chunk-X34IA4LR.js.map +1 -0
- package/dist/chunk-YF65VYRY.js +493 -0
- package/dist/chunk-YF65VYRY.js.map +1 -0
- package/dist/codes/index.d.ts +40 -0
- package/dist/codes/index.js +23 -0
- package/dist/codes/index.js.map +1 -0
- package/dist/compiled-types/index.d.ts +5 -0
- package/dist/compiled-types/index.js +1 -0
- package/dist/compiled-types/index.js.map +1 -0
- package/dist/generate/index.d.ts +47 -0
- package/dist/generate/index.js +11 -0
- package/dist/generate/index.js.map +1 -0
- package/dist/governance-BOa3KyiJ.d.ts +3279 -0
- package/dist/graph/index.d.ts +66 -0
- package/dist/graph/index.js +17 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/index.d.ts +11388 -0
- package/dist/index.js +8711 -0
- package/dist/index.js.map +1 -0
- package/dist/package-identity.d.ts +42 -0
- package/dist/package-identity.js +23 -0
- package/dist/package-identity.js.map +1 -0
- package/dist/preview/index.d.ts +484 -0
- package/dist/preview/index.js +1129 -0
- package/dist/preview/index.js.map +1 -0
- package/dist/react-types.d.ts +22 -0
- package/dist/react-types.js +1 -0
- package/dist/react-types.js.map +1 -0
- package/dist/registry.d.ts +2367 -0
- package/dist/registry.js +62 -0
- package/dist/registry.js.map +1 -0
- package/dist/schemas/index.d.ts +2584 -0
- package/dist/schemas/index.js +55 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/severity.d.ts +18 -0
- package/dist/severity.js +27 -0
- package/dist/severity.js.map +1 -0
- package/dist/test-utils.d.ts +14 -0
- package/dist/test-utils.js +63 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/topology/index.d.ts +48 -0
- package/dist/topology/index.js +7 -0
- package/dist/topology/index.js.map +1 -0
- package/dist/types-xJ2xyp_G.d.ts +119 -0
- package/package.json +120 -0
- package/src/__tests__/contract-parser.test.ts +614 -0
- package/src/__tests__/dtcg-generators.test.ts +439 -0
- package/src/__tests__/dtcg-parser.test.ts +884 -0
- package/src/__tests__/preview-runtime.test.tsx +111 -0
- package/src/__tests__/schema.test.ts +31 -0
- package/src/__tests__/severity.test.ts +34 -0
- package/src/__tests__/style-comparison.test.ts +662 -0
- package/src/__tests__/tokens-compat.test.ts +51 -0
- package/src/agent-format.test.ts +105 -0
- package/src/agent-format.ts +141 -0
- package/src/bundle.ts +149 -0
- package/src/codes/__tests__/codes.test.ts +73 -0
- package/src/codes/codes.ts +463 -0
- package/src/codes/index.ts +7 -0
- package/src/codes/source-finding-bridge.ts +113 -0
- package/src/compiled-types/index.ts +293 -0
- package/src/component-contract.ts +231 -0
- package/src/component-discovery.ts +80 -0
- package/src/composition.test.ts +262 -0
- package/src/composition.ts +318 -0
- package/src/config-paths.test.ts +78 -0
- package/src/config-paths.ts +47 -0
- package/src/config.ts +18 -0
- package/src/conform-prove.test.ts +274 -0
- package/src/conform-prove.ts +326 -0
- package/src/conform.ts +160 -0
- package/src/constants.ts +114 -0
- package/src/context.ts +2 -0
- package/src/contract/fixture.ts +110 -0
- package/src/contract/hash.test.ts +86 -0
- package/src/contract/hash.ts +195 -0
- package/src/contract/index.ts +43 -0
- package/src/contract/preimage.test.ts +254 -0
- package/src/contract/preimage.ts +365 -0
- package/src/contract/stamp.test.ts +100 -0
- package/src/contract/stamp.ts +105 -0
- package/src/contract-parser.ts +141 -0
- package/src/defineFragment.test.ts +257 -0
- package/src/defineFragment.ts +293 -0
- package/src/dtcg-generators.ts +324 -0
- package/src/dtcg-parser.ts +1 -0
- package/src/dtcg.ts +237 -0
- package/src/facts/__snapshots__/facts.test.ts.snap +124 -0
- package/src/facts/builders.ts +835 -0
- package/src/facts/compile.ts +422 -0
- package/src/facts/fact-index.ts +394 -0
- package/src/facts/facts.test.ts +494 -0
- package/src/facts/ids.ts +125 -0
- package/src/facts/index.ts +119 -0
- package/src/facts/types.ts +614 -0
- package/src/figma.ts +263 -0
- package/src/fragment-types.ts +214 -0
- package/src/generate/generate.test.ts +594 -0
- package/src/generate/index.ts +438 -0
- package/src/governance-integrity.test.ts +186 -0
- package/src/governance-integrity.ts +282 -0
- package/src/governance.test.ts +300 -0
- package/src/governance.ts +605 -0
- package/src/governance.type-test.ts +75 -0
- package/src/graph/engine.ts +446 -0
- package/src/graph/graph.test.ts +746 -0
- package/src/graph/index.ts +32 -0
- package/src/graph/serialization.ts +55 -0
- package/src/graph/types.ts +178 -0
- package/src/index.ts +891 -0
- package/src/local-canonical.ts +102 -0
- package/src/mcp-snapshot.ts +222 -0
- package/src/package-identity-match.ts +33 -0
- package/src/package-identity.generated.ts +233 -0
- package/src/package-identity.test.ts +99 -0
- package/src/package-identity.ts +157 -0
- package/src/performance-presets.ts +142 -0
- package/src/preview/comparison-artifact.test.ts +228 -0
- package/src/preview/comparison-artifact.ts +181 -0
- package/src/preview/comparison-context.ts +93 -0
- package/src/preview/comparison-evidence.ts +112 -0
- package/src/preview/comparison-format.ts +21 -0
- package/src/preview/comparison-presentation.ts +187 -0
- package/src/preview/comparison-references.test.ts +430 -0
- package/src/preview/comparison-references.ts +72 -0
- package/src/preview/comparison-score.ts +59 -0
- package/src/preview/comparison-styles.ts +197 -0
- package/src/preview/comparison.test.ts +559 -0
- package/src/preview/comparison.ts +488 -0
- package/src/preview/index.ts +10 -0
- package/src/preview/types.ts +309 -0
- package/src/preview/validation.test.ts +290 -0
- package/src/preview/validation.ts +356 -0
- package/src/preview-runtime.tsx +144 -0
- package/src/raw-html-canonical.test.ts +41 -0
- package/src/raw-html-canonical.ts +179 -0
- package/src/react-types.ts +21 -0
- package/src/registry-install-plan.ts +344 -0
- package/src/registry.test.ts +245 -0
- package/src/registry.ts +369 -0
- package/src/rules/a11y-required-accessible-name.ts +93 -0
- package/src/rules/color-math.test.ts +72 -0
- package/src/rules/color-math.ts +192 -0
- package/src/rules/components-forbidden-prop-value.ts +108 -0
- package/src/rules/components-prefer-library.ts +943 -0
- package/src/rules/components-unknown-prop.ts +94 -0
- package/src/rules/composition-pattern-schema.ts +125 -0
- package/src/rules/composition-pattern.test.ts +320 -0
- package/src/rules/composition-pattern.ts +271 -0
- package/src/rules/emit-gate.test.ts +42 -0
- package/src/rules/emit-gate.ts +60 -0
- package/src/rules/finding.ts +54 -0
- package/src/rules/index.ts +235 -0
- package/src/rules/jsx-preferred-component.ts +68 -0
- package/src/rules/jsx-preferred-import-path.ts +67 -0
- package/src/rules/presets.ts +57 -0
- package/src/rules/props-invalid-value.ts +115 -0
- package/src/rules/rules.test.ts +3636 -0
- package/src/rules/spacing-resolution.ts +184 -0
- package/src/rules/styles-no-raw-color.ts +329 -0
- package/src/rules/styles-no-raw-dimensions.ts +357 -0
- package/src/rules/styles-no-raw-spacing.ts +236 -0
- package/src/rules/styles-no-raw-typography.ts +237 -0
- package/src/rules/tailwind-arbitrary-color.test.ts +128 -0
- package/src/rules/tailwind-arbitrary-color.ts +93 -0
- package/src/rules/tailwind-arbitrary-spacing.test.ts +157 -0
- package/src/rules/tailwind-arbitrary-spacing.ts +120 -0
- package/src/rules/tailwind-forbidden-palette.test.ts +116 -0
- package/src/rules/tailwind-forbidden-palette.ts +107 -0
- package/src/rules/tailwind-off-scale-spacing-token.test.ts +100 -0
- package/src/rules/tailwind-off-scale-spacing-token.ts +108 -0
- package/src/rules/tailwind-raw-color-via-token.test.ts +108 -0
- package/src/rules/tailwind-raw-color-via-token.ts +82 -0
- package/src/rules/tailwind-rule-utils.ts +106 -0
- package/src/rules/tailwind-unknown-class.test.ts +157 -0
- package/src/rules/tailwind-unknown-class.ts +121 -0
- package/src/rules/taxonomy.test.ts +238 -0
- package/src/rules/theme-no-theme-coupled-literal.ts +132 -0
- package/src/rules/tiers.ts +111 -0
- package/src/rules/tokens-css-vars-must-be-defined.test.ts +244 -0
- package/src/rules/tokens-css-vars-must-be-defined.ts +156 -0
- package/src/rules/tokens-require-dual-fallback.ts +78 -0
- package/src/rules/types.ts +27 -0
- package/src/rules/utils.test.ts +110 -0
- package/src/rules/utils.ts +599 -0
- package/src/schema.ts +427 -0
- package/src/schemas/index.ts +275 -0
- package/src/schemas/schemas.test.ts +124 -0
- package/src/severity.ts +54 -0
- package/src/storyAdapter.test.ts +571 -0
- package/src/storyAdapter.ts +761 -0
- package/src/storyFilters.test.ts +350 -0
- package/src/storyFilters.ts +253 -0
- package/src/storybook-csf.ts +11 -0
- package/src/style-comparison.ts +423 -0
- package/src/test-utils.ts +68 -0
- package/src/token-parser.ts +2 -0
- package/src/token-types.ts +287 -0
- package/src/tokens/categories.ts +131 -0
- package/src/tokens/color.ts +193 -0
- package/src/tokens/design-token-parser.test.ts +159 -0
- package/src/tokens/design-token-parser.ts +402 -0
- package/src/tokens/dtcg.ts +428 -0
- package/src/tokens/index.ts +130 -0
- package/src/tokens/lookup.ts +19 -0
- package/src/tokens/scss.ts +171 -0
- package/src/tokens/tailwind-v4.ts +187 -0
- package/src/tokens/types.ts +43 -0
- package/src/topology/index.ts +9 -0
- package/src/topology/resolve-area.test.ts +174 -0
- package/src/topology/resolve-area.ts +215 -0
- package/src/types.ts +1057 -0
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parity tests for the canonical style comparison engine.
|
|
3
|
+
*
|
|
4
|
+
* These tests are adapted from packages/viewer/src/__tests__/style-utils.test.ts
|
|
5
|
+
* to prove that the core engine produces identical results to the previous
|
|
6
|
+
* viewer and CLI local implementations.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect } from "vitest";
|
|
9
|
+
import {
|
|
10
|
+
compareStyles,
|
|
11
|
+
compareStyleValue,
|
|
12
|
+
normalizeStyleValue,
|
|
13
|
+
compareColors,
|
|
14
|
+
parseColor,
|
|
15
|
+
compareNumericValues,
|
|
16
|
+
compareStylesWithTokens,
|
|
17
|
+
formatTokenSummary,
|
|
18
|
+
getComplianceBadge,
|
|
19
|
+
DEFAULT_STYLE_PROPERTIES,
|
|
20
|
+
DEFAULT_ENHANCED_STYLE_PROPERTIES,
|
|
21
|
+
} from "../style-comparison.js";
|
|
22
|
+
import type {
|
|
23
|
+
TokenLookup,
|
|
24
|
+
StyleDiffItem,
|
|
25
|
+
StyleComparisonResult,
|
|
26
|
+
EnhancedStyleComparisonResult,
|
|
27
|
+
NormalizedToken,
|
|
28
|
+
NormalizedStyleMap,
|
|
29
|
+
StyleComparisonOptions,
|
|
30
|
+
} from "../style-comparison.js";
|
|
31
|
+
|
|
32
|
+
// ─── parseColor ──────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
describe("parseColor", () => {
|
|
35
|
+
describe("hex colors", () => {
|
|
36
|
+
it("parses 6-digit hex color", () => {
|
|
37
|
+
expect(parseColor("#ff0000")).toEqual({ r: 255, g: 0, b: 0 });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("parses lowercase hex color", () => {
|
|
41
|
+
expect(parseColor("#00ff00")).toEqual({ r: 0, g: 255, b: 0 });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("parses mixed case hex color", () => {
|
|
45
|
+
expect(parseColor("#0000FF")).toEqual({ r: 0, g: 0, b: 255 });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("parses hex color with values 0-255", () => {
|
|
49
|
+
expect(parseColor("#336699")).toEqual({ r: 51, g: 102, b: 153 });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("returns null for 3-digit hex color", () => {
|
|
53
|
+
expect(parseColor("#f00")).toBeNull();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("rgb/rgba colors", () => {
|
|
58
|
+
it("parses rgb color", () => {
|
|
59
|
+
expect(parseColor("rgb(255, 0, 0)")).toEqual({
|
|
60
|
+
r: 255,
|
|
61
|
+
g: 0,
|
|
62
|
+
b: 0,
|
|
63
|
+
a: 1,
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("parses rgba color with alpha", () => {
|
|
68
|
+
expect(parseColor("rgba(255, 0, 0, 0.5)")).toEqual({
|
|
69
|
+
r: 255,
|
|
70
|
+
g: 0,
|
|
71
|
+
b: 0,
|
|
72
|
+
a: 0.5,
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("parses rgba color with alpha 0", () => {
|
|
77
|
+
expect(parseColor("rgba(0, 0, 0, 0)")).toEqual({
|
|
78
|
+
r: 0,
|
|
79
|
+
g: 0,
|
|
80
|
+
b: 0,
|
|
81
|
+
a: 0,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("parses rgba color with alpha 1", () => {
|
|
86
|
+
expect(parseColor("rgba(100, 150, 200, 1)")).toEqual({
|
|
87
|
+
r: 100,
|
|
88
|
+
g: 150,
|
|
89
|
+
b: 200,
|
|
90
|
+
a: 1,
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("handles extra whitespace", () => {
|
|
95
|
+
expect(parseColor("rgb( 255 , 128 , 64 )")).toEqual({
|
|
96
|
+
r: 255,
|
|
97
|
+
g: 128,
|
|
98
|
+
b: 64,
|
|
99
|
+
a: 1,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("invalid colors", () => {
|
|
105
|
+
it("returns null for color keywords", () => {
|
|
106
|
+
expect(parseColor("red")).toBeNull();
|
|
107
|
+
expect(parseColor("transparent")).toBeNull();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("returns null for hsl colors", () => {
|
|
111
|
+
expect(parseColor("hsl(0, 100%, 50%)")).toBeNull();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("returns null for empty string", () => {
|
|
115
|
+
expect(parseColor("")).toBeNull();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// ─── compareColors ───────────────────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
describe("compareColors", () => {
|
|
123
|
+
it("matches identical hex colors", () => {
|
|
124
|
+
expect(compareColors("#ff0000", "#ff0000", 0)).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("matches colors within tolerance", () => {
|
|
128
|
+
expect(compareColors("#ff0000", "#fe0000", 5)).toBe(true);
|
|
129
|
+
expect(compareColors("rgb(255, 0, 0)", "rgb(252, 0, 0)", 5)).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("fails colors outside tolerance", () => {
|
|
133
|
+
expect(compareColors("#ff0000", "#f00000", 5)).toBe(false);
|
|
134
|
+
expect(compareColors("rgb(255, 0, 0)", "rgb(240, 0, 0)", 5)).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("matches hex and rgba representations of same color", () => {
|
|
138
|
+
expect(compareColors("#ff0000", "rgba(255, 0, 0, 1)", 0)).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("matches colors with similar alpha", () => {
|
|
142
|
+
expect(
|
|
143
|
+
compareColors("rgba(255, 0, 0, 0.5)", "rgba(255, 0, 0, 0.52)", 5)
|
|
144
|
+
).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("fails colors with different alpha", () => {
|
|
148
|
+
expect(
|
|
149
|
+
compareColors("rgba(255, 0, 0, 0.5)", "rgba(255, 0, 0, 0.7)", 5)
|
|
150
|
+
).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("falls back to string comparison for unparseable colors", () => {
|
|
154
|
+
expect(compareColors("red", "red", 0)).toBe(true);
|
|
155
|
+
expect(compareColors("red", "blue", 0)).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// ─── compareNumericValues ────────────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
describe("compareNumericValues", () => {
|
|
162
|
+
it("matches identical values", () => {
|
|
163
|
+
expect(compareNumericValues("10px", "10px", 0)).toBe(true);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("matches values within tolerance", () => {
|
|
167
|
+
expect(compareNumericValues("10px", "11px", 1)).toBe(true);
|
|
168
|
+
expect(compareNumericValues("10px", "9px", 1)).toBe(true);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("fails values outside tolerance", () => {
|
|
172
|
+
expect(compareNumericValues("10px", "12px", 1)).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("handles values without units", () => {
|
|
176
|
+
expect(compareNumericValues("10", "11", 1)).toBe(true);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("handles decimal values", () => {
|
|
180
|
+
expect(compareNumericValues("10.5px", "11px", 1)).toBe(true);
|
|
181
|
+
expect(compareNumericValues("10.5px", "12px", 1)).toBe(false);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("falls back to string comparison for non-numeric values", () => {
|
|
185
|
+
expect(compareNumericValues("auto", "auto", 0)).toBe(true);
|
|
186
|
+
expect(compareNumericValues("auto", "none", 0)).toBe(false);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// ─── normalizeStyleValue ─────────────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
describe("normalizeStyleValue", () => {
|
|
193
|
+
it("trims whitespace", () => {
|
|
194
|
+
expect(normalizeStyleValue("any", " 10px ")).toBe("10px");
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("collapses multiple spaces", () => {
|
|
198
|
+
expect(normalizeStyleValue("any", "10px 20px")).toBe("10px 20px");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('normalizes "none" boxShadow to empty string', () => {
|
|
202
|
+
expect(normalizeStyleValue("boxShadow", "none")).toBe("");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('does not normalize "none" for other properties', () => {
|
|
206
|
+
expect(normalizeStyleValue("display", "none")).toBe("none");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("normalizes transparent color to 'transparent'", () => {
|
|
210
|
+
expect(normalizeStyleValue("backgroundColor", "rgba(0, 0, 0, 0)")).toBe(
|
|
211
|
+
"transparent"
|
|
212
|
+
);
|
|
213
|
+
expect(normalizeStyleValue("borderColor", "rgba( 0 , 0 , 0 , 0 )")).toBe(
|
|
214
|
+
"transparent"
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// ─── compareStyleValue ───────────────────────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
describe("compareStyleValue", () => {
|
|
222
|
+
describe("exact match", () => {
|
|
223
|
+
it("matches identical values", () => {
|
|
224
|
+
expect(compareStyleValue("fontFamily", "Inter", "Inter")).toBe(true);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it("matches after normalization", () => {
|
|
228
|
+
expect(compareStyleValue("fontFamily", " Inter ", "Inter")).toBe(true);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe("color properties", () => {
|
|
233
|
+
it("uses color comparison for backgroundColor", () => {
|
|
234
|
+
expect(
|
|
235
|
+
compareStyleValue("backgroundColor", "#ff0000", "rgb(255, 0, 0)")
|
|
236
|
+
).toBe(true);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("uses color comparison for borderColor", () => {
|
|
240
|
+
expect(
|
|
241
|
+
compareStyleValue("borderColor", "#00ff00", "rgb(0, 255, 0)")
|
|
242
|
+
).toBe(true);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("allows tolerance in color comparison", () => {
|
|
246
|
+
expect(
|
|
247
|
+
compareStyleValue("backgroundColor", "#ff0000", "rgb(252, 0, 0)")
|
|
248
|
+
).toBe(true);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
describe("numeric properties", () => {
|
|
253
|
+
it("uses numeric comparison for borderWidth", () => {
|
|
254
|
+
expect(compareStyleValue("borderWidth", "1px", "2px")).toBe(true);
|
|
255
|
+
expect(compareStyleValue("borderWidth", "1px", "3px")).toBe(false);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("uses numeric comparison for borderRadius", () => {
|
|
259
|
+
expect(compareStyleValue("borderRadius", "8px", "9px")).toBe(true);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("uses numeric comparison for fontSize", () => {
|
|
263
|
+
expect(compareStyleValue("fontSize", "16px", "17px")).toBe(true);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("uses numeric comparison for padding", () => {
|
|
267
|
+
expect(compareStyleValue("padding", "10px", "11px")).toBe(true);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("uses numeric comparison for gap", () => {
|
|
271
|
+
expect(compareStyleValue("gap", "16px", "17px")).toBe(true);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe("other properties", () => {
|
|
276
|
+
it("uses strict comparison for fontFamily", () => {
|
|
277
|
+
expect(compareStyleValue("fontFamily", "Inter", "Arial")).toBe(false);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("uses strict comparison for fontWeight", () => {
|
|
281
|
+
expect(compareStyleValue("fontWeight", "400", "500")).toBe(false);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("uses strict comparison for textAlign", () => {
|
|
285
|
+
expect(compareStyleValue("textAlign", "left", "center")).toBe(false);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// ─── compareStyles ───────────────────────────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
describe("compareStyles", () => {
|
|
293
|
+
it("returns match true when all styles match", () => {
|
|
294
|
+
const figmaStyles = {
|
|
295
|
+
backgroundColor: "#ff0000",
|
|
296
|
+
fontSize: "16px",
|
|
297
|
+
};
|
|
298
|
+
const renderedStyles = {
|
|
299
|
+
backgroundColor: "rgb(255, 0, 0)",
|
|
300
|
+
fontSize: "16px",
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const result = compareStyles(figmaStyles, renderedStyles);
|
|
304
|
+
expect(result.match).toBe(true);
|
|
305
|
+
expect(result.properties).toHaveLength(2);
|
|
306
|
+
expect(result.properties.every((p) => p.match)).toBe(true);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it("returns match false when some styles differ", () => {
|
|
310
|
+
const figmaStyles = {
|
|
311
|
+
backgroundColor: "#ff0000",
|
|
312
|
+
fontSize: "16px",
|
|
313
|
+
};
|
|
314
|
+
const renderedStyles = {
|
|
315
|
+
backgroundColor: "rgb(0, 255, 0)",
|
|
316
|
+
fontSize: "16px",
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
const result = compareStyles(figmaStyles, renderedStyles);
|
|
320
|
+
expect(result.match).toBe(false);
|
|
321
|
+
expect(
|
|
322
|
+
result.properties.find((p) => p.property === "backgroundColor")?.match
|
|
323
|
+
).toBe(false);
|
|
324
|
+
expect(
|
|
325
|
+
result.properties.find((p) => p.property === "fontSize")?.match
|
|
326
|
+
).toBe(true);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("only compares properties present in figmaStyles", () => {
|
|
330
|
+
const figmaStyles = {
|
|
331
|
+
backgroundColor: "#ff0000",
|
|
332
|
+
};
|
|
333
|
+
const renderedStyles = {
|
|
334
|
+
backgroundColor: "rgb(255, 0, 0)",
|
|
335
|
+
fontSize: "16px",
|
|
336
|
+
fontFamily: "Inter",
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const result = compareStyles(figmaStyles, renderedStyles);
|
|
340
|
+
expect(result.properties).toHaveLength(1);
|
|
341
|
+
expect(result.properties[0].property).toBe("backgroundColor");
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it("marks missing rendered styles as not matching", () => {
|
|
345
|
+
const figmaStyles = {
|
|
346
|
+
backgroundColor: "#ff0000",
|
|
347
|
+
borderRadius: "8px",
|
|
348
|
+
};
|
|
349
|
+
const renderedStyles = {
|
|
350
|
+
backgroundColor: "rgb(255, 0, 0)",
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const result = compareStyles(figmaStyles, renderedStyles);
|
|
354
|
+
const borderRadius = result.properties.find(
|
|
355
|
+
(p) => p.property === "borderRadius"
|
|
356
|
+
);
|
|
357
|
+
expect(borderRadius?.rendered).toBe("(not set)");
|
|
358
|
+
expect(borderRadius?.match).toBe(false);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("returns cleaned figmaStyles object", () => {
|
|
362
|
+
const figmaStyles = {
|
|
363
|
+
backgroundColor: "#ff0000",
|
|
364
|
+
fontSize: undefined,
|
|
365
|
+
fontFamily: "Inter",
|
|
366
|
+
};
|
|
367
|
+
const renderedStyles = {
|
|
368
|
+
backgroundColor: "rgb(255, 0, 0)",
|
|
369
|
+
fontFamily: "Inter",
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const result = compareStyles(figmaStyles, renderedStyles);
|
|
373
|
+
expect(result.figmaStyles).toEqual({
|
|
374
|
+
backgroundColor: "#ff0000",
|
|
375
|
+
fontFamily: "Inter",
|
|
376
|
+
});
|
|
377
|
+
expect(result.figmaStyles.fontSize).toBeUndefined();
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("preserves renderedStyles in output", () => {
|
|
381
|
+
const figmaStyles = {
|
|
382
|
+
backgroundColor: "#ff0000",
|
|
383
|
+
};
|
|
384
|
+
const renderedStyles = {
|
|
385
|
+
backgroundColor: "rgb(255, 0, 0)",
|
|
386
|
+
fontSize: "16px",
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
const result = compareStyles(figmaStyles, renderedStyles);
|
|
390
|
+
expect(result.renderedStyles).toBe(renderedStyles);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it("handles empty figmaStyles", () => {
|
|
394
|
+
const figmaStyles = {};
|
|
395
|
+
const renderedStyles = {
|
|
396
|
+
backgroundColor: "rgb(255, 0, 0)",
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const result = compareStyles(figmaStyles, renderedStyles);
|
|
400
|
+
expect(result.match).toBe(true);
|
|
401
|
+
expect(result.properties).toHaveLength(0);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it("compares all standard properties when present", () => {
|
|
405
|
+
const figmaStyles = {
|
|
406
|
+
backgroundColor: "#ff0000",
|
|
407
|
+
borderColor: "#000000",
|
|
408
|
+
borderWidth: "1px",
|
|
409
|
+
borderRadius: "4px",
|
|
410
|
+
fontFamily: "Inter",
|
|
411
|
+
fontSize: "16px",
|
|
412
|
+
fontWeight: "400",
|
|
413
|
+
lineHeight: "24px",
|
|
414
|
+
letterSpacing: "-0.5px",
|
|
415
|
+
textAlign: "left",
|
|
416
|
+
boxShadow: "0px 4px 8px rgba(0, 0, 0, 0.25)",
|
|
417
|
+
padding: "16px",
|
|
418
|
+
gap: "8px",
|
|
419
|
+
opacity: "0.9",
|
|
420
|
+
};
|
|
421
|
+
const renderedStyles = {
|
|
422
|
+
backgroundColor: "rgb(255, 0, 0)",
|
|
423
|
+
borderColor: "rgb(0, 0, 0)",
|
|
424
|
+
borderWidth: "1px",
|
|
425
|
+
borderRadius: "4px",
|
|
426
|
+
fontFamily: "Inter",
|
|
427
|
+
fontSize: "16px",
|
|
428
|
+
fontWeight: "400",
|
|
429
|
+
lineHeight: "24px",
|
|
430
|
+
letterSpacing: "-0.5px",
|
|
431
|
+
textAlign: "left",
|
|
432
|
+
boxShadow: "0px 4px 8px rgba(0, 0, 0, 0.25)",
|
|
433
|
+
padding: "16px",
|
|
434
|
+
gap: "8px",
|
|
435
|
+
opacity: "0.9",
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
const result = compareStyles(figmaStyles, renderedStyles);
|
|
439
|
+
expect(result.match).toBe(true);
|
|
440
|
+
expect(result.properties).toHaveLength(14);
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// ─── compareStylesWithTokens ─────────────────────────────────────────────────
|
|
445
|
+
|
|
446
|
+
describe("compareStylesWithTokens", () => {
|
|
447
|
+
it("works without token lookup (basic comparison)", () => {
|
|
448
|
+
const result = compareStylesWithTokens(
|
|
449
|
+
{ backgroundColor: "#ff0000" },
|
|
450
|
+
{ backgroundColor: "rgb(255, 0, 0)" }
|
|
451
|
+
);
|
|
452
|
+
expect(result.match).toBe(true);
|
|
453
|
+
expect(result.properties[0].isHardcoded).toBe(false);
|
|
454
|
+
expect(result.tokenSummary).toBeUndefined();
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it("includes color property in enhanced comparison", () => {
|
|
458
|
+
const result = compareStylesWithTokens(
|
|
459
|
+
{ color: "#000000" },
|
|
460
|
+
{ color: "#000000" }
|
|
461
|
+
);
|
|
462
|
+
expect(result.properties).toHaveLength(1);
|
|
463
|
+
expect(result.properties[0].property).toBe("color");
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it("identifies hardcoded values with token lookup", () => {
|
|
467
|
+
const mockLookup: TokenLookup = {
|
|
468
|
+
findByValue: (value: string) => {
|
|
469
|
+
if (value === "#ff0000") return ["--color-danger"];
|
|
470
|
+
return [];
|
|
471
|
+
},
|
|
472
|
+
getToken: (name: string) => {
|
|
473
|
+
if (name === "--color-danger") {
|
|
474
|
+
return {
|
|
475
|
+
name: "--color-danger",
|
|
476
|
+
rawValue: "#ff0000",
|
|
477
|
+
resolvedValue: "#ff0000",
|
|
478
|
+
category: "color",
|
|
479
|
+
level: 2,
|
|
480
|
+
referenceChain: [],
|
|
481
|
+
sourceFile: "tokens.css",
|
|
482
|
+
theme: "default",
|
|
483
|
+
selector: ":root",
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
return undefined;
|
|
487
|
+
},
|
|
488
|
+
calculateUsageSummary: () => ({
|
|
489
|
+
totalProperties: 1,
|
|
490
|
+
usingTokens: 0,
|
|
491
|
+
hardcoded: 1,
|
|
492
|
+
implicitMatches: 0,
|
|
493
|
+
compliancePercent: 0,
|
|
494
|
+
hardcodedProperties: [],
|
|
495
|
+
}),
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
const result = compareStylesWithTokens(
|
|
499
|
+
{ backgroundColor: "#ff0000" },
|
|
500
|
+
{ backgroundColor: "rgb(255, 0, 0)" },
|
|
501
|
+
mockLookup
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
expect(result.properties[0].figmaToken).toBe("--color-danger");
|
|
505
|
+
expect(result.properties[0].isHardcoded).toBe(true);
|
|
506
|
+
expect(result.properties[0].suggestedFix).toBeDefined();
|
|
507
|
+
expect(result.properties[0].suggestedFix?.tokenName).toBe(
|
|
508
|
+
"--color-danger"
|
|
509
|
+
);
|
|
510
|
+
expect(result.properties[0].suggestedFix?.codeFix).toBe(
|
|
511
|
+
"background-color: var(--color-danger);"
|
|
512
|
+
);
|
|
513
|
+
expect(result.tokenSummary).toBeDefined();
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it("does not flag as hardcoded when rendered uses a token", () => {
|
|
517
|
+
const mockLookup: TokenLookup = {
|
|
518
|
+
findByValue: () => ["--color-danger"],
|
|
519
|
+
getToken: () => undefined,
|
|
520
|
+
calculateUsageSummary: () => ({
|
|
521
|
+
totalProperties: 1,
|
|
522
|
+
usingTokens: 1,
|
|
523
|
+
hardcoded: 0,
|
|
524
|
+
implicitMatches: 0,
|
|
525
|
+
compliancePercent: 100,
|
|
526
|
+
hardcodedProperties: [],
|
|
527
|
+
}),
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
const result = compareStylesWithTokens(
|
|
531
|
+
{ backgroundColor: "#ff0000" },
|
|
532
|
+
{ backgroundColor: "#ff0000" },
|
|
533
|
+
mockLookup
|
|
534
|
+
);
|
|
535
|
+
|
|
536
|
+
expect(result.properties[0].isHardcoded).toBe(false);
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// ─── formatTokenSummary ──────────────────────────────────────────────────────
|
|
541
|
+
|
|
542
|
+
describe("formatTokenSummary", () => {
|
|
543
|
+
it("formats basic summary", () => {
|
|
544
|
+
const summary = formatTokenSummary({
|
|
545
|
+
totalProperties: 10,
|
|
546
|
+
usingTokens: 8,
|
|
547
|
+
hardcoded: 2,
|
|
548
|
+
implicitMatches: 0,
|
|
549
|
+
compliancePercent: 80,
|
|
550
|
+
hardcodedProperties: [],
|
|
551
|
+
});
|
|
552
|
+
expect(summary).toContain("Token Compliance: 80%");
|
|
553
|
+
expect(summary).toContain("8/10 properties using tokens");
|
|
554
|
+
expect(summary).toContain("2 hardcoded value(s) detected");
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
it("includes implicit matches when present", () => {
|
|
558
|
+
const summary = formatTokenSummary({
|
|
559
|
+
totalProperties: 10,
|
|
560
|
+
usingTokens: 7,
|
|
561
|
+
hardcoded: 0,
|
|
562
|
+
implicitMatches: 3,
|
|
563
|
+
compliancePercent: 70,
|
|
564
|
+
hardcodedProperties: [],
|
|
565
|
+
});
|
|
566
|
+
expect(summary).toContain("3 implicit match(es)");
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
it("omits hardcoded line when zero", () => {
|
|
570
|
+
const summary = formatTokenSummary({
|
|
571
|
+
totalProperties: 5,
|
|
572
|
+
usingTokens: 5,
|
|
573
|
+
hardcoded: 0,
|
|
574
|
+
implicitMatches: 0,
|
|
575
|
+
compliancePercent: 100,
|
|
576
|
+
hardcodedProperties: [],
|
|
577
|
+
});
|
|
578
|
+
expect(summary).not.toContain("hardcoded");
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
// ─── getComplianceBadge ──────────────────────────────────────────────────────
|
|
583
|
+
|
|
584
|
+
describe("getComplianceBadge", () => {
|
|
585
|
+
it("returns Excellent for 100%", () => {
|
|
586
|
+
expect(getComplianceBadge(100)).toEqual({
|
|
587
|
+
label: "Excellent",
|
|
588
|
+
color: "green",
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it("returns Good for 80-99%", () => {
|
|
593
|
+
expect(getComplianceBadge(80)).toEqual({ label: "Good", color: "blue" });
|
|
594
|
+
expect(getComplianceBadge(99)).toEqual({ label: "Good", color: "blue" });
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
it("returns Fair for 50-79%", () => {
|
|
598
|
+
expect(getComplianceBadge(50)).toEqual({ label: "Fair", color: "yellow" });
|
|
599
|
+
expect(getComplianceBadge(79)).toEqual({ label: "Fair", color: "yellow" });
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it("returns Poor for below 50%", () => {
|
|
603
|
+
expect(getComplianceBadge(0)).toEqual({ label: "Poor", color: "red" });
|
|
604
|
+
expect(getComplianceBadge(49)).toEqual({ label: "Poor", color: "red" });
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
609
|
+
|
|
610
|
+
describe("constants", () => {
|
|
611
|
+
it("DEFAULT_STYLE_PROPERTIES has 14 entries (no color)", () => {
|
|
612
|
+
expect(DEFAULT_STYLE_PROPERTIES).toHaveLength(14);
|
|
613
|
+
expect(DEFAULT_STYLE_PROPERTIES).not.toContain("color");
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
it("DEFAULT_ENHANCED_STYLE_PROPERTIES has 15 entries (includes color)", () => {
|
|
617
|
+
expect(DEFAULT_ENHANCED_STYLE_PROPERTIES).toHaveLength(15);
|
|
618
|
+
expect(DEFAULT_ENHANCED_STYLE_PROPERTIES).toContain("color");
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
it("enhanced properties is a superset of standard properties", () => {
|
|
622
|
+
for (const prop of DEFAULT_STYLE_PROPERTIES) {
|
|
623
|
+
expect(DEFAULT_ENHANCED_STYLE_PROPERTIES).toContain(prop);
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
// ─── Contract types compile-time check ───────────────────────────────────────
|
|
629
|
+
|
|
630
|
+
describe("contract types", () => {
|
|
631
|
+
it("NormalizedToken shape is valid", () => {
|
|
632
|
+
const token: NormalizedToken = {
|
|
633
|
+
name: "--color-primary",
|
|
634
|
+
value: "#0051c2",
|
|
635
|
+
category: "color",
|
|
636
|
+
source: "css-var",
|
|
637
|
+
originalName: "--color-primary",
|
|
638
|
+
theme: "default",
|
|
639
|
+
};
|
|
640
|
+
expect(token.name).toBe("--color-primary");
|
|
641
|
+
expect(token.source).toBe("css-var");
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
it("NormalizedStyleMap is a string record", () => {
|
|
645
|
+
const map: NormalizedStyleMap = {
|
|
646
|
+
backgroundColor: "#ff0000",
|
|
647
|
+
fontSize: "16px",
|
|
648
|
+
};
|
|
649
|
+
expect(map.backgroundColor).toBe("#ff0000");
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it("StyleComparisonOptions has expected shape", () => {
|
|
653
|
+
const opts: StyleComparisonOptions = {
|
|
654
|
+
colorTolerance: 10,
|
|
655
|
+
numericTolerance: 2,
|
|
656
|
+
alphaTolerance: 0.1,
|
|
657
|
+
properties: ["backgroundColor"],
|
|
658
|
+
theme: "dark",
|
|
659
|
+
};
|
|
660
|
+
expect(opts.colorTolerance).toBe(10);
|
|
661
|
+
});
|
|
662
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { parseDTCGFile } from "../dtcg-parser.js";
|
|
3
|
+
import { parseColor as parseStyleColor } from "../style-comparison.js";
|
|
4
|
+
import { parseTokenFile } from "../token-parser.js";
|
|
5
|
+
import {
|
|
6
|
+
parseColor,
|
|
7
|
+
parseDtcgTokens,
|
|
8
|
+
parseScssTokens,
|
|
9
|
+
parseTokens,
|
|
10
|
+
} from "../tokens/index.js";
|
|
11
|
+
|
|
12
|
+
describe("token module compatibility", () => {
|
|
13
|
+
it("keeps the old SCSS parser path wired to the core token module", () => {
|
|
14
|
+
const css = `
|
|
15
|
+
// Colors
|
|
16
|
+
:root {
|
|
17
|
+
--fui-color-accent: #ff0000;
|
|
18
|
+
--fui-color-brand: var(--fui-color-accent);
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
expect(parseTokenFile).toBe(parseScssTokens);
|
|
23
|
+
expect(parseTokenFile(css, "tokens.scss")).toEqual(
|
|
24
|
+
parseTokens(css, { format: "scss", filePath: "tokens.scss" }),
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("keeps the old DTCG parser path wired to the core token module", () => {
|
|
29
|
+
const content = JSON.stringify({
|
|
30
|
+
color: {
|
|
31
|
+
$type: "color",
|
|
32
|
+
accent: { $value: "#3b82f6" },
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
expect(parseDTCGFile).toBe(parseDtcgTokens);
|
|
37
|
+
expect(parseDTCGFile(content, "brand.tokens.json")).toEqual(
|
|
38
|
+
parseTokens(content, { format: "dtcg", filePath: "brand.tokens.json" }),
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("uses the same color parser in style comparison and the token module", () => {
|
|
43
|
+
expect(parseStyleColor).toBe(parseColor);
|
|
44
|
+
expect(parseStyleColor("rgb(255, 0, 0)")).toEqual({
|
|
45
|
+
r: 255,
|
|
46
|
+
g: 0,
|
|
47
|
+
b: 0,
|
|
48
|
+
a: 1,
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|