test(cli): add tests for invalid values

This commit is contained in:
Johann Schopplich
2025-11-04 10:17:20 +01:00
parent af298537a4
commit cdb90585fa
2 changed files with 140 additions and 92 deletions

View File

@@ -5,8 +5,8 @@ import * as path from 'node:path'
import process from 'node:process' import process from 'node:process'
import { defineCommand } from 'citty' import { defineCommand } from 'citty'
import { consola } from 'consola' import { consola } from 'consola'
import { name, version } from '../../toon/package.json' with { type: 'json' }
import { DEFAULT_DELIMITER, DELIMITERS } from '../../toon/src' import { DEFAULT_DELIMITER, DELIMITERS } from '../../toon/src'
import { name, version } from '../package.json' with { type: 'json' }
import { decodeToJson, encodeToToon } from './conversion' import { decodeToJson, encodeToToon } from './conversion'
import { detectMode } from './utils' import { detectMode } from './utils'

View File

@@ -1,8 +1,8 @@
import process from 'node:process' import process from 'node:process'
import { consola } from 'consola' import { consola } from 'consola'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { version } from '../../toon/package.json' with { type: 'json' }
import { DEFAULT_DELIMITER, encode } from '../../toon/src' import { DEFAULT_DELIMITER, encode } from '../../toon/src'
import { version } from '../package.json' with { type: 'json' }
import { createCliTestContext, runCli } from './utils' import { createCliTestContext, runCli } from './utils'
describe('toon CLI', () => { describe('toon CLI', () => {
@@ -14,113 +14,161 @@ describe('toon CLI', () => {
vi.restoreAllMocks() vi.restoreAllMocks()
}) })
it('prints the version when using --version', async () => { describe('version', () => {
const consolaLog = vi.spyOn(consola, 'log').mockImplementation(() => undefined) it('prints the version when using --version', async () => {
const consolaError = vi.spyOn(consola, 'error').mockImplementation(() => undefined) const consolaLog = vi.spyOn(consola, 'log').mockImplementation(() => undefined)
await runCli({ rawArgs: ['--version'] }) await runCli({ rawArgs: ['--version'] })
expect(consolaLog).toHaveBeenCalledWith(version) expect(consolaLog).toHaveBeenCalledWith(version)
expect(consolaError).not.toHaveBeenCalled() })
}) })
it('encodes a JSON file into a TOON file', async () => { describe('encode (JSON → TOON)', () => {
const data = { it('encodes a JSON file into a TOON file', async () => {
title: 'TOON test', const data = {
count: 3, title: 'TOON test',
nested: { ok: true }, count: 3,
} nested: { ok: true },
const context = await createCliTestContext({ }
'input.json': JSON.stringify(data, undefined, 2), const context = await createCliTestContext({
}) 'input.json': JSON.stringify(data, undefined, 2),
const consolaSuccess = vi.spyOn(consola, 'success').mockImplementation(() => undefined)
try {
await context.run(['input.json', '--output', 'output.toon'])
const output = await context.read('output.toon')
const expected = encode(data, {
delimiter: DEFAULT_DELIMITER,
indent: 2,
lengthMarker: false,
}) })
expect(output).toBe(expected) const consolaSuccess = vi.spyOn(consola, 'success').mockImplementation(() => undefined)
expect(consolaSuccess).toHaveBeenCalledWith('Encoded `input.json` → `output.toon`')
} try {
finally { await context.run(['input.json', '--output', 'output.toon'])
await context.cleanup()
} const output = await context.read('output.toon')
const expected = encode(data, {
delimiter: DEFAULT_DELIMITER,
indent: 2,
lengthMarker: false,
})
expect(output).toBe(expected)
expect(consolaSuccess).toHaveBeenCalledWith(expect.stringMatching(/Encoded .* → .*/))
}
finally {
await context.cleanup()
}
})
it('writes to stdout when output not specified', async () => {
const data = { ok: true }
const context = await createCliTestContext({
'input.json': JSON.stringify(data),
})
const stdout: string[] = []
const logSpy = vi.spyOn(console, 'log').mockImplementation((message?: unknown) => {
stdout.push(String(message ?? ''))
})
try {
await context.run(['input.json'])
expect(stdout).toHaveLength(1)
expect(stdout[0]).toBe(encode(data))
}
finally {
logSpy.mockRestore()
await context.cleanup()
}
})
}) })
it('decodes a TOON file into a JSON file', async () => { describe('decode (TOON → JSON)', () => {
const data = { it('decodes a TOON file into a JSON file', async () => {
items: ['alpha', 'beta'], const data = {
meta: { done: false }, items: ['alpha', 'beta'],
} meta: { done: false },
const toonInput = encode(data) }
const context = await createCliTestContext({ const toonInput = encode(data)
'input.toon': toonInput, const context = await createCliTestContext({
'input.toon': toonInput,
})
const consolaSuccess = vi.spyOn(consola, 'success').mockImplementation(() => undefined)
try {
await context.run(['input.toon', '--output', 'output.json'])
const output = await context.read('output.json')
expect(JSON.parse(output)).toEqual(data)
expect(consolaSuccess).toHaveBeenCalledWith(expect.stringMatching(/Decoded .* → .*/))
}
finally {
await context.cleanup()
}
}) })
const consolaSuccess = vi.spyOn(consola, 'success').mockImplementation(() => undefined)
try {
await context.run(['input.toon', '--output', 'output.json'])
const output = await context.read('output.json')
expect(JSON.parse(output)).toEqual(data)
expect(consolaSuccess).toHaveBeenCalledWith('Decoded `input.toon` → `output.json`')
}
finally {
await context.cleanup()
}
}) })
it('writes encoded TOON to stdout when no output file is provided', async () => { describe('error handling', () => {
const data = { ok: true } it('rejects invalid delimiter', async () => {
const context = await createCliTestContext({ const context = await createCliTestContext({
'input.json': JSON.stringify(data), 'input.json': JSON.stringify({ value: 1 }),
})
const consolaError = vi.spyOn(consola, 'error').mockImplementation(() => undefined)
const exitSpy = vi.mocked(process.exit)
try {
await context.run(['input.json', '--delimiter', ';'])
expect(exitSpy).toHaveBeenCalledWith(1)
const errorCall = consolaError.mock.calls.at(0)
expect(errorCall).toBeDefined()
const [error] = errorCall!
expect(error).toBeInstanceOf(Error)
expect(error.message).toContain('Invalid delimiter')
}
finally {
await context.cleanup()
}
}) })
const stdout: string[] = [] it('rejects invalid indent value', async () => {
const logSpy = vi.spyOn(console, 'log').mockImplementation((message?: unknown) => { const context = await createCliTestContext({
stdout.push(String(message ?? '')) 'input.json': JSON.stringify({ value: 1 }),
})
const consolaError = vi.spyOn(consola, 'error').mockImplementation(() => undefined)
const exitSpy = vi.mocked(process.exit)
try {
await context.run(['input.json', '--indent', 'abc'])
expect(exitSpy).toHaveBeenCalledWith(1)
const errorCall = consolaError.mock.calls.at(0)
expect(errorCall).toBeDefined()
const [error] = errorCall!
expect(error).toBeInstanceOf(Error)
expect(error.message).toContain('Invalid indent value')
}
finally {
await context.cleanup()
}
}) })
try { it('handles missing input file', async () => {
await context.run(['input.json']) const context = await createCliTestContext({})
expect(stdout).toHaveLength(1) const consolaError = vi.spyOn(consola, 'error').mockImplementation(() => undefined)
expect(stdout[0]).toBe(encode(data)) const exitSpy = vi.mocked(process.exit)
}
finally {
logSpy.mockRestore()
await context.cleanup()
}
})
it('throws on an invalid delimiter argument', async () => { try {
const context = await createCliTestContext({ await context.run(['nonexistent.json'])
'input.json': JSON.stringify({ value: 1 }),
expect(exitSpy).toHaveBeenCalledWith(1)
expect(consolaError).toHaveBeenCalled()
}
finally {
await context.cleanup()
}
}) })
const consolaError = vi.spyOn(consola, 'error').mockImplementation(() => undefined)
try {
await expect(context.run(['input.json', '--delimiter', ';'])).resolves.toBeUndefined()
const exitMock = vi.mocked(process.exit)
expect(exitMock).toHaveBeenCalledWith(1)
const errorCall = consolaError.mock.calls.at(0)
expect(errorCall).toBeDefined()
const [error] = errorCall!
expect(error.message).toContain('Invalid delimiter')
}
finally {
await context.cleanup()
}
}) })
}) })