94 lines
3.5 KiB
JavaScript
94 lines
3.5 KiB
JavaScript
// The arithmetic in codes.js decides whether a printed label scans, and it is
|
|
// the part no amount of looking at a screen will verify - a QR that is fine at
|
|
// 96 dpi on a monitor can be unreadable at 203 dpi on 0.5in stock.
|
|
|
|
import { describe, it, expect } from 'vitest'
|
|
|
|
import {
|
|
LABEL_PRESETS, BARCODE_DEFAULTS, qrModuleCount, qrFit, svgDataUri,
|
|
} from './codes'
|
|
|
|
describe('qrModuleCount', () => {
|
|
it('grows with the content', () => {
|
|
const short = qrModuleCount('AP-01')
|
|
const long = qrModuleCount('AP-01/'.repeat(20))
|
|
expect(short).toBeGreaterThan(0)
|
|
expect(long).toBeGreaterThan(short)
|
|
})
|
|
|
|
it('grows with error correction, because the redundancy is stored in modules', () => {
|
|
expect(qrModuleCount('WKSTN0042', 'H')).toBeGreaterThanOrEqual(
|
|
qrModuleCount('WKSTN0042', 'L'))
|
|
})
|
|
|
|
it('returns 0 rather than throwing on empty content', () => {
|
|
expect(qrModuleCount('')).toBe(0)
|
|
expect(qrModuleCount(null)).toBe(0)
|
|
})
|
|
})
|
|
|
|
describe('qrFit', () => {
|
|
it('calls a code too small when a module gets under two printer dots', () => {
|
|
// A long payload squeezed onto the 1.00 x 0.50in stock: the case the Tech
|
|
// Tools generator exists to warn about before 300 labels are printed.
|
|
const fit = qrFit('https://shopdb.example.net/assets/1234?from=label&sig=abcdef123456', {
|
|
sizeInches: 0.4286, dpi: 203, errorCorrection: 'H',
|
|
})
|
|
expect(fit.tooSmall).toBe(true)
|
|
expect(fit.dotsPerModule).toBeLessThan(2)
|
|
})
|
|
|
|
it('reports a whole number of dots per module as even', () => {
|
|
const modules = qrModuleCount('WKSTN0042', 'M')
|
|
// Choose the size that divides exactly at 4 dots per module.
|
|
const fit = qrFit('WKSTN0042', {
|
|
sizeInches: (modules * 4) / 203, dpi: 203, errorCorrection: 'M',
|
|
})
|
|
expect(fit.wholeDots).toBe(4)
|
|
expect(fit.even).toBe(true)
|
|
expect(fit.snapped).toBeCloseTo(fit.snapped, 5)
|
|
})
|
|
|
|
it('offers the nearest evenly-dividing size when the chosen one is uneven', () => {
|
|
const fit = qrFit('WKSTN0042', { sizeInches: 0.9, dpi: 203, errorCorrection: 'M' })
|
|
expect(fit.even).toBe(false)
|
|
expect(fit.snapped).toBeLessThanOrEqual(0.9)
|
|
expect(fit.snapped * 203 / fit.modules).toBeCloseTo(fit.wholeDots, 6)
|
|
})
|
|
|
|
it('is null when there is nothing to measure', () => {
|
|
expect(qrFit('', { sizeInches: 1 })).toBeNull()
|
|
expect(qrFit('WKSTN0042', {})).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('shared defaults', () => {
|
|
it('barcodes are CODE128 with no margin and no printed text', () => {
|
|
// The label draws its own caption, and the quiet zone is blank label. Both
|
|
// had drifted per-view before this module existed.
|
|
expect(BARCODE_DEFAULTS.format).toBe('CODE128')
|
|
expect(BARCODE_DEFAULTS.margin).toBe(0)
|
|
expect(BARCODE_DEFAULTS.displayValue).toBe(false)
|
|
})
|
|
|
|
it('every label preset is a complete, usable stock definition', () => {
|
|
expect(LABEL_PRESETS.length).toBeGreaterThan(0)
|
|
for (const preset of LABEL_PRESETS) {
|
|
expect(preset.id).toBeTruthy()
|
|
expect(preset.labelwidth).toBeGreaterThan(0)
|
|
expect(preset.labelheight).toBeGreaterThan(0)
|
|
// A code has to fit inside its label with its quiet zone on both sides.
|
|
expect(preset.codesize + 2 * preset.quiet).toBeLessThanOrEqual(preset.labelwidth)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('svgDataUri', () => {
|
|
it('escapes markup so the URI survives being put in a src attribute', () => {
|
|
const uri = svgDataUri('<svg><rect fill="#000"/></svg>')
|
|
expect(uri.startsWith('data:image/svg+xml;charset=utf-8,')).toBe(true)
|
|
expect(uri).not.toContain('<')
|
|
expect(uri).not.toContain('"')
|
|
})
|
|
})
|