Snapshot URL datasets locally on add; preview tabular data as a table

This commit is contained in:
2026-06-10 10:24:42 +03:00
parent eb5e7ac53a
commit 2410c6e965
23 changed files with 1239 additions and 148 deletions
+70 -3
View File
@@ -42,15 +42,82 @@ describe('save — create', () => {
expect(ds?.rowCount).toBe(2);
});
test('a valid URL infers format from the extension', () => {
test('save() does not commit a URL create — it must go through commitUrlSnapshot', () => {
store().startCreate();
store().updateForm({ name: 'Remote', source: 'url', input: 'https://example.com/data.csv' });
// The component fetches first; save() never touches the network, so a URL create
// through save() is a no-op rather than an unfetched record.
expect(store().save(T)).toBe(false);
expect(store().datasets).toHaveLength(0);
});
});
describe('commitUrlSnapshot — create from a fetched body', () => {
test('snapshots the body, infers format from content, and profiles it', () => {
store().startCreate();
store().updateForm({ name: 'Remote', source: 'url', input: 'https://example.com/data.csv' });
expect(store().save(T)).toBe(true);
expect(store().commitUrlSnapshot({ text: 'a,b\n1,2\n3,4' }, T)).toBe(true);
const ds = selectSelectedDataset(store());
expect(ds?.source).toBe('url');
expect(ds?.url).toBe('https://example.com/data.csv');
expect(ds?.format).toBe('csv');
expect(ds?.rowCount).toBeNull(); // URL datasets aren't profiled
expect(ds?.data).toBe('a,b\n1,2\n3,4');
expect(ds?.rowCount).toBe(2);
expect(ds?.columns).toEqual(['a', 'b']);
expect(ds?.fetchedAt).toBe(T.toISOString());
expect(store().view).toBe('detail');
});
test('a duplicate name is rejected even with a successful fetch', () => {
store().add(
createDataset({ name: 'Dupe', data: [{ a: 1 }], format: 'json', source: 'inline', now: T }),
);
store().startCreate();
store().updateForm({ name: 'dupe', source: 'url', input: 'https://x/y.csv' });
expect(store().commitUrlSnapshot({ text: 'a\n1' }, T)).toBe(false);
expect(store().formError).toMatch(/already exists/i);
});
});
describe('URL dataset edits & refresh', () => {
const seedUrl = () => {
store().startCreate();
store().updateForm({ name: 'Remote', source: 'url', input: 'https://x/y.csv' });
store().commitUrlSnapshot({ text: 'a,b\n1,2' }, T);
return store().selectedId!;
};
test('a metadata-only edit (same URL) updates name without re-fetching', () => {
const id = seedUrl();
store().startEdit();
store().updateForm({ name: 'Renamed' }); // URL field stays https://x/y.csv
const later = new Date('2026-07-01T00:00:00Z');
expect(store().save(later)).toBe(true);
const ds = store().datasets.find((d) => d.id === id)!;
expect(ds.name).toBe('Renamed');
expect(ds.data).toBe('a,b\n1,2'); // snapshot untouched
expect(ds.fetchedAt).toBe(T.toISOString()); // not re-fetched
expect(ds.modified).toBe(later.toISOString());
});
test('refreshDataset re-snapshots, re-profiles, and advances fetchedAt', () => {
const id = seedUrl();
const later = new Date('2026-07-01T00:00:00Z');
expect(store().refreshDataset(id, { text: 'a,b\n1,2\n3,4\n5,6' }, later)).toBe(true);
const ds = store().datasets.find((d) => d.id === id)!;
expect(ds.rowCount).toBe(3);
expect(ds.fetchedAt).toBe(later.toISOString());
expect(ds.modified).toBe(later.toISOString());
expect(ds.name).toBe('Remote'); // preserved
});
test('refreshDataset is a no-op for an inline dataset', () => {
store().add(
createDataset({ name: 'Inline', data: [{ a: 1 }], format: 'json', source: 'inline', now: T }),
);
const id = store().selectedId!;
expect(store().refreshDataset(id, { text: 'a\n1' }, T)).toBe(false);
});
});