Skip to content

Commit cdb9058

Browse files
test(cli): add tests for invalid values
1 parent af29853 commit cdb9058

File tree

2 files changed

+132
-84
lines changed

2 files changed

+132
-84
lines changed

packages/cli/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import * as path from 'node:path'
55
import process from 'node:process'
66
import { defineCommand } from 'citty'
77
import { consola } from 'consola'
8-
import { name, version } from '../../toon/package.json' with { type: 'json' }
98
import { DEFAULT_DELIMITER, DELIMITERS } from '../../toon/src'
9+
import { name, version } from '../package.json' with { type: 'json' }
1010
import { decodeToJson, encodeToToon } from './conversion'
1111
import { detectMode } from './utils'
1212

packages/cli/test/index.test.ts

Lines changed: 131 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import process from 'node:process'
22
import { consola } from 'consola'
33
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
4-
import { version } from '../../toon/package.json' with { type: 'json' }
54
import { DEFAULT_DELIMITER, encode } from '../../toon/src'
5+
import { version } from '../package.json' with { type: 'json' }
66
import { createCliTestContext, runCli } from './utils'
77

88
describe('toon CLI', () => {
@@ -14,113 +14,161 @@ describe('toon CLI', () => {
1414
vi.restoreAllMocks()
1515
})
1616

17-
it('prints the version when using --version', async () => {
18-
const consolaLog = vi.spyOn(consola, 'log').mockImplementation(() => undefined)
19-
const consolaError = vi.spyOn(consola, 'error').mockImplementation(() => undefined)
17+
describe('version', () => {
18+
it('prints the version when using --version', async () => {
19+
const consolaLog = vi.spyOn(consola, 'log').mockImplementation(() => undefined)
2020

21-
await runCli({ rawArgs: ['--version'] })
21+
await runCli({ rawArgs: ['--version'] })
2222

23-
expect(consolaLog).toHaveBeenCalledWith(version)
24-
expect(consolaError).not.toHaveBeenCalled()
23+
expect(consolaLog).toHaveBeenCalledWith(version)
24+
})
2525
})
2626

27-
it('encodes a JSON file into a TOON file', async () => {
28-
const data = {
29-
title: 'TOON test',
30-
count: 3,
31-
nested: { ok: true },
32-
}
33-
const context = await createCliTestContext({
34-
'input.json': JSON.stringify(data, undefined, 2),
35-
})
27+
describe('encode (JSON → TOON)', () => {
28+
it('encodes a JSON file into a TOON file', async () => {
29+
const data = {
30+
title: 'TOON test',
31+
count: 3,
32+
nested: { ok: true },
33+
}
34+
const context = await createCliTestContext({
35+
'input.json': JSON.stringify(data, undefined, 2),
36+
})
37+
38+
const consolaSuccess = vi.spyOn(consola, 'success').mockImplementation(() => undefined)
39+
40+
try {
41+
await context.run(['input.json', '--output', 'output.toon'])
42+
43+
const output = await context.read('output.toon')
44+
const expected = encode(data, {
45+
delimiter: DEFAULT_DELIMITER,
46+
indent: 2,
47+
lengthMarker: false,
48+
})
3649

37-
const consolaSuccess = vi.spyOn(consola, 'success').mockImplementation(() => undefined)
50+
expect(output).toBe(expected)
51+
expect(consolaSuccess).toHaveBeenCalledWith(expect.stringMatching(/Encoded .* .*/))
52+
}
53+
finally {
54+
await context.cleanup()
55+
}
56+
})
3857

39-
try {
40-
await context.run(['input.json', '--output', 'output.toon'])
58+
it('writes to stdout when output not specified', async () => {
59+
const data = { ok: true }
60+
const context = await createCliTestContext({
61+
'input.json': JSON.stringify(data),
62+
})
4163

42-
const output = await context.read('output.toon')
43-
const expected = encode(data, {
44-
delimiter: DEFAULT_DELIMITER,
45-
indent: 2,
46-
lengthMarker: false,
64+
const stdout: string[] = []
65+
const logSpy = vi.spyOn(console, 'log').mockImplementation((message?: unknown) => {
66+
stdout.push(String(message ?? ''))
4767
})
4868

49-
expect(output).toBe(expected)
50-
expect(consolaSuccess).toHaveBeenCalledWith('Encoded `input.json` → `output.toon`')
51-
}
52-
finally {
53-
await context.cleanup()
54-
}
55-
})
69+
try {
70+
await context.run(['input.json'])
5671

57-
it('decodes a TOON file into a JSON file', async () => {
58-
const data = {
59-
items: ['alpha', 'beta'],
60-
meta: { done: false },
61-
}
62-
const toonInput = encode(data)
63-
const context = await createCliTestContext({
64-
'input.toon': toonInput,
72+
expect(stdout).toHaveLength(1)
73+
expect(stdout[0]).toBe(encode(data))
74+
}
75+
finally {
76+
logSpy.mockRestore()
77+
await context.cleanup()
78+
}
6579
})
80+
})
6681

67-
const consolaSuccess = vi.spyOn(consola, 'success').mockImplementation(() => undefined)
82+
describe('decode (TOON → JSON)', () => {
83+
it('decodes a TOON file into a JSON file', async () => {
84+
const data = {
85+
items: ['alpha', 'beta'],
86+
meta: { done: false },
87+
}
88+
const toonInput = encode(data)
89+
const context = await createCliTestContext({
90+
'input.toon': toonInput,
91+
})
6892

69-
try {
70-
await context.run(['input.toon', '--output', 'output.json'])
93+
const consolaSuccess = vi.spyOn(consola, 'success').mockImplementation(() => undefined)
7194

72-
const output = await context.read('output.json')
73-
expect(JSON.parse(output)).toEqual(data)
74-
expect(consolaSuccess).toHaveBeenCalledWith('Decoded `input.toon` → `output.json`')
75-
}
76-
finally {
77-
await context.cleanup()
78-
}
79-
})
95+
try {
96+
await context.run(['input.toon', '--output', 'output.json'])
8097

81-
it('writes encoded TOON to stdout when no output file is provided', async () => {
82-
const data = { ok: true }
83-
const context = await createCliTestContext({
84-
'input.json': JSON.stringify(data),
98+
const output = await context.read('output.json')
99+
expect(JSON.parse(output)).toEqual(data)
100+
expect(consolaSuccess).toHaveBeenCalledWith(expect.stringMatching(/Decoded .* .*/))
101+
}
102+
finally {
103+
await context.cleanup()
104+
}
85105
})
106+
})
107+
108+
describe('error handling', () => {
109+
it('rejects invalid delimiter', async () => {
110+
const context = await createCliTestContext({
111+
'input.json': JSON.stringify({ value: 1 }),
112+
})
113+
114+
const consolaError = vi.spyOn(consola, 'error').mockImplementation(() => undefined)
115+
const exitSpy = vi.mocked(process.exit)
116+
117+
try {
118+
await context.run(['input.json', '--delimiter', ';'])
119+
120+
expect(exitSpy).toHaveBeenCalledWith(1)
86121

87-
const stdout: string[] = []
88-
const logSpy = vi.spyOn(console, 'log').mockImplementation((message?: unknown) => {
89-
stdout.push(String(message ?? ''))
122+
const errorCall = consolaError.mock.calls.at(0)
123+
expect(errorCall).toBeDefined()
124+
const [error] = errorCall!
125+
expect(error).toBeInstanceOf(Error)
126+
expect(error.message).toContain('Invalid delimiter')
127+
}
128+
finally {
129+
await context.cleanup()
130+
}
90131
})
91132

92-
try {
93-
await context.run(['input.json'])
133+
it('rejects invalid indent value', async () => {
134+
const context = await createCliTestContext({
135+
'input.json': JSON.stringify({ value: 1 }),
136+
})
94137

95-
expect(stdout).toHaveLength(1)
96-
expect(stdout[0]).toBe(encode(data))
97-
}
98-
finally {
99-
logSpy.mockRestore()
100-
await context.cleanup()
101-
}
102-
})
138+
const consolaError = vi.spyOn(consola, 'error').mockImplementation(() => undefined)
139+
const exitSpy = vi.mocked(process.exit)
140+
141+
try {
142+
await context.run(['input.json', '--indent', 'abc'])
103143

104-
it('throws on an invalid delimiter argument', async () => {
105-
const context = await createCliTestContext({
106-
'input.json': JSON.stringify({ value: 1 }),
144+
expect(exitSpy).toHaveBeenCalledWith(1)
145+
146+
const errorCall = consolaError.mock.calls.at(0)
147+
expect(errorCall).toBeDefined()
148+
const [error] = errorCall!
149+
expect(error).toBeInstanceOf(Error)
150+
expect(error.message).toContain('Invalid indent value')
151+
}
152+
finally {
153+
await context.cleanup()
154+
}
107155
})
108156

109-
const consolaError = vi.spyOn(consola, 'error').mockImplementation(() => undefined)
157+
it('handles missing input file', async () => {
158+
const context = await createCliTestContext({})
110159

111-
try {
112-
await expect(context.run(['input.json', '--delimiter', ';'])).resolves.toBeUndefined()
160+
const consolaError = vi.spyOn(consola, 'error').mockImplementation(() => undefined)
161+
const exitSpy = vi.mocked(process.exit)
113162

114-
const exitMock = vi.mocked(process.exit)
115-
expect(exitMock).toHaveBeenCalledWith(1)
163+
try {
164+
await context.run(['nonexistent.json'])
116165

117-
const errorCall = consolaError.mock.calls.at(0)
118-
expect(errorCall).toBeDefined()
119-
const [error] = errorCall!
120-
expect(error.message).toContain('Invalid delimiter')
121-
}
122-
finally {
123-
await context.cleanup()
124-
}
166+
expect(exitSpy).toHaveBeenCalledWith(1)
167+
expect(consolaError).toHaveBeenCalled()
168+
}
169+
finally {
170+
await context.cleanup()
171+
}
172+
})
125173
})
126174
})

0 commit comments

Comments
 (0)