/** * 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 { AnnotationData, AnnotationSourceType, AnnotationStyle, AnnotationType, AxisType, CategoricalColorNamespace, EventAnnotationLayer, FormulaAnnotationLayer, IntervalAnnotationLayer, TimeseriesAnnotationLayer, TimeseriesDataRecord, } from '@superset-ui/core'; import { supersetTheme } from '@apache-superset/core/ui'; import { OrientationType } from '../../src'; import { transformEventAnnotation, transformFormulaAnnotation, transformIntervalAnnotation, transformTimeseriesAnnotation, } from '../../src/Timeseries/transformers'; const mockData: TimeseriesDataRecord[] = [ { __timestamp: 10, }, { __timestamp: 20, }, ]; const mockFormulaAnnotationLayer: FormulaAnnotationLayer = { annotationType: AnnotationType.Formula as const, name: 'My Formula', show: true, style: AnnotationStyle.Solid, value: '50', showLabel: true, }; describe('transformFormulaAnnotation', () => { test('should transform data correctly', () => { expect( transformFormulaAnnotation( mockFormulaAnnotationLayer, mockData, '__timestamp', AxisType.Value, CategoricalColorNamespace.getScale(''), undefined, ).data, ).toEqual([ [10, 50], [20, 50], ]); }); test('should swap x and y for horizontal chart', () => { expect( transformFormulaAnnotation( mockFormulaAnnotationLayer, mockData, '__timestamp', AxisType.Value, CategoricalColorNamespace.getScale(''), undefined, OrientationType.Horizontal, ).data, ).toEqual([ [50, 10], [50, 20], ]); }); }); const mockIntervalAnnotationLayer: IntervalAnnotationLayer = { name: 'Interval annotation layer', annotationType: AnnotationType.Interval as const, sourceType: AnnotationSourceType.Native as const, color: null, style: AnnotationStyle.Solid, width: 1, show: true, showLabel: false, value: 1, }; const mockIntervalAnnotationData: AnnotationData = { 'Interval annotation layer': { records: [ { start_dttm: 10, end_dttm: 12, short_descr: 'Timeseries 1', long_descr: '', json_metadata: '', }, { start_dttm: 13, end_dttm: 15, short_descr: 'Timeseries 2', long_descr: '', json_metadata: '', }, ], }, }; describe('transformIntervalAnnotation', () => { test('should transform data correctly', () => { const result = transformIntervalAnnotation( mockIntervalAnnotationLayer, mockData, mockIntervalAnnotationData, CategoricalColorNamespace.getScale(''), supersetTheme, ); // Should return a single series with all intervals expect(result).toHaveLength(1); expect(result[0].markArea.data).toEqual([ [ { name: 'Interval annotation layer - Timeseries 1', xAxis: 10 }, { xAxis: 12 }, ], [ { name: 'Interval annotation layer - Timeseries 2', xAxis: 13 }, { xAxis: 15 }, ], ]); }); test('should combine labels for intervals with the same start date', () => { const duplicateStartDateData: AnnotationData = { 'Interval annotation layer': { records: [ { start_dttm: 10, end_dttm: 12, short_descr: 'Same start event 1', long_descr: '', json_metadata: '', }, { start_dttm: 10, end_dttm: 15, short_descr: 'Same start event 2', long_descr: '', json_metadata: '', }, { start_dttm: 10, end_dttm: 18, short_descr: 'Same start event 3', long_descr: '', json_metadata: '', }, ], }, }; const result = transformIntervalAnnotation( mockIntervalAnnotationLayer, mockData, duplicateStartDateData, CategoricalColorNamespace.getScale(''), supersetTheme, ); // Should return a single series expect(result).toHaveLength(1); // The markArea data should contain all 3 intervals expect(result[0].markArea.data).toHaveLength(3); // All intervals with the same start time should have the combined label const combinedLabel = 'Interval annotation layer - Same start event 1\nInterval annotation layer - Same start event 2\nInterval annotation layer - Same start event 3'; expect(result[0].markArea.data).toEqual([ [{ name: combinedLabel, xAxis: 10 }, { xAxis: 12 }], [{ name: combinedLabel, xAxis: 10 }, { xAxis: 15 }], [{ name: combinedLabel, xAxis: 10 }, { xAxis: 18 }], ]); }); test('should use yAxis for horizontal chart data', () => { const result = transformIntervalAnnotation( mockIntervalAnnotationLayer, mockData, mockIntervalAnnotationData, CategoricalColorNamespace.getScale(''), supersetTheme, undefined, OrientationType.Horizontal, ); // Should return a single series with all intervals expect(result).toHaveLength(1); expect(result[0].markArea.data).toEqual([ [ { name: 'Interval annotation layer - Timeseries 1', yAxis: 10 }, { yAxis: 12 }, ], [ { name: 'Interval annotation layer - Timeseries 2', yAxis: 13 }, { yAxis: 15 }, ], ]); }); }); const mockEventAnnotationLayer: EventAnnotationLayer = { annotationType: AnnotationType.Event, color: null, name: 'Event annotation layer', show: true, showLabel: false, sourceType: AnnotationSourceType.Native, style: AnnotationStyle.Solid, value: 1, width: 1, }; const mockEventAnnotationData: AnnotationData = { 'Event annotation layer': { records: [ { start_dttm: 10, end_dttm: 12, short_descr: 'Test annotation', long_descr: '', json_metadata: '', }, { start_dttm: 13, end_dttm: 15, short_descr: 'Test annotation 2', long_descr: '', json_metadata: '', }, ], }, }; describe('transformEventAnnotation', () => { test('should transform data correctly', () => { const result = transformEventAnnotation( mockEventAnnotationLayer, mockData, mockEventAnnotationData, CategoricalColorNamespace.getScale(''), supersetTheme, ); // Should return a single series with all events expect(result).toHaveLength(1); expect(result[0].markLine.data).toEqual([ { name: 'Event annotation layer - Test annotation', xAxis: 10, }, { name: 'Event annotation layer - Test annotation 2', xAxis: 13 }, ]); }); test('should combine labels for events with the same start date', () => { const duplicateStartDateData: AnnotationData = { 'Event annotation layer': { records: [ { start_dttm: 10, end_dttm: 12, short_descr: 'Same date event 1', long_descr: '', json_metadata: '', }, { start_dttm: 10, end_dttm: 15, short_descr: 'Same date event 2', long_descr: '', json_metadata: '', }, { start_dttm: 10, end_dttm: 18, short_descr: 'Same date event 3', long_descr: '', json_metadata: '', }, ], }, }; const result = transformEventAnnotation( mockEventAnnotationLayer, mockData, duplicateStartDateData, CategoricalColorNamespace.getScale(''), supersetTheme, ); // Should return a single series expect(result).toHaveLength(1); // Events on the same date are grouped into a single entry with combined label expect(result[0].markLine.data).toHaveLength(1); // The combined label should include all event names expect(result[0].markLine.data).toEqual([ { name: 'Event annotation layer - Same date event 1\nEvent annotation layer - Same date event 2\nEvent annotation layer - Same date event 3', xAxis: 10, }, ]); }); test('should use yAxis for horizontal chart data', () => { const result = transformEventAnnotation( mockEventAnnotationLayer, mockData, mockEventAnnotationData, CategoricalColorNamespace.getScale(''), supersetTheme, undefined, OrientationType.Horizontal, ); // Should return a single series with all events expect(result).toHaveLength(1); expect(result[0].markLine.data).toEqual([ { name: 'Event annotation layer - Test annotation', yAxis: 10, }, { name: 'Event annotation layer - Test annotation 2', yAxis: 13 }, ]); }); }); const mockTimeseriesAnnotationLayer: TimeseriesAnnotationLayer = { annotationType: AnnotationType.Timeseries, color: null, hideLine: false, name: 'Timeseries annotation layer', overrides: { time_range: null, }, show: true, showLabel: false, showMarkers: false, sourceType: AnnotationSourceType.Line, style: AnnotationStyle.Solid, value: 1, width: 1, }; const mockTimeseriesAnnotationData: AnnotationData = { 'Timeseries annotation layer': { records: [ { x: 10, y: 12 }, { x: 12, y: 15 }, { x: 15, y: 20 }, ], }, }; describe('transformTimeseriesAnnotation', () => { test('should transform data correctly', () => { expect( transformTimeseriesAnnotation( mockTimeseriesAnnotationLayer, 1, mockData, mockTimeseriesAnnotationData, CategoricalColorNamespace.getScale(''), ).map(annotation => annotation.data), ).toEqual([ [ [10, 12], [12, 15], [15, 20], ], ]); }); test('should swap x and y for horizontal chart', () => { expect( transformTimeseriesAnnotation( mockTimeseriesAnnotationLayer, 1, mockData, mockTimeseriesAnnotationData, CategoricalColorNamespace.getScale(''), undefined, OrientationType.Horizontal, ).map(annotation => annotation.data), ).toEqual([ [ [12, 10], [15, 12], [20, 15], ], ]); }); });