| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- /**
- * 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 { getNumberFormatter, NumberFormats } from '@superset-ui/core';
- import { SeriesOption } from 'echarts';
- import {
- extractForecastSeriesContext,
- extractForecastValuesFromTooltipParams,
- formatForecastTooltipSeries,
- rebaseForecastDatum,
- reorderForecastSeries,
- } from '../../src/utils/forecast';
- import { ForecastSeriesEnum } from '../../src/types';
- describe('extractForecastSeriesContext', () => {
- it('should extract the correct series name and type', () => {
- expect(extractForecastSeriesContext('abcd')).toEqual({
- name: 'abcd',
- type: ForecastSeriesEnum.Observation,
- });
- expect(extractForecastSeriesContext('qwerty__yhat')).toEqual({
- name: 'qwerty',
- type: ForecastSeriesEnum.ForecastTrend,
- });
- expect(extractForecastSeriesContext('X Y Z___yhat_upper')).toEqual({
- name: 'X Y Z_',
- type: ForecastSeriesEnum.ForecastUpper,
- });
- expect(extractForecastSeriesContext('1 2 3__yhat_lower')).toEqual({
- name: '1 2 3',
- type: ForecastSeriesEnum.ForecastLower,
- });
- });
- });
- describe('reorderForecastSeries', () => {
- it('should reorder the forecast series and preserve values', () => {
- const input: SeriesOption[] = [
- { id: `series${ForecastSeriesEnum.Observation}`, data: [10, 20, 30] },
- { id: `series${ForecastSeriesEnum.ForecastTrend}`, data: [15, 25, 35] },
- { id: `series${ForecastSeriesEnum.ForecastLower}`, data: [5, 15, 25] },
- { id: `series${ForecastSeriesEnum.ForecastUpper}`, data: [25, 35, 45] },
- ];
- const expectedOutput: SeriesOption[] = [
- { id: `series${ForecastSeriesEnum.ForecastLower}`, data: [5, 15, 25] },
- { id: `series${ForecastSeriesEnum.ForecastUpper}`, data: [25, 35, 45] },
- { id: `series${ForecastSeriesEnum.ForecastTrend}`, data: [15, 25, 35] },
- { id: `series${ForecastSeriesEnum.Observation}`, data: [10, 20, 30] },
- ];
- expect(reorderForecastSeries(input)).toEqual(expectedOutput);
- });
- it('should handle an empty array', () => {
- expect(reorderForecastSeries([])).toEqual([]);
- });
- it('should not reorder if no relevant series are present', () => {
- const input: SeriesOption[] = [{ id: 'some-other-series' }];
- expect(reorderForecastSeries(input)).toEqual(input);
- });
- it('should handle undefined ids', () => {
- const input: SeriesOption[] = [
- { id: `series${ForecastSeriesEnum.ForecastLower}` },
- { id: undefined },
- { id: `series${ForecastSeriesEnum.ForecastTrend}` },
- ];
- const expectedOutput: SeriesOption[] = [
- { id: `series${ForecastSeriesEnum.ForecastLower}` },
- { id: `series${ForecastSeriesEnum.ForecastTrend}` },
- { id: undefined },
- ];
- expect(reorderForecastSeries(input)).toEqual(expectedOutput);
- });
- });
- describe('rebaseForecastDatum', () => {
- it('should subtract lower confidence level from upper value', () => {
- expect(
- rebaseForecastDatum([
- {
- __timestamp: new Date('2001-01-01'),
- abc: 10,
- abc__yhat_lower: 1,
- abc__yhat_upper: 20,
- },
- {
- __timestamp: new Date('2001-01-01'),
- abc: 10,
- abc__yhat_lower: -10,
- abc__yhat_upper: 20,
- },
- {
- __timestamp: new Date('2002-01-01'),
- abc: 10,
- abc__yhat_lower: null,
- abc__yhat_upper: 20,
- },
- {
- __timestamp: new Date('2003-01-01'),
- abc: 10,
- abc__yhat_lower: 1,
- abc__yhat_upper: null,
- },
- ]),
- ).toEqual([
- {
- __timestamp: new Date('2001-01-01'),
- abc: 10,
- abc__yhat_lower: 1,
- abc__yhat_upper: 19,
- },
- {
- __timestamp: new Date('2001-01-01'),
- abc: 10,
- abc__yhat_lower: -10,
- abc__yhat_upper: 30,
- },
- {
- __timestamp: new Date('2002-01-01'),
- abc: 10,
- abc__yhat_lower: null,
- abc__yhat_upper: 20,
- },
- {
- __timestamp: new Date('2003-01-01'),
- abc: 10,
- abc__yhat_lower: 1,
- abc__yhat_upper: null,
- },
- ]);
- });
- it('should rename all series based on verboseMap but leave __timestamp alone', () => {
- expect(
- rebaseForecastDatum(
- [
- {
- __timestamp: new Date('2001-01-01'),
- abc: 10,
- abc__yhat_lower: 1,
- abc__yhat_upper: 20,
- },
- {
- __timestamp: new Date('2002-01-01'),
- abc: 10,
- abc__yhat_lower: null,
- abc__yhat_upper: 20,
- },
- {
- __timestamp: new Date('2003-01-01'),
- abc: 10,
- abc__yhat_lower: 1,
- abc__yhat_upper: null,
- },
- ],
- {
- abc: 'Abracadabra',
- __timestamp: 'Time',
- },
- ),
- ).toEqual([
- {
- __timestamp: new Date('2001-01-01'),
- Abracadabra: 10,
- Abracadabra__yhat_lower: 1,
- Abracadabra__yhat_upper: 19,
- },
- {
- __timestamp: new Date('2002-01-01'),
- Abracadabra: 10,
- Abracadabra__yhat_lower: null,
- Abracadabra__yhat_upper: 20,
- },
- {
- __timestamp: new Date('2003-01-01'),
- Abracadabra: 10,
- Abracadabra__yhat_lower: 1,
- Abracadabra__yhat_upper: null,
- },
- ]);
- });
- });
- test('extractForecastValuesFromTooltipParams should extract the proper data from tooltip params', () => {
- expect(
- extractForecastValuesFromTooltipParams([
- {
- marker: '<img>',
- seriesId: 'abc',
- value: [new Date(0), 10],
- },
- {
- marker: '<img>',
- seriesId: 'abc__yhat',
- value: [new Date(0), 1],
- },
- {
- marker: '<img>',
- seriesId: 'abc__yhat_lower',
- value: [new Date(0), 5],
- },
- {
- marker: '<img>',
- seriesId: 'abc__yhat_upper',
- value: [new Date(0), 6],
- },
- {
- marker: '<img>',
- seriesId: 'qwerty',
- value: [new Date(0), 2],
- },
- ]),
- ).toEqual({
- abc: {
- marker: '<img>',
- observation: 10,
- forecastTrend: 1,
- forecastLower: 5,
- forecastUpper: 6,
- },
- qwerty: {
- marker: '<img>',
- observation: 2,
- },
- });
- });
- test('extractForecastValuesFromTooltipParams should extract valid values', () => {
- expect(
- extractForecastValuesFromTooltipParams([
- {
- marker: '<img>',
- seriesId: 'foo',
- value: [0, 10],
- },
- {
- marker: '<img>',
- seriesId: 'bar',
- value: [100, 0],
- },
- ]),
- ).toEqual({
- foo: {
- marker: '<img>',
- observation: 10,
- },
- bar: {
- marker: '<img>',
- observation: 0,
- },
- });
- });
- const formatter = getNumberFormatter(NumberFormats.INTEGER);
- test('formatForecastTooltipSeries should apply format to value', () => {
- expect(
- formatForecastTooltipSeries({
- seriesName: 'abc',
- marker: '<img>',
- observation: 10.1,
- formatter,
- }),
- ).toEqual(['<img>abc', '10']);
- });
- test('formatForecastTooltipSeries should show falsy value', () => {
- expect(
- formatForecastTooltipSeries({
- seriesName: 'abc',
- marker: '<img>',
- observation: 0,
- formatter,
- }),
- ).toEqual(['<img>abc', '0']);
- });
- test('formatForecastTooltipSeries should format full forecast', () => {
- expect(
- formatForecastTooltipSeries({
- seriesName: 'qwerty',
- marker: '<img>',
- observation: 10.1,
- forecastTrend: 20.1,
- forecastLower: 5.1,
- forecastUpper: 7.1,
- formatter,
- }),
- ).toEqual(['<img>qwerty', '10, ŷ = 20 (5, 12)']);
- });
- test('formatForecastTooltipSeries should format forecast without observation', () => {
- expect(
- formatForecastTooltipSeries({
- seriesName: 'qwerty',
- marker: '<img>',
- forecastTrend: 20,
- forecastLower: 5,
- forecastUpper: 7,
- formatter,
- }),
- ).toEqual(['<img>qwerty', 'ŷ = 20 (5, 12)']);
- });
- test('formatForecastTooltipSeries should format forecast without point estimate', () => {
- expect(
- formatForecastTooltipSeries({
- seriesName: 'qwerty',
- marker: '<img>',
- observation: 10.1,
- forecastLower: 6,
- forecastUpper: 7,
- formatter,
- }),
- ).toEqual(['<img>qwerty', '10 (6, 13)']);
- });
- test('formatForecastTooltipSeries should format forecast with only confidence band', () => {
- expect(
- formatForecastTooltipSeries({
- seriesName: 'qwerty',
- marker: '<img>',
- forecastLower: 7,
- forecastUpper: 8,
- formatter,
- }),
- ).toEqual(['<img>qwerty', '(7, 15)']);
- });
|