import { beforeEach, describe, expect, test } from 'vitest'; import { createDataset } from '@core/dataset'; import { useDatasetStore } from '../stores/DatasetStore'; import { dataInfoAt } from './active-dataset'; /** Seed a library dataset, profiled from inline rows. */ function seed(name: string, rows: Record[]): void { useDatasetStore .getState() .add(createDataset({ name, data: rows, format: 'json', source: 'inline' })); } /** An offset inside the first occurrence of `marker` in `text`. */ function at(text: string, marker: string): number { return text.indexOf(marker) + 1; } const names = (cols: ReadonlyArray<{ name: string }>) => cols.map((c) => c.name).sort(); beforeEach(() => { useDatasetStore.getState().reset(); }); describe('dataInfoAt — view-scoped data context', () => { test('resolves a library binding case-insensitively', () => { seed('Sales', [{ region: 'W', revenue: 10 }]); const text = '{ "data": { "name": "sales" }, "encoding": { "x": { "field": "MK" } } }'; const info = dataInfoAt(text, at(text, 'MK')); expect(info.name).toBe('Sales'); expect(names(info.columnTypes)).toEqual(['region', 'revenue']); }); test('each layer sees its own dataset, not a sibling view’s', () => { seed('Sales', [{ region: 'W', revenue: 10 }]); seed('Regions', [{ region: 'W', population: 100 }]); const text = JSON.stringify( { layer: [ { data: { name: 'Sales' }, encoding: { x: { field: 'sales_mk' } } }, { data: { name: 'Regions' }, encoding: { y: { field: 'regions_mk' } } }, ], }, null, 2, ); const inLayer0 = dataInfoAt(text, at(text, 'sales_mk')); const inLayer1 = dataInfoAt(text, at(text, 'regions_mk')); expect(inLayer0.name).toBe('Sales'); expect(names(inLayer0.columnTypes)).toEqual(['region', 'revenue']); expect(inLayer1.name).toBe('Regions'); expect(names(inLayer1.columnTypes)).toEqual(['population', 'region']); }); test('a child without its own data inherits the parent binding', () => { seed('Sales', [{ region: 'W', revenue: 10 }]); const text = JSON.stringify( { data: { name: 'Sales' }, layer: [{ encoding: { x: { field: 'mk' } } }] }, null, 2, ); const info = dataInfoAt(text, at(text, '"mk"')); expect(info.name).toBe('Sales'); expect(names(info.columnTypes)).toEqual(['region', 'revenue']); }); test('profiles an inline binding (ghost dataset, no name)', () => { const text = '{ "data": { "values": [{ "a": 1, "b": 2 }] }, "encoding": { "x": { "field": "MK" } } }'; const info = dataInfoAt(text, at(text, 'MK')); expect(info.name).toBeNull(); expect(names(info.columnTypes)).toEqual(['a', 'b']); }); test('profiles a self-defined top-level datasets binding', () => { const text = JSON.stringify( { datasets: { local: [{ c: 1, d: 2 }] }, data: { name: 'local' }, encoding: { x: { field: 'mk' } }, }, null, 2, ); const info = dataInfoAt(text, at(text, '"mk"')); expect(info.name).toBeNull(); expect(names(info.columnTypes)).toEqual(['c', 'd']); }); test('offers ancestor-derived fields, not a sibling view’s', () => { seed('Sales', [{ region: 'W', revenue: 10 }]); const text = JSON.stringify( { data: { name: 'Sales' }, transform: [{ calculate: 'datum.revenue * 2', as: 'shared' }], layer: [ { transform: [{ calculate: 'x', as: 'inner' }], encoding: { x: { field: 'mk0' } } }, { transform: [{ calculate: 'y', as: 'sibling' }], encoding: { y: { field: 'mk1' } } }, ], }, null, 2, ); const fields = dataInfoAt(text, at(text, 'mk0')).fields.map((f) => f.name); expect(fields).toContain('shared'); // top-level (inherited) expect(fields).toContain('inner'); // this view expect(fields).not.toContain('sibling'); // the other layer }); test('a url/generator binding yields no static columns', () => { const url = '{ "data": { "url": "x.csv" }, "encoding": { "x": { "field": "MK" } } }'; expect(dataInfoAt(url, at(url, 'MK')).columnTypes).toEqual([]); }); });