import { parseTsvBody, valuesToMarkdownTable, } from '../src/features/sheets/sheetsApi'; import { _parseSheetAttrs } from '../src/agent'; describe('parseTsvBody', () => { test('returns [] for empty / whitespace input', () => { expect(parseTsvBody('')).toEqual([]); expect(parseTsvBody(' ')).toEqual([]); expect(parseTsvBody('\n\n')).toEqual([]); }); test('parses tab-separated rows', () => { const body = '이름\t나이\t직책\n민지\t29\t디자이너\n준호\t31\t개발자'; expect(parseTsvBody(body)).toEqual([ ['이름', '나이', '직책'], ['민지', '29', '디자이너'], ['준호', '31', '개발자'], ]); }); test('falls back to pipe-separated when no tab present', () => { const body = '이름 | 나이\n민지 | 29\n준호 | 31'; expect(parseTsvBody(body)).toEqual([ ['이름', '나이'], ['민지', '29'], ['준호', '31'], ]); }); test('strips leading and trailing blank lines (LLM artifact)', () => { const body = '\n\n이름\t나이\n민지\t29\n\n'; expect(parseTsvBody(body)).toEqual([ ['이름', '나이'], ['민지', '29'], ]); }); test('preserves empty cells when tabs are present', () => { const body = 'A\t\tC'; expect(parseTsvBody(body)).toEqual([['A', '', 'C']]); }); }); describe('valuesToMarkdownTable', () => { test('empty → placeholder', () => { expect(valuesToMarkdownTable([])).toBe('_(empty)_'); }); test('renders header + separator + rows', () => { const out = valuesToMarkdownTable([ ['이름', '나이'], ['민지', 29], ['준호', 31], ]); const lines = out.split('\n'); expect(lines[0]).toBe('| 이름 | 나이 |'); expect(lines[1]).toBe('|---|---|'); expect(lines[2]).toBe('| 민지 | 29 |'); expect(lines[3]).toBe('| 준호 | 31 |'); }); test('truncates beyond maxRows + adds note', () => { const big: any[][] = [['col']]; for (let i = 0; i < 60; i++) big.push([`row${i}`]); const out = valuesToMarkdownTable(big, 10); expect(out).toContain('| col |'); expect(out).toContain('| row0 |'); expect(out).toContain('| row8 |'); // 10 rows total = header + 9 data expect(out).not.toContain('| row9 |'); expect(out).toContain('51 more rows truncated'); }); test('escapes pipe characters inside cell values', () => { const out = valuesToMarkdownTable([ ['a|b', 'c'], ['d', 'e|f'], ]); expect(out).toContain('| a\\|b | c |'); expect(out).toContain('| d | e\\|f |'); }); }); describe('_parseSheetAttrs', () => { test('parses spreadsheet_id + range with double quotes', () => { const a = _parseSheetAttrs(' spreadsheet_id="1abc" range="Sheet1!A1:D20" '); expect(a.spreadsheetId).toBe('1abc'); expect(a.range).toBe('Sheet1!A1:D20'); }); test('accepts camelCase alias spreadsheetId', () => { const a = _parseSheetAttrs('spreadsheetId="1xyz" range="A:B"'); expect(a.spreadsheetId).toBe('1xyz'); }); test('accepts sheet_id alias', () => { const a = _parseSheetAttrs(`sheet_id='1qrs' range='Tab1'`); expect(a.spreadsheetId).toBe('1qrs'); expect(a.range).toBe('Tab1'); }); test('parses bare (unquoted) values', () => { const a = _parseSheetAttrs('spreadsheet_id=1simple range=Sheet1!A1'); expect(a.spreadsheetId).toBe('1simple'); expect(a.range).toBe('Sheet1!A1'); }); test('returns empty for missing attrs', () => { expect(_parseSheetAttrs('')).toEqual({}); expect(_parseSheetAttrs('foo="bar"')).toEqual({}); }); });