Files
superset2/superset-frontend/plugins/plugin-chart-echarts/test/Gauge/transformProps.test.ts

728 lines
19 KiB
TypeScript

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import {
CategoricalColorNamespace,
ChartProps,
SqlaFormData,
VizType,
} from '@superset-ui/core';
import { supersetTheme } from '@apache-superset/core/ui';
import transformProps, {
getIntervalBoundsAndColors,
} from '../../src/Gauge/transformProps';
import { EchartsGaugeChartProps } from '../../src/Gauge/types';
describe('Echarts Gauge transformProps', () => {
const baseFormData: SqlaFormData = {
datasource: '26__table',
viz_type: VizType.Gauge,
metric: 'count',
adhocFilters: [],
rowLimit: 10,
minVal: 0,
maxVal: 100,
startAngle: 225,
endAngle: -45,
colorScheme: 'SUPERSET_DEFAULT',
fontSize: 14,
numberFormat: 'SMART_NUMBER',
valueFormatter: '{value}',
showPointer: true,
animation: true,
showAxisTick: false,
showSplitLine: false,
splitNumber: 10,
showProgress: true,
overlap: true,
roundCap: false,
};
it('should transform chart props for no group by column', () => {
const formData: SqlaFormData = { ...baseFormData, groupby: [] };
const queriesData = [
{
colnames: ['count'],
data: [
{
count: 16595,
},
],
},
];
const chartPropsConfig = {
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
};
const chartProps = new ChartProps(chartPropsConfig);
const result = transformProps(chartProps as EchartsGaugeChartProps);
// Test core properties
expect(result.width).toBe(800);
expect(result.height).toBe(600);
// Test series data
const seriesData = (result.echartOptions as any).series[0].data;
expect(seriesData).toHaveLength(1);
expect(seriesData[0].value).toBe(16595);
expect(seriesData[0].name).toBe('');
expect(seriesData[0].itemStyle.color).toBe('#1f77b4');
// Test detail and title positions
expect(seriesData[0].title.offsetCenter).toEqual(['0%', '20%']);
expect(seriesData[0].title.fontSize).toBe(14);
expect(seriesData[0].detail.offsetCenter).toEqual(['0%', '32.6%']);
expect(seriesData[0].detail.fontSize).toBe(16.8);
});
it('should transform chart props for single group by column', () => {
const formData: SqlaFormData = {
...baseFormData,
groupby: ['year'],
};
const queriesData = [
{
colnames: ['year', 'count'],
data: [
{
year: 1988,
count: 15,
},
{
year: 1995,
count: 219,
},
],
},
];
const chartPropsConfig = {
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
};
const chartProps = new ChartProps(chartPropsConfig);
const result = transformProps(chartProps as EchartsGaugeChartProps);
// Test core properties
expect(result.width).toBe(800);
expect(result.height).toBe(600);
// Test series data
const seriesData = (result.echartOptions as any).series[0].data;
expect(seriesData).toHaveLength(2);
// First data point
expect(seriesData[0].value).toBe(15);
expect(seriesData[0].name).toBe('year: 1988');
expect(seriesData[0].itemStyle.color).toBe('#1f77b4');
expect(seriesData[0].title.offsetCenter).toEqual(['0%', '20%']);
expect(seriesData[0].title.fontSize).toBe(14);
expect(seriesData[0].detail.offsetCenter).toEqual(['0%', '32.6%']);
expect(seriesData[0].detail.fontSize).toBe(16.8);
// Second data point
expect(seriesData[1].value).toBe(219);
expect(seriesData[1].name).toBe('year: 1995');
expect(seriesData[1].itemStyle.color).toBe('#ff7f0e');
expect(seriesData[1].title.offsetCenter).toEqual(['0%', '48%']);
expect(seriesData[1].title.fontSize).toBe(14);
expect(seriesData[1].detail.offsetCenter).toEqual(['0%', '60.6%']);
expect(seriesData[1].detail.fontSize).toBe(16.8);
});
it('should transform chart props for multiple group by columns', () => {
const formData: SqlaFormData = {
...baseFormData,
groupby: ['year', 'platform'],
};
const queriesData = [
{
colnames: ['year', 'platform', 'count'],
data: [
{
year: 2011,
platform: 'PC',
count: 140,
},
{
year: 2008,
platform: 'PC',
count: 76,
},
],
},
];
const chartPropsConfig = {
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
};
const chartProps = new ChartProps(chartPropsConfig);
const result = transformProps(chartProps as EchartsGaugeChartProps);
// Test core properties
expect(result.width).toBe(800);
expect(result.height).toBe(600);
// Test series data
const seriesData = (result.echartOptions as any).series[0].data;
expect(seriesData).toHaveLength(2);
// First data point
expect(seriesData[0].value).toBe(140);
expect(seriesData[0].name).toBe('year: 2011, platform: PC');
expect(seriesData[0].itemStyle.color).toBe('#1f77b4');
expect(seriesData[0].title.offsetCenter).toEqual(['0%', '20%']);
expect(seriesData[0].title.fontSize).toBe(14);
expect(seriesData[0].detail.offsetCenter).toEqual(['0%', '32.6%']);
expect(seriesData[0].detail.fontSize).toBe(16.8);
// Second data point
expect(seriesData[1].value).toBe(76);
expect(seriesData[1].name).toBe('year: 2008, platform: PC');
expect(seriesData[1].itemStyle.color).toBe('#ff7f0e');
expect(seriesData[1].title.offsetCenter).toEqual(['0%', '48%']);
expect(seriesData[1].title.fontSize).toBe(14);
expect(seriesData[1].detail.offsetCenter).toEqual(['0%', '60.6%']);
expect(seriesData[1].detail.fontSize).toBe(16.8);
});
it('should transform chart props for intervals', () => {
const formData: SqlaFormData = {
...baseFormData,
groupby: ['year', 'platform'],
intervals: '60,100',
intervalColorIndices: '1,2',
minVal: 20,
};
const queriesData = [
{
colnames: ['year', 'platform', 'count'],
data: [
{
year: 2011,
platform: 'PC',
count: 140,
},
{
year: 2008,
platform: 'PC',
count: 76,
},
],
},
];
const chartPropsConfig = {
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
};
const chartProps = new ChartProps(chartPropsConfig);
const result = transformProps(chartProps as EchartsGaugeChartProps);
// Test core properties
expect(result.width).toBe(800);
expect(result.height).toBe(600);
// Test axisLine intervals
const { axisLine } = (result.echartOptions as any).series[0];
expect(axisLine.roundCap).toBe(false);
expect(axisLine.lineStyle.width).toBe(14);
expect(axisLine.lineStyle.color).toEqual([
[0.5, '#1f77b4'],
[1, '#ff7f0e'],
]);
// Test series data
const seriesData = (result.echartOptions.series as any)[0].data;
expect(seriesData).toHaveLength(2);
// First data point
expect(seriesData[0].value).toBe(140);
expect(seriesData[0].name).toBe('year: 2011, platform: PC');
expect(seriesData[0].itemStyle.color).toBe('#1f77b4');
// Second data point
expect(seriesData[1].value).toBe(76);
expect(seriesData[1].name).toBe('year: 2008, platform: PC');
expect(seriesData[1].itemStyle.color).toBe('#ff7f0e');
});
});
describe('Min/Max calculation and axis labels', () => {
const baseFormData: SqlaFormData = {
datasource: '26__table',
viz_type: VizType.Gauge,
metric: 'count',
adhocFilters: [],
rowLimit: 10,
startAngle: 225,
endAngle: -45,
colorScheme: 'SUPERSET_DEFAULT',
fontSize: 14,
numberFormat: 'SMART_NUMBER',
valueFormatter: '{value}',
showPointer: true,
animation: true,
showAxisTick: false,
showSplitLine: false,
splitNumber: 10,
showProgress: true,
overlap: true,
roundCap: false,
groupby: [],
};
it('should use provided minVal and maxVal when valid numbers', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: 10,
maxVal: 100,
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: 50 }, { count: 75 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.min).toBe(10);
expect(series.max).toBe(100);
});
it('should calculate min/max from data when minVal is null', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: null,
maxVal: 100,
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: 20 }, { count: 80 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.min).toBe(0);
expect(series.max).toBe(100);
});
it('should calculate min/max from data when maxVal is null', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: 0,
maxVal: null,
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: 20 }, { count: 80 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.min).toBe(0);
expect(series.max).toBe(160);
});
it('should calculate min/max from data when both are null', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: null,
maxVal: null,
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: 15 }, { count: 45 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.min).toBe(0);
expect(series.max).toBe(90);
});
it('should calculate min/max from data when minVal is empty string', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: '' as any,
maxVal: 200,
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: 30 }, { count: 60 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.min).toBe(0);
expect(series.max).toBe(200);
});
it('should calculate min/max from data when maxVal is invalid string', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: 0,
maxVal: 'invalid' as any,
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: 25 }, { count: 75 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.min).toBe(0);
expect(series.max).toBe(150);
});
it('should handle negative values in min/max calculation', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: null,
maxVal: null,
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: -20 }, { count: 40 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.min).toBe(-40);
expect(series.max).toBe(80);
});
it('should generate axis labels correctly based on min, max, and splitNumber', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: 0,
maxVal: 100,
splitNumber: 5,
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: 50 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.min).toBe(0);
expect(series.max).toBe(100);
expect(series.splitNumber).toBe(5);
expect(series.axisLabel).toBeDefined();
expect(series.axisLabel.formatter).toBeDefined();
});
it('should calculate axis label length correctly for different number formats', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: 0,
maxVal: 1000,
splitNumber: 10,
numberFormat: ',d',
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: 500 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.axisLabel).toBeDefined();
expect(series.axisLabel.formatter).toBeDefined();
expect(typeof series.axisLabel.formatter).toBe('function');
});
it('should integrate interval bounds and colors with calculated min/max', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: null,
maxVal: null,
intervals: '20,60',
intervalColorIndices: '1,2',
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: 10 }, { count: 50 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.min).toBe(0);
expect(series.max).toBe(100);
const { axisLine } = series;
expect(axisLine.lineStyle.color).toEqual(
expect.arrayContaining([
expect.arrayContaining([expect.any(Number), expect.any(String)]),
]),
);
});
it('should handle zero values in data correctly', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: null,
maxVal: null,
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: 0 }, { count: 0 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.min).toBe(0);
expect(series.max).toBe(0);
});
it('should handle string minVal/maxVal that can be converted to numbers', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: '10' as any,
maxVal: '200' as any,
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: 50 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.min).toBe(10);
expect(series.max).toBe(200);
});
it('should handle different splitNumber values', () => {
const formData: SqlaFormData = {
...baseFormData,
minVal: 0,
maxVal: 100,
splitNumber: 20,
};
const queriesData = [
{
colnames: ['count'],
data: [{ count: 50 }],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
const result = transformProps(chartProps as EchartsGaugeChartProps);
const series = (result.echartOptions as any).series[0];
expect(series.splitNumber).toBe(20);
});
});
describe('getIntervalBoundsAndColors', () => {
it('should generate correct interval bounds and colors', () => {
const colorFn = CategoricalColorNamespace.getScale(
'supersetColors' as string,
);
expect(getIntervalBoundsAndColors('', '', colorFn, 0, 10)).toEqual([]);
expect(getIntervalBoundsAndColors('4, 10', '1, 2', colorFn, 0, 10)).toEqual(
[
[0.4, '#1f77b4'],
[1, '#ff7f0e'],
],
);
expect(
getIntervalBoundsAndColors('4, 8, 10', '9, 8, 7', colorFn, 0, 10),
).toEqual([
[0.4, '#bcbd22'],
[0.8, '#7f7f7f'],
[1, '#e377c2'],
]);
expect(getIntervalBoundsAndColors('4, 10', '1, 2', colorFn, 2, 10)).toEqual(
[
[0.25, '#1f77b4'],
[1, '#ff7f0e'],
],
);
expect(
getIntervalBoundsAndColors('-4, 0', '1, 2', colorFn, -10, 0),
).toEqual([
[0.6, '#1f77b4'],
[1, '#ff7f0e'],
]);
expect(
getIntervalBoundsAndColors('-4, -2', '1, 2', colorFn, -10, -2),
).toEqual([
[0.75, '#1f77b4'],
[1, '#ff7f0e'],
]);
});
});