Skip to content
Draft
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
20 changes: 20 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ export default [
},
Comment thread
Adarshkumar0509 marked this conversation as resolved.
Comment thread
Adarshkumar0509 marked this conversation as resolved.
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
moduleDirectory: ['node_modules', path.resolve(frontendDir, 'src')],
},
alias: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
map: [
Expand Down Expand Up @@ -215,4 +219,20 @@ export default [
},
},
eslintConfigPrettier,
{
files: ['frontend/**/*.{ts,tsx,js,jsx}'],
rules: {
'import/order': 'off',
Comment thread
Adarshkumar0509 marked this conversation as resolved.
},
},
{
files: ['frontend/__tests__/**/*.{ts,tsx}'],
rules: {
'import/no-duplicates': 'off',
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'no-undef': 'off',
},
},
Comment thread
Adarshkumar0509 marked this conversation as resolved.
]
2 changes: 0 additions & 2 deletions frontend/__tests__/a11y/components/ChapterMap.a11y.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ const mockZoomControl = {
remove: jest.fn(),
}

/* eslint-disable @typescript-eslint/naming-convention */
jest.mock('leaflet', () => ({
map: jest.fn(() => mockMap),
tileLayer: jest.fn(() => ({
Expand All @@ -60,7 +59,6 @@ jest.mock('leaflet', () => ({
zoom: jest.fn(() => mockZoomControl),
},
}))
/* eslint-enable @typescript-eslint/naming-convention */

// Mock CSS imports
jest.mock('leaflet/dist/leaflet.css', () => ({}))
Expand Down
8 changes: 4 additions & 4 deletions frontend/__tests__/unit/components/BreadCrumbs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
test('renders navigation element with correct aria-label', () => {
render(<BreadCrumbRenderer items={mockItems} />)

const nav = screen.getByRole('navigation')
const nav = screen.getByRole('list', { name: 'breadcrumb' })
Comment thread
Adarshkumar0509 marked this conversation as resolved.
expect(nav).toHaveAttribute('aria-label', 'breadcrumb')
})

Expand Down Expand Up @@ -60,8 +60,8 @@
test('renders chevron separators between items', () => {
const { container } = render(<BreadCrumbRenderer items={mockItems} />)

const separators = container.querySelectorAll('[data-slot="separator"]')
expect(separators).toHaveLength(2)
const separators = container.querySelectorAll('ol li svg')
expect(separators.length).toBe(mockItems.length - 1)

Check warning on line 64 in frontend/__tests__/unit/components/BreadCrumbs.test.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer "expect(separators).toHaveLength(mockItems.length - 1)" over this generic assertion for better reporting; it works on any object with a numeric length property.

See more on https://sonarcloud.io/project/issues?id=OWASP_Nest&issues=AZ_nmJT1P2IEEuXpFsj7&open=AZ_nmJT1P2IEEuXpFsj7&pullRequest=5389
Comment thread
Adarshkumar0509 marked this conversation as resolved.
})

test('handles single item (home only)', () => {
Expand All @@ -76,7 +76,7 @@
test('handles empty items array', () => {
const { container } = render(<BreadCrumbRenderer items={[]} />)

const breadcrumbList = container.querySelector('[data-slot="list"]')
const breadcrumbList = container.querySelector('ol')
expect(breadcrumbList?.children).toHaveLength(0)
})

Expand Down
2 changes: 0 additions & 2 deletions frontend/__tests__/unit/components/ChapterMap.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ const mockZoomControl = {
remove: jest.fn(),
}

/* eslint-disable @typescript-eslint/naming-convention */
jest.mock('leaflet', () => ({
map: jest.fn(() => mockMap),
tileLayer: jest.fn(() => ({
Expand All @@ -62,7 +61,6 @@ jest.mock('leaflet', () => ({
zoom: jest.fn(() => mockZoomControl),
},
}))
/* eslint-enable @typescript-eslint/naming-convention */

// Mock CSS imports
jest.mock('leaflet/dist/leaflet.css', () => ({}))
Expand Down
2 changes: 0 additions & 2 deletions frontend/__tests__/unit/components/EntityActions.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,6 @@ describe('EntityActions', () => {
}

mockDeleteMutation.mockImplementationOnce(({ update }) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (update) update(mockCache as any)
return Promise.resolve({ data: { deleteModule: true } })
})
Expand Down Expand Up @@ -1046,7 +1045,6 @@ describe('EntityActions', () => {
}

mockDeleteMutation.mockImplementationOnce(({ update }) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (update) update(mockCache as any)
return Promise.resolve({ data: { deleteModule: true } })
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const getMockHealthMetric = (): HealthMetricsProps[] => [

// Using `any` intentionally for testing incomplete health metric data.
// The structure may not match the full HealthMetric type, hence typing it strictly would be misleading.
// eslint-disable-next-line @typescript-eslint/no-explicit-any

const getMockIncompleteHealthMetric = (): any[] => [
{
createdAt: '2025-07-23T00:00:00Z',
Expand Down
2 changes: 1 addition & 1 deletion frontend/__tests__/unit/components/IssuesTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const MockLabelList = (props: MockLabelListProps) => {

jest.mock('components/LabelList', () => ({
// Must match the module export name for the mock to be used by IssuesTable
// eslint-disable-next-line @typescript-eslint/naming-convention -- component export name

LabelList: jest.fn((props: MockLabelListProps) => <MockLabelList {...props} />),
}))

Expand Down
3 changes: 0 additions & 3 deletions frontend/__tests__/unit/components/ModuleCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ jest.mock('@dnd-kit/core', () => ({
closestCenter: jest.fn(),
useSensor: jest.fn(() => ({})),
useSensors: jest.fn(() => []),
// eslint-disable-next-line @typescript-eslint/naming-convention
KeyboardSensor: jest.fn(),
// eslint-disable-next-line @typescript-eslint/naming-convention
PointerSensor: jest.fn(),
}))

Expand Down Expand Up @@ -58,7 +56,6 @@ jest.mock('@dnd-kit/sortable', () => ({
}))

jest.mock('@dnd-kit/utilities', () => ({
// eslint-disable-next-line @typescript-eslint/naming-convention
CSS: { Transform: { toString: () => null } },
}))

Expand Down
221 changes: 133 additions & 88 deletions frontend/__tests__/unit/components/ModuleForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,102 +26,147 @@ jest.mock('@apollo/client/react', () => ({

// Mock heroui components
jest.mock('@heroui/react', () => ({
Autocomplete: ({
children,
inputValue,
_selectedKey,
onInputChange,
onSelectionChange,
isInvalid,
errorMessage,
isLoading,
label,
ComboBox: Object.assign(
({
children,
inputValue,
onInputChange,
onSelectionChange,
isInvalid,
className,
}: {
children?: React.ReactNode
inputValue?: string
onInputChange?: (value: string) => void
onSelectionChange?: (key: React.Key | null) => void
isInvalid?: boolean
className?: string
}) => (
<div data-testid="autocomplete">
<input
data-testid="autocomplete-input"
value={inputValue || ''}
onChange={(e) => onInputChange?.(e.target.value)}
/>
<div data-testid="combobox-children">{children}</div>
<button
type="button"
data-testid="autocomplete-select-item"
onClick={() => onSelectionChange?.('project-1')}
>
Select Project 1
</button>
<button
type="button"
data-testid="autocomplete-select-all"
onClick={() => onSelectionChange?.('all' as any)}
>
Select All
</button>
<button
type="button"
data-testid="autocomplete-clear"
onClick={() => {
onInputChange?.('')
onSelectionChange?.(null)
}}
>
Clear Selection
</button>
<button
type="button"
data-testid="autocomplete-select-single"
onClick={() => onSelectionChange?.('project-1')}
>
Select Single Key
</button>
</div>
),
{
InputGroup: ({ children }: { children?: React.ReactNode }) => (
<div data-testid="combobox-input-group">{children}</div>
),
Popover: ({ children }: { children?: React.ReactNode }) => (
<div data-testid="combobox-popover">{children}</div>
),
Trigger: () => <button data-testid="combobox-trigger" />,
}
),
Input: ({
id,
placeholder,
className,
onChange,
}: {
children: React.ReactNode
inputValue?: string
_selectedKey?: string | null
onInputChange?: (value: string) => void
onSelectionChange?: (key: React.Key | Set<React.Key> | 'all') => void
isInvalid?: boolean
errorMessage?: string
isLoading?: boolean
label?: string
id?: string
}) => (
<div data-testid="autocomplete">
<label htmlFor={id}>{label}</label>
<input
id={id}
data-testid="autocomplete-input"
value={inputValue || ''}
data-selected-key={_selectedKey ?? ''}
onChange={(e) => onInputChange?.(e.target.value)}
data-loading={isLoading}
data-invalid={isInvalid}
/>
{errorMessage && <span data-testid="autocomplete-error">{errorMessage}</span>}
<div data-testid="autocomplete-items">{children}</div>
<button
type="button"
data-testid="autocomplete-select-item"
onClick={() => onSelectionChange?.(new Set(['project-1']))}
>
Select Project 1
</button>
<button
type="button"
data-testid="autocomplete-select-all"
onClick={() => onSelectionChange?.('all')}
>
Select All
</button>
<button
type="button"
data-testid="autocomplete-clear"
onClick={() => {
onInputChange?.('')
onSelectionChange?.(null)
}}
>
Clear Selection
</button>
<button
type="button"
data-testid="autocomplete-select-single"
onClick={() => onSelectionChange?.('project-1')}
>
Select Single Key
</button>
</div>
),
AutocompleteItem: ({
placeholder?: string
className?: string
onChange?: React.ChangeEventHandler<HTMLInputElement>
}) => <input id={id} placeholder={placeholder} className={className} onChange={onChange} />,
Label: ({
children,
textValue,
htmlFor,
className,
}: {
children: React.ReactNode
textValue?: string
children?: React.ReactNode
htmlFor?: string
className?: string
}) => (
<div data-testid="autocomplete-item" data-text-value={textValue}>
<label htmlFor={htmlFor} className={className}>
{children}
</div>
</label>
),
Switch: ({
isSelected,
onValueChange,
'aria-label': ariaLabel,
}: {
isSelected?: boolean
onValueChange?: (value: boolean) => void
'aria-label'?: string
}) => (
<input
type="checkbox"
role="switch"
aria-label={ariaLabel}
checked={!!isSelected}
onChange={(e) => onValueChange?.(e.target.checked)}
/>
ListBox: Object.assign(
({ children }: { children?: React.ReactNode }) => (
<div data-testid="autocomplete-items">{children}</div>
),
{
Item: ({
children,
id,
textValue,
}: {
children?: React.ReactNode
id?: string
textValue?: string
}) => (
<div data-testid="autocomplete-item" data-id={id} data-text-value={textValue}>
{children}
</div>
),
}
),
FieldError: ({ children }: { children?: React.ReactNode }) => (
<span data-testid="autocomplete-error">{children}</span>
),
Switch: Object.assign(
({
isSelected,
onChange,
'aria-label': ariaLabel,
children,
}: {
isSelected?: boolean
onChange?: (value: boolean) => void
'aria-label'?: string
children?: React.ReactNode
}) => (
<div>
<input
type="checkbox"
role="switch"
aria-label={ariaLabel}
checked={!!isSelected}
onChange={(e) => onChange?.(e.target.checked)}
/>
{children}
</div>
),
{
Content: ({ children }: { children?: React.ReactNode }) => <div>{children}</div>,
Control: ({ children }: { children?: React.ReactNode }) => <div>{children}</div>,
Thumb: () => <span />,
}
),
}))

Expand Down
Loading
Loading