import { MojComponentTestHelper } from '../../test-utils/MojComponentTestHelper'
import { setupComponentTest } from '../../test-utils/setupComponentTest'
import { MOJSortableTable } from './mojSortableTable'
vi.mock('nunjucks')
describe('mojSortableTable', () => {
setupComponentTest()
const helper = new MojComponentTestHelper(MOJSortableTable)
describe('Rows transformation', () => {
it('should pass through rows unchanged', async () => {
// Arrange
const rows = [
[{ text: 'John Smith' }, { text: '2024-01-15' }, { text: 'Active' }],
[{ text: 'Jane Doe' }, { text: '2024-02-20' }, { text: 'Pending' }],
]
// Act
const params = await helper.getParams({ rows })
// Assert
expect(params.rows).toEqual(rows)
})
it('should pass through rows with HTML cells', async () => {
// Arrange
const rows = [
[{ html: 'John Smith' }, { text: '2024-01-15' }],
[{ html: 'Jane Doe' }, { text: '2024-02-20' }],
]
// Act
const params = await helper.getParams({ rows })
// Assert
expect(params.rows).toEqual(rows)
})
it('should pass through rows with cell attributes', async () => {
// Arrange
const rows = [
[
{ text: 'John Smith', attributes: { 'data-id': 'user-1' } },
{ text: '2024-01-15', format: 'numeric' },
],
]
// Act
const params = await helper.getParams({ rows })
// Assert
expect(params.rows).toEqual(rows)
})
it('should pass through rows with colspan and rowspan', async () => {
// Arrange
const rows = [[{ text: 'Spanning cell', colspan: 3, rowspan: 2 }]]
// Act
const params = await helper.getParams({ rows })
// Assert
expect(params.rows).toEqual(rows)
})
})
describe('Head transformation', () => {
it('should pass through head cells unchanged', async () => {
// Arrange
const head = [{ text: 'Name' }, { text: 'Date' }, { text: 'Status' }]
// Act
const params = await helper.getParams({
rows: [],
head,
})
// Assert
expect(params.head).toEqual(head)
})
it('should pass through head cells with HTML buttons for sorting', async () => {
// Arrange
const head = [
{ html: '' },
{ html: '' },
{ html: '' },
]
// Act
const params = await helper.getParams({
rows: [],
head,
})
// Assert
expect(params.head).toEqual(head)
})
it('should pass through head cells with format and classes', async () => {
// Arrange
const head = [
{ text: 'Name', classes: 'govuk-table__header--custom' },
{ text: 'Amount', format: 'numeric' },
]
// Act
const params = await helper.getParams({
rows: [],
head,
})
// Assert
expect(params.head).toEqual(head)
})
it('should pass through head cells with colspan and rowspan', async () => {
// Arrange
const head = [{ text: 'Combined Header', colspan: 2 }]
// Act
const params = await helper.getParams({
rows: [],
head,
})
// Assert
expect(params.head).toEqual(head)
})
it('should leave head undefined when not provided', async () => {
// Arrange & Act
const params = await helper.getParams({
rows: [[{ text: 'Data' }]],
})
// Assert
expect(params.head).toBeUndefined()
})
})
describe('Caption transformation', () => {
it('should pass through caption text', async () => {
// Arrange & Act
const params = await helper.getParams({
rows: [],
caption: 'User List',
})
// Assert
expect(params.caption).toBe('User List')
})
it('should pass through captionClasses', async () => {
// Arrange & Act
const params = await helper.getParams({
rows: [],
caption: 'User List',
captionClasses: 'govuk-table__caption--l',
})
// Assert
expect(params.captionClasses).toBe('govuk-table__caption--l')
})
it('should leave caption undefined when not provided', async () => {
// Arrange & Act
const params = await helper.getParams({
rows: [[{ text: 'Data' }]],
})
// Assert
expect(params.caption).toBeUndefined()
})
it('should leave captionClasses undefined when not provided', async () => {
// Arrange & Act
const params = await helper.getParams({
rows: [[{ text: 'Data' }]],
caption: 'User List',
})
// Assert
expect(params.captionClasses).toBeUndefined()
})
})
describe('firstCellIsHeader transformation', () => {
it('should pass through firstCellIsHeader when true', async () => {
// Arrange & Act
const params = await helper.getParams({
rows: [],
firstCellIsHeader: true,
})
// Assert
expect(params.firstCellIsHeader).toBe(true)
})
it('should pass through firstCellIsHeader when false', async () => {
// Arrange & Act
const params = await helper.getParams({
rows: [],
firstCellIsHeader: false,
})
// Assert
expect(params.firstCellIsHeader).toBe(false)
})
it('should leave firstCellIsHeader undefined when not provided', async () => {
// Arrange & Act
const params = await helper.getParams({
rows: [[{ text: 'Data' }]],
})
// Assert
expect(params.firstCellIsHeader).toBeUndefined()
})
})
describe('Data module attribute', () => {
it('should add data-module attribute with moj-sortable-table value', async () => {
// Arrange & Act
const params = await helper.getParams({
rows: [[{ text: 'Data' }]],
})
// Assert
expect(params.attributes).toHaveProperty('data-module', 'moj-sortable-table')
})
it('should merge user attributes with data-module attribute', async () => {
// Arrange
const attributes = {
'data-testid': 'my-table',
'data-tracking': 'table-viewed',
}
// Act
const params = await helper.getParams({
rows: [[{ text: 'Data' }]],
attributes,
})
// Assert
expect(params.attributes).toEqual({
'data-testid': 'my-table',
'data-tracking': 'table-viewed',
'data-module': 'moj-sortable-table',
})
})
it('should override user-provided data-module attribute', async () => {
// Arrange
const attributes = {
'data-module': 'custom-module',
'data-other': 'value',
}
// Act
const params = await helper.getParams({
rows: [[{ text: 'Data' }]],
attributes,
})
// Assert
expect(params.attributes).toEqual({
'data-other': 'value',
'data-module': 'moj-sortable-table',
})
})
})
describe('Optional attributes', () => {
it('should pass through classes', async () => {
// Arrange & Act
const params = await helper.getParams({
rows: [],
classes: 'custom-table-class',
})
// Assert
expect(params.classes).toBe('custom-table-class')
})
it('should leave classes undefined when not provided', async () => {
// Arrange & Act
const params = await helper.getParams({
rows: [[{ text: 'Data' }]],
})
// Assert
expect(params.classes).toBeUndefined()
})
it('should pass through attributes when provided', async () => {
// Arrange
const attributes = {
'data-testid': 'sortable-table',
'aria-label': 'Sortable user list',
}
// Act
const params = await helper.getParams({
rows: [],
attributes,
})
// Assert
expect(params.attributes).toMatchObject(attributes)
})
})
describe('Template and context', () => {
it('should use GOV.UK table template', async () => {
// Arrange & Act
const { template } = await helper.executeComponent({
rows: [[{ text: 'Data' }]],
})
// Assert
expect(template).toBe('govuk/components/table/template.njk')
})
it('should wrap params in context object', async () => {
// Arrange & Act
const { context } = await helper.executeComponent({
rows: [[{ text: 'Data' }]],
})
// Assert
expect(context).toHaveProperty('params')
expect((context as { params: Record }).params).toHaveProperty('rows')
})
it('should include all params in context', async () => {
// Arrange
const head = [{ html: '' }, { html: '' }]
const rows = [
[{ text: 'John Smith' }, { text: 'Active' }],
[{ text: 'Jane Doe' }, { text: 'Pending' }],
]
// Act
const { context } = await helper.executeComponent({
head,
rows,
caption: 'User List',
captionClasses: 'govuk-table__caption--l',
firstCellIsHeader: true,
classes: 'custom-table',
attributes: { 'data-test': 'value' },
})
// Assert
const params = (context as { params: Record }).params
expect(params).toHaveProperty('head', head)
expect(params).toHaveProperty('rows', rows)
expect(params).toHaveProperty('caption', 'User List')
expect(params).toHaveProperty('captionClasses', 'govuk-table__caption--l')
expect(params).toHaveProperty('firstCellIsHeader', true)
expect(params).toHaveProperty('classes', 'custom-table')
expect(params).toHaveProperty('attributes')
expect(params.attributes).toHaveProperty('data-module', 'moj-sortable-table')
})
})
})