mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Add dataset library, extract-to-dataset, and render-time reference resolution
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { extractDatasetRefs, recomputeDatasetRefs, renameDatasetInSpec } from './spec-refs';
|
||||
|
||||
describe('extractDatasetRefs', () => {
|
||||
test('collects a top-level named-data reference', () => {
|
||||
expect(extractDatasetRefs({ data: { name: 'Sales' }, mark: 'bar' })).toEqual(['Sales']);
|
||||
});
|
||||
|
||||
test('collects per-layer references', () => {
|
||||
const spec = {
|
||||
layer: [
|
||||
{ data: { name: 'A' }, mark: 'bar' },
|
||||
{ data: { name: 'B' }, mark: 'line' },
|
||||
],
|
||||
};
|
||||
expect(extractDatasetRefs(spec).sort()).toEqual(['A', 'B']);
|
||||
});
|
||||
|
||||
test('collects references inside concat and a child spec (facet/repeat)', () => {
|
||||
const spec = {
|
||||
facet: { field: 'g' },
|
||||
spec: { hconcat: [{ data: { name: 'C' } }, { vconcat: [{ data: { name: 'D' } }] }] },
|
||||
};
|
||||
expect(extractDatasetRefs(spec).sort()).toEqual(['C', 'D']);
|
||||
});
|
||||
|
||||
test('accepts a JSON-text spec', () => {
|
||||
const spec = JSON.stringify({ data: { name: 'FromString' } });
|
||||
expect(extractDatasetRefs(spec)).toEqual(['FromString']);
|
||||
});
|
||||
|
||||
test('unparseable string yields no refs', () => {
|
||||
expect(extractDatasetRefs('{ not valid json')).toEqual([]);
|
||||
});
|
||||
|
||||
test('inline-data and url-data references (no name) are ignored', () => {
|
||||
expect(extractDatasetRefs({ data: { values: [{ a: 1 }] } })).toEqual([]);
|
||||
expect(extractDatasetRefs({ data: { url: 'http://x/y.csv' } })).toEqual([]);
|
||||
});
|
||||
|
||||
test('excludes names the spec defines for itself via top-level datasets', () => {
|
||||
const spec = {
|
||||
datasets: { foo: [{ a: 1 }] },
|
||||
data: { name: 'foo' },
|
||||
layer: [{ data: { name: 'Library' } }],
|
||||
};
|
||||
// "foo" is self-defined and not a library dependency; "Library" is.
|
||||
expect(extractDatasetRefs(spec)).toEqual(['Library']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('recomputeDatasetRefs', () => {
|
||||
test('returns sorted, de-duped names', () => {
|
||||
const spec = {
|
||||
layer: [{ data: { name: 'B' } }, { data: { name: 'A' } }, { data: { name: 'B' } }],
|
||||
};
|
||||
expect(recomputeDatasetRefs(spec)).toEqual(['A', 'B']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('renameDatasetInSpec', () => {
|
||||
test('rewrites every occurrence of the old name', () => {
|
||||
const spec = {
|
||||
data: { name: 'Old' },
|
||||
layer: [{ data: { name: 'Old' } }, { data: { name: 'Other' } }],
|
||||
};
|
||||
const out = renameDatasetInSpec(spec, 'Old', 'New');
|
||||
expect(out.data.name).toBe('New');
|
||||
expect(out.layer[0].data.name).toBe('New');
|
||||
expect(out.layer[1].data.name).toBe('Other');
|
||||
});
|
||||
|
||||
test('preserves other keys on the data object', () => {
|
||||
const spec = { data: { name: 'Old', format: { type: 'csv' } } };
|
||||
const out = renameDatasetInSpec(spec, 'Old', 'New');
|
||||
expect(out.data).toEqual({ name: 'New', format: { type: 'csv' } });
|
||||
});
|
||||
|
||||
test('does not mutate the input', () => {
|
||||
const spec = { data: { name: 'Old' } };
|
||||
const before = structuredClone(spec);
|
||||
renameDatasetInSpec(spec, 'Old', 'New');
|
||||
expect(spec).toEqual(before);
|
||||
});
|
||||
|
||||
test('preserves object shape for an object spec', () => {
|
||||
const out = renameDatasetInSpec({ data: { name: 'Old' } }, 'Old', 'New');
|
||||
expect(typeof out).toBe('object');
|
||||
});
|
||||
|
||||
test('preserves string shape for a string spec (returns pretty JSON text)', () => {
|
||||
const out = renameDatasetInSpec(JSON.stringify({ data: { name: 'Old' } }), 'Old', 'New');
|
||||
expect(typeof out).toBe('string');
|
||||
expect(JSON.parse(out)).toEqual({ data: { name: 'New' } });
|
||||
});
|
||||
|
||||
test('returns an unparseable string spec unchanged', () => {
|
||||
const bad = '{ not json';
|
||||
expect(renameDatasetInSpec(bad, 'Old', 'New')).toBe(bad);
|
||||
});
|
||||
|
||||
test("does not rename a name defined by the spec's own top-level datasets", () => {
|
||||
const spec = { datasets: { Old: [{ a: 1 }] }, data: { name: 'Old' } };
|
||||
const out = renameDatasetInSpec(spec, 'Old', 'New');
|
||||
expect(out.data.name).toBe('Old'); // self-defined — left untouched
|
||||
expect(Object.keys(out.datasets)).toEqual(['Old']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user