mirror of https://github.com/buster-so/buster.git
clean bar line tests
This commit is contained in:
parent
31c3cbc1b7
commit
72150c2ee1
|
@ -1,285 +1,177 @@
|
||||||
import { type MetricYml, MetricYmlSchema } from '@buster/server-shared/metrics';
|
|
||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import { validateAndAdjustBarLineAxes } from './bar-line-axis-validator';
|
import { validateAndAdjustBarLineAxes } from './bar-line-axis-validator';
|
||||||
|
import type { ChartConfigProps } from '@buster/server-shared/metrics';
|
||||||
|
|
||||||
describe('validateAndAdjustBarLineAxes', () => {
|
describe('validateAndAdjustBarLineAxes', () => {
|
||||||
it('should return unchanged config for non-bar/line charts', () => {
|
it('should return unchanged config for non-bar/line charts', () => {
|
||||||
const metricYml = MetricYmlSchema.parse({
|
const chartConfig = {
|
||||||
name: 'Pie Chart',
|
selectedChartType: 'pie',
|
||||||
description: 'Test pie chart',
|
columnLabelFormats: {},
|
||||||
timeFrame: '2024',
|
} as ChartConfigProps;
|
||||||
sql: 'SELECT category, count FROM data',
|
|
||||||
chartConfig: {
|
|
||||||
selectedChartType: 'pie',
|
|
||||||
columnLabelFormats: {},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = validateAndAdjustBarLineAxes(metricYml.chartConfig);
|
const result = validateAndAdjustBarLineAxes(chartConfig);
|
||||||
expect(result).toEqual(metricYml.chartConfig);
|
expect(result).toEqual(chartConfig);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return valid when Y axis has numeric columns', () => {
|
it('should return valid when Y axis has numeric columns', () => {
|
||||||
const metricYml = MetricYmlSchema.parse({
|
const chartConfig = {
|
||||||
name: 'Sales by Month',
|
selectedChartType: 'line',
|
||||||
description: 'Monthly sales data',
|
columnLabelFormats: {
|
||||||
timeFrame: '2024',
|
month: {
|
||||||
sql: 'SELECT month, sales FROM data',
|
columnType: 'date',
|
||||||
chartConfig: {
|
|
||||||
selectedChartType: 'line',
|
|
||||||
columnLabelFormats: {
|
|
||||||
month: {
|
|
||||||
columnType: 'date',
|
|
||||||
style: 'date',
|
|
||||||
replaceMissingDataWith: null,
|
|
||||||
numberSeparatorStyle: null,
|
|
||||||
},
|
|
||||||
sales: {
|
|
||||||
columnType: 'number',
|
|
||||||
style: 'currency',
|
|
||||||
replaceMissingDataWith: 0,
|
|
||||||
numberSeparatorStyle: ',',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
barAndLineAxis: {
|
sales: {
|
||||||
x: ['month'],
|
columnType: 'number',
|
||||||
y: ['sales'],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
barAndLineAxis: {
|
||||||
|
x: ['month'],
|
||||||
|
y: ['sales'],
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
|
||||||
const result = validateAndAdjustBarLineAxes(metricYml.chartConfig);
|
const result = validateAndAdjustBarLineAxes(chartConfig);
|
||||||
// Function returns the config directly if valid, not an object with isValid property
|
expect(result).toEqual(chartConfig);
|
||||||
expect(result).toEqual(metricYml.chartConfig);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should swap axes when Y has non-numeric and X has numeric columns', () => {
|
it('should swap axes when Y has non-numeric and X has numeric columns', () => {
|
||||||
const metricYml = MetricYmlSchema.parse({
|
const chartConfig = {
|
||||||
name: 'Sales Rep by Revenue',
|
selectedChartType: 'bar',
|
||||||
description: 'Revenue by sales rep',
|
columnLabelFormats: {
|
||||||
timeFrame: '2024',
|
revenue: {
|
||||||
sql: 'SELECT revenue, sales_rep FROM data',
|
columnType: 'number',
|
||||||
chartConfig: {
|
|
||||||
selectedChartType: 'bar',
|
|
||||||
columnLabelFormats: {
|
|
||||||
revenue: {
|
|
||||||
columnType: 'number',
|
|
||||||
style: 'currency',
|
|
||||||
replaceMissingDataWith: 0,
|
|
||||||
numberSeparatorStyle: ',',
|
|
||||||
},
|
|
||||||
sales_rep: {
|
|
||||||
columnType: 'string',
|
|
||||||
style: 'string',
|
|
||||||
replaceMissingDataWith: null,
|
|
||||||
numberSeparatorStyle: null,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
barAndLineAxis: {
|
sales_rep: {
|
||||||
x: ['revenue'],
|
columnType: 'string',
|
||||||
y: ['sales_rep'],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
barAndLineAxis: {
|
||||||
|
x: ['revenue'],
|
||||||
|
y: ['sales_rep'],
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
|
||||||
const result = validateAndAdjustBarLineAxes(metricYml.chartConfig);
|
const result = validateAndAdjustBarLineAxes(chartConfig);
|
||||||
// Function returns adjusted config with swapped axes
|
|
||||||
expect(result.barAndLineAxis?.x).toEqual(['sales_rep']);
|
expect(result.barAndLineAxis?.x).toEqual(['sales_rep']);
|
||||||
expect(result.barAndLineAxis?.y).toEqual(['revenue']);
|
expect(result.barAndLineAxis?.y).toEqual(['revenue']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw error when Y has non-numeric and X also has non-numeric columns', () => {
|
it('should throw error when Y has non-numeric and X also has non-numeric columns', () => {
|
||||||
const metricYml = MetricYmlSchema.parse({
|
const chartConfig = {
|
||||||
name: 'Categories by Region',
|
selectedChartType: 'line',
|
||||||
description: 'Product categories by region',
|
columnLabelFormats: {
|
||||||
timeFrame: '2024',
|
region: {
|
||||||
sql: 'SELECT region, category FROM data',
|
columnType: 'string',
|
||||||
chartConfig: {
|
|
||||||
selectedChartType: 'line',
|
|
||||||
columnLabelFormats: {
|
|
||||||
region: {
|
|
||||||
columnType: 'string',
|
|
||||||
style: 'string',
|
|
||||||
replaceMissingDataWith: null,
|
|
||||||
numberSeparatorStyle: null,
|
|
||||||
},
|
|
||||||
category: {
|
|
||||||
columnType: 'string',
|
|
||||||
style: 'string',
|
|
||||||
replaceMissingDataWith: null,
|
|
||||||
numberSeparatorStyle: null,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
barAndLineAxis: {
|
category: {
|
||||||
x: ['region'],
|
columnType: 'string',
|
||||||
y: ['category'],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
barAndLineAxis: {
|
||||||
|
x: ['region'],
|
||||||
|
y: ['category'],
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
|
||||||
// Function throws an error for invalid configurations
|
expect(() => validateAndAdjustBarLineAxes(chartConfig)).toThrowError(
|
||||||
expect(() => validateAndAdjustBarLineAxes(metricYml.chartConfig)).toThrowError(
|
|
||||||
/Bar and line charts require numeric values on the Y axis.*category \(string\)/
|
/Bar and line charts require numeric values on the Y axis.*category \(string\)/
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle multiple columns on axes', () => {
|
it('should handle multiple columns on axes', () => {
|
||||||
const metricYml = MetricYmlSchema.parse({
|
const chartConfig = {
|
||||||
name: 'Multi Column Chart',
|
selectedChartType: 'line',
|
||||||
description: 'Chart with multiple columns',
|
columnLabelFormats: {
|
||||||
timeFrame: '2024',
|
date: {
|
||||||
sql: 'SELECT date, region, sales, profit FROM data',
|
columnType: 'date',
|
||||||
chartConfig: {
|
|
||||||
selectedChartType: 'line',
|
|
||||||
columnLabelFormats: {
|
|
||||||
date: {
|
|
||||||
columnType: 'date',
|
|
||||||
style: 'date',
|
|
||||||
replaceMissingDataWith: null,
|
|
||||||
numberSeparatorStyle: null,
|
|
||||||
},
|
|
||||||
region: {
|
|
||||||
columnType: 'string',
|
|
||||||
style: 'string',
|
|
||||||
replaceMissingDataWith: null,
|
|
||||||
numberSeparatorStyle: null,
|
|
||||||
},
|
|
||||||
sales: {
|
|
||||||
columnType: 'number',
|
|
||||||
style: 'currency',
|
|
||||||
replaceMissingDataWith: 0,
|
|
||||||
numberSeparatorStyle: ',',
|
|
||||||
},
|
|
||||||
profit: {
|
|
||||||
columnType: 'number',
|
|
||||||
style: 'currency',
|
|
||||||
replaceMissingDataWith: 0,
|
|
||||||
numberSeparatorStyle: ',',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
barAndLineAxis: {
|
region: {
|
||||||
x: ['date'],
|
columnType: 'string',
|
||||||
y: ['sales', 'profit'],
|
},
|
||||||
|
sales: {
|
||||||
|
columnType: 'number',
|
||||||
|
},
|
||||||
|
profit: {
|
||||||
|
columnType: 'number',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
barAndLineAxis: {
|
||||||
|
x: ['date'],
|
||||||
|
y: ['sales', 'profit'],
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
|
||||||
const result = validateAndAdjustBarLineAxes(metricYml.chartConfig);
|
const result = validateAndAdjustBarLineAxes(chartConfig);
|
||||||
expect(result).toEqual(metricYml.chartConfig);
|
expect(result).toEqual(chartConfig);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return error when Y has mixed numeric and non-numeric columns', () => {
|
it('should throw error when Y has mixed numeric and non-numeric columns', () => {
|
||||||
const metricYml: MetricYml = {
|
const chartConfig = {
|
||||||
name: 'Mixed Y Axis',
|
selectedChartType: 'bar',
|
||||||
description: 'Chart with mixed column types on Y',
|
columnLabelFormats: {
|
||||||
timeFrame: '2024',
|
month: {
|
||||||
sql: 'SELECT month, sales, category FROM data',
|
columnType: 'date',
|
||||||
chartConfig: {
|
|
||||||
selectedChartType: 'bar',
|
|
||||||
columnLabelFormats: {
|
|
||||||
month: {
|
|
||||||
columnType: 'date',
|
|
||||||
style: 'date',
|
|
||||||
replaceMissingDataWith: null,
|
|
||||||
numberSeparatorStyle: null,
|
|
||||||
},
|
|
||||||
sales: {
|
|
||||||
columnType: 'number',
|
|
||||||
style: 'currency',
|
|
||||||
replaceMissingDataWith: 0,
|
|
||||||
numberSeparatorStyle: ',',
|
|
||||||
},
|
|
||||||
category: {
|
|
||||||
columnType: 'string',
|
|
||||||
style: 'string',
|
|
||||||
replaceMissingDataWith: null,
|
|
||||||
numberSeparatorStyle: null,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
barAndLineAxis: {
|
sales: {
|
||||||
x: ['month'],
|
columnType: 'number',
|
||||||
y: ['sales', 'category'],
|
},
|
||||||
|
category: {
|
||||||
|
columnType: 'string',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
barAndLineAxis: {
|
||||||
|
x: ['month'],
|
||||||
|
y: ['sales', 'category'],
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
|
||||||
const result = validateAndAdjustBarLineAxes(metricYml);
|
expect(() => validateAndAdjustBarLineAxes(chartConfig)).toThrowError(/category \(string\)/);
|
||||||
expect(result.isValid).toBe(false);
|
|
||||||
expect(result.shouldSwapAxes).toBe(false);
|
|
||||||
expect(result.error).toContain('category (string)');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle date columns as non-numeric', () => {
|
it('should handle date columns as non-numeric', () => {
|
||||||
const metricYml: MetricYml = {
|
const chartConfig = {
|
||||||
name: 'Date on Y Axis',
|
selectedChartType: 'line',
|
||||||
description: 'Chart with date on Y axis',
|
columnLabelFormats: {
|
||||||
timeFrame: '2024',
|
sales: {
|
||||||
sql: 'SELECT sales, order_date FROM data',
|
columnType: 'number',
|
||||||
chartConfig: {
|
|
||||||
selectedChartType: 'line',
|
|
||||||
columnLabelFormats: {
|
|
||||||
sales: {
|
|
||||||
columnType: 'number',
|
|
||||||
style: 'currency',
|
|
||||||
replaceMissingDataWith: 0,
|
|
||||||
numberSeparatorStyle: ',',
|
|
||||||
},
|
|
||||||
order_date: {
|
|
||||||
columnType: 'date',
|
|
||||||
style: 'date',
|
|
||||||
replaceMissingDataWith: null,
|
|
||||||
numberSeparatorStyle: null,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
barAndLineAxis: {
|
order_date: {
|
||||||
x: ['sales'],
|
columnType: 'date',
|
||||||
y: ['order_date'],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
barAndLineAxis: {
|
||||||
|
x: ['sales'],
|
||||||
|
y: ['order_date'],
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
|
||||||
const result = validateAndAdjustBarLineAxes(metricYml);
|
const result = validateAndAdjustBarLineAxes(chartConfig);
|
||||||
expect(result.isValid).toBe(true);
|
expect(result.barAndLineAxis?.x).toEqual(['order_date']);
|
||||||
expect(result.shouldSwapAxes).toBe(true);
|
expect(result.barAndLineAxis?.y).toEqual(['sales']);
|
||||||
expect(result.adjustedYml?.chartConfig?.barAndLineAxis?.x).toEqual(['order_date']);
|
|
||||||
expect(result.adjustedYml?.chartConfig?.barAndLineAxis?.y).toEqual(['sales']);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle missing barAndLineAxis gracefully', () => {
|
it('should handle missing barAndLineAxis gracefully', () => {
|
||||||
const metricYml: MetricYml = {
|
const chartConfig = {
|
||||||
name: 'Missing Axis Config',
|
selectedChartType: 'bar',
|
||||||
description: 'Chart without axis config',
|
columnLabelFormats: {},
|
||||||
timeFrame: '2024',
|
} as any;
|
||||||
sql: 'SELECT * FROM data',
|
|
||||||
chartConfig: {
|
|
||||||
selectedChartType: 'bar',
|
|
||||||
columnLabelFormats: {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = validateAndAdjustBarLineAxes(metricYml);
|
const result = validateAndAdjustBarLineAxes(chartConfig);
|
||||||
expect(result.isValid).toBe(true);
|
expect(result).toEqual(chartConfig);
|
||||||
expect(result.shouldSwapAxes).toBe(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle empty axis arrays', () => {
|
it('should throw error for empty axis arrays', () => {
|
||||||
const metricYml: MetricYml = {
|
const chartConfig = {
|
||||||
name: 'Empty Axes',
|
selectedChartType: 'line',
|
||||||
description: 'Chart with empty axes',
|
columnLabelFormats: {},
|
||||||
timeFrame: '2024',
|
barAndLineAxis: {
|
||||||
sql: 'SELECT * FROM data',
|
x: [],
|
||||||
chartConfig: {
|
y: [],
|
||||||
selectedChartType: 'line',
|
|
||||||
columnLabelFormats: {},
|
|
||||||
barAndLineAxis: {
|
|
||||||
x: [],
|
|
||||||
y: [],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
} as any;
|
||||||
|
|
||||||
const result = validateAndAdjustBarLineAxes(metricYml);
|
expect(() => validateAndAdjustBarLineAxes(chartConfig)).toThrowError(
|
||||||
expect(result.isValid).toBe(false);
|
/Bar and line charts require at least one column for each axis/
|
||||||
expect(result.shouldSwapAxes).toBe(false);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue