feat: add JSON escape utility with comprehensive edge case tests

This commit is contained in:
2026-08-12 20:41:12 +08:00
parent e3fe330745
commit 8b83fb5c53
3 changed files with 79 additions and 0 deletions
@@ -0,0 +1,63 @@
import { describe, it, expect } from 'vitest';
import { safeStringify, isValidJson } from '../escape.js';
describe('safeStringify', () => {
it('returns valid JSON for simple objects', () => {
const result = safeStringify({ ok: true, data: { id: 'p_123' } });
expect(() => JSON.parse(result)).not.toThrow();
expect(JSON.parse(result)).toEqual({ ok: true, data: { id: 'p_123' } });
});
it('escapes double quotes in string values', () => {
const result = safeStringify({ text: 'He said "hello"' });
const parsed = JSON.parse(result);
expect(parsed.text).toBe('He said "hello"');
});
it('escapes backslashes in string values', () => {
const result = safeStringify({ path: 'C:\\Users\\test' });
const parsed = JSON.parse(result);
expect(parsed.path).toBe('C:\\Users\\test');
});
it('escapes control characters (newline, tab)', () => {
const result = safeStringify({ text: 'line1\nline2\tindented' });
const parsed = JSON.parse(result);
expect(parsed.text).toBe('line1\nline2\tindented');
});
it('handles unicode characters', () => {
const result = safeStringify({ text: '你好世界 🌍' });
const parsed = JSON.parse(result);
expect(parsed.text).toBe('你好世界 🌍');
});
it('handles HTML-like content without breaking JSON', () => {
const html = '<div class="main">Hello</div>';
const result = safeStringify({ html });
const parsed = JSON.parse(result);
expect(parsed.html).toBe(html);
});
it('handles empty string and null', () => {
expect(JSON.parse(safeStringify({ a: '' }))).toEqual({ a: '' });
expect(JSON.parse(safeStringify({ a: null }))).toEqual({ a: null });
});
it('handles arrays with special characters', () => {
const result = safeStringify({ items: ['a"b', 'c\\d', 'e\nf'] });
const parsed = JSON.parse(result);
expect(parsed.items).toEqual(['a"b', 'c\\d', 'e\nf']);
});
});
describe('isValidJson', () => {
it('returns true for valid JSON', () => {
expect(isValidJson('{"ok":true}')).toBe(true);
});
it('returns false for invalid JSON', () => {
expect(isValidJson('{ok:true}')).toBe(false);
expect(isValidJson('')).toBe(false);
});
});
+15
View File
@@ -0,0 +1,15 @@
// JSON safe serialization / JSON 安全序列化
// Always use this instead of manual string concatenation for JSON output
export function safeStringify(obj: unknown): string {
return JSON.stringify(obj);
}
export function isValidJson(str: string): boolean {
try {
JSON.parse(str);
return true;
} catch {
return false;
}
}
+1
View File
@@ -8,3 +8,4 @@ export {
type CanvasNoiseConfig, type CanvasNoiseConfig,
type PermissionState, type PermissionState,
} from './types/fingerprint.js'; } from './types/fingerprint.js';
export { safeStringify, isValidJson } from './escape.js';