feat: add JSON escape utility with comprehensive edge case tests
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -8,3 +8,4 @@ export {
|
||||
type CanvasNoiseConfig,
|
||||
type PermissionState,
|
||||
} from './types/fingerprint.js';
|
||||
export { safeStringify, isValidJson } from './escape.js';
|
||||
|
||||
Reference in New Issue
Block a user