mirror of
https://github.com/apache/superset.git
synced 2026-06-08 09:09:27 +00:00
283 lines
8.5 KiB
TypeScript
283 lines
8.5 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 { GenericDataType } from '@apache-superset/core/api/core';
|
|
import transformProps from './transformProps';
|
|
import { BigNumberWithTrendlineChartProps, BigNumberDatum } from '../types';
|
|
|
|
// Mock chart-controls to avoid styled-components issues in Jest
|
|
jest.mock('@superset-ui/chart-controls', () => ({
|
|
aggregationChoices: {
|
|
raw: {
|
|
label: 'Force server-side aggregation',
|
|
compute: (data: number[]) => data[0] ?? null,
|
|
},
|
|
LAST_VALUE: {
|
|
label: 'Last Value',
|
|
compute: (data: number[]) => data[0] ?? null,
|
|
},
|
|
sum: {
|
|
label: 'Total (Sum)',
|
|
compute: (data: number[]) => data.reduce((a, b) => a + b, 0),
|
|
},
|
|
mean: {
|
|
label: 'Average (Mean)',
|
|
compute: (data: number[]) =>
|
|
data.reduce((a, b) => a + b, 0) / data.length,
|
|
},
|
|
min: { label: 'Minimum', compute: (data: number[]) => Math.min(...data) },
|
|
max: { label: 'Maximum', compute: (data: number[]) => Math.max(...data) },
|
|
median: {
|
|
label: 'Median',
|
|
compute: (data: number[]) => {
|
|
const sorted = [...data].sort((a, b) => a - b);
|
|
const mid = Math.floor(sorted.length / 2);
|
|
return sorted.length % 2 === 0
|
|
? (sorted[mid - 1] + sorted[mid]) / 2
|
|
: sorted[mid];
|
|
},
|
|
},
|
|
},
|
|
}));
|
|
|
|
jest.mock('@superset-ui/core', () => ({
|
|
GenericDataType: { Temporal: 2, String: 1 },
|
|
extractTimegrain: jest.fn(() => 'P1D'),
|
|
getMetricLabel: jest.fn(metric => metric),
|
|
getXAxisLabel: jest.fn(() => '__timestamp'),
|
|
getValueFormatter: jest.fn(() => ({
|
|
format: (v: number) => `$${v}`,
|
|
})),
|
|
getNumberFormatter: jest.fn(() => (v: number) => `${(v * 100).toFixed(1)}%`),
|
|
t: jest.fn(v => v),
|
|
tooltipHtml: jest.fn(() => '<div>tooltip</div>'),
|
|
NumberFormats: {
|
|
PERCENT_SIGNED_1_POINT: '.1%',
|
|
},
|
|
}));
|
|
|
|
jest.mock('../utils', () => ({
|
|
getDateFormatter: jest.fn(() => (v: any) => `${v}pm`),
|
|
parseMetricValue: jest.fn(val => Number(val)),
|
|
getOriginalLabel: jest.fn((metric, metrics) => {
|
|
console.log(metrics);
|
|
return metric;
|
|
}),
|
|
}));
|
|
|
|
jest.mock('../../utils/tooltip', () => ({
|
|
getDefaultTooltip: jest.fn(() => ({})),
|
|
}));
|
|
|
|
jest.mock('../../utils/formatters', () => ({
|
|
getXAxisFormatter: jest.fn(() => String),
|
|
}));
|
|
|
|
jest.mock('../../constants', () => ({
|
|
TIMESERIES_CONSTANTS: {
|
|
gridOffsetBottom: 20,
|
|
gridOffsetLeft: 20,
|
|
gridOffsetRight: 20,
|
|
gridOffsetTop: 20,
|
|
},
|
|
}));
|
|
|
|
describe('BigNumberWithTrendline transformProps', () => {
|
|
const onContextMenu = jest.fn();
|
|
const baseFormData = {
|
|
headerFontSize: 20,
|
|
metric: 'value',
|
|
subtitle: 'subtitle message',
|
|
subtitleFontSize: 14,
|
|
forceTimestampFormatting: false,
|
|
timeFormat: 'YYYY-MM-DD',
|
|
yAxisFormat: 'SMART_NUMBER',
|
|
compareLag: 1,
|
|
compareSuffix: 'WoW',
|
|
colorPicker: { r: 0, g: 0, b: 0 },
|
|
currencyFormat: { symbol: '$', symbolPosition: 'prefix' },
|
|
};
|
|
|
|
const baseDatasource = {
|
|
currencyFormats: { value: '$0,0.00' },
|
|
columnFormats: { value: '$0,0.00' },
|
|
metrics: [{ metric_name: 'value', d3format: '.2f' }],
|
|
};
|
|
|
|
const baseHooks = { onContextMenu };
|
|
const baseRawFormData = { dummy: 'raw' };
|
|
|
|
it('should return null bigNumber when no data is provided', () => {
|
|
const chartProps = {
|
|
width: 400,
|
|
height: 300,
|
|
queriesData: [{ data: [] as unknown as BigNumberDatum[], coltypes: [] }],
|
|
formData: baseFormData,
|
|
rawFormData: baseRawFormData,
|
|
hooks: baseHooks,
|
|
datasource: baseDatasource,
|
|
theme: { colors: { grayscale: { light5: '#eee' } } },
|
|
};
|
|
|
|
const result = transformProps(
|
|
chartProps as unknown as BigNumberWithTrendlineChartProps,
|
|
);
|
|
expect(result.bigNumber).toBeNull();
|
|
expect(result.subtitle).toBe('subtitle message');
|
|
});
|
|
|
|
it('should calculate subheader as percent change with suffix', () => {
|
|
const chartProps = {
|
|
width: 500,
|
|
height: 400,
|
|
queriesData: [
|
|
{
|
|
data: [
|
|
{ __timestamp: 2, value: 110 },
|
|
{ __timestamp: 1, value: 100 },
|
|
] as unknown as BigNumberDatum[],
|
|
colnames: ['__timestamp', 'value'],
|
|
coltypes: ['TEMPORAL', 'NUMERIC'],
|
|
},
|
|
],
|
|
formData: baseFormData,
|
|
rawFormData: baseRawFormData,
|
|
hooks: baseHooks,
|
|
datasource: baseDatasource,
|
|
theme: { colors: { grayscale: { light5: '#eee' } } },
|
|
};
|
|
|
|
const result = transformProps(
|
|
chartProps as unknown as BigNumberWithTrendlineChartProps,
|
|
);
|
|
expect(result.subheader).toBe('10.0% WoW');
|
|
});
|
|
|
|
it('should compute bigNumber from parseMetricValue', () => {
|
|
const chartProps = {
|
|
width: 600,
|
|
height: 450,
|
|
queriesData: [
|
|
{
|
|
data: [
|
|
{ __timestamp: 2, value: '456' },
|
|
] as unknown as BigNumberDatum[],
|
|
colnames: ['__timestamp', 'value'],
|
|
coltypes: [GenericDataType.Temporal, GenericDataType.String],
|
|
},
|
|
],
|
|
formData: baseFormData,
|
|
rawFormData: baseRawFormData,
|
|
hooks: baseHooks,
|
|
datasource: baseDatasource,
|
|
theme: { colors: { grayscale: { light5: '#eee' } } },
|
|
};
|
|
|
|
const result = transformProps(
|
|
chartProps as unknown as BigNumberWithTrendlineChartProps,
|
|
);
|
|
expect(result.bigNumber).toEqual(456);
|
|
});
|
|
|
|
it('should use formatTime as headerFormatter for Temporal/String or forced', () => {
|
|
const formData = { ...baseFormData, forceTimestampFormatting: true };
|
|
const chartProps = {
|
|
width: 600,
|
|
height: 450,
|
|
queriesData: [
|
|
{
|
|
data: [
|
|
{ __timestamp: 2, value: '123' },
|
|
] as unknown as BigNumberDatum[],
|
|
colnames: ['__timestamp', 'value'],
|
|
coltypes: [0, GenericDataType.String],
|
|
},
|
|
],
|
|
formData,
|
|
rawFormData: baseRawFormData,
|
|
hooks: baseHooks,
|
|
datasource: baseDatasource,
|
|
theme: { colors: { grayscale: { light5: '#eee' } } },
|
|
};
|
|
|
|
const result = transformProps(
|
|
chartProps as unknown as BigNumberWithTrendlineChartProps,
|
|
);
|
|
expect(result.headerFormatter(5)).toBe('5pm');
|
|
});
|
|
|
|
it('should use numberFormatter when not Temporal/String and not forced', () => {
|
|
const formData = { ...baseFormData, forceTimestampFormatting: false };
|
|
const chartProps = {
|
|
width: 600,
|
|
height: 450,
|
|
queriesData: [
|
|
{
|
|
data: [{ __timestamp: 2, value: 500 }] as unknown as BigNumberDatum[],
|
|
colnames: ['__timestamp', 'value'],
|
|
coltypes: [0, 0],
|
|
},
|
|
],
|
|
formData,
|
|
rawFormData: baseRawFormData,
|
|
hooks: baseHooks,
|
|
datasource: baseDatasource,
|
|
theme: { colors: { grayscale: { light5: '#eee' } } },
|
|
};
|
|
|
|
const result = transformProps(
|
|
chartProps as unknown as BigNumberWithTrendlineChartProps,
|
|
);
|
|
expect(result.headerFormatter.format(500)).toBe('$500');
|
|
});
|
|
|
|
it('should use last data point for comparison when big number comes from aggregated data', () => {
|
|
const chartProps = {
|
|
width: 500,
|
|
height: 400,
|
|
queriesData: [
|
|
{
|
|
data: [
|
|
{ __timestamp: 3, value: 150 },
|
|
{ __timestamp: 2, value: 100 },
|
|
{ __timestamp: 1, value: 110 },
|
|
] as unknown as BigNumberDatum[],
|
|
colnames: ['__timestamp', 'value'],
|
|
coltypes: ['TEMPORAL', 'NUMERIC'],
|
|
},
|
|
{
|
|
data: [{ value: 360 }],
|
|
colnames: ['value'],
|
|
coltypes: ['NUMERIC'],
|
|
},
|
|
],
|
|
formData: { ...baseFormData, aggregation: 'sum' },
|
|
rawFormData: baseRawFormData,
|
|
hooks: baseHooks,
|
|
datasource: baseDatasource,
|
|
theme: { colors: { grayscale: { light5: '#eee' } } },
|
|
};
|
|
|
|
const result = transformProps(
|
|
chartProps as unknown as BigNumberWithTrendlineChartProps,
|
|
);
|
|
expect(result.bigNumber).toBe(360);
|
|
expect(result.subheader).toBe('50.0% WoW');
|
|
});
|
|
});
|