Files
shopdb-flask/plugins/tools/frontend/labelPages.spec.js

114 lines
4.6 KiB
JavaScript

// Page order is the one thing here that costs money to get wrong: a run of
// badge cards with the backs one card out looks perfect until someone reads
// one. These tests pin the order and the CSV contract.
import { describe, it, expect } from 'vitest'
import { parseCsv, splitCsvLine, buildPages, MAX_LABELS } from './labelPages'
const sides = pages => pages.map(page => page.side).join(' ')
const contents = pages => pages.map(page => page.content || '-').join(' ')
describe('splitCsvLine', () => {
it('keeps a comma that is inside quotes', () => {
expect(splitCsvLine('WJ-0001,"Bay 12, press",2')).toEqual(['WJ-0001', 'Bay 12, press', '2'])
})
it('unescapes a doubled quote', () => {
expect(splitCsvLine('x,"the ""big"" one"')).toEqual(['x', 'the "big" one'])
})
})
describe('parseCsv', () => {
it('reads a named header in any column order', () => {
const rows = parseCsv('copies,label,content\n3,Line 1,WJ-0001')
expect(rows).toEqual([
{ content: 'WJ-0001', label: 'Line 1', copies: 3, back: '', backlabel: '' },
])
})
it('falls back to positional columns with no header', () => {
const rows = parseCsv('WJ-0001,Line 1,2,WJ-0001-B,Reverse')
expect(rows[0]).toEqual({
content: 'WJ-0001', label: 'Line 1', copies: 2,
back: 'WJ-0001-B', backlabel: 'Reverse',
})
})
it('skips a row with no content rather than printing a blank sticker', () => {
expect(parseCsv('content,label\n,orphan\nWJ-0002,real')).toHaveLength(1)
})
it('defaults copies to 1 when it is missing or junk', () => {
expect(parseCsv('content,copies\nA,\nB,zero')[0].copies).toBe(1)
expect(parseCsv('content,copies\nA,\nB,zero')[1].copies).toBe(1)
})
})
describe('buildPages without a back side', () => {
it('expands copies and emits fronts only', () => {
const { pages, backs } = buildPages([{ content: 'A', label: 'a', copies: 3 }])
expect(sides(pages)).toBe('front front front')
expect(backs).toHaveLength(0)
})
it('caps cards, not pages, and says how many it dropped', () => {
const { fronts, dropped } = buildPages([{ content: 'A', copies: MAX_LABELS + 5 }])
expect(fronts).toHaveLength(MAX_LABELS)
expect(dropped).toBe(5)
})
})
describe('buildPages with a back side', () => {
const rows = [
{ content: 'A', label: 'a', copies: 2, back: 'A2', backlabel: 'a2' },
{ content: 'B', label: 'b', copies: 1, back: 'B2', backlabel: 'b2' },
]
it('interleaves front and back, which is what a duplex driver takes', () => {
const { pages } = buildPages(rows, { back: { enabled: true, source: 'column', order: 'interleave' } })
expect(sides(pages)).toBe('front back front back front back')
expect(contents(pages)).toBe('A A2 A A2 B B2')
})
it('groups every front then every back, for a flip-and-reload printer', () => {
const { pages } = buildPages(rows, { back: { enabled: true, source: 'column', order: 'grouped' } })
expect(sides(pages)).toBe('front front front back back back')
// Same card order in both halves, or the flipped stack pairs up wrong.
expect(contents(pages)).toBe('A A B A2 A2 B2')
})
it('still emits a back for a row that left the back column empty', () => {
const { pages } = buildPages(
[{ content: 'A', copies: 1 }, { content: 'B', copies: 1, back: 'B2' }],
{ back: { enabled: true, source: 'column', order: 'interleave' } })
// A page count that changes per row would shift every later back onto the
// wrong front.
expect(sides(pages)).toBe('front back front back')
expect(contents(pages)).toBe('A - B B2')
})
it('repeats the front payload when the back is the same code', () => {
const { backs } = buildPages(rows, { back: { enabled: true, source: 'code' } })
expect(contents(backs)).toBe('A A B')
})
it('puts the same fixed text on every back and no code', () => {
const { backs } = buildPages(rows, { back: { enabled: true, source: 'text', text: 'Property of GE' } })
expect(backs.every(page => page.content === '')).toBe(true)
expect(backs.every(page => page.label === 'Property of GE')).toBe(true)
})
it('leaves a blank back entirely empty but still counted', () => {
const { pages, backs } = buildPages(rows, { back: { enabled: true, source: 'blank' } })
expect(pages).toHaveLength(6)
expect(backs.every(page => !page.content && !page.label)).toBe(true)
})
it('falls back to the fixed text when a row has no back label', () => {
const { backs } = buildPages([{ content: 'A', copies: 1, back: 'A2' }],
{ back: { enabled: true, source: 'column', text: 'Return to IT' } })
expect(backs[0].label).toBe('Return to IT')
})
})