Tasks: import TickTick backup CSV (csv: keys, matched against API rows, rekeyed when the API returns them)
All checks were successful
Deploy production / deploy (push) Successful in 2m29s
All checks were successful
Deploy production / deploy (push) Successful in 2m29s
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
mapTask,
|
||||
mergeCsv,
|
||||
parseTickTickCsv,
|
||||
moreComplete,
|
||||
normTitle,
|
||||
slugify,
|
||||
@@ -81,3 +83,58 @@ describe('ticktick mapping', () => {
|
||||
expect(normTitle('Book haircut!')).toBe('book haircut');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ticktick backup csv', () => {
|
||||
const csv = [
|
||||
'"Date: 2026-10-24+0000"',
|
||||
'"Status: ',
|
||||
'0 Normal',
|
||||
'2 Completed"',
|
||||
'"Folder Name","List Name","Title","Kind","Tags","Content","Is Check list","Start Date","Due Date","Reminder","Repeat","Priority","Status","Created Time","Completed Time","Order","Timezone","Is All Day","Is Floating","Column Name","Column Order","View Mode","taskId","parentId","projectKind"',
|
||||
'"","Inbox","Known","TEXT","","","N","","","","","0","2","2025-01-01T10:00:00+0000","2025-01-02T10:00:00+0000","0","Europe/Amsterdam","false","false",,,"list","1","","TASK"',
|
||||
'"","Inbox","Old won\'t do","TEXT","a, b","line ""one""\nline two","N","","","","","5","-1","2024-01-01T10:00:00+0000","2024-02-01T10:00:00+0000","0","Europe/Amsterdam","false","false",,,"list","2","1","TASK"',
|
||||
'"Old","Trips","Pack","CHECKLIST","","▫Tea\n▪Jerky\n","Y","","","","","0","0","2023-01-01T10:00:00+0000","","0","Europe/Amsterdam","false","false",,,"list","3","","TASK"',
|
||||
''
|
||||
].join('\n');
|
||||
const api = {
|
||||
projects: [],
|
||||
tasks: [
|
||||
{
|
||||
id: 'api1',
|
||||
projectId: 'inbox9',
|
||||
title: 'Known',
|
||||
status: 2,
|
||||
createdTime: '2025-01-01T10:00:00.000+0000'
|
||||
}
|
||||
]
|
||||
};
|
||||
it('parses the backup, including multi-line quoted fields and checklists', () => {
|
||||
const t = parseTickTickCsv(csv);
|
||||
expect(t).toHaveLength(3);
|
||||
expect(t[1]).toMatchObject({ content: 'line "one"\nline two', tags: ['a', 'b'], status: -1 });
|
||||
expect(t[2].items).toEqual([
|
||||
{ id: 'item1', title: 'Tea', status: 0 },
|
||||
{ id: 'item2', title: 'Jerky', status: 1 }
|
||||
]);
|
||||
});
|
||||
it('skips rows the API already has and keys the rest stably', () => {
|
||||
const a = mergeCsv(api, parseTickTickCsv(csv));
|
||||
const b = mergeCsv(api, parseTickTickCsv(csv));
|
||||
expect(a.report).toMatchObject({
|
||||
csvRows: 3,
|
||||
matchedListTitleCreated: 1,
|
||||
added: 2,
|
||||
newLists: ['Trips']
|
||||
});
|
||||
const added = a.data.tasks.slice(1);
|
||||
expect(added.map((t) => t.id)).toEqual(b.data.tasks.slice(1).map((t) => t.id));
|
||||
expect(added[0].id).toMatch(/^csv:/);
|
||||
expect(added[0]).toMatchObject({ projectId: 'inbox9', parentId: 'api1' });
|
||||
expect(added[1].projectId).toBe('csvlist:Trips');
|
||||
expect(a.data.projects).toContainEqual({
|
||||
id: 'csvlist:Trips',
|
||||
name: 'Old / Trips',
|
||||
closed: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user