first commit

This commit is contained in:
2025-04-24 13:11:28 +08:00
commit ff9c54d5e4
5960 changed files with 834111 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
import { expect } from 'chai'
import { screen, render } from '@testing-library/react'
import Icon from '../../../../frontend/js/shared/components/icon'
describe('<Icon />', function () {
it('renders basic fa classes', function () {
const { container } = render(<Icon type="angle-down" />)
const element = container.querySelector('i.fa.fa-angle-down')
expect(element).to.exist
})
it('renders with aria-hidden', function () {
const { container } = render(<Icon type="angle-down" />)
const element = container.querySelector('i[aria-hidden="true"]')
expect(element).to.exist
})
it('renders accessible label', function () {
render(<Icon type="angle-down" accessibilityLabel="Accessible Foo" />)
screen.getByText('Accessible Foo')
})
it('renders with spin', function () {
const { container } = render(<Icon type="angle-down" spin />)
const element = container.querySelector('i.fa.fa-angle-down.fa-spin')
expect(element).to.exist
})
it('renders with fw', function () {
const { container } = render(<Icon type="angle-down" fw />)
const element = container.querySelector('i.fa.fa-angle-down.fa-fw')
expect(element).to.exist
})
it('renders with modifier', function () {
const { container } = render(<Icon type="angle-down" modifier="2x" />)
const element = container.querySelector('i.fa.fa-angle-down.fa-2x')
expect(element).to.exist
})
it('renders with custom clases', function () {
const { container } = render(
<Icon type="angle-down" className="custom-icon-class" />
)
const element = container.querySelector(
'i.fa.fa-angle-down.custom-icon-class'
)
expect(element).to.exist
})
})

View File

@@ -0,0 +1,62 @@
import { expect } from 'chai'
import { screen, render } from '@testing-library/react'
import Notification from '../../../../frontend/js/shared/components/notification'
import * as eventTracking from '@/infrastructure/event-tracking'
import sinon from 'sinon'
describe('<Notification />', function () {
let sendMBSpy: sinon.SinonSpy
beforeEach(function () {
sendMBSpy = sinon.spy(eventTracking, 'sendMB')
})
afterEach(function () {
sendMBSpy.restore()
})
it('renders and is not dismissible by default', function () {
render(<Notification type="info" content={<p>A notification</p>} />)
screen.getByText('A notification')
expect(screen.queryByRole('button', { name: 'Close' })).to.be.null
})
it('renders with action', function () {
render(
<Notification
type="info"
content={<p>A notification</p>}
action={<a href="/">Action</a>}
/>
)
screen.getByText('A notification')
screen.getByRole('link', { name: 'Action' })
})
it('renders with close button', function () {
render(
<Notification type="info" content={<p>A notification</p>} isDismissible />
)
screen.getByText('A notification')
screen.getByRole('button', { name: 'Close' })
})
it('renders with title and content passed as HTML', function () {
render(
<Notification
type="info"
content={<p>A notification</p>}
title="A title"
/>
)
screen.getByText('A title')
screen.getByText('A notification')
})
it('renders with content when passed as a string', function () {
render(
<Notification type="info" content="A notification" title="A title" />
)
screen.getByText('A notification')
})
})

View File

@@ -0,0 +1,67 @@
import { expect } from 'chai'
import { render, screen } from '@testing-library/react'
import Pagination from '../../../../frontend/js/shared/components/pagination'
describe('<Pagination />', function () {
it('renders with current page handled', async function () {
render(
<Pagination currentPage={6} totalPages={10} handlePageClick={() => {}} />
)
await screen.findByLabelText('Page 6, Current Page')
})
it('renders with nearby page buttons and prev/next button', async function () {
render(
<Pagination currentPage={2} totalPages={4} handlePageClick={() => {}} />
)
await screen.findByLabelText('Page 2, Current Page')
await screen.findByLabelText('Go to page 1')
await screen.findByLabelText('Go to page 3')
await screen.findByLabelText('Go to page 4')
await screen.findByLabelText('Go to Previous Page')
await screen.findByLabelText('Go to Next Page')
})
it('does not render the prev button when expected', async function () {
render(
<Pagination currentPage={1} totalPages={2} handlePageClick={() => {}} />
)
await screen.findByLabelText('Page 1, Current Page')
await screen.findByLabelText('Go to Next Page')
expect(screen.queryByLabelText('Go to Prev Page')).to.be.null
})
it('does not render the next button when expected', async function () {
render(
<Pagination currentPage={2} totalPages={2} handlePageClick={() => {}} />
)
await screen.findByLabelText('Page 2, Current Page')
await screen.findByLabelText('Go to Previous Page')
expect(screen.queryByLabelText('Go to Next Page')).to.be.null
})
it('renders 1 ellipses when there are more pages than buttons and on first page', async function () {
render(
<Pagination currentPage={1} totalPages={10} handlePageClick={() => {}} />
)
const ellipses = await screen.findAllByText('…')
expect(ellipses.length).to.equal(1)
})
it('renders 1 ellipses when on last page and there are more previous pages than buttons', async function () {
render(
<Pagination currentPage={10} totalPages={10} handlePageClick={() => {}} />
)
const ellipses = await screen.findAllByText('…')
expect(ellipses.length).to.equal(1)
})
it('renders 2 ellipses when there are more pages than buttons', async function () {
render(
<Pagination currentPage={5} totalPages={10} handlePageClick={() => {}} />
)
const ellipses = await screen.findAllByText('…')
expect(ellipses.length).to.equal(2)
})
it('only renders the number of page buttons set by maxOtherPageButtons', async function () {
render(
<Pagination currentPage={1} totalPages={100} handlePageClick={() => {}} />
)
const items = document.querySelectorAll('button')
expect(items.length).to.equal(6) // 5 page buttons + next button
})
})

View File

@@ -0,0 +1,16 @@
import { expect } from 'chai'
import { render } from '@testing-library/react'
import Processing from '../../../../frontend/js/shared/components/processing'
describe('<Processing />', function () {
it('renders processing UI when isProcessing is true', function () {
const { container } = render(<Processing isProcessing />)
const element = container.querySelector('i.fa.fa-refresh')
expect(element).to.exist
})
it('does not render processing UI when isProcessing is false', function () {
const { container } = render(<Processing isProcessing={false} />)
const element = container.querySelector('i.fa.fa-refresh')
expect(element).to.not.exist
})
})

View File

@@ -0,0 +1,57 @@
import { expect } from 'chai'
import { render, screen } from '@testing-library/react'
import RadioChip from '@/shared/components/radio-chip'
describe('<RadioChip />', function () {
const defaultProps = {
name: 'test',
label: 'Test',
value: 'testValue',
onChange: () => {},
}
describe('component renders and with label', function () {
it('renders and label is provided', function () {
render(<RadioChip {...defaultProps} />)
screen.getByText('Test')
})
})
describe('props', function () {
it('should be checked when the checked prop is provided', function () {
render(<RadioChip {...defaultProps} checked />)
const radioChip = screen.getByRole('radio') as HTMLInputElement
expect(radioChip.checked).to.equal(true)
})
it('should be disabled when the disabled prop is provided', function () {
render(<RadioChip {...defaultProps} disabled />)
const radioChip = screen.getByRole('radio') as HTMLInputElement
expect(radioChip.disabled).to.equal(true)
})
it('should have the required attribute when the required prop is provided', function () {
render(<RadioChip {...defaultProps} required />)
const radioChip = screen.getByRole('radio') as HTMLInputElement
expect(radioChip.required).to.equal(true)
})
it('should use the provided name prop', function () {
render(<RadioChip {...defaultProps} name="testName" />)
const radioChip = screen.getByRole('radio') as HTMLInputElement
expect(radioChip.name).to.equal('testName')
})
it('should use the provided value prop', function () {
render(<RadioChip {...defaultProps} />)
const radioChip = screen.getByRole('radio') as HTMLInputElement
expect(radioChip.value).to.equal('testValue')
})
it('should have the data-disabled attribute when the disabled prop is provided', function () {
render(<RadioChip {...defaultProps} disabled />)
const label = screen.getByText('Test')?.closest('label')
expect(label?.getAttribute('data-disabled')).to.equal('true')
})
})
})