-
Notifications
You must be signed in to change notification settings - Fork 11
feat (icons): Icons API end points for retrieving icons. #209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
3b20863
a0d3855
da36b8d
2437572
bc103e1
e38d0a1
0d52a5a
a8654f0
b380631
2b8c40c
160618a
9c287db
ce4583d
695fcf7
9123ae7
8702c57
1f2d9bc
e5434ab
3a6c91f
615255e
44876af
9ce4cf0
967eaf1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,14 +55,14 @@ | |
| "@nanostores/react": "^0.8.4", | ||
| "@patternfly/ast-helpers": "1.4.0-alpha.190", | ||
| "@patternfly/patternfly": "^6.0.0", | ||
| "@patternfly/quickstarts": "^6.0.0", | ||
| "@patternfly/react-code-editor": "^6.2.2", | ||
| "@patternfly/react-core": "^6.0.0", | ||
| "@patternfly/react-drag-drop": "^6.0.0", | ||
| "@patternfly/react-icons": "^6.0.0", | ||
| "@patternfly/react-styles": "^6.0.0", | ||
| "@patternfly/react-table": "^6.0.0", | ||
| "@patternfly/react-tokens": "^6.0.0", | ||
| "@patternfly/quickstarts": "^6.0.0", | ||
| "@types/react": "^18.3.23", | ||
| "@types/react-dom": "^18.3.7", | ||
| "astro": "^5.15.9", | ||
|
|
@@ -74,6 +74,7 @@ | |
| "react-docgen": "^7.1.1", | ||
| "react-dom": "^18.3.1", | ||
| "react-error-boundary": "^6.0.0", | ||
| "react-icons": "^5.5.0", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably want to remove this import
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 to remove |
||
| "sass": "^1.90.0", | ||
| "typescript": "^5.9.2" | ||
| }, | ||
|
|
@@ -82,6 +83,8 @@ | |
| "@babel/preset-react": "^7.26.3", | ||
| "@babel/preset-typescript": "^7.26.0", | ||
| "@eslint/js": "^9.16.0", | ||
| "@patternfly/react-data-view": "^6.0.0", | ||
| "@patternfly/react-user-feedback": "^6.0.0", | ||
| "@semantic-release/git": "^10.0.1", | ||
| "@testing-library/jest-dom": "^6.6.3", | ||
| "@testing-library/react": "^16.1.0", | ||
|
|
@@ -110,9 +113,7 @@ | |
| "ts-jest": "^29.2.5", | ||
| "ts-node": "^10.9.2", | ||
| "typescript-eslint": "^8.15.0", | ||
| "wrangler": "^4.20.0", | ||
| "@patternfly/react-user-feedback": "^6.0.0", | ||
| "@patternfly/react-data-view": "^6.0.0" | ||
| "wrangler": "^4.20.0" | ||
| }, | ||
| "config": { | ||
| "commitizen": { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| import { GET } from '../../../../../../pages/api/[version]/icons/[iconName]' | ||
|
|
||
| const mockApiIndex = { | ||
| versions: ['v5', 'v6'], | ||
| sections: {}, | ||
| pages: {}, | ||
| tabs: {}, | ||
| } | ||
|
|
||
| const mockSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><circle cx="256" cy="256" r="200"/></svg>' | ||
|
|
||
| const mockIconSvgs: Record<string, Record<string, string>> = { | ||
| fa: { FaCircle: mockSvg }, | ||
| } | ||
|
|
||
| const mockIconsIndex = { | ||
| icons: [ | ||
| { name: 'circle', reactName: 'FaCircle', style: 'solid', usage: '', unicode: '', set: 'fa' }, | ||
| ], | ||
| } | ||
|
|
||
| function createFetchMock(): typeof fetch { | ||
| return jest.fn((input: RequestInfo | URL) => { | ||
| const url = typeof input === 'string' ? input : input.toString() | ||
| if (url.includes('/iconsIndex.json')) { | ||
| return Promise.resolve({ | ||
| ok: true, | ||
| json: () => Promise.resolve(mockIconsIndex), | ||
| } as Response) | ||
| } | ||
| const match = url.match(/\/iconsSvgs\/([^/]+)\.json/) | ||
| if (match) { | ||
| const setId = match[1] | ||
| const svgs = mockIconSvgs[setId] ?? {} | ||
| return Promise.resolve({ | ||
| ok: true, | ||
| json: () => Promise.resolve(svgs), | ||
| } as Response) | ||
| } | ||
| return Promise.resolve({ | ||
| ok: true, | ||
| json: () => Promise.resolve(mockApiIndex), | ||
| } as Response) | ||
| }) as typeof fetch | ||
| } | ||
|
Comment on lines
+22
to
+45
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The standard fix is to save the original reference and restore it in 🛠️ Proposed fix+const originalFetch = global.fetch
+afterEach(() => {
+ global.fetch = originalFetch
+})
it('returns SVG markup for valid icon', async () => {
global.fetch = createFetchMock()
...
- jest.restoreAllMocks()
})
it('returns 404 when icon is not found', async () => {
global.fetch = createFetchMock()
...
- jest.restoreAllMocks()
})
// ... remove jest.restoreAllMocks() from all remaining test bodiesAlternatively, use +afterEach(() => {
+ jest.restoreAllMocks()
+})
it('returns SVG markup for valid icon', async () => {
- global.fetch = createFetchMock()
+ jest.spyOn(global, 'fetch').mockImplementation(createFetchMock())
...
- jest.restoreAllMocks()
})Also applies to: 47-176 🤖 Prompt for AI Agents |
||
|
|
||
| it('returns SVG markup for valid icon', async () => { | ||
| global.fetch = createFetchMock() | ||
|
|
||
| const response = await GET({ | ||
| params: { version: 'v6', iconName: 'FaCircle' }, | ||
| url: new URL('http://localhost:4321/api/v6/icons/FaCircle'), | ||
| } as any) | ||
| const body = await response.text() | ||
|
|
||
| expect(response.status).toBe(200) | ||
| expect(response.headers.get('Content-Type')).toBe( | ||
| 'image/svg+xml; charset=utf-8', | ||
| ) | ||
| expect(body).toBe(mockSvg) | ||
| expect(body).toContain('<svg') | ||
|
|
||
| jest.restoreAllMocks() | ||
| }) | ||
|
|
||
| it('returns 404 when icon is not found', async () => { | ||
| global.fetch = createFetchMock() | ||
|
|
||
| const response = await GET({ | ||
| params: { version: 'v6', iconName: 'FaNonExistent' }, | ||
| url: new URL('http://localhost:4321/api/v6/icons/FaNonExistent'), | ||
| } as any) | ||
| const body = await response.json() | ||
|
|
||
| expect(response.status).toBe(404) | ||
| expect(body).toHaveProperty('error') | ||
| expect(body.error).toContain('FaNonExistent') | ||
| expect(body.error).toContain('not found') | ||
|
|
||
| jest.restoreAllMocks() | ||
| }) | ||
|
|
||
| it('returns 404 when icon name is not in index', async () => { | ||
| global.fetch = createFetchMock() | ||
|
|
||
| const response = await GET({ | ||
| params: { version: 'v6', iconName: 'invalid' }, | ||
| url: new URL('http://localhost:4321/api/v6/icons/invalid'), | ||
| } as any) | ||
| const body = await response.json() | ||
|
|
||
| expect(response.status).toBe(404) | ||
| expect(body).toHaveProperty('error') | ||
| expect(body.error).toContain('invalid') | ||
| expect(body.error).toContain('not found') | ||
|
|
||
| jest.restoreAllMocks() | ||
| }) | ||
|
|
||
| it('returns 400 when icon name parameter is missing', async () => { | ||
| global.fetch = createFetchMock() | ||
|
|
||
| const response = await GET({ | ||
| params: { version: 'v6' }, | ||
| url: new URL('http://localhost:4321/api/v6/icons'), | ||
| } as any) | ||
| const body = await response.json() | ||
|
|
||
| expect(response.status).toBe(400) | ||
| expect(body).toHaveProperty('error') | ||
| expect(body.error).toContain('Icon name parameter is required') | ||
|
|
||
| jest.restoreAllMocks() | ||
| }) | ||
|
|
||
| it('returns 404 for nonexistent version', async () => { | ||
| global.fetch = createFetchMock() | ||
|
|
||
| const response = await GET({ | ||
| params: { version: 'v99', iconName: 'FaCircle' }, | ||
| url: new URL('http://localhost:4321/api/v99/icons/FaCircle'), | ||
| } as any) | ||
| const body = await response.json() | ||
|
|
||
| expect(response.status).toBe(404) | ||
| expect(body).toHaveProperty('error') | ||
| expect(body.error).toContain('v99') | ||
| expect(body.error).toContain('not found') | ||
|
|
||
| jest.restoreAllMocks() | ||
| }) | ||
|
|
||
| it('returns 400 when version parameter is missing', async () => { | ||
| global.fetch = createFetchMock() | ||
|
|
||
| const response = await GET({ | ||
| params: { iconName: 'FaCircle' }, | ||
| url: new URL('http://localhost:4321/api/icons/FaCircle'), | ||
| } as any) | ||
| const body = await response.json() | ||
|
|
||
| expect(response.status).toBe(400) | ||
| expect(body).toHaveProperty('error') | ||
| expect(body.error).toContain('Version parameter is required') | ||
|
|
||
| jest.restoreAllMocks() | ||
| }) | ||
|
|
||
| it('returns 500 when fetchApiIndex fails', async () => { | ||
| global.fetch = jest.fn((input: RequestInfo | URL) => { | ||
| const url = typeof input === 'string' ? input : input.toString() | ||
| if (url.includes('apiIndex.json')) { | ||
| return Promise.resolve({ | ||
| ok: false, | ||
| status: 500, | ||
| statusText: 'Internal Server Error', | ||
| } as Response) | ||
| } | ||
| return Promise.resolve({ | ||
| ok: true, | ||
| json: () => Promise.resolve({}), | ||
| } as Response) | ||
| }) as typeof fetch | ||
|
|
||
| const response = await GET({ | ||
| params: { version: 'v6', iconName: 'FaCircle' }, | ||
| url: new URL('http://localhost:4321/api/v6/icons/FaCircle'), | ||
| } as any) | ||
| const body = await response.json() | ||
|
|
||
| expect(response.status).toBe(500) | ||
| expect(body).toHaveProperty('error') | ||
| expect(body.error).toBe('Failed to fetch API index') | ||
|
|
||
| jest.restoreAllMocks() | ||
| }) | ||
Uh oh!
There was an error while loading. Please reload this page.