UNPKG

@gooddata/react-components

Version:

GoodData.UI - A powerful JavaScript library for building analytical applications

1,375 lines (1,229 loc) • 98.4 kB
// (C) 2019-2020 GoodData Corporation import cloneDeep = require("lodash/cloneDeep"); import set = require("lodash/set"); import { ATTRIBUTE, DATE_DATASET_ATTRIBUTE, METRIC } from "../../constants/bucket"; import { applyUiConfig, filterOutArithmeticMeasuresFromDerived, filterOutDerivedMeasures, filterOutIncompatibleArithmeticMeasures, findDerivedBucketItem, findDerivedBucketItems, findMasterBucketItem, findMasterBucketItems, generateBucketTitleId, getAllAttributeItemsWithPreference, getAllMeasuresShowOnSecondaryAxis, getAttributeItems, getBucketItemsWithExcludeByType, getComparisonTypeFromFilters, getDateFilter, getDerivedTypesFromArithmeticMeasure, getFilteredMeasuresForStackedCharts, getFirstMasterWithDerived, getFirstValidMeasure, getItemsFromBuckets, hasDerivedBucketItems, isDateBucketItem, keepOnlyMasterAndDerivedMeasuresOfType, limitNumberOfMeasuresInBuckets, noColumnsAndHasOneMeasure, noRowsAndHasOneMeasure, removeAllArithmeticMeasuresFromDerived, removeAllDerivedMeasures, removeDuplicateBucketItems, sanitizeFilters, setBucketTitles, getOccupiedMeasureBucketsLocalIdentifiers, transformMeasureBuckets, IMeasureBucketItemsLimit, } from "../bucketHelper"; import { IBucket, IBucketItem, IExtendedReferencePoint, IFilters, IUiConfig, } from "../../interfaces/Visualization"; import { DEFAULT_BASE_CHART_UICONFIG } from "../../constants/uiConfig"; import * as referencePointMocks from "../../mocks/referencePointMocks"; import { createInternalIntl } from "../internalIntlProvider"; import { Execution, VisualizationObject } from "@gooddata/typings"; import { visualizationObjectMock } from "../../mocks/visualizationObjectMocks"; import { OverTimeComparisonTypes } from "../../../interfaces/OverTimeComparison"; import * as BucketNames from "../../../constants/bucketNames"; import { VisType, VisualizationTypes } from "../../../constants/visualizationTypes"; import { DEFAULT_LOCALE } from "../../../constants/localization"; const simpleMeasure1 = { localIdentifier: "m1" }; const simpleMeasure2 = { localIdentifier: "m2" }; const derivedMeasureSP = { localIdentifier: "m1_pop", masterLocalIdentifier: "m1", overTimeComparisonType: OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR, }; const derivedMeasureSP2 = { localIdentifier: "m1_pop_2", masterLocalIdentifier: "m1", overTimeComparisonType: OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR, }; const derivedMeasurePP = { localIdentifier: "m2_pop", masterLocalIdentifier: "m2", overTimeComparisonType: OverTimeComparisonTypes.PREVIOUS_PERIOD, }; const arithmeticMeasure = { localIdentifier: "am0", operandLocalIdentifiers: ["m1", "m2"], operator: "sum", }; const arithmeticMeasureFromDerivedMeasureSP = { localIdentifier: "am1", operandLocalIdentifiers: ["m1", "m1_pop"], operator: "sum", }; const arithmeticMeasureFromDerivedMeasurePP = { localIdentifier: "am2", operandLocalIdentifiers: ["m1", "m2_pop"], operator: "sum", }; const arithmeticMeasureFromDerivedMeasuresOfBothTypes = { localIdentifier: "am3", operandLocalIdentifiers: ["m2_pop", "m1_pop"], operator: "sum", }; const invalidArithmeticMeasure = { localIdentifier: "am4", operandLocalIdentifiers: ["m1", "non-existing-measure"], operator: "sum", }; const cyclicArithmeticMeasure = { localIdentifier: "am5", operandLocalIdentifiers: ["m1", "am5"], operator: "sum", }; const incompleteArithmeticMeasure = { localIdentifier: "am6", operandLocalIdentifiers: ["m1", null], operator: "sum", }; describe("sanitizeFilters", () => { it("should keep just attribute filters that have corresponding attribute in buckets", () => { const extendedReferencePoint: IExtendedReferencePoint = { ...referencePointMocks.oneAttributeTwoFiltersReferencePoint, uiConfig: DEFAULT_BASE_CHART_UICONFIG, }; const newExtendedReferencePoint = sanitizeFilters(extendedReferencePoint); expect(newExtendedReferencePoint.filters.items).toHaveLength(1); }); it("should keep attribute filters that are not auto-created even if the corresponding attribute is not in buckets", () => { const newReferencePoint = cloneDeep(referencePointMocks.oneAttributeTwoFiltersReferencePoint); newReferencePoint.filters.items[1].autoCreated = false; const extendedReferencePoint: IExtendedReferencePoint = { ...newReferencePoint, uiConfig: DEFAULT_BASE_CHART_UICONFIG, }; const newExtendedReferencePoint = sanitizeFilters(extendedReferencePoint); expect(newExtendedReferencePoint.filters.items).toHaveLength(2); }); it("should keep just measure value filters based on measures that exist in extended reference point", () => { const newReferencePoint = cloneDeep(referencePointMocks.measureValueFilterReferencePoint); newReferencePoint.buckets[0].items.splice(1); const extendedReferencePoint: IExtendedReferencePoint = { ...newReferencePoint, uiConfig: DEFAULT_BASE_CHART_UICONFIG, }; const newExtendedReferencePoint = sanitizeFilters(extendedReferencePoint); expect(newExtendedReferencePoint.filters.items).toHaveLength(1); }); it("should remove all measure value filters when there is no attribute or date in buckets", () => { const newReferencePoint = cloneDeep(referencePointMocks.measureValueFilterReferencePoint); newReferencePoint.buckets[1].items = []; const extendedReferencePoint: IExtendedReferencePoint = { ...newReferencePoint, uiConfig: DEFAULT_BASE_CHART_UICONFIG, }; const newExtendedReferencePoint = sanitizeFilters(extendedReferencePoint); expect(newExtendedReferencePoint.filters.items).toHaveLength(0); }); it("should handle reference point without filters", () => { const newReferencePoint = cloneDeep(referencePointMocks.measureValueFilterReferencePoint); delete newReferencePoint.filters; const extendedReferencePoint: IExtendedReferencePoint = { ...newReferencePoint, uiConfig: DEFAULT_BASE_CHART_UICONFIG, }; const newExtendedReferencePoint = sanitizeFilters(extendedReferencePoint); expect(newExtendedReferencePoint.filters).toEqual({ localIdentifier: "filters", items: [], }); }); }); describe("Bucket title", () => { it("should generate title by keyName and visualizationType", () => { expect(generateBucketTitleId("metrics", "table")).toEqual("dashboard.bucket.metrics_title.table"); }); it("should set translated bucket titles for all buckets except filters when intl provided", () => { const visualizationType = VisualizationTypes.BAR; const intl = createInternalIntl(DEFAULT_LOCALE); const expectedUiconfig: IUiConfig = cloneDeep(DEFAULT_BASE_CHART_UICONFIG); set(expectedUiconfig, ["buckets", "measures", "title"], "Measures"); set(expectedUiconfig, ["buckets", "view", "title"], "View by"); set(expectedUiconfig, ["buckets", "stack", "title"], "Stack by"); const referencePoint: IExtendedReferencePoint = { buckets: referencePointMocks.emptyReferencePoint.buckets, filters: referencePointMocks.emptyReferencePoint.filters, uiConfig: DEFAULT_BASE_CHART_UICONFIG, }; expect(setBucketTitles(referencePoint, visualizationType, intl)).toEqual(expectedUiconfig); }); it("should set untranslated bucket titles for all buckets except filters when intl not provided", () => { const visualizationType = VisualizationTypes.BAR; const expectedUiconfig: IUiConfig = cloneDeep(DEFAULT_BASE_CHART_UICONFIG); set(expectedUiconfig, ["buckets", "measures", "title"], generateBucketTitleId("measures", "bar")); set(expectedUiconfig, ["buckets", "view", "title"], generateBucketTitleId("view", "bar")); set(expectedUiconfig, ["buckets", "stack", "title"], generateBucketTitleId("stack", "bar")); const referencePoint: IExtendedReferencePoint = { buckets: referencePointMocks.emptyReferencePoint.buckets, filters: referencePointMocks.emptyReferencePoint.filters, uiConfig: DEFAULT_BASE_CHART_UICONFIG, }; expect(setBucketTitles(referencePoint, visualizationType)).toEqual(expectedUiconfig); }); it("should not create bucket titles for disabled buckets", () => { const visualizationType = VisualizationTypes.PIE; const intl = createInternalIntl(DEFAULT_LOCALE); const expectedUiconfig: IUiConfig = cloneDeep(DEFAULT_BASE_CHART_UICONFIG); set(expectedUiconfig, ["buckets", "measures", "title"], "Measures"); set(expectedUiconfig, ["buckets", "view", "title"], "View by"); set(expectedUiconfig, ["buckets", "stack", "enabled"], false); const uiConfigWithDisabledBucket: IUiConfig = cloneDeep(DEFAULT_BASE_CHART_UICONFIG); set(uiConfigWithDisabledBucket, ["buckets", "stack", "enabled"], false); const referencePoint: IExtendedReferencePoint = { buckets: referencePointMocks.emptyReferencePoint.buckets, filters: referencePointMocks.emptyReferencePoint.filters, uiConfig: uiConfigWithDisabledBucket, }; expect(setBucketTitles(referencePoint, visualizationType, intl)).toEqual(expectedUiconfig); }); }); describe("getAllAttributeItemsWithPreference", () => { it("should return all attributes with prefered first", () => { const buckets = cloneDeep(referencePointMocks.wrongBucketsOrderReferencePoint).buckets; expect(getAllAttributeItemsWithPreference(buckets, ["view", "stack"])).toEqual([ { localIdentifier: "a1", type: "attribute", aggregation: null, attribute: "attr.owner.department", }, { localIdentifier: "a2", type: "attribute", aggregation: null, attribute: "attr.stage.iswon", }, ]); }); }); describe("getAttributeItems", () => { it("should return all attributes ", () => { const buckets = cloneDeep(referencePointMocks.bucketsJustStackReferencePoint).buckets; expect(getAttributeItems(buckets)).toEqual([ { localIdentifier: "a2", type: "attribute", aggregation: null, attribute: "attr.stage.iswon", }, ]); }); }); describe("getBucketItemsWithExcludeByType", () => { it("should return all measures when exclude is empty", () => { const buckets = cloneDeep(referencePointMocks.twoMeasureBucketsReferencePoint).buckets; expect(getBucketItemsWithExcludeByType(buckets, [], ["metric"])).toEqual([ { localIdentifier: "m1", type: "metric", aggregation: null, attribute: "aazb6kroa3iC", showInPercent: null, showOnSecondaryAxis: null, }, { localIdentifier: "m2", type: "metric", aggregation: null, attribute: "af2Ewj9Re2vK", showInPercent: null, showOnSecondaryAxis: null, }, { localIdentifier: "m3", type: "metric", aggregation: null, attribute: "dt.opportunitysnapshot.snapshotdate", showInPercent: null, showOnSecondaryAxis: true, }, { localIdentifier: "m4", type: "metric", aggregation: null, attribute: "acfWntEMcom0", showInPercent: null, showOnSecondaryAxis: true, }, ]); }); it("should return measures only from secondary measure bucket", () => { const buckets = cloneDeep(referencePointMocks.twoMeasureBucketsReferencePoint).buckets; expect(getBucketItemsWithExcludeByType(buckets, ["measures"], ["metric"])).toEqual([ { localIdentifier: "m3", type: "metric", aggregation: null, attribute: "dt.opportunitysnapshot.snapshotdate", showInPercent: null, showOnSecondaryAxis: true, }, { localIdentifier: "m4", type: "metric", aggregation: null, attribute: "acfWntEMcom0", showInPercent: null, showOnSecondaryAxis: true, }, ]); }); it("should return no attributes because viewBy is excluded", () => { const buckets = cloneDeep(referencePointMocks.twoMeasureBucketsReferencePoint).buckets; expect(getBucketItemsWithExcludeByType(buckets, ["view"], ["attribute"])).toEqual([]); }); }); describe("isDateBucketItem", () => { it("should not fail when passed null or undefined", () => { expect(isDateBucketItem(null)).toBe(false); expect(isDateBucketItem(undefined)).toBe(false); }); it("should not fail when passed incomplete bucket item", () => { const bucketItem: IBucketItem = { localIdentifier: "", }; expect(isDateBucketItem(bucketItem)).toBe(false); }); it("should return true if the attribute prop matches date id", () => { const bucketItem: IBucketItem = { localIdentifier: "", attribute: DATE_DATASET_ATTRIBUTE, }; expect(isDateBucketItem(bucketItem)).toBe(true); }); it("should return false if the attribute prop does not match date id", () => { const bucketItem: IBucketItem = { localIdentifier: "", attribute: "something", }; expect(isDateBucketItem(bucketItem)).toBe(false); }); }); describe("getDateFilter", () => { it("should get date filter from filter bucket", () => { const filterBucket: IFilters = { localIdentifier: "filters", items: [ { localIdentifier: "f1", filters: [referencePointMocks.attributeFilter], }, { localIdentifier: "f1", filters: [referencePointMocks.dateFilter], }, ], }; expect(getDateFilter(filterBucket)).toBe(referencePointMocks.dateFilter); }); it("should get comparison type NOTHING when date filter not present", () => { const filterBucket: IFilters = { localIdentifier: "filters", items: [ { localIdentifier: "f1", filters: [referencePointMocks.attributeFilter], }, ], }; expect(getDateFilter(filterBucket)).toBe(null); }); it("should get comparison type NOTHING when bucket is empty", () => { const filterBucket: IFilters = { localIdentifier: "filters", items: [], }; expect(getDateFilter(filterBucket)).toBe(null); }); }); describe("getUsedComparisonType", () => { it("should get comparison type SP from date filter set to SP", () => { const overTimeComparisonType = OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR; const filterBucket: IFilters = { localIdentifier: "filters", items: [ { localIdentifier: "f1", filters: [referencePointMocks.attributeFilter], }, { localIdentifier: "f1", filters: [referencePointMocks.dateFilterSamePeriodPreviousYear], }, ], }; expect(getComparisonTypeFromFilters(filterBucket)).toBe(overTimeComparisonType); }); it("should get comparison type NOTHING when date filter not present", () => { const filterBucket: IFilters = { localIdentifier: "filters", items: [ { localIdentifier: "f1", filters: [referencePointMocks.attributeFilter], }, ], }; expect(getComparisonTypeFromFilters(filterBucket)).toBe(OverTimeComparisonTypes.NOTHING); }); it("should get comparison type NOTHING when bucket is empty", () => { const filterBucket: IFilters = { localIdentifier: "filters", items: [], }; expect(getComparisonTypeFromFilters(filterBucket)).toBe(OverTimeComparisonTypes.NOTHING); }); it("should get comparison type NOTHING from date filter without comparison setting", () => { const filterBucket: IFilters = { localIdentifier: "filters", items: [ { localIdentifier: "f1", filters: [referencePointMocks.attributeFilter], }, { localIdentifier: "f1", filters: [referencePointMocks.dateFilter], }, ], }; expect(getComparisonTypeFromFilters(filterBucket)).toBe(OverTimeComparisonTypes.NOTHING); }); }); describe("getDerivedTypesFromArithmeticMeasure", () => { it("should return empty array when arithmetic measure does have any derived measure in its ancestors", () => { const buckets = [ { localIdentifier: "measures", items: [arithmeticMeasure, simpleMeasure1, simpleMeasure2], }, ]; expect(getDerivedTypesFromArithmeticMeasure(arithmeticMeasure, buckets)).toEqual([]); }); it("should return array with SP when arithmetic measure has one SP derived measure in its ancestors", () => { const buckets = [ { localIdentifier: "measures", items: [arithmeticMeasureFromDerivedMeasureSP, simpleMeasure1, derivedMeasureSP], }, ]; expect(getDerivedTypesFromArithmeticMeasure(arithmeticMeasureFromDerivedMeasureSP, buckets)).toEqual([ OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR, ]); }); // tslint:disable-next-line:max-line-length it("should return array with SP when arithmetic measure has one SP derived measure in its ancestors and is in different bucket", () => { const buckets = [ { localIdentifier: "measures", items: [simpleMeasure1, derivedMeasureSP], }, { localIdentifier: "secondary_measures", items: [arithmeticMeasureFromDerivedMeasureSP], }, ]; expect(getDerivedTypesFromArithmeticMeasure(arithmeticMeasureFromDerivedMeasureSP, buckets)).toEqual([ OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR, ]); }); it("should return array with SP when arithmetic measure has two SP derived measures in its ancestors", () => { const arithmeticMeasure = { localIdentifier: "am", operandLocalIdentifiers: ["m1_pop", "m1_pop"], operator: "sum", }; const buckets = [ { localIdentifier: "measures", items: [arithmeticMeasure, derivedMeasureSP], }, ]; expect(getDerivedTypesFromArithmeticMeasure(arithmeticMeasure, buckets)).toEqual([ OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR, ]); }); it("should return array with PP when arithmetic measure has one PP derived measure in its ancestors", () => { const buckets = [ { localIdentifier: "measures", items: [arithmeticMeasureFromDerivedMeasurePP, simpleMeasure1, derivedMeasurePP], }, ]; expect(getDerivedTypesFromArithmeticMeasure(arithmeticMeasureFromDerivedMeasurePP, buckets)).toEqual([ OverTimeComparisonTypes.PREVIOUS_PERIOD, ]); }); it("should return array with PP when arithmetic measure has two PP derived measures in its ancestors", () => { const arithmeticMeasure = { localIdentifier: "am", operandLocalIdentifiers: ["m2_pop", "m2_pop"], operator: "sum", }; const buckets = [ { localIdentifier: "measures", items: [arithmeticMeasure, derivedMeasurePP], }, ]; expect(getDerivedTypesFromArithmeticMeasure(arithmeticMeasure, buckets)).toEqual([ OverTimeComparisonTypes.PREVIOUS_PERIOD, ]); }); // tslint:disable-next-line:max-line-length it("should return array with SP and PP when arithmetic measure has one SP and PP derived measure in its ancestors", () => { const buckets = [ { localIdentifier: "measures", items: [arithmeticMeasureFromDerivedMeasuresOfBothTypes, derivedMeasureSP, derivedMeasurePP], }, ]; expect( getDerivedTypesFromArithmeticMeasure(arithmeticMeasureFromDerivedMeasuresOfBothTypes, buckets), ).toEqual([ OverTimeComparisonTypes.PREVIOUS_PERIOD, OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR, ]); }); // tslint:disable-next-line:max-line-length it("should return array with SP and PP when arithmetic measure has multiple SP and PP derived measures in its ancestors", () => { const arithmeticMeasure1 = { localIdentifier: "am1", operandLocalIdentifiers: ["m1", "am2"], operator: "sum", }; const arithmeticMeasure2 = { localIdentifier: "am2", operandLocalIdentifiers: ["m1_pop", "am3"], operator: "sum", }; const arithmeticMeasure3 = { localIdentifier: "am3", operandLocalIdentifiers: ["am4", "m1_pop"], operator: "sum", }; const arithmeticMeasure4 = { localIdentifier: "am4", operandLocalIdentifiers: ["m1_pop_2", "m2_pop"], operator: "sum", }; const buckets = [ { localIdentifier: "measures", items: [ arithmeticMeasure1, arithmeticMeasure2, arithmeticMeasure3, arithmeticMeasure4, derivedMeasureSP, derivedMeasurePP, derivedMeasureSP2, ], }, ]; expect(getDerivedTypesFromArithmeticMeasure(arithmeticMeasure1, buckets)).toEqual([ OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR, OverTimeComparisonTypes.PREVIOUS_PERIOD, ]); }); it("should return array with SP when invalid arithmetic measure has SP derived measure in its ancestors", () => { const buckets = [ { localIdentifier: "measures", items: [ arithmeticMeasureFromDerivedMeasuresOfBothTypes, // second operand is not in allMeasures derivedMeasureSP, ], }, ]; expect( getDerivedTypesFromArithmeticMeasure(arithmeticMeasureFromDerivedMeasuresOfBothTypes, buckets), ).toEqual([OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR]); }); it("should return array with SP when incomplete arithmetic measure has SP derived measure in its ancestors", () => { const arithmeticMeasure = { localIdentifier: "am", operandLocalIdentifiers: ["m1_pop", null], operator: "sum", }; const buckets = [ { localIdentifier: "measures", items: [arithmeticMeasure, derivedMeasureSP], }, ]; expect(getDerivedTypesFromArithmeticMeasure(arithmeticMeasure, buckets)).toEqual([ OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR, ]); }); // tslint:disable-next-line:max-line-length it("should return empty array when incomplete arithmetic measure does have any derived measure in its ancestors", () => { const arithmeticMeasure = { localIdentifier: "am", operandLocalIdentifiers: ["m1", null], operator: "sum", }; const buckets = [ { localIdentifier: "measures", items: [arithmeticMeasure, simpleMeasure1], }, ]; expect(getDerivedTypesFromArithmeticMeasure(arithmeticMeasure, buckets)).toEqual([]); }); // tslint:disable-next-line:max-line-length it("should return empty array when cyclic arithmetic measure does have any derived measure in its ancestors", () => { const buckets = [ { localIdentifier: "measures", items: [cyclicArithmeticMeasure, simpleMeasure1], }, ]; expect(getDerivedTypesFromArithmeticMeasure(cyclicArithmeticMeasure, buckets)).toEqual([]); }); it("should return array with SP when cyclic arithmetic measure has SP derived measure in its ancestors", () => { const arithmeticMeasure1 = { localIdentifier: "am1", operandLocalIdentifiers: ["m1", "am2"], operator: "sum", }; const arithmeticMeasure2 = { localIdentifier: "am2", operandLocalIdentifiers: ["m1_pop", "am3"], operator: "sum", }; const arithmeticMeasure3 = { localIdentifier: "am3", operandLocalIdentifiers: ["m1", "am1"], operator: "sum", }; const buckets = [ { localIdentifier: "measures", items: [ arithmeticMeasure1, arithmeticMeasure2, arithmeticMeasure3, simpleMeasure1, simpleMeasure2, ], }, ]; expect(getDerivedTypesFromArithmeticMeasure(arithmeticMeasure1, buckets)).toEqual([]); }); }); describe("filterOutDerivedMeasures", () => { it("should filter out derived measures no matter of derived type", () => { const measures = [ simpleMeasure1, derivedMeasureSP, simpleMeasure2, derivedMeasurePP, arithmeticMeasure, arithmeticMeasureFromDerivedMeasureSP, arithmeticMeasureFromDerivedMeasurePP, arithmeticMeasureFromDerivedMeasuresOfBothTypes, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, ]; expect(filterOutDerivedMeasures(measures)).toEqual([ measures[0], measures[2], arithmeticMeasure, arithmeticMeasureFromDerivedMeasureSP, arithmeticMeasureFromDerivedMeasurePP, arithmeticMeasureFromDerivedMeasuresOfBothTypes, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, ]); }); }); describe("filterOutArithmeticMeasuresFromDerived", () => { it("should remove all AM made from derived measures from all buckets", () => { const buckets = [ { localIdentifier: "measures", items: [ simpleMeasure2, derivedMeasurePP, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, ], }, { localIdentifier: "secondary_measures", items: [ simpleMeasure1, derivedMeasureSP, arithmeticMeasure, arithmeticMeasureFromDerivedMeasureSP, arithmeticMeasureFromDerivedMeasurePP, arithmeticMeasureFromDerivedMeasuresOfBothTypes, ], }, ]; expect(filterOutArithmeticMeasuresFromDerived(buckets[1].items, buckets)).toEqual([ simpleMeasure1, derivedMeasureSP, arithmeticMeasure, ]); }); }); describe("keepOnlyMasterAndDerivedMeasuresOfType", () => { const measures = [ simpleMeasure1, derivedMeasureSP, simpleMeasure2, derivedMeasurePP, arithmeticMeasure, arithmeticMeasureFromDerivedMeasureSP, arithmeticMeasureFromDerivedMeasurePP, arithmeticMeasureFromDerivedMeasuresOfBothTypes, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, ]; it("should filter out all derived measures when filtering NOTHING", () => { expect(keepOnlyMasterAndDerivedMeasuresOfType(measures, OverTimeComparisonTypes.NOTHING)).toEqual([ measures[0], measures[2], arithmeticMeasure, arithmeticMeasureFromDerivedMeasureSP, arithmeticMeasureFromDerivedMeasurePP, arithmeticMeasureFromDerivedMeasuresOfBothTypes, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, ]); }); it("should filter out all derived but not SP derived", () => { expect( keepOnlyMasterAndDerivedMeasuresOfType( measures, OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR, ), ).toEqual([ measures[0], derivedMeasureSP, measures[2], arithmeticMeasure, arithmeticMeasureFromDerivedMeasureSP, arithmeticMeasureFromDerivedMeasurePP, arithmeticMeasureFromDerivedMeasuresOfBothTypes, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, ]); }); it("should filter out all derived but not PP derived", () => { expect( keepOnlyMasterAndDerivedMeasuresOfType(measures, OverTimeComparisonTypes.PREVIOUS_PERIOD), ).toEqual([ measures[0], measures[2], derivedMeasurePP, arithmeticMeasure, arithmeticMeasureFromDerivedMeasureSP, arithmeticMeasureFromDerivedMeasurePP, arithmeticMeasureFromDerivedMeasuresOfBothTypes, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, ]); }); }); describe("filterOutIncompatibleArithmeticMeasures", () => { const buckets = [ { localIdentifier: "measures", items: [ simpleMeasure1, derivedMeasureSP, simpleMeasure2, derivedMeasurePP, arithmeticMeasure, arithmeticMeasureFromDerivedMeasureSP, arithmeticMeasureFromDerivedMeasurePP, arithmeticMeasureFromDerivedMeasuresOfBothTypes, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, ], }, ]; it("should filter out all AM from derived measures when filtering NOTHING", () => { expect( filterOutIncompatibleArithmeticMeasures( buckets[0].items, buckets, OverTimeComparisonTypes.NOTHING, ), ).toEqual([ simpleMeasure1, derivedMeasureSP, simpleMeasure2, derivedMeasurePP, arithmeticMeasure, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, ]); }); it("should filter out all AM from derived measures but not the one made from SP", () => { const result = filterOutIncompatibleArithmeticMeasures( buckets[0].items, buckets, OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR, ); expect(result).toEqual([ simpleMeasure1, derivedMeasureSP, simpleMeasure2, derivedMeasurePP, arithmeticMeasure, arithmeticMeasureFromDerivedMeasureSP, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, ]); }); it("should filter out all AM from derived measures but not the one made from PP", () => { const result = filterOutIncompatibleArithmeticMeasures( buckets[0].items, buckets, OverTimeComparisonTypes.PREVIOUS_PERIOD, ); expect(result).toEqual([ simpleMeasure1, derivedMeasureSP, simpleMeasure2, derivedMeasurePP, arithmeticMeasure, arithmeticMeasureFromDerivedMeasurePP, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, ]); }); it("should filter out all AM made from derived but not the one from SP derived across all buckets", () => { const buckets = [ { localIdentifier: "measures", items: [simpleMeasure1, derivedMeasureSP, derivedMeasurePP], }, { localIdentifier: "secondary_measures", items: [arithmeticMeasureFromDerivedMeasureSP, arithmeticMeasureFromDerivedMeasurePP], }, ]; const result = filterOutIncompatibleArithmeticMeasures( buckets[1].items, buckets, OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR, ); expect(result).toEqual([arithmeticMeasureFromDerivedMeasureSP]); }); }); describe("applyUiConfig", () => { it("should apply itemsLimit=0", () => { const buckets = cloneDeep(referencePointMocks.multipleMetricsAndCategoriesReferencePoint).buckets; const uiConfig = cloneDeep(DEFAULT_BASE_CHART_UICONFIG); uiConfig.buckets[BucketNames.MEASURES].itemsLimit = 0; uiConfig.buckets[BucketNames.VIEW].itemsLimit = 0; const expectedBuckets = cloneDeep(referencePointMocks.multipleMetricsAndCategoriesReferencePoint) .buckets; expectedBuckets[0].items = []; expectedBuckets[1].items = []; const referencePoint: IExtendedReferencePoint = { buckets, filters: referencePointMocks.emptyReferencePoint.filters, uiConfig, }; expect(applyUiConfig(referencePoint).buckets).toEqual(expectedBuckets); }); it("should apply itemsLimit=1", () => { const buckets = cloneDeep(referencePointMocks.multipleMetricsAndCategoriesReferencePoint).buckets; const uiConfig = cloneDeep(DEFAULT_BASE_CHART_UICONFIG); uiConfig.buckets[BucketNames.MEASURES].itemsLimit = 1; uiConfig.buckets[BucketNames.VIEW].itemsLimit = 1; const expectedBuckets = cloneDeep(referencePointMocks.multipleMetricsAndCategoriesReferencePoint) .buckets; expectedBuckets[0].items = expectedBuckets[0].items.slice(0, 1); expectedBuckets[1].items = expectedBuckets[1].items.slice(0, 1); const referencePoint: IExtendedReferencePoint = { buckets, filters: referencePointMocks.emptyReferencePoint.filters, uiConfig, }; expect(applyUiConfig(referencePoint).buckets).toEqual(expectedBuckets); }); it("should NOT count derived items to itemsLimit", () => { const buckets = cloneDeep(referencePointMocks.measureWithDerivedAsFirstRefPoint).buckets; const uiConfig = cloneDeep(DEFAULT_BASE_CHART_UICONFIG); uiConfig.buckets[BucketNames.MEASURES].itemsLimit = 1; uiConfig.buckets[BucketNames.VIEW].itemsLimit = 1; const expectedBuckets = cloneDeep(referencePointMocks.measureWithDerivedAsFirstRefPoint).buckets; const referencePoint: IExtendedReferencePoint = { buckets, filters: referencePointMocks.emptyReferencePoint.filters, uiConfig, }; expect(applyUiConfig(referencePoint).buckets).toEqual(expectedBuckets); }); it("should handle undefined itemsLimit", () => { const buckets = cloneDeep(referencePointMocks.multipleMetricsAndCategoriesReferencePoint).buckets; const uiConfig = cloneDeep(DEFAULT_BASE_CHART_UICONFIG); uiConfig.buckets[BucketNames.MEASURES].itemsLimit = undefined; uiConfig.buckets[BucketNames.VIEW].itemsLimit = undefined; const expectedBuckets = cloneDeep(referencePointMocks.multipleMetricsAndCategoriesReferencePoint) .buckets; const referencePoint: IExtendedReferencePoint = { buckets, filters: referencePointMocks.emptyReferencePoint.filters, uiConfig, }; expect(applyUiConfig(referencePoint).buckets).toEqual(expectedBuckets); }); }); describe("getFirstMasterHavingDerived", () => { it("should find first master measure and return it together with its derived measures", () => { const measures = [ referencePointMocks.derivedMeasureItems[1], referencePointMocks.derivedMeasureItems[0], referencePointMocks.masterMeasureItems[0], referencePointMocks.masterMeasureItems[1], ]; const result = getFirstMasterWithDerived(measures); expect(result).toEqual([ referencePointMocks.derivedMeasureItems[0], referencePointMocks.masterMeasureItems[0], ]); }); it("should keep the order of master and derived measures", () => { const measures = [ referencePointMocks.derivedMeasureItems[1], referencePointMocks.masterMeasureItems[0], referencePointMocks.derivedMeasureItems[0], referencePointMocks.masterMeasureItems[1], ]; const result = getFirstMasterWithDerived(measures); expect(result).toEqual([ referencePointMocks.masterMeasureItems[0], referencePointMocks.derivedMeasureItems[0], ]); }); it("should handle only masters", () => { const measures: IBucketItem[] = [ referencePointMocks.masterMeasureItems[1], referencePointMocks.masterMeasureItems[0], ]; const result = getFirstMasterWithDerived(measures); expect(result).toEqual([referencePointMocks.masterMeasureItems[1]]); }); it("should handle empty array", () => { const measures: IBucketItem[] = []; const result = getFirstMasterWithDerived(measures); expect(result).toEqual([]); }); }); describe("removeAllDerivedMeasures", () => { it("removes derived measures from all buckets", () => { const refPoint: IExtendedReferencePoint = { buckets: [ { localIdentifier: "measures", items: [ referencePointMocks.derivedMeasureItems[1], referencePointMocks.derivedMeasureItems[0], referencePointMocks.masterMeasureItems[0], referencePointMocks.masterMeasureItems[1], ], }, { localIdentifier: "secondary_measures", items: [referencePointMocks.masterMeasureItems[2]], }, { localIdentifier: "tertiary_measures", items: [referencePointMocks.derivedMeasureItems[2]], }, ], filters: { localIdentifier: "filters", items: [], }, uiConfig: { buckets: {}, }, }; const result = removeAllDerivedMeasures(refPoint); expect(result.buckets).toEqual([ { localIdentifier: "measures", items: [referencePointMocks.masterMeasureItems[0], referencePointMocks.masterMeasureItems[1]], }, { localIdentifier: "secondary_measures", items: [referencePointMocks.masterMeasureItems[2]], }, { localIdentifier: "tertiary_measures", items: [], }, ]); }); }); describe("removeAllArithmeticMeasuresFromDerived", () => { const arithmeticMeasureFromDerivedMeasure = { localIdentifier: "am10", operandLocalIdentifiers: ["m1", "m1_pop"], operator: "sum", }; const deepArithmeticMeasureFromDerivedMeasureInThirdLevel = { localIdentifier: "am11", operandLocalIdentifiers: ["m1", "am10"], operator: "sum", }; it("should remove all AM made from derived measures from all buckets", () => { const refPoint: IExtendedReferencePoint = { buckets: [ { localIdentifier: "measures", items: [ simpleMeasure1, derivedMeasureSP, simpleMeasure2, derivedMeasurePP, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, arithmeticMeasureFromDerivedMeasure, ], }, { localIdentifier: "secondary_measures", items: [ arithmeticMeasure, arithmeticMeasureFromDerivedMeasureSP, arithmeticMeasureFromDerivedMeasurePP, ], }, { localIdentifier: "tertiary_measures", items: [ arithmeticMeasureFromDerivedMeasuresOfBothTypes, deepArithmeticMeasureFromDerivedMeasureInThirdLevel, ], }, ], filters: { localIdentifier: "filters", items: [], }, uiConfig: { buckets: {}, }, }; const result = removeAllArithmeticMeasuresFromDerived(refPoint); expect(result.buckets).toEqual([ { localIdentifier: "measures", items: [ simpleMeasure1, derivedMeasureSP, simpleMeasure2, derivedMeasurePP, invalidArithmeticMeasure, incompleteArithmeticMeasure, cyclicArithmeticMeasure, ], }, { localIdentifier: "secondary_measures", items: [arithmeticMeasure], }, { localIdentifier: "tertiary_measures", items: [], }, ]); }); }); describe("findMasterBucketItem", () => { it("should find master measure", () => { const bucketItems: IBucketItem[] = [ referencePointMocks.derivedMeasureItems[1], referencePointMocks.derivedMeasureItems[0], referencePointMocks.masterMeasureItems[0], referencePointMocks.masterMeasureItems[1], ]; const masterBucketItem = findMasterBucketItem( referencePointMocks.derivedMeasureItems[0], bucketItems, ); expect(masterBucketItem).toEqual(referencePointMocks.masterMeasureItems[0]); }); it("should return undefined when master measure is not found", () => { const bucketItems: IBucketItem[] = [ referencePointMocks.derivedMeasureItems[1], referencePointMocks.derivedMeasureItems[0], referencePointMocks.masterMeasureItems[0], referencePointMocks.masterMeasureItems[1], ]; const masterBucketItem = findMasterBucketItem( referencePointMocks.derivedMeasureItems[2], bucketItems, ); expect(masterBucketItem).toEqual(undefined); }); }); describe("findMasterBucketItems", () => { it("should find all master measures", () => { const bucketItems: IBucketItem[] = [ referencePointMocks.derivedMeasureItems[1], referencePointMocks.derivedMeasureItems[0], referencePointMocks.masterMeasureItems[0], referencePointMocks.masterMeasureItems[1], ]; const masterBucketItems = findMasterBucketItems(bucketItems); expect(masterBucketItems).toEqual([ referencePointMocks.masterMeasureItems[0], referencePointMocks.masterMeasureItems[1], ]); }); it("should return empty array when master measures are not found", () => { const bucketItems: IBucketItem[] = [ referencePointMocks.derivedMeasureItems[1], referencePointMocks.derivedMeasureItems[0], ]; const masterBucketItems = findMasterBucketItems(bucketItems); expect(masterBucketItems).toEqual([]); }); }); describe("findDerivedBucketItems", () => { it("should find all master measures", () => { const bucketItems: IBucketItem[] = [ referencePointMocks.masterMeasureItems[0], referencePointMocks.masterMeasureItems[1], referencePointMocks.derivedMeasureItems[3], referencePointMocks.derivedMeasureItems[4], referencePointMocks.masterMeasureItems[3], ]; const derivedBucketItems = findDerivedBucketItems( referencePointMocks.masterMeasureItems[3], bucketItems, ); expect(derivedBucketItems).toEqual([ referencePointMocks.derivedMeasureItems[3], referencePointMocks.derivedMeasureItems[4], ]); }); it("should return empty array when derived measures are not found", () => { const bucketItems: IBucketItem[] = [ referencePointMocks.masterMeasureItems[0], referencePointMocks.masterMeasureItems[1], referencePointMocks.masterMeasureItems[3], ]; const derivedBucketItems = findDerivedBucketItems( referencePointMocks.masterMeasureItems[3], bucketItems, ); expect(derivedBucketItems).toEqual([]); }); }); describe("findDerivedBucketItem", () => { it("should return undefined when there is no derived bucket item for the master measure", () => { const bucketItems: IBucketItem[] = [ referencePointMocks.masterMeasureItems[0], referencePointMocks.derivedMeasureItems[1], ]; const derivedBucketItems = findDerivedBucketItem( referencePointMocks.masterMeasureItems[0], bucketItems, ); expect(derivedBucketItems).toBeUndefined(); }); it("should return derived bucket item for the master measure when it exists", () => { const bucketItems: IBucketItem[] = [ referencePointMocks.masterMeasureItems[0], referencePointMocks.derivedMeasureItems[0], ]; const derivedBucketItems = findDerivedBucketItem( referencePointMocks.masterMeasureItems[0], bucketItems, ); expect(derivedBucketItems).toEqual(referencePointMocks.derivedMeasureItems[0]); }); }); describe("hasDerivedBucketItems", () => { it("should return false when there is no derived bucket item for the master measure across buckets", () => { const buckets: IBucket[] = [ { localIdentifier: BucketNames.MEASURES, items: [referencePointMocks.masterMeasureItems[0]], }, { localIdentifier: BucketNames.SECONDARY_MEASURES, items: [referencePointMocks.masterMeasureItems[1]], }, ]; const hasSomeDerivedBucketItems = hasDerivedBucketItems( referencePointMocks.masterMeasureItems[0], buckets, ); expect(hasSomeDerivedBucketItems).toEqual(false); }); it("should return true when there is a derived bucket item for the master measure in the same bucket", () => { const buckets: IBucket[] = [ { localIdentifier: BucketNames.MEASURES, items: [ referencePointMocks.masterMeasureItems[0], referencePointMocks.derivedMeasureItems[0], ], }, { localIdentifier: BucketNames.SECONDARY_MEASURES, items: [referencePointMocks.masterMeasureItems[1]], }, ]; const hasSomeDerivedBucketItems = hasDerivedBucketItems( referencePointMocks.masterMeasureItems[0], buckets, ); expect(hasSomeDerivedBucketItems).toEqual(true); }); it("should return true when th