mirror of
https://github.com/apache/superset.git
synced 2026-05-12 19:35:17 +00:00
test(plugin-chart-echarts): regression guards for temporal x-axis labels on timeseries charts (#39208)
This commit is contained in:
@@ -19,11 +19,13 @@
|
||||
import {
|
||||
NumberFormats,
|
||||
SMART_DATE_ID,
|
||||
SMART_DATE_VERBOSE_ID,
|
||||
TimeFormatter,
|
||||
TimeGranularity,
|
||||
} from '@superset-ui/core';
|
||||
import {
|
||||
getPercentFormatter,
|
||||
getTooltipTimeFormatter,
|
||||
getXAxisFormatter,
|
||||
} from '../../src/utils/formatters';
|
||||
|
||||
@@ -179,3 +181,53 @@ test('getXAxisFormatter without time grain should use standard smart date behavi
|
||||
|
||||
expect(standardResult).toBe(timeGrainResult);
|
||||
});
|
||||
|
||||
// Regression tests for echarts-timeseries-epoch-x-axis-labels investigation.
|
||||
// The bug report was that temporal x-axis labels could render as "NaN"
|
||||
// in some edge cases that we could not reproduce locally. The tests below
|
||||
// lock in the current behavior of the formatters so that a future refactor
|
||||
// surfaces any change in contract.
|
||||
|
||||
test('getTooltipTimeFormatter returns a TimeFormatter with SMART_DATE_VERBOSE id for SMART_DATE_ID', () => {
|
||||
const formatter = getTooltipTimeFormatter(SMART_DATE_ID);
|
||||
expect(formatter).toBeInstanceOf(TimeFormatter);
|
||||
expect((formatter as TimeFormatter).id).toBe(SMART_DATE_VERBOSE_ID);
|
||||
});
|
||||
|
||||
test('getTooltipTimeFormatter returns a TimeFormatter for a custom format string', () => {
|
||||
const customFormat = '%Y-%m-%d %H:%M';
|
||||
const formatter = getTooltipTimeFormatter(customFormat);
|
||||
expect(formatter).toBeInstanceOf(TimeFormatter);
|
||||
expect((formatter as TimeFormatter).id).toBe(customFormat);
|
||||
});
|
||||
|
||||
test('getTooltipTimeFormatter falls back to the String constructor when no format is supplied', () => {
|
||||
expect(getTooltipTimeFormatter()).toBe(String);
|
||||
expect(getTooltipTimeFormatter(undefined)).toBe(String);
|
||||
});
|
||||
|
||||
test('getXAxisFormatter produces stable SMART_DATE output for a valid Date', () => {
|
||||
// Documents the current happy-path output format so unexpected changes are
|
||||
// caught during review.
|
||||
const formatter = getXAxisFormatter(SMART_DATE_ID) as TimeFormatter;
|
||||
const result = formatter.format(new Date('2025-01-15T00:00:00.000Z'));
|
||||
expect(typeof result).toBe('string');
|
||||
expect(result).not.toMatch(/NaN/);
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('getXAxisFormatter returns a string for an Invalid Date without throwing', () => {
|
||||
// If a caller ever passes an Invalid Date (the originally-suspected cause
|
||||
// of epoch-ms axis labels showing NaN in echarts), the formatter must
|
||||
// still return a string instead of throwing, so echarts does not blow up
|
||||
// the chart render. The *content* of that string is format-dependent and
|
||||
// intentionally not asserted here — only that it is a string.
|
||||
const formatter = getXAxisFormatter(SMART_DATE_ID) as TimeFormatter;
|
||||
const invalid = new Date(Number.NaN);
|
||||
expect(() => formatter.format(invalid)).not.toThrow();
|
||||
expect(typeof formatter.format(invalid)).toBe('string');
|
||||
|
||||
const customFormatter = getXAxisFormatter('%Y-%m-%d') as TimeFormatter;
|
||||
expect(() => customFormatter.format(invalid)).not.toThrow();
|
||||
expect(typeof customFormatter.format(invalid)).toBe('string');
|
||||
});
|
||||
|
||||
@@ -1419,6 +1419,22 @@ test('getAxisType treats numeric as category for bar charts', () => {
|
||||
).toEqual(AxisType.Value);
|
||||
});
|
||||
|
||||
test('getAxisType does not coerce Numeric x-axis to Time regardless of values', () => {
|
||||
// Regression guard for echarts-timeseries-epoch-x-axis-labels investigation:
|
||||
// getAxisType only considers the coltype reported by the query, never the
|
||||
// actual values. Numeric coltype must stay on a Value axis so a future
|
||||
// change that introduces implicit temporal coercion is surfaced here.
|
||||
expect(getAxisType(false, false, GenericDataType.Numeric)).toEqual(
|
||||
AxisType.Value,
|
||||
);
|
||||
expect(getAxisType(false, false, GenericDataType.Temporal)).toEqual(
|
||||
AxisType.Time,
|
||||
);
|
||||
expect(getAxisType(false, false, GenericDataType.String)).toEqual(
|
||||
AxisType.Category,
|
||||
);
|
||||
});
|
||||
|
||||
test('getMinAndMaxFromBounds returns empty object when not truncating', () => {
|
||||
expect(
|
||||
getMinAndMaxFromBounds(
|
||||
|
||||
Reference in New Issue
Block a user