Results.utils.test.ts201 lines · main
1import { describe, expect, it } from 'vitest'
2
3import {
4 convertResultsToCSV,
5 convertResultsToJSON,
6 convertResultsToMarkdown,
7 formatResults,
8 getResultsHeaders,
9 isLargeValue,
10} from './Results.utils'
11
12describe('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})