Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3b20863
feat: Added icon api endpoints.
dlabaj Feb 4, 2026
a0d3855
feat: Added icons end points.
dlabaj Feb 4, 2026
da36b8d
chore: Updated with a few modifications.
dlabaj Feb 6, 2026
2437572
fixed broken test.
dlabaj Feb 6, 2026
bc103e1
chore: fix lint errors.
dlabaj Feb 6, 2026
e38d0a1
fix: Added prerender step for cloudflare.
dlabaj Feb 6, 2026
0d52a5a
fix: Updated to prerender svgs as well for cloud flare.
dlabaj Feb 6, 2026
a8654f0
Updated to react name as the end point.
dlabaj Feb 11, 2026
b380631
feat: Refactor icon utilities to use @patternfly/react-icons and upda…
dlabaj Feb 17, 2026
2b8c40c
updated to use prerelease to get static icons.
dlabaj Feb 17, 2026
160618a
Updates from review.
dlabaj Feb 17, 2026
9c287db
Updated with review comments.
dlabaj Feb 17, 2026
ce4583d
Fix issue with fs on cloudflare for the /icons endpoint.
dlabaj Feb 18, 2026
695fcf7
fixed borken test, and failing link.
dlabaj Apr 17, 2026
9123ae7
chore: fixed issue with end point that broke when switching to react …
dlabaj Jun 3, 2026
8702c57
fix: resolve route collision between iconSet and iconName endpoints
dlabaj Jun 8, 2026
1f2d9bc
Fix icons API /[iconName] endpoints when deployed to Cloudflare Workers
wise-king-sullyman Jun 9, 2026
e5434ab
Fix build order issue causing /[iconName] api to fail when deployed
wise-king-sullyman Jun 9, 2026
3a6c91f
Revert "Fix icons API /[iconName] endpoints when deployed to Cloudfla…
wise-king-sullyman Jun 9, 2026
615255e
Update deps to 6.5 releases
wise-king-sullyman Jun 10, 2026
44876af
Increase Node ram for build
wise-king-sullyman Jun 10, 2026
9ce4cf0
Update snapshots
wise-king-sullyman Jun 10, 2026
967eaf1
Fix icons API /[iconName] endpoints when deployed to Cloudflare Workers
wise-king-sullyman Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably want to remove this import

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to remove

"sass": "^1.90.0",
"typescript": "^5.9.2"
},
Expand All @@ -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",
Expand Down Expand Up @@ -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": {
Expand Down
176 changes: 176 additions & 0 deletions src/__tests__/pages/api/__tests__/[version]/icons/[iconName].test.ts
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

jest.restoreAllMocks() does not restore direct global.fetch assignments.

jest.restoreAllMocks() only reverts mocks created with jest.spyOn. Since every test assigns global.fetch = jest.fn(...) directly, restoreAllMocks() is a no-op for fetch cleanup — the mock leaks to any subsequent test files that share this global. Additionally, placing cleanup inside the test body means it won't execute when an assertion throws.

The standard fix is to save the original reference and restore it in afterEach, or use jest.spyOn:

🛠️ 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 bodies

Alternatively, use jest.spyOn so that restoreAllMocks works as intended:

+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
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/pages/api/__tests__/`[version]/icons/[iconName].test.ts around
lines 22 - 45, The tests assign global.fetch directly (via createFetchMock)
which jest.restoreAllMocks() won't revert; save the original fetch before you
override it and restore it in afterEach, or replace direct assignments with
jest.spyOn(global, 'fetch').mockImplementation(createFetchMock()) so
restoreAllMocks() works — update the test file to capture originalFetch =
global.fetch (in beforeEach/at top), set global.fetch = createFetchMock() or use
jest.spyOn for each test, and in afterEach call jest.restoreAllMocks() and if
you saved originalFetch reassign global.fetch = originalFetch to ensure no mock
leaks.


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()
})
Loading