UNPKG

@oslokommune/punkt-elements

Version:

Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo

365 lines (284 loc) 11.9 kB
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 { PktLinkCard, type TLinkCardSkin } from './linkcard' import type { IPktLinkCard } from './linkcard' import './linkcard' export interface LinkCardTestConfig extends IPktLinkCard, BaseTestConfig {} // Use shared framework export const createLinkCardTest = async (config: LinkCardTestConfig = {}) => { const { container, element } = await createElementTest< CustomElementFor<'pkt-linkcard'>, LinkCardTestConfig >('pkt-linkcard', config) const link = element.querySelector('a') as HTMLAnchorElement return { container, linkCard: element, link, } } expect.extend(toHaveNoViolations) // Test data constants const VALID_SKINS: TLinkCardSkin[] = [ 'normal', 'no-padding', 'blue', 'beige', 'green', 'gray', 'beige-outline', 'gray-outline', ] const SAMPLE_ICONS = ['arrow-right', 'external-link', 'download', 'info'] // Cleanup after each test afterEach(() => { document.body.innerHTML = '' }) // ----------- THE TESTS ----------- describe('PktLinkCard', () => { describe('Basic Rendering', () => { test('renders without errors', async () => { const { linkCard, link } = await createLinkCardTest() expect(linkCard).toBeInTheDocument() expect(link).toBeInTheDocument() expect(link).toHaveClass('pkt-linkcard') }) test('renders with default properties', async () => { const { linkCard, link } = await createLinkCardTest() expect(linkCard.title).toBe('') expect(linkCard.href).toBe('#') expect(linkCard.iconName).toBe('') expect(linkCard.openInNewTab).toBe(false) expect(linkCard.skin).toBe('normal') expect(linkCard.external).toBe(false) expect(link.getAttribute('href')).toBe('#') expect(link.getAttribute('target')).toBe('_self') // When openInNewTab is false, rel should be null (ifDefined behavior) expect(link.getAttribute('rel')).toBeNull() }) test('renders content in slot', async () => { const content = '<p>Custom link card content</p>' const { linkCard } = await createLinkCardTest({ content }) const textSlot = linkCard.querySelector('.pkt-linkcard__text') expect(textSlot).toBeInTheDocument() expect(textSlot?.innerHTML).toContain(content) }) }) describe('Title Functionality', () => { test('renders title when provided', async () => { const title = 'Test Link Card Title' const { linkCard } = await createLinkCardTest({ title }) expect(linkCard.title).toBe(title) const titleElement = linkCard.querySelector('.pkt-linkcard__title') expect(titleElement).toBeInTheDocument() expect(titleElement?.textContent).toBe(title) }) test('does not render title when not provided', async () => { const { linkCard } = await createLinkCardTest() const titleElement = linkCard.querySelector('.pkt-linkcard__title') expect(titleElement).not.toBeInTheDocument() }) test('applies correct title classes', async () => { const title = 'Test Title' const { linkCard } = await createLinkCardTest({ title }) const titleElement = linkCard.querySelector('.pkt-linkcard__title') expect(titleElement).toHaveClass('pkt-linkcard__title') }) }) describe('Link Functionality', () => { test('renders with custom href', async () => { const href = '/custom-path' const { linkCard, link } = await createLinkCardTest({ href }) expect(linkCard.href).toBe(href) expect(link.getAttribute('href')).toBe(href) }) test('handles openInNewTab correctly', async () => { const { linkCard, link } = await createLinkCardTest({ openInNewTab: true }) expect(linkCard.openInNewTab).toBe(true) expect(link.getAttribute('target')).toBe('_blank') }) test('handles openInNewTab false correctly', async () => { const { linkCard, link } = await createLinkCardTest({ openInNewTab: false }) expect(linkCard.openInNewTab).toBe(false) expect(link.getAttribute('target')).toBe('_self') }) test('applies correct rel attribute for new tab', async () => { const { link } = await createLinkCardTest({ openInNewTab: true }) expect(link.getAttribute('rel')).toBe('noopener noreferrer') }) }) describe('Icon Functionality', () => { test('renders icon when iconName provided', async () => { const iconName = 'arrow-right' const { linkCard } = await createLinkCardTest({ iconName }) expect(linkCard.iconName).toBe(iconName) const icon = linkCard.querySelector('pkt-icon') expect(icon).toBeInTheDocument() expect(icon).toHaveClass('pkt-link__icon') expect(icon?.getAttribute('name')).toBe(iconName) }) test('does not render icon when iconName not provided', async () => { const { linkCard } = await createLinkCardTest() const icon = linkCard.querySelector('pkt-icon') expect(icon).not.toBeInTheDocument() }) test('renders with different icon names', async () => { for (const iconName of SAMPLE_ICONS) { const { linkCard } = await createLinkCardTest({ iconName }) const icon = linkCard.querySelector('pkt-icon') expect(icon?.getAttribute('name')).toBe(iconName) } }) }) describe('Skin Variations', () => { test('applies default skin class', async () => { const { link } = await createLinkCardTest() expect(link).toHaveClass('pkt-linkcard') expect(link).toHaveClass('pkt-linkcard--normal') }) test('applies different skin classes correctly', async () => { for (const skin of VALID_SKINS) { const { link } = await createLinkCardTest({ skin }) expect(link).toHaveClass('pkt-linkcard') expect(link).toHaveClass(`pkt-linkcard--${skin}`) } }) test('handles skin property changes', async () => { const { linkCard, link } = await createLinkCardTest({ skin: 'blue' }) expect(link).toHaveClass('pkt-linkcard--blue') // Change skin linkCard.skin = 'green' await linkCard.updateComplete expect(link).toHaveClass('pkt-linkcard--green') expect(link).not.toHaveClass('pkt-linkcard--blue') }) }) describe('Property Updates', () => { test('updates title dynamically', async () => { const { linkCard } = await createLinkCardTest({ title: 'Initial Title' }) expect(linkCard.querySelector('.pkt-linkcard__title')?.textContent).toBe('Initial Title') linkCard.title = 'Updated Title' await linkCard.updateComplete expect(linkCard.querySelector('.pkt-linkcard__title')?.textContent).toBe('Updated Title') }) test('updates href dynamically', async () => { const { linkCard, link } = await createLinkCardTest({ href: '/initial' }) expect(link.getAttribute('href')).toBe('/initial') linkCard.href = '/updated' await linkCard.updateComplete expect(link.getAttribute('href')).toBe('/updated') }) test('updates openInNewTab dynamically', async () => { const { linkCard, link } = await createLinkCardTest({ openInNewTab: false }) expect(link.getAttribute('target')).toBe('_self') linkCard.openInNewTab = true await linkCard.updateComplete expect(link.getAttribute('target')).toBe('_blank') }) }) describe('Complex Configurations', () => { test('renders with all properties set', async () => { const config: LinkCardTestConfig = { title: 'Complete Link Card', href: '/complete-path', iconName: 'arrow-right', openInNewTab: true, skin: 'blue', content: '<p>Complete content</p>', } const { linkCard, link } = await createLinkCardTest(config) // Verify all properties expect(linkCard.title).toBe(config.title) expect(linkCard.href).toBe(config.href) expect(linkCard.iconName).toBe(config.iconName) expect(linkCard.openInNewTab).toBe(config.openInNewTab) expect(linkCard.skin).toBe(config.skin) // Verify DOM elements expect(link.getAttribute('href')).toBe(config.href) expect(link.getAttribute('target')).toBe('_blank') expect(link).toHaveClass(`pkt-linkcard--${config.skin}`) const titleElement = linkCard.querySelector('.pkt-linkcard__title') expect(titleElement?.textContent).toBe(config.title) const icon = linkCard.querySelector('pkt-icon') expect(icon?.getAttribute('name')).toBe(config.iconName) const textSlot = linkCard.querySelector('.pkt-linkcard__text') expect(textSlot?.innerHTML).toContain(config.content) }) test('renders minimal configuration', async () => { const { linkCard, link } = await createLinkCardTest({ href: '/minimal' }) expect(linkCard.href).toBe('/minimal') expect(link.getAttribute('href')).toBe('/minimal') expect(link).toHaveClass('pkt-linkcard--normal') // Should not have title or icon expect(linkCard.querySelector('.pkt-linkcard__title')).not.toBeInTheDocument() expect(linkCard.querySelector('pkt-icon')).not.toBeInTheDocument() }) }) describe('Edge Cases', () => { test('handles empty string values', async () => { const { linkCard } = await createLinkCardTest({ title: '', href: '', iconName: '', }) expect(linkCard.title).toBe('') expect(linkCard.href).toBe('') expect(linkCard.iconName).toBe('') // Should not render title or icon with empty strings expect(linkCard.querySelector('.pkt-linkcard__title')).not.toBeInTheDocument() expect(linkCard.querySelector('pkt-icon')).not.toBeInTheDocument() }) test('handles special characters in title', async () => { const specialTitle = 'Title with "quotes" & <symbols>' const { linkCard } = await createLinkCardTest() // Set title via property to avoid HTML attribute encoding issues linkCard.title = specialTitle await linkCard.updateComplete expect(linkCard.title).toBe(specialTitle) const titleElement = linkCard.querySelector('.pkt-linkcard__title') expect(titleElement).toBeInTheDocument() expect(titleElement?.textContent).toBe(specialTitle) }) }) describe('Accessibility', () => { test('has no accessibility violations', async () => { const { linkCard } = await createLinkCardTest({ title: 'Accessible Link Card', href: '/accessible', }) const results = await axe(linkCard) expect(results).toHaveNoViolations() }) test('maintains proper link semantics', async () => { const { link } = await createLinkCardTest({ title: 'Semantic Link', href: '/semantic', }) expect(link.tagName).toBe('A') expect(link.getAttribute('href')).toBe('/semantic') expect(link.textContent).toContain('Semantic Link') }) test('handles external link accessibility', async () => { const { link } = await createLinkCardTest({ title: 'External Link', href: 'https://example.com', openInNewTab: true, }) expect(link.getAttribute('target')).toBe('_blank') expect(link.getAttribute('rel')).toBe('noopener noreferrer') }) }) describe('Type Safety', () => { test('validates interface implementation', () => { const linkCard = new PktLinkCard() // Check that all interface properties exist expect(linkCard).toHaveProperty('title') expect(linkCard).toHaveProperty('href') expect(linkCard).toHaveProperty('iconName') expect(linkCard).toHaveProperty('openInNewTab') expect(linkCard).toHaveProperty('skin') }) }) })