Skip to content

Commit 2ac006c

Browse files
Merge pull request #203 from thatblindgeye/iss172_designTokenAPI
feat(API): added support for design tokens
2 parents e5e316b + 7cd6479 commit 2ac006c

File tree

9 files changed

+1357
-0
lines changed

9 files changed

+1357
-0
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { GET } from '../../../../../pages/api/[version]/tokens'
2+
3+
const mockApiIndex = {
4+
versions: ['v5', 'v6'],
5+
sections: {},
6+
pages: {},
7+
tabs: {},
8+
}
9+
10+
jest.mock('../../../../../utils/tokens', () => ({
11+
getTokenCategories: jest.fn(() => [
12+
'c',
13+
'chart',
14+
'global',
15+
'hidden',
16+
'l',
17+
't',
18+
]),
19+
}))
20+
21+
it('returns sorted token categories for valid version', async () => {
22+
global.fetch = jest.fn(() =>
23+
Promise.resolve({
24+
ok: true,
25+
json: () => Promise.resolve(mockApiIndex),
26+
} as Response),
27+
)
28+
29+
const response = await GET({
30+
params: { version: 'v6' },
31+
url: new URL('http://localhost:4321/api/v6/tokens'),
32+
} as any)
33+
const body = await response.json()
34+
35+
expect(response.status).toBe(200)
36+
expect(response.headers.get('Content-Type')).toBe(
37+
'application/json; charset=utf-8',
38+
)
39+
expect(Array.isArray(body)).toBe(true)
40+
expect(body).toEqual(['c', 'chart', 'global', 'hidden', 'l', 't'])
41+
42+
jest.restoreAllMocks()
43+
})
44+
45+
it('returns categories alphabetically sorted', async () => {
46+
global.fetch = jest.fn(() =>
47+
Promise.resolve({
48+
ok: true,
49+
json: () => Promise.resolve(mockApiIndex),
50+
} as Response),
51+
)
52+
53+
const response = await GET({
54+
params: { version: 'v6' },
55+
url: new URL('http://localhost:4321/api/v6/tokens'),
56+
} as any)
57+
const body = await response.json()
58+
59+
const sorted = [...body].sort()
60+
expect(body).toEqual(sorted)
61+
62+
jest.restoreAllMocks()
63+
})
64+
65+
it('returns 404 error for nonexistent version', async () => {
66+
global.fetch = jest.fn(() =>
67+
Promise.resolve({
68+
ok: true,
69+
json: () => Promise.resolve(mockApiIndex),
70+
} as Response),
71+
)
72+
73+
const response = await GET({
74+
params: { version: 'v99' },
75+
url: new URL('http://localhost:4321/api/v99/tokens'),
76+
} as any)
77+
const body = await response.json()
78+
79+
expect(response.status).toBe(404)
80+
expect(body).toHaveProperty('error')
81+
expect(body.error).toContain('v99')
82+
expect(body.error).toContain('not found')
83+
84+
jest.restoreAllMocks()
85+
})
86+
87+
it('returns 400 error when version parameter is missing', async () => {
88+
global.fetch = jest.fn(() =>
89+
Promise.resolve({
90+
ok: true,
91+
json: () => Promise.resolve(mockApiIndex),
92+
} as Response),
93+
)
94+
95+
const response = await GET({
96+
params: {},
97+
url: new URL('http://localhost:4321/api/tokens'),
98+
} as any)
99+
const body = await response.json()
100+
101+
expect(response.status).toBe(400)
102+
expect(body).toHaveProperty('error')
103+
expect(body.error).toContain('Version parameter is required')
104+
105+
jest.restoreAllMocks()
106+
})
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import { GET } from '../../../../../../pages/api/[version]/tokens/[category]'
2+
3+
const mockApiIndex = {
4+
versions: ['v5', 'v6'],
5+
sections: {},
6+
pages: {},
7+
tabs: {},
8+
}
9+
10+
const mockTokens = {
11+
c: [
12+
{
13+
name: '--pf-v6-c-alert--Color',
14+
value: '#000',
15+
var: 'var(--pf-v6-c-alert--Color)',
16+
},
17+
{
18+
name: '--pf-v6-c-button--Color',
19+
value: '#fff',
20+
var: 'var(--pf-v6-c-button--Color)',
21+
},
22+
],
23+
t: [
24+
{
25+
name: '--pf-v6-t-global--Color',
26+
value: '#333',
27+
var: 'var(--pf-v6-t-global--Color)',
28+
},
29+
],
30+
}
31+
32+
jest.mock('../../../../../../utils/tokens', () => ({
33+
getTokenCategories: jest.fn(() => ['c', 't']),
34+
getTokensForCategory: jest.fn(
35+
(category: string) => mockTokens[category as keyof typeof mockTokens],
36+
),
37+
filterTokens: jest.fn((tokens, filter) =>
38+
tokens.filter((token: any) =>
39+
token.name.toLowerCase().includes(filter.toLowerCase()),
40+
),
41+
),
42+
}))
43+
44+
it('returns tokens for valid category', async () => {
45+
global.fetch = jest.fn(() =>
46+
Promise.resolve({
47+
ok: true,
48+
json: () => Promise.resolve(mockApiIndex),
49+
} as Response),
50+
)
51+
52+
const response = await GET({
53+
params: { version: 'v6', category: 'c' },
54+
url: new URL('http://localhost:4321/api/v6/tokens/c'),
55+
} as any)
56+
const body = await response.json()
57+
58+
expect(response.status).toBe(200)
59+
expect(response.headers.get('Content-Type')).toBe(
60+
'application/json; charset=utf-8',
61+
)
62+
expect(Array.isArray(body)).toBe(true)
63+
expect(body).toHaveLength(2)
64+
expect(body[0]).toHaveProperty('name')
65+
expect(body[0]).toHaveProperty('value')
66+
expect(body[0]).toHaveProperty('var')
67+
68+
jest.restoreAllMocks()
69+
})
70+
71+
it('filters tokens when filter parameter is provided', async () => {
72+
global.fetch = jest.fn(() =>
73+
Promise.resolve({
74+
ok: true,
75+
json: () => Promise.resolve(mockApiIndex),
76+
} as Response),
77+
)
78+
79+
const response = await GET({
80+
params: { version: 'v6', category: 'c' },
81+
url: new URL('http://localhost:4321/api/v6/tokens/c?filter=alert'),
82+
} as any)
83+
const body = await response.json()
84+
85+
expect(response.status).toBe(200)
86+
expect(Array.isArray(body)).toBe(true)
87+
expect(body).toHaveLength(1)
88+
expect(body[0].name).toContain('alert')
89+
90+
jest.restoreAllMocks()
91+
})
92+
93+
it('returns empty array when filter yields no matches', async () => {
94+
global.fetch = jest.fn(() =>
95+
Promise.resolve({
96+
ok: true,
97+
json: () => Promise.resolve(mockApiIndex),
98+
} as Response),
99+
)
100+
101+
const response = await GET({
102+
params: { version: 'v6', category: 'c' },
103+
url: new URL('http://localhost:4321/api/v6/tokens/c?filter=nonexistent'),
104+
} as any)
105+
const body = await response.json()
106+
107+
expect(response.status).toBe(200)
108+
expect(Array.isArray(body)).toBe(true)
109+
expect(body).toHaveLength(0)
110+
111+
jest.restoreAllMocks()
112+
})
113+
114+
it('returns 404 error for invalid category with valid categories list', async () => {
115+
global.fetch = jest.fn(() =>
116+
Promise.resolve({
117+
ok: true,
118+
json: () => Promise.resolve(mockApiIndex),
119+
} as Response),
120+
)
121+
122+
const response = await GET({
123+
params: { version: 'v6', category: 'invalid' },
124+
url: new URL('http://localhost:4321/api/v6/tokens/invalid'),
125+
} as any)
126+
const body = await response.json()
127+
128+
expect(response.status).toBe(404)
129+
expect(body).toHaveProperty('error')
130+
expect(body.error).toContain('invalid')
131+
expect(body.error).toContain('not found')
132+
expect(body).toHaveProperty('validCategories')
133+
expect(Array.isArray(body.validCategories)).toBe(true)
134+
expect(body.validCategories).toContain('c')
135+
expect(body.validCategories).toContain('t')
136+
137+
jest.restoreAllMocks()
138+
})
139+
140+
it('returns 404 error for nonexistent version', async () => {
141+
global.fetch = jest.fn(() =>
142+
Promise.resolve({
143+
ok: true,
144+
json: () => Promise.resolve(mockApiIndex),
145+
} as Response),
146+
)
147+
148+
const response = await GET({
149+
params: { version: 'v99', category: 'c' },
150+
url: new URL('http://localhost:4321/api/v99/tokens/c'),
151+
} as any)
152+
const body = await response.json()
153+
154+
expect(response.status).toBe(404)
155+
expect(body).toHaveProperty('error')
156+
expect(body.error).toContain('v99')
157+
158+
jest.restoreAllMocks()
159+
})
160+
161+
it('returns 400 error when parameters are missing', async () => {
162+
global.fetch = jest.fn(() =>
163+
Promise.resolve({
164+
ok: true,
165+
json: () => Promise.resolve(mockApiIndex),
166+
} as Response),
167+
)
168+
169+
const response = await GET({
170+
params: {},
171+
url: new URL('http://localhost:4321/api/tokens/'),
172+
} as any)
173+
const body = await response.json()
174+
175+
expect(response.status).toBe(400)
176+
expect(body).toHaveProperty('error')
177+
expect(body.error).toContain('required')
178+
179+
jest.restoreAllMocks()
180+
})
181+
182+
it('filter is case-insensitive', async () => {
183+
global.fetch = jest.fn(() =>
184+
Promise.resolve({
185+
ok: true,
186+
json: () => Promise.resolve(mockApiIndex),
187+
} as Response),
188+
)
189+
190+
const response = await GET({
191+
params: { version: 'v6', category: 'c' },
192+
url: new URL('http://localhost:4321/api/v6/tokens/c?filter=ALERT'),
193+
} as any)
194+
const body = await response.json()
195+
196+
expect(response.status).toBe(200)
197+
expect(Array.isArray(body)).toBe(true)
198+
expect(body).toHaveLength(1)
199+
200+
jest.restoreAllMocks()
201+
})

0 commit comments

Comments
 (0)