blob: 5625da29e288730c9b7a095202bb2f88ef497d44 [file] [log] [blame]
/**
* 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 { getColorFormatters } from '@superset-ui/chart-controls';
import { BigNumberTotalChartProps } from '../types';
import transformProps from './transformProps';
jest.mock('@superset-ui/chart-controls', () => ({
getColorFormatters: jest.fn(),
}));
jest.mock('@superset-ui/core', () => ({
GenericDataType: { Temporal: 2, String: 1 },
getMetricLabel: jest.fn(metric => metric),
extractTimegrain: jest.fn(() => 'P1D'),
getValueFormatter: jest.fn(() => (v: any) => `$${v}`),
}));
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;
}),
}));
describe('BigNumberTotal transformProps', () => {
const onContextMenu = jest.fn();
const baseFormData = {
headerFontSize: 20,
metric: 'value',
subheader: 'sub header text',
subheaderFontSize: 14,
forceTimestampFormatting: false,
timeFormat: 'YYYY-MM-DD',
yAxisFormat: 'SMART_NUMBER',
conditionalFormatting: [{ color: 'red', op: '>', value: 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: [], coltypes: [] }],
formData: baseFormData,
rawFormData: baseRawFormData,
hooks: baseHooks,
datasource: baseDatasource,
};
const result = transformProps(
chartProps as unknown as BigNumberTotalChartProps,
);
expect(result.bigNumber).toBeNull();
expect(result.width).toBe(400);
expect(result.height).toBe(300);
expect(result.subtitle).toBe(baseFormData.subheader);
expect(result.onContextMenu).toBe(onContextMenu);
expect(result.refs).toEqual({});
// headerFormatter should be set even if there's no data
expect(typeof result.headerFormatter).toBe('function');
// colorThresholdFormatters fallback to empty array when getColorFormatters returns falsy
expect(result.colorThresholdFormatters).toEqual([]);
});
it('should convert subheader to subtitle', () => {
const chartProps = {
width: 400,
height: 300,
queriesData: [{ data: [], coltypes: [] }],
formData: { ...baseFormData, subheader: 'test' },
rawFormData: baseRawFormData,
hooks: baseHooks,
datasource: baseDatasource,
};
const result = transformProps(
chartProps as unknown as BigNumberTotalChartProps,
);
expect(result.subtitle).toBe('test');
});
const baseChartProps = {
width: 400,
height: 300,
queriesData: [{ data: [], coltypes: [] }],
rawFormData: { dummy: 'raw' },
hooks: { onContextMenu: jest.fn() },
datasource: {
currencyFormats: { value: '$0,0.00' },
columnFormats: { value: '$0,0.00' },
metrics: [{ metric_name: 'value', d3format: '.2f' }],
},
};
it('uses subtitle font size when subtitle is provided', () => {
const result = transformProps({
...baseChartProps,
formData: {
subtitle: 'Subtitle wins',
subheader: 'Fallback subheader',
subtitleFontSize: 0.4,
subheaderFontSize: 0.99,
metric: 'value',
headerFontSize: 0.3,
yAxisFormat: 'SMART_NUMBER',
timeFormat: 'smart_date',
},
} as unknown as BigNumberTotalChartProps);
expect(result.subtitle).toBe('Subtitle wins');
expect(result.subtitleFontSize).toBe(0.4);
});
it('should compute bigNumber using parseMetricValue when data exists', () => {
const chartProps = {
width: 500,
height: 400,
queriesData: [
{ data: [{ value: '456' }], coltypes: [GenericDataType.String] },
],
formData: { ...baseFormData, forceTimestampFormatting: false },
rawFormData: baseRawFormData,
hooks: baseHooks,
datasource: baseDatasource,
sortBy: 'value',
};
const result = transformProps(
chartProps as unknown as BigNumberTotalChartProps,
);
// parseMetricValue converts '456' to number 456 by our mock
expect(result.bigNumber).toEqual(456);
});
it('should use formatTime as headerFormatter for Temporal or String types or forced formatting', () => {
// Case 1: Temporal type
const chartPropsTemporal = {
width: 600,
height: 450,
queriesData: [
{ data: [{ value: '789' }], coltypes: [GenericDataType.Temporal] },
],
formData: { ...baseFormData, forceTimestampFormatting: false },
rawFormData: baseRawFormData,
hooks: baseHooks,
datasource: baseDatasource,
};
const resultTemporal = transformProps(
chartPropsTemporal as unknown as BigNumberTotalChartProps,
);
expect(resultTemporal.headerFormatter(5)).toBe('5pm');
// Case 2: String type regardless of forcing formatting
const chartPropsString = {
width: 600,
height: 450,
queriesData: [
{ data: [{ value: '789' }], coltypes: [GenericDataType.String] },
],
formData: { ...baseFormData, forceTimestampFormatting: false },
rawFormData: baseRawFormData,
hooks: baseHooks,
datasource: baseDatasource,
};
const resultString = transformProps(
chartPropsString as unknown as BigNumberTotalChartProps,
);
expect(resultString.headerFormatter(5)).toBe('5pm');
// Case 3: Forced timestamp formatting
const chartPropsForced = {
width: 600,
height: 450,
queriesData: [{ data: [{ value: '789' }], coltypes: [0] }], // non-temporal/non-string
formData: { ...baseFormData, forceTimestampFormatting: true },
rawFormData: baseRawFormData,
hooks: baseHooks,
datasource: baseDatasource,
};
const resultForced = transformProps(
chartPropsForced as unknown as BigNumberTotalChartProps,
);
expect(resultForced.headerFormatter(5)).toBe('5pm');
});
it('should use numberFormatter as headerFormatter when not Temporal/String and no forced formatting', () => {
const chartProps = {
width: 700,
height: 500,
queriesData: [{ data: [{ value: '321' }], coltypes: [0] }], // non-temporal/non-string
formData: { ...baseFormData, forceTimestampFormatting: false },
rawFormData: baseRawFormData,
hooks: baseHooks,
datasource: baseDatasource,
};
const result = transformProps(
chartProps as unknown as BigNumberTotalChartProps,
);
expect(result.headerFormatter(500)).toBe('$500');
});
it('should propagate colorThresholdFormatters from getColorFormatters', () => {
// Override the getColorFormatters mock to return specific value
const mockFormatters = [{ formatter: 'red' }];
(getColorFormatters as jest.Mock).mockReturnValueOnce(mockFormatters);
const chartProps = {
width: 800,
height: 600,
queriesData: [
{ data: [{ value: '100' }], coltypes: [GenericDataType.Temporal] },
],
formData: baseFormData,
rawFormData: baseRawFormData,
hooks: baseHooks,
datasource: baseDatasource,
};
const result = transformProps(
chartProps as unknown as BigNumberTotalChartProps,
);
expect(result.colorThresholdFormatters).toEqual(mockFormatters);
});
});