File size: 3,391 Bytes
766d85d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import { describe, it, expect } from 'vitest'
import { katColor, itemWeight, parseCsvLine, parseImportLines } from './packingListPanel.helpers'
import { KAT_COLORS } from './packingListPanel.constants'

describe('packingListPanel.helpers', () => {
  describe('katColor', () => {
    it('maps a category to its palette slot by index', () => {
      const cats = ['Documents', 'Clothing', 'Toiletries']
      expect(katColor('Documents', cats)).toBe(KAT_COLORS[0])
      expect(katColor('Clothing', cats)).toBe(KAT_COLORS[1])
      expect(katColor('Toiletries', cats)).toBe(KAT_COLORS[2])
    })

    it('cycles the palette when the index exceeds palette length', () => {
      const cats = Array.from({ length: KAT_COLORS.length + 1 }, (_, i) => `cat${i}`)
      expect(katColor(`cat${KAT_COLORS.length}`, cats)).toBe(KAT_COLORS[0])
    })

    it('falls back to a deterministic hash when the category is not in the list', () => {
      const a = katColor('Missing', ['Other'])
      const b = katColor('Missing', ['Other'])
      expect(a).toBe(b)
      expect(KAT_COLORS).toContain(a)
    })

    it('falls back to hash when no category list is provided', () => {
      const color = katColor('Anything')
      expect(KAT_COLORS).toContain(color)
    })
  })

  describe('itemWeight', () => {
    it('multiplies unit weight by quantity', () => {
      expect(itemWeight({ weight_grams: 250, quantity: 3 })).toBe(750)
    })

    it('defaults quantity to 1 and weight to 0', () => {
      expect(itemWeight({ weight_grams: 120 })).toBe(120)
      expect(itemWeight({ quantity: 5 })).toBe(0)
      expect(itemWeight({})).toBe(0)
    })

    it('treats null weight/quantity as their defaults', () => {
      expect(itemWeight({ weight_grams: null, quantity: null })).toBe(0)
      expect(itemWeight({ weight_grams: 100, quantity: null })).toBe(100)
    })
  })

  describe('parseCsvLine', () => {
    it('splits on comma, semicolon and tab and trims fields', () => {
      expect(parseCsvLine('a, b ;c\td')).toEqual(['a', 'b', 'c', 'd'])
    })

    it('keeps quoted separators inside one field', () => {
      expect(parseCsvLine('Clothing,"Shirt, blue",200')).toEqual(['Clothing', 'Shirt, blue', '200'])
    })

    it('returns the single field for a line without separators', () => {
      expect(parseCsvLine('Passport')).toEqual(['Passport'])
    })
  })

  describe('parseImportLines', () => {
    it('parses a full row into name/category/weight/bag/checked', () => {
      const [row] = parseImportLines('Documents, Passport, 50, Backpack, checked')
      expect(row).toEqual({ name: 'Passport', category: 'Documents', weight_grams: '50', bag: 'Backpack', checked: true })
    })

    it('treats "1" as checked and anything else as unchecked', () => {
      expect(parseImportLines('Cat, A, , , 1')[0].checked).toBe(true)
      expect(parseImportLines('Cat, B, , , nope')[0].checked).toBe(false)
    })

    it('treats a single value as just a name with no category', () => {
      const [row] = parseImportLines('Sunglasses')
      expect(row).toEqual({ name: 'Sunglasses', category: undefined, weight_grams: undefined, bag: undefined, checked: false })
    })

    it('skips blank lines and rows without a name', () => {
      const rows = parseImportLines('Documents, Passport\n\n   \n,')
      expect(rows).toHaveLength(1)
      expect(rows[0].name).toBe('Passport')
    })
  })
})