-
-
Notifications
You must be signed in to change notification settings - Fork 187
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Merge pull request #5120 from kobotoolbox/task-1046-add-unit-test-for…
…-component [TASK-1046] Introduce unit tests for UI components
Showing
10 changed files
with
24,208 additions
and
16,814 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import type {Config} from 'jest'; | ||
|
||
const config: Config = { | ||
verbose: true, | ||
testEnvironment: 'jsdom', | ||
roots: ['<rootDir>/../js', '<rootDir>'], | ||
moduleNameMapper: { | ||
'^js/(.*)$': '<rootDir>/../js/$1', | ||
'\\.(css|scss)$': 'identity-obj-proxy', | ||
}, | ||
setupFilesAfterEnv: ['<rootDir>/setupJestTest.ts'], | ||
transform: {'^.+\\.(t|j)sx?$': '@swc/jest'}, | ||
}; | ||
|
||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import '@testing-library/jest-dom/jest-globals'; | ||
import '@testing-library/jest-dom'; | ||
|
||
global.t = (str: string) => str; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import Button from './button'; | ||
|
||
import {render, screen} from '@testing-library/react'; | ||
import {describe, it, expect, jest} from '@jest/globals'; | ||
import userEvent from '@testing-library/user-event'; | ||
|
||
const user = userEvent.setup(); | ||
|
||
describe('Enabled button', () => { | ||
// Mock | ||
const handleClickFunction = jest.fn(); | ||
|
||
beforeEach(() => { | ||
render( | ||
<Button | ||
type='primary' | ||
size='l' | ||
label='Button Label' | ||
onClick={handleClickFunction} | ||
/> | ||
); | ||
}); | ||
|
||
it('should render', async () => { | ||
// Assert | ||
expect(screen.getByLabelText('Button Label')).toBeInTheDocument(); | ||
}); | ||
|
||
it('should be clickable', async () => { | ||
// Act | ||
const button = screen.getByLabelText('Button Label'); | ||
await user.click(button); | ||
|
||
expect(handleClickFunction).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
|
||
describe('Disabled button', () => { | ||
// Mock | ||
const handleClickFunction = jest.fn(); | ||
|
||
beforeEach(() => { | ||
render( | ||
<Button | ||
type='primary' | ||
size='l' | ||
label='Button Label' | ||
onClick={handleClickFunction} | ||
isDisabled | ||
/> | ||
); | ||
}); | ||
|
||
it('should render', async () => { | ||
// Assert | ||
expect(screen.getByLabelText('Button Label')).toBeInTheDocument(); | ||
}); | ||
|
||
it('should not be clickable', async () => { | ||
// Act | ||
const button = screen.getByLabelText('Button Label'); | ||
await user.click(button); | ||
|
||
expect(handleClickFunction).not.toHaveBeenCalled(); | ||
}); | ||
}); |
136 changes: 136 additions & 0 deletions
136
jsapp/js/components/special/koboAccessibleSelect.spec.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import {fireEvent, render, screen} from '@testing-library/react'; | ||
import {describe, it, expect, jest} from '@jest/globals'; | ||
import userEvent from '@testing-library/user-event'; | ||
import type {KoboSelectOption} from './koboAccessibleSelect'; | ||
import KoboSelect3 from './koboAccessibleSelect'; | ||
import {useState} from 'react'; | ||
|
||
const options: KoboSelectOption[] = [ | ||
{value: '1', label: 'Apple'}, | ||
{value: '2', label: 'Banana'}, | ||
{value: '3', label: 'Avocado'}, | ||
]; | ||
|
||
// A wrapper is needed for the component to retain value changes | ||
const Wrapper = ({onChange}: {onChange: (newValue: string) => void}) => { | ||
const [value, setValue] = useState<string>(''); | ||
|
||
const handleChange = (newValue: string | null = '') => { | ||
setValue(newValue || ''); | ||
onChange(newValue || ''); | ||
}; | ||
|
||
return ( | ||
<KoboSelect3 | ||
name='testSelect' | ||
options={options} | ||
value={value} | ||
onChange={handleChange} | ||
/> | ||
); | ||
}; | ||
|
||
|
||
describe('KoboSelect3', () => { | ||
const user = userEvent.setup(); | ||
|
||
// Mock | ||
const scrollIntoViewMock = jest.fn(); | ||
window.HTMLElement.prototype.scrollIntoView = scrollIntoViewMock; | ||
const onChangeMock = jest.fn(); | ||
|
||
beforeEach(() => { | ||
render(<Wrapper onChange={onChangeMock} />); | ||
}); | ||
|
||
it('should render with proper placeholder', async () => { | ||
const trigger = screen.getByRole('combobox'); | ||
const triggerLabel = trigger.querySelector('label'); | ||
expect(trigger).toBeInTheDocument(); | ||
expect(triggerLabel).toHaveTextContent('Select…'); | ||
}); | ||
|
||
it('should have the list closed on start', async () => { | ||
const list = screen.getByRole('listbox'); | ||
expect(list.dataset.expanded).toBe('false'); | ||
}); | ||
|
||
it('should have a list with the correct items count', async () => { | ||
const listOptions = screen.getAllByRole('option'); | ||
expect(listOptions).toHaveLength(3); | ||
}); | ||
|
||
it('should be selectable by mouse click', async () => { | ||
const trigger = screen.getByRole('combobox'); | ||
const list = screen.getByRole('listbox'); | ||
const listOptions = screen.getAllByRole('option'); | ||
|
||
// Clicks the trigger | ||
await user.click(trigger); | ||
expect(list.dataset.expanded).toBe('true'); | ||
|
||
// Select first option | ||
await user.click(listOptions[0]); | ||
|
||
// Onchange should be called with the correct value | ||
expect(onChangeMock).lastCalledWith(options[0].value); | ||
|
||
expect(list.dataset.expanded).toBe('false'); | ||
}); | ||
|
||
it('should be selectable by keyboard arrows', async () => { | ||
const trigger = screen.getByRole('combobox'); | ||
const triggerLabel = trigger.querySelector('label'); | ||
|
||
// No item selected | ||
expect(triggerLabel).toHaveTextContent('Select…'); | ||
|
||
// Increase option on arrow down | ||
fireEvent.keyDown(trigger, {key: 'ArrowDown'}); | ||
expect(onChangeMock).lastCalledWith(options[0].value); | ||
expect(triggerLabel).toHaveTextContent(options[0].label); | ||
|
||
// Increase option on arrow right | ||
fireEvent.keyDown(trigger, {key: 'ArrowRight'}); | ||
expect(onChangeMock).lastCalledWith(options[1].value); | ||
expect(triggerLabel).toHaveTextContent(options[1].label); | ||
fireEvent.keyDown(trigger, {key: 'ArrowRight'}); | ||
expect(onChangeMock).lastCalledWith(options[2].value); | ||
expect(triggerLabel).toHaveTextContent(options[2].label); | ||
|
||
// Don't go past the last one | ||
onChangeMock.mockReset(); | ||
fireEvent.keyDown(trigger, {key: 'ArrowDown'}); | ||
expect(onChangeMock).not.toHaveBeenCalled(); | ||
expect(triggerLabel).toHaveTextContent(options[2].label); | ||
|
||
// Decrease option on arrow up | ||
fireEvent.keyDown(trigger, {key: 'ArrowUp'}); | ||
expect(onChangeMock).lastCalledWith(options[1].value); | ||
expect(triggerLabel).toHaveTextContent(options[1].label); | ||
|
||
// Decrease option on arrow left | ||
fireEvent.keyDown(trigger, {key: 'ArrowLeft'}); | ||
expect(onChangeMock).lastCalledWith(options[0].value); | ||
expect(triggerLabel).toHaveTextContent(options[0].label); | ||
|
||
// Don't go past the first one | ||
onChangeMock.mockReset(); | ||
fireEvent.keyDown(trigger, {key: 'ArrowUp'}); | ||
expect(onChangeMock).not.toHaveBeenCalled(); | ||
expect(triggerLabel).toHaveTextContent(options[0].label); | ||
}); | ||
|
||
it('should be selectable by typing', async () => { | ||
const trigger = screen.getByRole('combobox'); | ||
const triggerLabel = trigger.querySelector('label'); | ||
|
||
// No item selected | ||
expect(triggerLabel).toHaveTextContent('Select…'); | ||
|
||
// Type 'b' to select Banana | ||
fireEvent.keyDown(trigger, {key: 'b'}); | ||
expect(onChangeMock).lastCalledWith(options[1].value); | ||
expect(triggerLabel).toHaveTextContent(options[1].label); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.