Results.utils.test.ts201 lines · main
| 1 | import { describe, expect, it } from 'vitest' |
| 2 | |
| 3 | import { |
| 4 | convertResultsToCSV, |
| 5 | convertResultsToJSON, |
| 6 | convertResultsToMarkdown, |
| 7 | formatResults, |
| 8 | getResultsHeaders, |
| 9 | isLargeValue, |
| 10 | } from './Results.utils' |
| 11 | |
| 12 | describe('Results.utils', () => { |
| 13 | describe('formatResults', () => { |
| 14 | it('should stringify object values', () => { |
| 15 | const results = [{ id: 1, data: { nested: true } }] |
| 16 | const formatted = formatResults(results) |
| 17 | expect(formatted).toEqual([{ id: 1, data: '{"nested":true}' }]) |
| 18 | }) |
| 19 | |
| 20 | it('should stringify array values', () => { |
| 21 | const results = [{ id: 1, tags: ['a', 'b'] }] |
| 22 | const formatted = formatResults(results) |
| 23 | expect(formatted).toEqual([{ id: 1, tags: '["a","b"]' }]) |
| 24 | }) |
| 25 | |
| 26 | it('should stringify null values', () => { |
| 27 | const results = [{ id: 1, value: null }] |
| 28 | const formatted = formatResults(results) |
| 29 | expect(formatted).toEqual([{ id: 1, value: 'null' }]) |
| 30 | }) |
| 31 | |
| 32 | it('should leave primitive values unchanged', () => { |
| 33 | const results = [{ name: 'test', count: 42, active: true }] |
| 34 | const formatted = formatResults(results) |
| 35 | expect(formatted).toEqual([{ name: 'test', count: 42, active: true }]) |
| 36 | }) |
| 37 | |
| 38 | it('should return empty array for empty input', () => { |
| 39 | expect(formatResults([])).toEqual([]) |
| 40 | }) |
| 41 | }) |
| 42 | |
| 43 | describe('convertResultsToMarkdown', () => { |
| 44 | it('should return undefined for empty results', () => { |
| 45 | expect(convertResultsToMarkdown([])).toBeUndefined() |
| 46 | }) |
| 47 | |
| 48 | it('should convert results to a markdown table', () => { |
| 49 | const results = [ |
| 50 | { id: 1, name: 'Alice' }, |
| 51 | { id: 2, name: 'Bob' }, |
| 52 | ] |
| 53 | const md = convertResultsToMarkdown(results) |
| 54 | expect(md).toContain('| id | name |') |
| 55 | expect(md).toContain('| 1 | Alice |') |
| 56 | expect(md).toContain('| 2 | Bob |') |
| 57 | }) |
| 58 | |
| 59 | it('should stringify nested objects in markdown output', () => { |
| 60 | const results = [{ id: 1, meta: { role: 'admin' } }] |
| 61 | const md = convertResultsToMarkdown(results) |
| 62 | expect(md).toContain('{"role":"admin"}') |
| 63 | }) |
| 64 | }) |
| 65 | |
| 66 | describe('convertResultsToJSON', () => { |
| 67 | it('should return undefined for empty results', () => { |
| 68 | expect(convertResultsToJSON([])).toBeUndefined() |
| 69 | }) |
| 70 | |
| 71 | it('should return formatted JSON string', () => { |
| 72 | const results = [{ id: 1, name: 'Alice' }] |
| 73 | const json = convertResultsToJSON(results) |
| 74 | expect(json).toBe(JSON.stringify(results, null, 2)) |
| 75 | }) |
| 76 | |
| 77 | it('should preserve nested object structure', () => { |
| 78 | const results = [{ id: 1, meta: { role: 'admin' } }] |
| 79 | const json = convertResultsToJSON(results) |
| 80 | const parsed = JSON.parse(json!) |
| 81 | expect(parsed[0].meta.role).toBe('admin') |
| 82 | }) |
| 83 | }) |
| 84 | |
| 85 | describe('getResultsHeaders', () => { |
| 86 | it('should return undefined for empty results', () => { |
| 87 | expect(getResultsHeaders([])).toBeUndefined() |
| 88 | }) |
| 89 | |
| 90 | it('should return keys from the first row', () => { |
| 91 | const results = [{ id: 1, name: 'Alice' }] |
| 92 | expect(getResultsHeaders(results)).toEqual(['id', 'name']) |
| 93 | }) |
| 94 | |
| 95 | it('should preserve key order from the first row', () => { |
| 96 | const results = [{ z: 1, a: 2, m: 3 }] |
| 97 | expect(getResultsHeaders(results)).toEqual(['z', 'a', 'm']) |
| 98 | }) |
| 99 | |
| 100 | it('should use only the first row for headers', () => { |
| 101 | const results = [{ id: 1 }, { id: 2, extra: 'bonus' }] |
| 102 | expect(getResultsHeaders(results)).toEqual(['id']) |
| 103 | }) |
| 104 | }) |
| 105 | |
| 106 | describe('isLargeValue', () => { |
| 107 | it('returns false for null', () => { |
| 108 | expect(isLargeValue(null)).toBe(false) |
| 109 | }) |
| 110 | |
| 111 | it('returns false for undefined', () => { |
| 112 | expect(isLargeValue(undefined)).toBe(false) |
| 113 | }) |
| 114 | |
| 115 | it('returns false for an empty string', () => { |
| 116 | expect(isLargeValue('')).toBe(false) |
| 117 | }) |
| 118 | |
| 119 | it('returns false for a short string under the threshold', () => { |
| 120 | expect(isLargeValue('hello')).toBe(false) |
| 121 | }) |
| 122 | |
| 123 | it('returns false for a string at the 60-char boundary', () => { |
| 124 | expect(isLargeValue('a'.repeat(60))).toBe(false) |
| 125 | }) |
| 126 | |
| 127 | it('returns true for a string just over the 60-char threshold', () => { |
| 128 | expect(isLargeValue('a'.repeat(61))).toBe(true) |
| 129 | }) |
| 130 | |
| 131 | it('returns true for a short string containing a newline', () => { |
| 132 | expect(isLargeValue('hello\nworld')).toBe(true) |
| 133 | }) |
| 134 | |
| 135 | it('returns true for an object', () => { |
| 136 | expect(isLargeValue({ a: 1 })).toBe(true) |
| 137 | }) |
| 138 | |
| 139 | it('returns true for an array', () => { |
| 140 | expect(isLargeValue([1, 2, 3])).toBe(true) |
| 141 | }) |
| 142 | |
| 143 | it('returns false for a number', () => { |
| 144 | expect(isLargeValue(42)).toBe(false) |
| 145 | }) |
| 146 | |
| 147 | it('returns false for a boolean', () => { |
| 148 | expect(isLargeValue(true)).toBe(false) |
| 149 | }) |
| 150 | }) |
| 151 | |
| 152 | describe('convertResultsToCSV', () => { |
| 153 | it('should return undefined for empty results', () => { |
| 154 | expect(convertResultsToCSV([])).toBeUndefined() |
| 155 | }) |
| 156 | |
| 157 | it('should include header row from first result keys', () => { |
| 158 | const results = [{ id: 1, name: 'Alice' }] |
| 159 | const csv = convertResultsToCSV(results) |
| 160 | expect(csv).toContain('id,name') |
| 161 | }) |
| 162 | |
| 163 | it('should include data rows', () => { |
| 164 | const results = [ |
| 165 | { id: 1, name: 'Alice' }, |
| 166 | { id: 2, name: 'Bob' }, |
| 167 | ] |
| 168 | const csv = convertResultsToCSV(results) |
| 169 | expect(csv).toContain('Alice') |
| 170 | expect(csv).toContain('Bob') |
| 171 | }) |
| 172 | |
| 173 | it('should stringify object values', () => { |
| 174 | const results = [{ id: 1, meta: { role: 'admin' } }] |
| 175 | const csv = convertResultsToCSV(results) |
| 176 | // CSV wraps values containing special chars in quotes and escapes inner quotes |
| 177 | expect(csv).toContain('"{""role"":""admin""}"') |
| 178 | }) |
| 179 | |
| 180 | it('should stringify array values', () => { |
| 181 | const results = [{ id: 1, tags: ['a', 'b'] }] |
| 182 | const csv = convertResultsToCSV(results) |
| 183 | // CSV wraps values containing special chars in quotes and escapes inner quotes |
| 184 | expect(csv).toContain('"[""a"",""b""]"') |
| 185 | }) |
| 186 | |
| 187 | it('should stringify null values', () => { |
| 188 | const results = [{ id: 1, value: null }] |
| 189 | const csv = convertResultsToCSV(results) |
| 190 | expect(csv).toContain('null') |
| 191 | }) |
| 192 | |
| 193 | it('should preserve column order from first row', () => { |
| 194 | const results = [{ z: 1, a: 2, m: 3 }] |
| 195 | const csv = convertResultsToCSV(results) |
| 196 | // split on CRLF line endings produced by papaparse |
| 197 | const headerRow = csv!.split('\r\n')[0] |
| 198 | expect(headerRow).toBe('z,a,m') |
| 199 | }) |
| 200 | }) |
| 201 | }) |