Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,30 +1,12 @@
import { GET } from '../../../../../../../pages/api/[version]/[section]/[page]/props'
import { getConfig } from '../../../../../../../../cli/getConfig'
import { sentenceCase, removeSubsection } from '../../../../../../../utils/case'

/**
* Mock getConfig to return a test configuration
* Mock fetchProps to return props data
*/
jest.mock('../../../../../../../../cli/getConfig', () => ({
getConfig: jest.fn().mockResolvedValue({
outputDir: '/mock/output/dir',
}),
}))

/**
* Mock node:path join function
*/
const mockJoin = jest.fn((...paths: string[]) => paths.join('/'))
jest.mock('node:path', () => ({
join: (...args: any[]) => mockJoin(...args),
}))

/**
* Mock node:fs readFileSync function
*/
const mockReadFileSync = jest.fn()
jest.mock('node:fs', () => ({
readFileSync: (...args: any[]) => mockReadFileSync(...args),
const mockFetchProps = jest.fn()
jest.mock('../../../../../../../utils/propsData/fetch', () => ({
fetchProps: (...args: any[]) => mockFetchProps(...args),
}))

/**
Expand Down Expand Up @@ -109,10 +91,7 @@ const mockData = {

beforeEach(() => {
jest.clearAllMocks()
// Reset process.cwd mock
process.cwd = jest.fn(() => '/mock/workspace')
// Reset mockReadFileSync to return default mock data
mockReadFileSync.mockReturnValue(JSON.stringify(mockData))
mockFetchProps.mockResolvedValue(mockData)
})

it('returns props data for a valid page', async () => {
Expand Down Expand Up @@ -181,12 +160,8 @@ it('returns 400 error when page parameter is missing', async () => {
expect(body.error).toContain('Page parameter is required')
})

it('returns 500 error when props.json file is not found', async () => {
mockReadFileSync.mockImplementation(() => {
const error = new Error('ENOENT: no such file or directory')
; (error as any).code = 'ENOENT'
throw error
})
it('returns 500 error when fetchProps fails', async () => {
mockFetchProps.mockRejectedValueOnce(new Error('Network error'))

const response = await GET({
params: { version: 'v6', section: 'components', page: 'alert' },
Expand All @@ -196,13 +171,13 @@ it('returns 500 error when props.json file is not found', async () => {

expect(response.status).toBe(500)
expect(body).toHaveProperty('error')
expect(body.error).toBe('Props data not found')
expect(body.error).toBe('Failed to load props data')
expect(body).toHaveProperty('details')
expect(body.details).toContain('ENOENT')
expect(body.details).toBe('Network error')
})

it('returns 500 error when props.json contains invalid JSON', async () => {
mockReadFileSync.mockReturnValue('invalid json content')
it('returns 500 error when fetchProps throws a non-Error object', async () => {
mockFetchProps.mockRejectedValueOnce('String error')

const response = await GET({
params: { version: 'v6', section: 'components', page: 'alert' },
Expand All @@ -212,73 +187,9 @@ it('returns 500 error when props.json contains invalid JSON', async () => {

expect(response.status).toBe(500)
expect(body).toHaveProperty('error')
expect(body.error).toBe('Props data not found')
expect(body.error).toBe('Failed to load props data')
expect(body).toHaveProperty('details')
})

it('returns 500 error when file read throws an error', async () => {
mockReadFileSync.mockImplementation(() => {
throw new Error('Permission denied')
})

const response = await GET({
params: { version: 'v6', section: 'components', page: 'alert' },
url: new URL('http://localhost:4321/api/v6/components/alert/props'),
} as any)
const body = await response.json()

expect(response.status).toBe(500)
expect(body).toHaveProperty('error')
expect(body.error).toBe('Props data not found')
expect(body).toHaveProperty('details')
expect(body.details).toContain('Permission denied')
})

it('uses default outputDir when config does not provide one', async () => {
jest.mocked(getConfig).mockResolvedValueOnce({
content: [],
propsGlobs: [],
outputDir: '',
})

const response = await GET({
params: { version: 'v6', section: 'components', page: 'alert' },
url: new URL('http://localhost:4321/api/v6/components/alert/props'),
} as any)
const body = await response.json()

expect(response.status).toBe(200)
expect(body).toHaveProperty('name')
expect(mockJoin).toHaveBeenCalledWith('/mock/workspace/dist', 'props.json')
})

it('uses custom outputDir from config when provided', async () => {
jest.mocked(getConfig).mockResolvedValueOnce({
outputDir: '/custom/output/path',
content: [],
propsGlobs: [],
})

const response = await GET({
params: { version: 'v6', section: 'components', page: 'alert' },
url: new URL('http://localhost:4321/api/v6/components/alert/props'),
} as any)
const body = await response.json()

expect(response.status).toBe(200)
expect(body).toHaveProperty('name')
// Verify that join was called with custom outputDir
expect(mockJoin).toHaveBeenCalledWith('/custom/output/path', 'props.json')
})

it('reads props.json from the correct file path', async () => {
await GET({
params: { version: 'v6', section: 'components', page: 'alert' },
url: new URL('http://localhost:4321/api/v6/components/alert/props'),
} as any)

// Verify readFileSync was called with the correct path
expect(mockReadFileSync).toHaveBeenCalledWith('/mock/output/dir/props.json')
expect(body.details).toBe('String error')
})

it('returns full props structure with all fields', async () => {
Expand Down Expand Up @@ -328,14 +239,13 @@ it('handles props with required field', async () => {
})

it('handles components with empty props array', async () => {
const emptyPropsData = {
mockFetchProps.mockResolvedValueOnce({
'Empty Component': {
name: 'EmptyComponent',
description: '',
props: [],
},
}
mockReadFileSync.mockReturnValueOnce(JSON.stringify(emptyPropsData))
})

const response = await GET({
params: { version: 'v6', section: 'components', page: 'empty-component' },
Expand All @@ -350,8 +260,6 @@ it('handles components with empty props array', async () => {
})

it('handles request when tab is in URL path but not in params', async () => {
// Note: props.ts route is at [page] level, so tab parameter is not available
// This test verifies the route works correctly with just page parameter
const response = await GET({
params: { version: 'v6', section: 'components', page: 'alert' },
url: new URL('http://localhost:4321/api/v6/components/alert/react/props'),
Expand All @@ -364,8 +272,7 @@ it('handles request when tab is in URL path but not in params', async () => {
})

it('removes subsection from page name before looking up props', async () => {
// Add test data for checkbox component
const dataWithSubsection = {
mockFetchProps.mockResolvedValueOnce({
...mockData,
Checkbox: {
name: 'Checkbox',
Expand All @@ -378,8 +285,7 @@ it('removes subsection from page name before looking up props', async () => {
},
],
},
}
mockReadFileSync.mockReturnValueOnce(JSON.stringify(dataWithSubsection))
})

const response = await GET({
params: { version: 'v6', section: 'components', page: 'forms_checkbox' },
Expand Down
123 changes: 16 additions & 107 deletions src/__tests__/pages/api/__tests__/[version]/[section]/names.test.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,11 @@
import { GET } from '../../../../../../pages/api/[version]/[section]/names'
import { getConfig } from '../../../../../../../cli/getConfig'

/**
* Mock getConfig to return a test configuration
* Mock fetchProps to return props data
*/
jest.mock('../../../../../../../cli/getConfig', () => ({
getConfig: jest.fn().mockResolvedValue({
outputDir: '/mock/output/dir',
}),
}))

/**
* Mock node:path join function
*/
const mockJoin = jest.fn((...paths: string[]) => paths.join('/'))
jest.mock('node:path', () => ({
join: (...args: any[]) => mockJoin(...args),
}))

/**
* Mock node:fs readFileSync function
*/
const mockReadFileSync = jest.fn()
jest.mock('node:fs', () => ({
readFileSync: (...args: any[]) => mockReadFileSync(...args),
const mockFetchProps = jest.fn()
jest.mock('../../../../../../utils/propsData/fetch', () => ({
fetchProps: (...args: any[]) => mockFetchProps(...args),
}))

const mockData = {
Expand Down Expand Up @@ -86,10 +68,7 @@ const mockData = {

beforeEach(() => {
jest.clearAllMocks()
// Reset process.cwd mock
process.cwd = jest.fn(() => '/mock/workspace')
// Reset mockReadFileSync to return default mock data
mockReadFileSync.mockReturnValue(JSON.stringify(mockData))
mockFetchProps.mockResolvedValue(mockData)
})

it('returns filtered component names from props.json data', async () => {
Expand All @@ -110,17 +89,15 @@ it('returns filtered component names from props.json data', async () => {
})

it('filters out all keys containing "Props" case-insensitively', async () => {
const testData = {
mockFetchProps.mockResolvedValueOnce({
Alert: {},
Button: {},
AlertProps: {},
ALERTPROPS: {},
alertprops: {},
ComponentProps: {},
SomeComponentProps: {},
}

mockReadFileSync.mockReturnValue(JSON.stringify(testData))
})

const response = await GET({
params: { version: 'v6', section: 'components' },
Expand All @@ -138,13 +115,11 @@ it('filters out all keys containing "Props" case-insensitively', async () => {
})

it('returns empty array when props.json has no valid component names', async () => {
const testData = {
mockFetchProps.mockResolvedValueOnce({
AlertProps: {},
ButtonProps: {},
ComponentProps: {},
}

mockReadFileSync.mockReturnValue(JSON.stringify(testData))
})

const response = await GET({
params: { version: 'v6', section: 'components' },
Expand All @@ -158,7 +133,7 @@ it('returns empty array when props.json has no valid component names', async ()
})

it('returns empty array when props.json is empty', async () => {
mockReadFileSync.mockReturnValue(JSON.stringify({}))
mockFetchProps.mockResolvedValueOnce({})

const response = await GET({
params: { version: 'v6', section: 'components' },
Expand All @@ -171,12 +146,8 @@ it('returns empty array when props.json is empty', async () => {
expect(body).toEqual([])
})

it('returns 500 error when props.json file is not found', async () => {
mockReadFileSync.mockImplementation(() => {
const error = new Error('ENOENT: no such file or directory')
; (error as any).code = 'ENOENT'
throw error
})
it('returns 500 error when fetchProps fails', async () => {
mockFetchProps.mockRejectedValueOnce(new Error('Network error'))

const response = await GET({
params: { version: 'v6', section: 'components' },
Expand All @@ -188,11 +159,11 @@ it('returns 500 error when props.json file is not found', async () => {
expect(body).toHaveProperty('error')
expect(body.error).toBe('Component names data not found')
expect(body).toHaveProperty('details')
expect(body.details).toContain('ENOENT')
expect(body.details).toBe('Network error')
})

it('returns 500 error when props.json contains invalid JSON', async () => {
mockReadFileSync.mockReturnValue('invalid json content')
it('returns 500 error when fetchProps throws a non-Error object', async () => {
mockFetchProps.mockRejectedValueOnce('String error')

const response = await GET({
params: { version: 'v6', section: 'components' },
Expand All @@ -204,67 +175,5 @@ it('returns 500 error when props.json contains invalid JSON', async () => {
expect(body).toHaveProperty('error')
expect(body.error).toBe('Component names data not found')
expect(body).toHaveProperty('details')
})

it('returns 500 error when file read throws an error', async () => {
mockReadFileSync.mockImplementation(() => {
throw new Error('Permission denied')
})

const response = await GET({
params: { version: 'v6', section: 'components' },
url: new URL('http://localhost:4321/api/v6/components/names'),
} as any)
const body = await response.json()

expect(response.status).toBe(500)
expect(body).toHaveProperty('error')
expect(body.error).toBe('Component names data not found')
expect(body).toHaveProperty('details')
expect(body.details).toContain('Permission denied')
})

it('uses default outputDir when config does not provide one', async () => {
jest.mocked(getConfig).mockResolvedValueOnce({
content: [],
propsGlobs: [],
outputDir: '',
})

const response = await GET({
params: { version: 'v6', section: 'components' },
url: new URL('http://localhost:4321/api/v6/components/names'),
} as any)
const body = await response.json()

expect(response.status).toBe(200)
expect(Array.isArray(body)).toBe(true)
expect(mockJoin).toHaveBeenCalledWith('/mock/workspace/dist', 'props.json')
})

it('uses custom outputDir from config when provided', async () => {
jest.mocked(getConfig).mockResolvedValueOnce({
outputDir: '/custom/output/path',
content: [],
propsGlobs: [],
})

const response = await GET({
params: { version: 'v6', section: 'components' },
url: new URL('http://localhost:4321/api/v6/components/names'),
} as any)
const body = await response.json()

expect(response.status).toBe(200)
expect(Array.isArray(body)).toBe(true)
expect(mockJoin).toHaveBeenCalledWith('/custom/output/path', 'props.json')
})

it('reads props.json from the correct file path', async () => {
await GET({
params: { version: 'v6', section: 'components' },
url: new URL('http://localhost:4321/api/v6/components/names'),
} as any)

expect(mockReadFileSync).toHaveBeenCalledWith('/mock/output/dir/props.json')
expect(body.details).toBe('String error')
})
Loading