@cdx-ui/styles 0.0.1-beta.87 → 0.0.1-beta.88

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.
@@ -0,0 +1,72 @@
1
+ import {
2
+ OVERRIDE_SCHEMA_VERSION,
3
+ SUPPORTED_OVERRIDE_SCHEMA_VERSIONS,
4
+ INPUT_TOKEN_MAP,
5
+ presetFonts,
6
+ } from '../types';
7
+ import type { Preset } from '../types';
8
+
9
+ describe('OVERRIDE_SCHEMA_VERSION', () => {
10
+ it('is the current 1.0.0 schema version', () => {
11
+ expect(OVERRIDE_SCHEMA_VERSION).toBe('1.0.0');
12
+ });
13
+
14
+ it('matches semver MAJOR.MINOR.PATCH shape', () => {
15
+ expect(OVERRIDE_SCHEMA_VERSION).toMatch(/^\d+\.\d+\.\d+$/);
16
+ });
17
+ });
18
+
19
+ describe('SUPPORTED_OVERRIDE_SCHEMA_VERSIONS', () => {
20
+ it('includes the current OVERRIDE_SCHEMA_VERSION', () => {
21
+ expect(SUPPORTED_OVERRIDE_SCHEMA_VERSIONS).toContain(OVERRIDE_SCHEMA_VERSION);
22
+ });
23
+
24
+ it('is a non-empty array of semver strings', () => {
25
+ expect(Array.isArray(SUPPORTED_OVERRIDE_SCHEMA_VERSIONS)).toBe(true);
26
+ expect(SUPPORTED_OVERRIDE_SCHEMA_VERSIONS.length).toBeGreaterThan(0);
27
+ for (const v of SUPPORTED_OVERRIDE_SCHEMA_VERSIONS) {
28
+ expect(v).toMatch(/^\d+\.\d+\.\d+$/);
29
+ }
30
+ });
31
+ });
32
+
33
+ describe('INPUT_TOKEN_MAP', () => {
34
+ it('maps every known input key to a dotted token path', () => {
35
+ expect(INPUT_TOKEN_MAP).toEqual({
36
+ brandPrimary: 'color.brand',
37
+ accentPrimary: 'color.accent',
38
+ basePrimary: 'color.base',
39
+ displayFont: 'font.display',
40
+ });
41
+ });
42
+
43
+ it('uses dotted-path token namespaces for every value', () => {
44
+ for (const value of Object.values(INPUT_TOKEN_MAP)) {
45
+ expect(value).toMatch(/^[a-z]+(\.[a-z]+)+$/);
46
+ }
47
+ });
48
+ });
49
+
50
+ describe('presetFonts', () => {
51
+ const expectedPresets: Preset[] = ['poise', 'prestige', 'pulse'];
52
+
53
+ it('exposes exactly the three built-in presets as keys', () => {
54
+ expect(Object.keys(presetFonts).sort()).toEqual([...expectedPresets].sort());
55
+ });
56
+
57
+ it.each(expectedPresets)('declares 3 display font families for %s', (preset) => {
58
+ expect(presetFonts[preset]).toHaveLength(3);
59
+ for (const family of presetFonts[preset]) {
60
+ expect(typeof family).toBe('string');
61
+ expect(family.length).toBeGreaterThan(0);
62
+ }
63
+ });
64
+
65
+ it('matches the canonical preset → families map', () => {
66
+ expect(presetFonts).toEqual({
67
+ poise: ['Crimson Pro', 'Bitter', 'DM Sans'],
68
+ prestige: ['Libre Caslon Text', 'Cormorant', 'Libre Franklin'],
69
+ pulse: ['Outfit', 'Manrope', 'Public Sans'],
70
+ });
71
+ });
72
+ });
@@ -0,0 +1,27 @@
1
+ /* eslint-disable @typescript-eslint/no-deprecated -- this entire test exercises the deprecated alias intentionally */
2
+ import { useFonts } from 'expo-font';
3
+ import * as useCdxFontsModule from '../useCdxFonts';
4
+ import * as useForgeFontsModule from '../useForgeFonts';
5
+
6
+ jest.mock('react-native', () => ({
7
+ Platform: { OS: 'ios' },
8
+ }));
9
+
10
+ jest.mock('expo-font', () => ({
11
+ useFonts: jest.fn(() => [true, null]),
12
+ }));
13
+
14
+ const mockedUseFonts = useFonts as jest.MockedFunction<typeof useFonts>;
15
+
16
+ describe('useCdxFonts', () => {
17
+ it('re-exports useForgeFonts as the exact same function (deprecated alias)', () => {
18
+ expect(useCdxFontsModule.useCdxFonts).toBe(useForgeFontsModule.useForgeFonts);
19
+ });
20
+
21
+ it('invokes the underlying useFonts when called', () => {
22
+ mockedUseFonts.mockClear();
23
+ const result = useCdxFontsModule.useCdxFonts();
24
+ expect(mockedUseFonts).toHaveBeenCalledTimes(1);
25
+ expect(result).toEqual({ loaded: true, error: null });
26
+ });
27
+ });
@@ -0,0 +1,226 @@
1
+ /**
2
+ * `useForgeFonts` decides which web-only fonts to bundle at *module evaluation*
3
+ * time (the `webFonts` const reads `Platform.OS` once). To exercise both
4
+ * branches we re-require the module under `jest.isolateModules` with a fresh
5
+ * `react-native` mock per case.
6
+ */
7
+
8
+ // ---------------------------------------------------------------------------
9
+ // Helpers
10
+ // ---------------------------------------------------------------------------
11
+
12
+ interface CallResult {
13
+ hookResult: { loaded: boolean; error: Error | null };
14
+ capturedMap: Record<string, unknown>;
15
+ mockUseFonts: jest.Mock;
16
+ }
17
+
18
+ function setupAndCall(opts: {
19
+ platformOS: 'ios' | 'android' | 'web';
20
+ result?: [boolean, Error | null];
21
+ }): CallResult {
22
+ const result: [boolean, Error | null] = opts.result ?? [true, null];
23
+ const mockUseFonts = jest.fn((_map: Record<string, unknown>) => result);
24
+
25
+ let hookResult: CallResult['hookResult'] = { loaded: false, error: null };
26
+
27
+ jest.isolateModules(() => {
28
+ jest.doMock('react-native', () => ({ Platform: { OS: opts.platformOS } }));
29
+ jest.doMock('expo-font', () => ({ useFonts: mockUseFonts }));
30
+
31
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
32
+ const mod = require('../useForgeFonts') as typeof import('../useForgeFonts');
33
+ hookResult = mod.useForgeFonts();
34
+ });
35
+
36
+ const capturedMap: Record<string, unknown> = mockUseFonts.mock.calls[0][0];
37
+
38
+ return { hookResult, capturedMap, mockUseFonts };
39
+ }
40
+
41
+ // Expected display font keys — every preset, every weight, every italic variant.
42
+ // Mirrors the `displayFonts` map in `useForgeFonts.ts`.
43
+ const POISE_DISPLAY_FONT_KEYS = [
44
+ 'Crimson Pro',
45
+ 'Crimson Pro Medium',
46
+ 'Crimson Pro SemiBold',
47
+ 'Crimson Pro Bold',
48
+ 'Crimson Pro Italic',
49
+ 'Crimson Pro Medium Italic',
50
+ 'Crimson Pro SemiBold Italic',
51
+ 'Crimson Pro Bold Italic',
52
+ 'Bitter',
53
+ 'Bitter Medium',
54
+ 'Bitter SemiBold',
55
+ 'Bitter Bold',
56
+ 'Bitter Italic',
57
+ 'Bitter Medium Italic',
58
+ 'Bitter SemiBold Italic',
59
+ 'Bitter Bold Italic',
60
+ 'DM Sans',
61
+ 'DM Sans Medium',
62
+ 'DM Sans SemiBold',
63
+ 'DM Sans Bold',
64
+ 'DM Sans Italic',
65
+ 'DM Sans Medium Italic',
66
+ 'DM Sans SemiBold Italic',
67
+ 'DM Sans Bold Italic',
68
+ ];
69
+
70
+ const PRESTIGE_DISPLAY_FONT_KEYS = [
71
+ 'Libre Caslon Text',
72
+ 'Libre Caslon Text Medium',
73
+ 'Libre Caslon Text SemiBold',
74
+ 'Libre Caslon Text Bold',
75
+ 'Libre Caslon Text Italic',
76
+ 'Libre Caslon Text Medium Italic',
77
+ 'Libre Caslon Text SemiBold Italic',
78
+ 'Libre Caslon Text Bold Italic',
79
+ 'Cormorant',
80
+ 'Cormorant Medium',
81
+ 'Cormorant SemiBold',
82
+ 'Cormorant Bold',
83
+ 'Cormorant Italic',
84
+ 'Cormorant Medium Italic',
85
+ 'Cormorant SemiBold Italic',
86
+ 'Cormorant Bold Italic',
87
+ 'Libre Franklin',
88
+ 'Libre Franklin Medium',
89
+ 'Libre Franklin SemiBold',
90
+ 'Libre Franklin Bold',
91
+ 'Libre Franklin Italic',
92
+ 'Libre Franklin Medium Italic',
93
+ 'Libre Franklin SemiBold Italic',
94
+ 'Libre Franklin Bold Italic',
95
+ ];
96
+
97
+ const PULSE_DISPLAY_FONT_KEYS = [
98
+ 'Outfit',
99
+ 'Outfit Medium',
100
+ 'Outfit SemiBold',
101
+ 'Outfit Bold',
102
+ 'Outfit Italic',
103
+ 'Outfit Medium Italic',
104
+ 'Outfit SemiBold Italic',
105
+ 'Outfit Bold Italic',
106
+ 'Manrope',
107
+ 'Manrope Medium',
108
+ 'Manrope SemiBold',
109
+ 'Manrope Bold',
110
+ 'Manrope Italic',
111
+ 'Manrope Medium Italic',
112
+ 'Manrope SemiBold Italic',
113
+ 'Manrope Bold Italic',
114
+ 'Public Sans',
115
+ 'Public Sans Medium',
116
+ 'Public Sans SemiBold',
117
+ 'Public Sans Bold',
118
+ 'Public Sans Italic',
119
+ 'Public Sans Medium Italic',
120
+ 'Public Sans SemiBold Italic',
121
+ 'Public Sans Bold Italic',
122
+ ];
123
+
124
+ const ALL_DISPLAY_FONT_KEYS = [
125
+ ...POISE_DISPLAY_FONT_KEYS,
126
+ ...PRESTIGE_DISPLAY_FONT_KEYS,
127
+ ...PULSE_DISPLAY_FONT_KEYS,
128
+ ];
129
+
130
+ const WEB_ONLY_FONT_KEYS = [
131
+ 'Inter',
132
+ 'Inter Medium',
133
+ 'Inter SemiBold',
134
+ 'Inter Bold',
135
+ 'Inter Italic',
136
+ 'Inter Medium Italic',
137
+ 'Inter SemiBold Italic',
138
+ 'Inter Bold Italic',
139
+ 'IBM Plex Mono',
140
+ 'IBM Plex Mono Medium',
141
+ 'IBM Plex Mono SemiBold',
142
+ 'IBM Plex Mono Bold',
143
+ 'IBM Plex Mono Italic',
144
+ 'IBM Plex Mono Medium Italic',
145
+ 'IBM Plex Mono SemiBold Italic',
146
+ 'IBM Plex Mono Bold Italic',
147
+ ];
148
+
149
+ // ---------------------------------------------------------------------------
150
+ // Tests
151
+ // ---------------------------------------------------------------------------
152
+
153
+ describe('useForgeFonts', () => {
154
+ describe('return shape', () => {
155
+ it('returns { loaded, error } in the loaded state', () => {
156
+ const { hookResult } = setupAndCall({ platformOS: 'ios', result: [true, null] });
157
+ expect(hookResult).toEqual({ loaded: true, error: null });
158
+ });
159
+
160
+ it('returns { loaded: false, error: null } while still loading', () => {
161
+ const { hookResult } = setupAndCall({ platformOS: 'ios', result: [false, null] });
162
+ expect(hookResult).toEqual({ loaded: false, error: null });
163
+ });
164
+
165
+ it('propagates the error from useFonts', () => {
166
+ const err = new Error('boom');
167
+ const { hookResult } = setupAndCall({ platformOS: 'ios', result: [false, err] });
168
+ expect(hookResult.loaded).toBe(false);
169
+ expect(hookResult.error).toBe(err);
170
+ });
171
+ });
172
+
173
+ describe('font map (display fonts)', () => {
174
+ it.each(['ios', 'android', 'web'] as const)(
175
+ 'passes every display font (Poise + Prestige + Pulse) when Platform.OS is %s',
176
+ (platformOS) => {
177
+ const { capturedMap } = setupAndCall({ platformOS });
178
+ for (const key of ALL_DISPLAY_FONT_KEYS) {
179
+ expect(capturedMap).toHaveProperty(key);
180
+ }
181
+ },
182
+ );
183
+
184
+ it('passes 72 display font keys (24 per preset × 3 presets)', () => {
185
+ const { capturedMap } = setupAndCall({ platformOS: 'ios' });
186
+ const displayKeys = Object.keys(capturedMap).filter((k) => !WEB_ONLY_FONT_KEYS.includes(k));
187
+ expect(displayKeys).toHaveLength(ALL_DISPLAY_FONT_KEYS.length);
188
+ });
189
+ });
190
+
191
+ describe('web-only fonts', () => {
192
+ it('excludes Inter and IBM Plex Mono on ios', () => {
193
+ const { capturedMap } = setupAndCall({ platformOS: 'ios' });
194
+ for (const key of WEB_ONLY_FONT_KEYS) {
195
+ expect(capturedMap).not.toHaveProperty(key);
196
+ }
197
+ });
198
+
199
+ it('excludes Inter and IBM Plex Mono on android', () => {
200
+ const { capturedMap } = setupAndCall({ platformOS: 'android' });
201
+ for (const key of WEB_ONLY_FONT_KEYS) {
202
+ expect(capturedMap).not.toHaveProperty(key);
203
+ }
204
+ });
205
+
206
+ it('includes every Inter and IBM Plex Mono weight/italic on web', () => {
207
+ const { capturedMap } = setupAndCall({ platformOS: 'web' });
208
+ for (const key of WEB_ONLY_FONT_KEYS) {
209
+ expect(capturedMap).toHaveProperty(key);
210
+ }
211
+ });
212
+
213
+ it('still passes display fonts alongside web fonts when on web', () => {
214
+ const { capturedMap } = setupAndCall({ platformOS: 'web' });
215
+ const totalKeys = Object.keys(capturedMap);
216
+ expect(totalKeys).toHaveLength(ALL_DISPLAY_FONT_KEYS.length + WEB_ONLY_FONT_KEYS.length);
217
+ });
218
+ });
219
+
220
+ describe('useFonts invocation', () => {
221
+ it('calls useFonts exactly once per hook call', () => {
222
+ const { mockUseFonts } = setupAndCall({ platformOS: 'ios' });
223
+ expect(mockUseFonts).toHaveBeenCalledTimes(1);
224
+ });
225
+ });
226
+ });
@@ -1,2 +1,4 @@
1
+ import { useForgeFonts } from './useForgeFonts';
2
+
1
3
  /** @deprecated Use `useForgeFonts` instead. Will be removed in a future major release. */
2
- export { useForgeFonts as useCdxFonts } from './useForgeFonts';
4
+ export const useCdxFonts = useForgeFonts;