123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- import {LegendComponentOption} from 'echarts';
- import {Series} from 'sentry/types/echarts';
- import {
- axisLabelFormatter,
- axisLabelFormatterUsingAggregateOutputType,
- categorizeDuration,
- findRangeOfMultiSeries,
- getDurationUnit,
- tooltipFormatter,
- tooltipFormatterUsingAggregateOutputType,
- } from 'sentry/utils/discover/charts';
- import {aggregateOutputType} from 'sentry/utils/discover/fields';
- import {HOUR, MINUTE, SECOND} from 'sentry/utils/formatters';
- describe('tooltipFormatter()', () => {
- it('formats values', () => {
- const cases: [string, number, string][] = [
- // function, input, expected
- ['count()', 0.1, '0.1'],
- ['avg(thing)', 0.125126, '0.125'],
- ['failure_rate()', 0.66123, '66.12%'],
- ['p50()', 100, '100.00ms'],
- ['p50()', 100.23, '100.23ms'],
- ['p50()', 1200, '1.20s'],
- ['p50()', 86400000, '1.00d'],
- ];
- for (const scenario of cases) {
- expect(tooltipFormatter(scenario[1], aggregateOutputType(scenario[0]))).toEqual(
- scenario[2]
- );
- }
- });
- });
- describe('tooltipFormatterUsingAggregateOutputType()', () => {
- it('formats values', () => {
- const cases: [string, number, string][] = [
- // function, input, expected
- ['number', 0.1, '0.1'],
- ['integer', 0.125, '0.125'],
- ['percentage', 0.6612, '66.12%'],
- ['duration', 321, '321.00ms'],
- ['size', 416 * 1024, '416.0 KiB'],
- ['', 444, '444'],
- ];
- for (const scenario of cases) {
- expect(tooltipFormatterUsingAggregateOutputType(scenario[1], scenario[0])).toEqual(
- scenario[2]
- );
- }
- });
- });
- describe('axisLabelFormatter()', () => {
- it('formats values', () => {
- const cases: [string, number, string][] = [
- // type, input, expected
- ['count()', 0.1, '0.1'],
- ['avg(thing)', 0.125126, '0.125'],
- ['failure_rate()', 0.66123, '66%'],
- ['p50()', 100, '100ms'],
- ['p50()', 541, '541ms'],
- ['p50()', 1200, '1s'],
- ['p50()', 60000, '1min'],
- ['p50()', 120000, '2min'],
- ['p50()', 3600000, '1hr'],
- ['p50()', 86400000, '1d'],
- ];
- for (const scenario of cases) {
- expect(axisLabelFormatter(scenario[1], aggregateOutputType(scenario[0]))).toEqual(
- scenario[2]
- );
- }
- });
- describe('When a duration unit is passed', () => {
- const getAxisLabels = (axisValues: number[], durationUnit: number) => {
- return axisValues.map(value =>
- axisLabelFormatter(value, 'duration', undefined, durationUnit)
- );
- };
- const generateDurationUnit = (axisValues: number[]) => {
- const max = Math.max(...axisValues);
- const min = Math.min(...axisValues);
- return categorizeDuration((max + min) * 0.5);
- };
- it('should not contain duplicate axis labels', () => {
- const axisValues = [40 * SECOND, 50 * SECOND, 60 * SECOND, 70 * SECOND];
- const durationUnit = generateDurationUnit(axisValues);
- const labels = getAxisLabels(axisValues, durationUnit);
- expect(labels.length).toBe(new Set(labels).size);
- });
- it('should use the same duration unit', () => {
- const axisValues = [50 * MINUTE, 150 * MINUTE, 250 * MINUTE, 350 * MINUTE];
- const durationUnit = generateDurationUnit(axisValues);
- const labels = getAxisLabels(axisValues, durationUnit);
- expect(labels.length).toBe(labels.filter(label => label.endsWith('hr')).length);
- });
- });
- });
- describe('axisLabelFormatterUsingAggregateOutputType()', () => {
- it('formats values', () => {
- const cases: [string, number, string][] = [
- // type, input, expected
- ['number', 0.1, '0.1'],
- ['integer', 0.125, '0.125'],
- ['percentage', 0.6612, '66%'],
- ['duration', 321, '321ms'],
- ['size', 416 * 1024, '416 KiB'],
- ['', 444, '444'],
- ];
- for (const scenario of cases) {
- expect(
- axisLabelFormatterUsingAggregateOutputType(scenario[1], scenario[0])
- ).toEqual(scenario[2]);
- }
- });
- });
- describe('findRangeOfMultiSeries()', () => {
- const series: Series[] = [
- {
- seriesName: 'p100()',
- data: [
- {name: 1, value: 2300},
- {name: 2, value: 1900},
- {name: 3, value: 1950},
- ],
- },
- {
- seriesName: 'p95()',
- data: [
- {name: 1, value: 300},
- {name: 2, value: 280},
- {name: 3, value: 290},
- ],
- },
- {
- seriesName: 'p50()',
- data: [
- {name: 1, value: 100},
- {name: 2, value: 50},
- {name: 3, value: 80},
- ],
- },
- ];
- it('should find min and max when no items selected in legend', () => {
- expect(findRangeOfMultiSeries(series)).toStrictEqual({max: 2300, min: 50});
- });
- it('should find min and max when series has no data', () => {
- const noDataSeries: Series[] = [
- {
- seriesName: 'p100()',
- data: [
- {name: 1, value: 2300},
- {name: 2, value: 1900},
- {name: 3, value: 1950},
- ],
- },
- {
- seriesName: 'p95()',
- data: [],
- },
- {
- seriesName: 'p50()',
- data: [],
- },
- ];
- expect(findRangeOfMultiSeries(noDataSeries)).toStrictEqual({max: 2300, min: 1900});
- });
- it('should not find range if no items selected', () => {
- const legend: LegendComponentOption = {
- selected: {'p100()': false, 'p95()': false, 'p50()': false},
- };
- expect(findRangeOfMultiSeries(series, legend)).toStrictEqual(undefined);
- });
- it('should ignore p100 series if not selected', () => {
- const legend: LegendComponentOption = {
- selected: {'p100()': false},
- };
- expect(findRangeOfMultiSeries(series, legend)).toStrictEqual({max: 300, min: 50});
- });
- it('should ignore p50 series if not selected', () => {
- const legend: LegendComponentOption = {
- selected: {'p50()': false},
- };
- expect(findRangeOfMultiSeries(series, legend)).toStrictEqual({max: 2300, min: 280});
- });
- it('should display p100 value if selected and in legend object', () => {
- const legend: LegendComponentOption = {
- selected: {'p100()': true},
- };
- expect(findRangeOfMultiSeries(series, legend)).toStrictEqual({max: 2300, min: 50});
- });
- });
- describe('getDurationUnit()', () => {
- const MILLISECOND = 1;
- const generateSeries = (axisValues: number[]): Series[] => {
- return [
- {
- seriesName: 'p100()',
- data: axisValues.map((val, idx) => ({name: idx, value: val})),
- },
- ];
- };
- it('should return ms during transtion between ms to s', () => {
- const series = generateSeries([700, 800, 900, SECOND, 1.1 * SECOND]);
- expect(getDurationUnit(series)).toBe(MILLISECOND);
- });
- it('should return s during transtion between s to min', () => {
- const series = generateSeries([40 * SECOND, 50 * SECOND, MINUTE, 1.3 * MINUTE]);
- expect(getDurationUnit(series)).toBe(SECOND);
- });
- it('should return ms if y range is small', () => {
- const series = generateSeries([1000, 1050, 1100, 1150, 1200]);
- expect(getDurationUnit(series)).toBe(MILLISECOND);
- });
- it('should return min if yAxis range >= 5 min', () => {
- const series = generateSeries([1 * MINUTE, 2 * MINUTE, 4 * MINUTE, 6 * MINUTE]);
- expect(getDurationUnit(series)).toBe(MINUTE);
- });
- it('should return sec if yAxis range < 5 min', () => {
- const series = generateSeries([1 * MINUTE, 2 * MINUTE, 4 * MINUTE, 5 * MINUTE]);
- expect(getDurationUnit(series)).toBe(SECOND);
- });
- it('should use second with ms yAxis range if label length is long', () => {
- const series = generateSeries([4 * HOUR, 4.0001 * HOUR, 4.0002 * HOUR]);
- const durationUnit = getDurationUnit(series);
- const numOfDigits = ((4.0001 * HOUR) / durationUnit).toFixed(0).length;
- expect(numOfDigits).toBeLessThan(6);
- expect(durationUnit).not.toBe(MILLISECOND);
- });
- it('Should return ms if all values are 0', () => {
- const series = generateSeries([0, 0, 0]);
- const durationUnit = getDurationUnit(series);
- expect(durationUnit).toBe(MILLISECOND);
- });
- });
|