@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
241 lines (185 loc) • 8.12 kB
text/typescript
import '@testing-library/jest-dom'
import { axe, toHaveNoViolations } from 'jest-axe'
import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
import { CustomElementFor } from '../../tests/component-registry'
import './backlink'
expect.extend(toHaveNoViolations)
export interface BacklinkTestConfig extends BaseTestConfig {
href?: string
text?: string
ariaLabel?: string
}
// Use shared framework
export const createBacklinkTest = async (config: BacklinkTestConfig = {}) => {
const { container, element } = await createElementTest<
CustomElementFor<'pkt-backlink'>,
BacklinkTestConfig
>('pkt-backlink', config)
return {
container,
backlink: element,
}
}
describe('PktBackLink', () => {
describe('Rendering and basic functionality', () => {
test('renders without errors', async () => {
const { backlink } = await createBacklinkTest()
expect(backlink).toBeInTheDocument()
expect(backlink).toBeTruthy()
const nav = backlink.querySelector('nav')
expect(nav).toBeInTheDocument()
})
test('renders with correct structure', async () => {
const { backlink } = await createBacklinkTest()
const nav = backlink.querySelector('nav')
const link = nav?.querySelector('a')
const icon = link?.querySelector('pkt-icon')
const textSpan = link?.querySelector('.pkt-back-link__text')
expect(nav).toHaveClass('pkt-back-link')
expect(link).toHaveClass('pkt-link')
expect(link).toHaveClass('pkt-link--icon-left')
expect(icon).toHaveClass('pkt-back-link__icon')
expect(icon).toHaveClass('pkt-icon')
expect(icon).toHaveClass('pkt-link__icon')
expect(textSpan).toHaveClass('pkt-back-link__text')
})
test('renders icon correctly', async () => {
const { backlink } = await createBacklinkTest()
const icon = backlink.querySelector('pkt-icon')
expect(icon).toBeInTheDocument()
expect(icon?.getAttribute('name')).toBe('chevron-thin-left')
expect(icon?.getAttribute('aria-hidden')).toBe('true')
})
})
describe('Properties and attributes', () => {
test('applies default properties correctly', async () => {
const { backlink } = await createBacklinkTest()
expect(backlink.href).toBe('')
expect(backlink.text).toBe('Forsiden')
expect(backlink.ariaLabel).toBe('')
const nav = backlink.querySelector('nav')
const link = nav?.querySelector('a')
const textSpan = link?.querySelector('.pkt-back-link__text')
expect(link?.getAttribute('href')).toBe('/')
expect(textSpan?.textContent).toBe('Forsiden')
expect(nav?.getAttribute('aria-label')).toBe('Gå tilbake til forrige side')
})
test('handles href property correctly', async () => {
const { backlink } = await createBacklinkTest({ href: '/previous-page' })
expect(backlink.href).toBe('/previous-page')
const link = backlink.querySelector('a')
expect(link?.getAttribute('href')).toBe('/previous-page')
// Test href updates
backlink.href = '/another-page'
await backlink.updateComplete
expect(link?.getAttribute('href')).toBe('/another-page')
})
test('handles text property correctly', async () => {
const { backlink } = await createBacklinkTest({ text: 'Back to Home' })
expect(backlink.text).toBe('Back to Home')
// The text attribute should be removed from the custom element
expect(backlink.getAttribute('text')).toBe(null)
const textSpan = backlink.querySelector('.pkt-back-link__text')
expect(textSpan?.textContent).toBe('Back to Home')
// Test text updates
backlink.text = 'Return to Start'
await backlink.updateComplete
expect(textSpan?.textContent).toBe('Return to Start')
})
test('handles ariaLabel property correctly', async () => {
const { backlink } = await createBacklinkTest({
ariaLabel: 'Navigate back to previous section',
})
expect(backlink.ariaLabel).toBe('Navigate back to previous section')
const nav = backlink.querySelector('nav')
expect(nav?.getAttribute('aria-label')).toBe('Navigate back to previous section')
// Test ariaLabel updates
backlink.ariaLabel = 'Go back to main menu'
await backlink.updateComplete
expect(nav?.getAttribute('aria-label')).toBe('Go back to main menu')
})
test('uses default aria-label when none provided', async () => {
const { backlink } = await createBacklinkTest()
expect(backlink.ariaLabel).toBe('')
const nav = backlink.querySelector('nav')
expect(nav?.getAttribute('aria-label')).toBe('Gå tilbake til forrige side')
})
test('handles empty href by defaulting to "/"', async () => {
const { backlink } = await createBacklinkTest({ href: '' })
expect(backlink.href).toBe('')
const link = backlink.querySelector('a')
expect(link?.getAttribute('href')).toBe('/')
})
})
describe('Attribute cleanup', () => {
test('removes reflected attributes from the element', async () => {
const { backlink } = await createBacklinkTest({ href: '/test', text: 'Test Text' })
// The attributeChangedCallback should remove these attributes from the element
// (this is implementation-specific behavior in the component)
expect(backlink.hasAttribute('href')).toBe(false)
expect(backlink.hasAttribute('text')).toBe(false)
expect(backlink.hasAttribute('arialabel')).toBe(false)
})
})
describe('Link functionality', () => {
test('creates clickable link with correct href', async () => {
const { backlink } = await createBacklinkTest({
href: '/dashboard',
text: 'Back to Dashboard',
})
const link = backlink.querySelector('a')
expect(link).toBeInTheDocument()
expect(link?.getAttribute('href')).toBe('/dashboard')
expect(link?.textContent?.trim()).toContain('Back to Dashboard')
})
test('handles complex URLs correctly', async () => {
const complexUrl = 'https://example.com/path?query=value#section'
const { backlink } = await createBacklinkTest({ href: complexUrl })
const link = backlink.querySelector('a')
expect(link?.getAttribute('href')).toBe(complexUrl)
})
})
describe('Accessibility', () => {
test('has correct ARIA attributes', async () => {
const { backlink } = await createBacklinkTest({
text: 'Home',
ariaLabel: 'Return to main page',
})
const nav = backlink.querySelector('nav')
const icon = backlink.querySelector('pkt-icon')
expect(nav?.getAttribute('aria-label')).toBe('Return to main page')
expect(icon?.getAttribute('aria-hidden')).toBe('true')
})
test('provides semantic navigation structure', async () => {
const { backlink } = await createBacklinkTest()
const nav = backlink.querySelector('nav')
const link = nav?.querySelector('a')
expect(nav).toBeInTheDocument()
expect(link).toBeInTheDocument()
expect(nav?.tagName.toLowerCase()).toBe('nav')
expect(link?.tagName.toLowerCase()).toBe('a')
})
test('renders with no WCAG errors with axe - default backlink', async () => {
const { container } = await createBacklinkTest()
const results = await axe(container)
expect(results).toHaveNoViolations()
})
test('renders with no WCAG errors with axe - custom backlink', async () => {
const { container } = await createBacklinkTest({
href: '/categories',
text: 'Back to Categories',
ariaLabel: 'Navigate back to category listing',
})
const results = await axe(container)
expect(results).toHaveNoViolations()
})
test('renders with no WCAG errors with axe - external link', async () => {
const { container } = await createBacklinkTest({
href: 'https://example.com',
text: 'Back to External Site',
})
const results = await axe(container)
expect(results).toHaveNoViolations()
})
})
})