@gooddata/react-components
Version:
GoodData.UI - A powerful JavaScript library for building analytical applications
1,375 lines (1,229 loc) • 98.4 kB
text/typescript
// (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