@datarailsshared/dr_renderer 1.3.57 → 1.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +44 -0
- package/.whitesource +3 -0
- package/README.md +45 -3
- package/package.json +5 -6
- package/src/charts/dr_donut_chart.js +3 -3
- package/src/charts/dr_gauge_categories_summary_chart.js +17 -17
- package/src/charts/dr_gauge_chart.js +226 -64
- package/src/dr-renderer-helpers.js +30 -13
- package/src/dr_pivottable.js +21 -29
- package/src/errors.js +174 -0
- package/src/highcharts_renderer.js +530 -343
- package/src/index.d.ts +63 -0
- package/src/index.js +14 -1
- package/src/pivot.css +0 -11
- package/src/pivottable.js +469 -508
- package/src/seriesPointStyles-helper.js +1 -1
- package/src/smart_queries_helper.js +62 -14
- package/src/types/errors.d.ts +120 -0
- package/src/types/index.d.ts +2 -0
- package/src/value.formatter.js +41 -0
- package/tests/dr-renderer-helpers.test.js +33 -0
- package/tests/dr_gauge_chart.test.js +88 -0
- package/tests/errors.test.js +157 -0
- package/tests/highcharts_renderer.test.js +1029 -67
- package/tests/mock/widgets.json +1 -3
- package/tests/ptCreateDrillDownSeriesToDrilldownChart.test.js +511 -0
- package/tests/value.formatter.test.js +143 -0
- package/tsconfig.json +2 -2
- package/tsconfig.tsbuildinfo +7 -0
- package/.github/workflows/build-deploy.yml +0 -28
- package/types/index.d.ts +0 -1
- /package/{types → src/types}/graph-table-renderer.d.ts +0 -0
@@ -8,6 +8,7 @@ import addInDynamicRanges from './mock/add-in-dynamic-ranges.json';
|
|
8
8
|
import widgets from './mock/widgets.json';
|
9
9
|
import initPivotTable from "../src/pivottable";
|
10
10
|
import initDRPivotTable from "../src/dr_pivottable";
|
11
|
+
import valueFormatter from "../src/value.formatter";
|
11
12
|
import { DrGaugeChart, GAUGE_OPTIONS_DEFAULT } from "../src/charts/dr_gauge_chart";
|
12
13
|
|
13
14
|
const mockDrChartRender = jest.fn();
|
@@ -68,6 +69,8 @@ let _document = document;
|
|
68
69
|
let Highcharts;
|
69
70
|
|
70
71
|
describe('highcharts_renderer', () => {
|
72
|
+
let getAggregatorPercentageValueIfRequiredMock;
|
73
|
+
|
71
74
|
beforeAll(() => {
|
72
75
|
Highcharts = {
|
73
76
|
charts: [{
|
@@ -92,6 +95,12 @@ describe('highcharts_renderer', () => {
|
|
92
95
|
|
93
96
|
highchartsRenderer = getHighchartsRenderer($, _document, Highcharts, lodash.cloneDeep(DEFAULT_USER_COLORS), highchartsRenderer,
|
94
97
|
DataFormatter, lodash, moment, true);
|
98
|
+
|
99
|
+
getAggregatorPercentageValueIfRequiredMock = jest.spyOn(valueFormatter, 'getAggregatorPercentageValueIfRequired').mockImplementation(() => null);
|
100
|
+
});
|
101
|
+
|
102
|
+
afterAll(() => {
|
103
|
+
getAggregatorPercentageValueIfRequiredMock.mockRestore();
|
95
104
|
});
|
96
105
|
|
97
106
|
describe('Function filterFloat', () => {
|
@@ -511,19 +520,574 @@ describe('highcharts_renderer', () => {
|
|
511
520
|
});
|
512
521
|
|
513
522
|
describe('function defaultDataLabelFormatter', () => {
|
523
|
+
let mockPivotData;
|
514
524
|
let funcContext;
|
515
525
|
let opts;
|
516
526
|
|
517
527
|
beforeEach(() => {
|
518
528
|
highchartsRenderer.enabledNewWidgetValueFormatting = false;
|
519
|
-
|
520
|
-
|
529
|
+
highchartsRenderer.delimer = ' , ';
|
530
|
+
|
531
|
+
funcContext = {
|
532
|
+
y: 12345.678,
|
533
|
+
series: {
|
534
|
+
name: 'TestSeries',
|
535
|
+
userOptions: {},
|
536
|
+
options: {}
|
537
|
+
},
|
538
|
+
point: {
|
539
|
+
name: 'TestPoint',
|
540
|
+
options: {}
|
541
|
+
}
|
542
|
+
};
|
543
|
+
opts = {};
|
544
|
+
|
545
|
+
mockPivotData = {
|
546
|
+
rowAttrs: ['row1'],
|
547
|
+
colAttrs: ['col1'],
|
548
|
+
getColKeys: jest.fn(() => [['col1'], ['col2']]),
|
549
|
+
getRowKeys: jest.fn(() => [['row1'], ['row2']]),
|
550
|
+
getAggregator: jest.fn(() => ({
|
551
|
+
value: () => 1000
|
552
|
+
}))
|
553
|
+
};
|
554
|
+
|
555
|
+
spyOn(highchartsRenderer, 'getSeriesNameInFormatterContext').and.returnValue('TestSeries');
|
556
|
+
spyOn(highchartsRenderer, 'getColsInFormatterContext').and.returnValue(['col1']);
|
557
|
+
spyOn(highchartsRenderer, 'getOthersName').and.returnValue('Others');
|
558
|
+
spyOn(highchartsRenderer, 'getDrOthersInAxisState').and.returnValue({});
|
559
|
+
spyOn(highchartsRenderer, 'transformRowsAndColsForBreakdown').and.returnValue({
|
560
|
+
rows: ['row1'],
|
561
|
+
cols: ['col1']
|
562
|
+
});
|
563
|
+
spyOn(highchartsRenderer, 'replaceDrOthersKeys');
|
564
|
+
spyOn(highchartsRenderer, 'selfStartsWith').and.returnValue(false);
|
565
|
+
spyOn(highchartsRenderer, 'isChartWithMultiValues').and.returnValue(true);
|
566
|
+
|
567
|
+
global.$ = {
|
568
|
+
pivotUtilities: {
|
569
|
+
getFormattedNumber: jest.fn(() => '1,234.56')
|
570
|
+
}
|
571
|
+
};
|
521
572
|
});
|
522
573
|
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
574
|
+
describe('No pivotData is provided', () => {
|
575
|
+
it('should return formatted number as local string', () => {
|
576
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(null, {});
|
577
|
+
let result = fn.call(funcContext);
|
578
|
+
|
579
|
+
expect(result).toBe('12,345.678');
|
580
|
+
});
|
581
|
+
|
582
|
+
it('should handle unit sign removal when labelOptions are provided', () => {
|
583
|
+
const labelOptions = { useUnitAbbreviation: false };
|
584
|
+
opts = { chartOptions: { label: labelOptions } };
|
585
|
+
|
586
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(null, opts);
|
587
|
+
let result = fn.call(funcContext);
|
588
|
+
|
589
|
+
expect(result).toBe('12,345.678');
|
590
|
+
});
|
591
|
+
});
|
592
|
+
|
593
|
+
describe('pivotData is provided', () => {
|
594
|
+
beforeEach(() => {
|
595
|
+
funcContext.y = 1234.56;
|
596
|
+
});
|
597
|
+
|
598
|
+
it('should format value using pivot data aggregator when show_value is true', () => {
|
599
|
+
opts = { chartOptions: { label: { show_value: true } } };
|
600
|
+
|
601
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
602
|
+
let result = fn.call(funcContext);
|
603
|
+
|
604
|
+
expect(highchartsRenderer.getSeriesNameInFormatterContext).toHaveBeenCalledWith(funcContext);
|
605
|
+
expect(highchartsRenderer.getColsInFormatterContext).toHaveBeenCalledWith(funcContext);
|
606
|
+
expect(mockPivotData.getAggregator).toHaveBeenCalled();
|
607
|
+
expect(result).toBe('1,234.56');
|
608
|
+
});
|
609
|
+
|
610
|
+
it('should return empty string when show_value is false and not drill-down pie', () => {
|
611
|
+
opts = { chartOptions: { label: { show_value: false } } };
|
612
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
613
|
+
let result = fn.call(funcContext);
|
614
|
+
|
615
|
+
expect(result).toBe('');
|
616
|
+
});
|
617
|
+
|
618
|
+
it('should handle empty row attributes', () => {
|
619
|
+
mockPivotData.rowAttrs = [];
|
620
|
+
opts = { chartOptions: { label: { show_value: true } } };
|
621
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
622
|
+
let result = fn.call(funcContext);
|
623
|
+
|
624
|
+
expect(result).toBe('1,234.56');
|
625
|
+
});
|
626
|
+
|
627
|
+
it('should handle totalSeries className', () => {
|
628
|
+
funcContext.series.options.className = 'totalSeries';
|
629
|
+
opts = { chartOptions: { label: { show_value: true } } };
|
630
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
631
|
+
let result = fn.call(funcContext);
|
632
|
+
|
633
|
+
expect(result).toBe('1,234.56');
|
634
|
+
});
|
635
|
+
|
636
|
+
it('should handle trendSeries className', () => {
|
637
|
+
funcContext.series.options.className = 'trendSeries';
|
638
|
+
opts = { chartOptions: { label: { show_value: true } } };
|
639
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
640
|
+
let result = fn.call(funcContext);
|
641
|
+
|
642
|
+
expect(result).toBe('1,234.56');
|
643
|
+
});
|
644
|
+
});
|
645
|
+
|
646
|
+
describe('Drill-down pie chart scenarios', () => {
|
647
|
+
beforeEach(() => {
|
648
|
+
funcContext.y = 500;
|
649
|
+
});
|
650
|
+
|
651
|
+
it('should handle drill-down pie when series name starts with "Series " and chart does not have multi values', () => {
|
652
|
+
highchartsRenderer.selfStartsWith.and.returnValue(true);
|
653
|
+
highchartsRenderer.getSeriesNameInFormatterContext.and.returnValue('Series 1');
|
654
|
+
highchartsRenderer.isChartWithMultiValues.and.returnValue(false);
|
655
|
+
|
656
|
+
opts = { chartOptions: {} };
|
657
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts, true);
|
658
|
+
let result = fn.call(funcContext);
|
659
|
+
|
660
|
+
expect(highchartsRenderer.selfStartsWith).toHaveBeenCalledWith('Series 1', 'Series ');
|
661
|
+
expect(highchartsRenderer.isChartWithMultiValues).toHaveBeenCalledWith(mockPivotData);
|
662
|
+
expect(result).toBe('500');
|
663
|
+
});
|
664
|
+
|
665
|
+
it('should handle drill-down pie when is a multi values chart', () => {
|
666
|
+
highchartsRenderer.isChartWithMultiValues.and.returnValue(true);
|
667
|
+
|
668
|
+
opts = { chartOptions: {} };
|
669
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts, true);
|
670
|
+
let result = fn.call(funcContext);
|
671
|
+
|
672
|
+
expect(highchartsRenderer.isChartWithMultiValues).toHaveBeenCalledWith(mockPivotData);
|
673
|
+
expect(result).toBe('500');
|
674
|
+
});
|
675
|
+
|
676
|
+
it('should use point name for columns when cols is null in drill-down pie', () => {
|
677
|
+
highchartsRenderer.getColsInFormatterContext.and.returnValue(null);
|
678
|
+
funcContext.point.name = 'DrillDownPoint';
|
679
|
+
|
680
|
+
opts = { chartOptions: {} };
|
681
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts, true);
|
682
|
+
let result = fn.call(funcContext);
|
683
|
+
|
684
|
+
expect(result).toBe('500');
|
685
|
+
});
|
686
|
+
|
687
|
+
it('should swap rows and cols for drill-down pie when series name does not start with "Series "', () => {
|
688
|
+
highchartsRenderer.selfStartsWith.and.returnValue(false);
|
689
|
+
highchartsRenderer.getSeriesNameInFormatterContext.and.returnValue('CustomSeries');
|
690
|
+
|
691
|
+
opts = { chartOptions: {} };
|
692
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts, true);
|
693
|
+
let result = fn.call(funcContext);
|
694
|
+
|
695
|
+
expect(result).toBe('500');
|
696
|
+
});
|
697
|
+
});
|
698
|
+
|
699
|
+
describe('Delta column handling', () => {
|
700
|
+
it('should replace variant name when delta column field is series', () => {
|
701
|
+
opts = {
|
702
|
+
chartOptions: {
|
703
|
+
label: { show_value: true },
|
704
|
+
delta_column: {
|
705
|
+
field: 'series',
|
706
|
+
name: 'test_variant'
|
707
|
+
}
|
708
|
+
}
|
709
|
+
};
|
710
|
+
|
711
|
+
highchartsRenderer.getSeriesNameInFormatterContext.and.returnValue('test_variant' + highchartsRenderer.delimer + 'other');
|
712
|
+
|
713
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
714
|
+
let result = fn.call(funcContext);
|
715
|
+
|
716
|
+
expect(result).toBe('12,345.678');
|
717
|
+
});
|
718
|
+
|
719
|
+
it('should handle variant name matching when series name differs by underscores from delta column name', () => {
|
720
|
+
opts = {
|
721
|
+
chartOptions: {
|
722
|
+
label: { show_value: true },
|
723
|
+
delta_column: {
|
724
|
+
field: 'series',
|
725
|
+
name: 'test_variant'
|
726
|
+
}
|
727
|
+
}
|
728
|
+
};
|
729
|
+
|
730
|
+
highchartsRenderer.getSeriesNameInFormatterContext.and.returnValue('testvariant' + highchartsRenderer.delimer + 'other');
|
731
|
+
|
732
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
733
|
+
let result = fn.call(funcContext);
|
734
|
+
|
735
|
+
expect(result).toBe('12,345.678');
|
736
|
+
});
|
737
|
+
});
|
738
|
+
|
739
|
+
describe('Label options handling', () => {
|
740
|
+
it('should return raw value when show_out_of_x_axis is true but percentage logic is not triggered', () => {
|
741
|
+
opts = {
|
742
|
+
chartOptions: {
|
743
|
+
label: {
|
744
|
+
show_value: true,
|
745
|
+
show_out_of_x_axis: true
|
746
|
+
}
|
747
|
+
}
|
748
|
+
};
|
749
|
+
|
750
|
+
funcContext.y = 250;
|
751
|
+
mockPivotData.getAggregator = jest.fn(() => ({ value: () => 1000 }));
|
752
|
+
|
753
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
754
|
+
let result = fn.call(funcContext);
|
755
|
+
|
756
|
+
expect(result).toBe('250');
|
757
|
+
});
|
758
|
+
|
759
|
+
it('should return raw value when show_out_of_data_series is true but percentage logic is not triggered', () => {
|
760
|
+
opts = {
|
761
|
+
chartOptions: {
|
762
|
+
label: {
|
763
|
+
show_value: true,
|
764
|
+
show_out_of_data_series: true
|
765
|
+
}
|
766
|
+
}
|
767
|
+
};
|
768
|
+
|
769
|
+
funcContext.y = 200;
|
770
|
+
mockPivotData.getAggregator = jest.fn(() => ({ value: () => 800 }));
|
771
|
+
|
772
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
773
|
+
let result = fn.call(funcContext);
|
774
|
+
|
775
|
+
expect(result).toBe('200');
|
776
|
+
});
|
777
|
+
|
778
|
+
it('should return raw value when both percentage options are enabled but percentage logic is not triggered', () => {
|
779
|
+
opts = {
|
780
|
+
chartOptions: {
|
781
|
+
label: {
|
782
|
+
show_value: true,
|
783
|
+
show_out_of_x_axis: true,
|
784
|
+
show_out_of_data_series: true
|
785
|
+
}
|
786
|
+
}
|
787
|
+
};
|
788
|
+
|
789
|
+
funcContext.y = 250;
|
790
|
+
let callCount = 0;
|
791
|
+
mockPivotData.getAggregator = jest.fn(() => {
|
792
|
+
callCount++;
|
793
|
+
return { value: () => callCount === 1 ? 1000 : 800 };
|
794
|
+
});
|
795
|
+
|
796
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
797
|
+
let result = fn.call(funcContext);
|
798
|
+
|
799
|
+
expect(result).toBe('250');
|
800
|
+
});
|
801
|
+
|
802
|
+
it('should show only percentages without value when show_value is false but percentages are enabled', () => {
|
803
|
+
opts = {
|
804
|
+
chartOptions: {
|
805
|
+
label: {
|
806
|
+
show_value: false,
|
807
|
+
show_out_of_x_axis: true
|
808
|
+
}
|
809
|
+
}
|
810
|
+
};
|
811
|
+
|
812
|
+
funcContext.y = 250;
|
813
|
+
mockPivotData.getAggregator = jest.fn(() => ({ value: () => 1000 }));
|
814
|
+
|
815
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
816
|
+
let result = fn.call(funcContext);
|
817
|
+
|
818
|
+
expect(result).toBe('(25%)');
|
819
|
+
});
|
820
|
+
|
821
|
+
it('should not add percentage when value is falsy', () => {
|
822
|
+
opts = {
|
823
|
+
chartOptions: {
|
824
|
+
label: {
|
825
|
+
show_value: true,
|
826
|
+
show_out_of_x_axis: true
|
827
|
+
}
|
828
|
+
}
|
829
|
+
};
|
830
|
+
|
831
|
+
funcContext.y = 0;
|
832
|
+
mockPivotData.getAggregator = jest.fn(() => ({ value: () => 1000 }));
|
833
|
+
|
834
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
835
|
+
let result = fn.call(funcContext);
|
836
|
+
|
837
|
+
expect(result).toBe('0');
|
838
|
+
});
|
839
|
+
|
840
|
+
it('should not add percentage when axisTotal is falsy', () => {
|
841
|
+
opts = {
|
842
|
+
chartOptions: {
|
843
|
+
label: {
|
844
|
+
show_value: true,
|
845
|
+
show_out_of_x_axis: true
|
846
|
+
}
|
847
|
+
}
|
848
|
+
};
|
849
|
+
|
850
|
+
funcContext.y = 250;
|
851
|
+
mockPivotData.getAggregator = jest.fn(() => ({ value: () => 0 }));
|
852
|
+
|
853
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
854
|
+
let result = fn.call(funcContext);
|
855
|
+
|
856
|
+
expect(result).toBe('250');
|
857
|
+
});
|
858
|
+
});
|
859
|
+
|
860
|
+
describe('Object column handling', () => {
|
861
|
+
it('should extract name property from object columns', () => {
|
862
|
+
highchartsRenderer.getColsInFormatterContext.and.returnValue({ name: 'ObjectColumn' });
|
863
|
+
|
864
|
+
opts = { chartOptions: { label: { show_value: true } } };
|
865
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
866
|
+
let result = fn.call(funcContext);
|
867
|
+
|
868
|
+
expect(result).toBe('12,345.678');
|
869
|
+
});
|
870
|
+
});
|
871
|
+
|
872
|
+
describe('Column initialization handling', () => {
|
873
|
+
it('should initialize cols to empty array when cols is falsy after array check', () => {
|
874
|
+
spyOn(lodash, 'isArray').and.returnValue(true);
|
875
|
+
highchartsRenderer.getColsInFormatterContext.and.returnValue(null);
|
876
|
+
|
877
|
+
opts = { chartOptions: { label: { show_value: true } } };
|
878
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
879
|
+
let result = fn.call(funcContext);
|
880
|
+
|
881
|
+
expect(result).toBe('12,345.678');
|
882
|
+
expect(lodash.isArray).toHaveBeenCalledWith(null);
|
883
|
+
});
|
884
|
+
});
|
885
|
+
|
886
|
+
describe('Waterfall breakdown handling', () => {
|
887
|
+
beforeEach(() => {
|
888
|
+
funcContext.series.options.className = 'waterfallBreakdown';
|
889
|
+
});
|
890
|
+
|
891
|
+
it('should transform rows and cols for waterfall breakdown', () => {
|
892
|
+
opts = { chartOptions: { label: { show_value: true } } };
|
893
|
+
|
894
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
895
|
+
let result = fn.call(funcContext);
|
896
|
+
|
897
|
+
expect(highchartsRenderer.transformRowsAndColsForBreakdown).toHaveBeenCalled();
|
898
|
+
expect(result).toBe('12,345.678');
|
899
|
+
});
|
900
|
+
|
901
|
+
it('should return raw value for waterfall breakdown when show_out_of_data_series is true but percentage logic is not triggered', () => {
|
902
|
+
opts = {
|
903
|
+
chartOptions: {
|
904
|
+
label: {
|
905
|
+
show_value: true,
|
906
|
+
show_out_of_data_series: true
|
907
|
+
}
|
908
|
+
}
|
909
|
+
};
|
910
|
+
|
911
|
+
funcContext.y = 300;
|
912
|
+
mockPivotData.getRowKeys = jest.fn(() => ([['row1'], ['row2']]));
|
913
|
+
|
914
|
+
let callCount = 0;
|
915
|
+
mockPivotData.getAggregator = jest.fn((rows, cols) => {
|
916
|
+
if (rows.length === 1) return { value: () => 400 };
|
917
|
+
if (rows.length === 0 && cols.length > 0) return { value: () => 1200 };
|
918
|
+
return { value: () => 800 };
|
919
|
+
});
|
920
|
+
|
921
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
922
|
+
let result = fn.call(funcContext);
|
923
|
+
|
924
|
+
expect(result).toBe('300');
|
925
|
+
});
|
926
|
+
|
927
|
+
it('should calculate percentage using dataSeriesTotal when axisTotal is falsy (waterfall breakdown)', () => {
|
928
|
+
opts = {
|
929
|
+
chartOptions: {
|
930
|
+
label: {
|
931
|
+
show_value: false,
|
932
|
+
show_out_of_data_series: true
|
933
|
+
}
|
934
|
+
}
|
935
|
+
};
|
936
|
+
|
937
|
+
funcContext.y = 200; // value = 200
|
938
|
+
mockPivotData.getRowKeys = jest.fn(() => ([['row1'], ['row2']]));
|
939
|
+
|
940
|
+
let callCount = 0;
|
941
|
+
mockPivotData.getAggregator = jest.fn((rows, cols) => {
|
942
|
+
callCount++;
|
943
|
+
|
944
|
+
if (rows.length === 1) {
|
945
|
+
return { value: () => 400 };
|
946
|
+
}
|
947
|
+
if (rows.length === 0 && cols.length > 0) {
|
948
|
+
return { value: () => 0 };
|
949
|
+
}
|
950
|
+
|
951
|
+
return { value: () => 200 };
|
952
|
+
});
|
953
|
+
|
954
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
955
|
+
let result = fn.call(funcContext);
|
956
|
+
|
957
|
+
expect(result).toBe('(25%)');
|
958
|
+
});
|
959
|
+
|
960
|
+
it('should not add percentage when dataSeriesTotal is falsy (non-waterfall breakdown)', () => {
|
961
|
+
funcContext.series.options.className = 'regularSeries';
|
962
|
+
|
963
|
+
opts = {
|
964
|
+
chartOptions: {
|
965
|
+
label: {
|
966
|
+
show_value: false,
|
967
|
+
show_out_of_data_series: true
|
968
|
+
}
|
969
|
+
}
|
970
|
+
};
|
971
|
+
|
972
|
+
funcContext.y = 200;
|
973
|
+
|
974
|
+
mockPivotData.getAggregator = jest.fn((rows, cols) => {
|
975
|
+
if (cols.length === 0) {
|
976
|
+
return { value: () => 0 };
|
977
|
+
}
|
978
|
+
return { value: () => 200 };
|
979
|
+
});
|
980
|
+
|
981
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
982
|
+
let result = fn.call(funcContext);
|
983
|
+
|
984
|
+
expect(result).toBe('');
|
985
|
+
});
|
986
|
+
});
|
987
|
+
|
988
|
+
describe('Error handling', () => {
|
989
|
+
it('should fallback to basic formatting when aggregator throws error', () => {
|
990
|
+
mockPivotData.getAggregator = jest.fn(() => { throw new Error('Test error'); });
|
991
|
+
|
992
|
+
opts = { chartOptions: { label: { show_value: true } } };
|
993
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
994
|
+
let result = fn.call(funcContext);
|
995
|
+
|
996
|
+
expect(result).toBe('12,345.678');
|
997
|
+
});
|
998
|
+
|
999
|
+
it('should handle null columns gracefully', () => {
|
1000
|
+
highchartsRenderer.getColsInFormatterContext.and.returnValue(null);
|
1001
|
+
|
1002
|
+
opts = { chartOptions: { label: { show_value: true } } };
|
1003
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
1004
|
+
let result = fn.call(funcContext);
|
1005
|
+
|
1006
|
+
expect(result).toBe('12,345.678');
|
1007
|
+
});
|
1008
|
+
|
1009
|
+
it('should handle empty options gracefully for drill-down pie', () => {
|
1010
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, {}, true);
|
1011
|
+
let result = fn.call(funcContext);
|
1012
|
+
|
1013
|
+
expect(result).toBe('12,345.678');
|
1014
|
+
});
|
1015
|
+
});
|
1016
|
+
|
1017
|
+
describe('Unit abbreviation options (fallback behavior)', () => {
|
1018
|
+
it('should fallback to raw value formatting when useUnitAbbreviation is false and getFormattedNumber returns abbreviated value', () => {
|
1019
|
+
opts = {
|
1020
|
+
chartOptions: {
|
1021
|
+
label: {
|
1022
|
+
show_value: true,
|
1023
|
+
useUnitAbbreviation: false
|
1024
|
+
}
|
1025
|
+
}
|
1026
|
+
};
|
1027
|
+
|
1028
|
+
jest.spyOn(global.$.pivotUtilities, 'getFormattedNumber').mockReturnValue('12.5K');
|
1029
|
+
|
1030
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
1031
|
+
let result = fn.call(funcContext);
|
1032
|
+
|
1033
|
+
expect(result).toBe('12,345.678');
|
1034
|
+
});
|
1035
|
+
|
1036
|
+
it('should fallback to raw value formatting when useUnitAbbreviation is true and getFormattedNumber returns abbreviated value', () => {
|
1037
|
+
opts = {
|
1038
|
+
chartOptions: {
|
1039
|
+
label: {
|
1040
|
+
show_value: true,
|
1041
|
+
useUnitAbbreviation: true
|
1042
|
+
}
|
1043
|
+
}
|
1044
|
+
};
|
1045
|
+
|
1046
|
+
jest.spyOn(global.$.pivotUtilities, 'getFormattedNumber').mockReturnValue('12.5K');
|
1047
|
+
|
1048
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
1049
|
+
let result = fn.call(funcContext);
|
1050
|
+
|
1051
|
+
expect(result).toBe('12,345.678');
|
1052
|
+
});
|
1053
|
+
|
1054
|
+
it('should fallback to raw value formatting when getFormattedNumber returns M-abbreviated value regardless of useUnitAbbreviation setting', () => {
|
1055
|
+
opts = {
|
1056
|
+
chartOptions: {
|
1057
|
+
label: {
|
1058
|
+
show_value: true,
|
1059
|
+
useUnitAbbreviation: false
|
1060
|
+
}
|
1061
|
+
}
|
1062
|
+
};
|
1063
|
+
|
1064
|
+
jest.spyOn(global.$.pivotUtilities, 'getFormattedNumber').mockReturnValue('1.2M');
|
1065
|
+
|
1066
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
1067
|
+
let result = fn.call(funcContext);
|
1068
|
+
|
1069
|
+
expect(result).toBe('12,345.678');
|
1070
|
+
});
|
1071
|
+
});
|
1072
|
+
|
1073
|
+
describe('Others name handling', () => {
|
1074
|
+
it('should handle others name replacement', () => {
|
1075
|
+
opts = {
|
1076
|
+
total_value_options: { some: 'option' },
|
1077
|
+
chartOptions: { label: { show_value: true } }
|
1078
|
+
};
|
1079
|
+
|
1080
|
+
highchartsRenderer.getOthersName.and.returnValue('CustomOthers');
|
1081
|
+
highchartsRenderer.getDrOthersInAxisState.and.returnValue({ cols: true });
|
1082
|
+
|
1083
|
+
let fn = highchartsRenderer.defaultDataLabelFormatter(mockPivotData, opts);
|
1084
|
+
let result = fn.call(funcContext);
|
1085
|
+
|
1086
|
+
expect(highchartsRenderer.getOthersName).toHaveBeenCalledWith(opts);
|
1087
|
+
expect(highchartsRenderer.getDrOthersInAxisState).toHaveBeenCalledWith(mockPivotData, 'CustomOthers');
|
1088
|
+
expect(highchartsRenderer.replaceDrOthersKeys).toHaveBeenCalled();
|
1089
|
+
expect(result).toBe('12,345.678');
|
1090
|
+
});
|
527
1091
|
});
|
528
1092
|
});
|
529
1093
|
|
@@ -1508,7 +2072,7 @@ describe('highcharts_renderer', () => {
|
|
1508
2072
|
field: 'series',
|
1509
2073
|
name: 'TEST_test',
|
1510
2074
|
same_yaxis: true,
|
1511
|
-
is_percentage:
|
2075
|
+
is_percentage: false,
|
1512
2076
|
}
|
1513
2077
|
}
|
1514
2078
|
};
|
@@ -1522,10 +2086,10 @@ describe('highcharts_renderer', () => {
|
|
1522
2086
|
highchartsRenderer.updateBackwardCompatibleWidgetOptions(currentOptions, null);
|
1523
2087
|
expect(currentOptions.comboOptions).toEqual({
|
1524
2088
|
secondaryAxisSettings: {
|
1525
|
-
name: '
|
2089
|
+
name: 'Secondary Axis',
|
1526
2090
|
max: null,
|
1527
2091
|
min: null,
|
1528
|
-
is_percentage:
|
2092
|
+
is_percentage: false
|
1529
2093
|
},
|
1530
2094
|
seriesOptions: [{
|
1531
2095
|
series: 'TEST_test',
|
@@ -2064,17 +2628,20 @@ describe('highcharts_renderer', () => {
|
|
2064
2628
|
it('Should return General format if there are no widget_values_format', () => {
|
2065
2629
|
aggregatorObject.widget_values_format = null;
|
2066
2630
|
expect(aggregatorObject.format(123.4567, false)).toBe('123.46');
|
2631
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2067
2632
|
});
|
2068
2633
|
|
2069
2634
|
it('Should return widget format if it\'s not calculated value', () => {
|
2070
2635
|
aggregatorObject.widget_values_format = '\"$\"#,###.###';
|
2071
2636
|
expect(aggregatorObject.format(1123.4567, false)).toBe('$1,123.457');
|
2637
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2072
2638
|
});
|
2073
2639
|
|
2074
2640
|
it('Should return calculated value format if it\'s calculated value', () => {
|
2075
2641
|
aggregator = highchartsRenderer.rhPivotAggregatorSum(arg, widget_values_format, is_graph, render_options, calculated_info);
|
2076
2642
|
aggregatorObject = aggregator({}, ['Region average'], '');
|
2077
2643
|
expect(aggregatorObject.format(1123.45678, false)).toBe('112345.68%');
|
2644
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2078
2645
|
});
|
2079
2646
|
|
2080
2647
|
it('if FF enabledNewWidgetValueFormatting is and some of secondaryAxis is true widget values format must be from seriesOptions and widget_value_format to equal first seriesOptions format', () => {
|
@@ -2091,6 +2658,7 @@ describe('highcharts_renderer', () => {
|
|
2091
2658
|
aggregatorObject = aggregator({}, ['Profit'], '');
|
2092
2659
|
aggregatorObject.push({ DR_Values: 'Profit', Profit: 123 });
|
2093
2660
|
expect(aggregatorObject.format(1123.45678, false)).toBe('$1,123.457');
|
2661
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2094
2662
|
});
|
2095
2663
|
});
|
2096
2664
|
});
|
@@ -2265,17 +2833,20 @@ describe('highcharts_renderer', () => {
|
|
2265
2833
|
it('Should return General format if there are no widget_values_format', () => {
|
2266
2834
|
aggregatorObject.widget_values_format = null;
|
2267
2835
|
expect(aggregatorObject.format(123.4567, false)).toBe('123.46');
|
2836
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2268
2837
|
});
|
2269
2838
|
|
2270
2839
|
it('Should return widget format if it\'s not calculated value', () => {
|
2271
2840
|
aggregatorObject.widget_values_format = '\"$\"#,###.###';
|
2272
2841
|
expect(aggregatorObject.format(1123.4567, false)).toBe('$1,123.457');
|
2842
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2273
2843
|
});
|
2274
2844
|
|
2275
2845
|
it('Should return calculated value format if it\'s calculated value', () => {
|
2276
2846
|
aggregator = highchartsRenderer.rhPivotCount(arg, widget_values_format, is_graph, render_options, calculated_info);
|
2277
2847
|
aggregatorObject = aggregator({}, ['Region average'], '');
|
2278
2848
|
expect(aggregatorObject.format(1123.45678, false)).toBe('112345.68%');
|
2849
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2279
2850
|
});
|
2280
2851
|
|
2281
2852
|
it('if FF enabledNewWidgetValueFormatting is and some of secondaryAxis is true widget values format must be from seriesOptions and widget_value_format to equal first seriesOptions format', () => {
|
@@ -2292,6 +2863,7 @@ describe('highcharts_renderer', () => {
|
|
2292
2863
|
aggregatorObject = aggregator({}, ['Profit'], '');
|
2293
2864
|
aggregatorObject.push({ DR_Values: 'Profit', Profit: 123 });
|
2294
2865
|
expect(aggregatorObject.format(1123.45678, false)).toBe('$1,123.457');
|
2866
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2295
2867
|
});
|
2296
2868
|
});
|
2297
2869
|
});
|
@@ -2424,6 +2996,7 @@ describe('highcharts_renderer', () => {
|
|
2424
2996
|
it('Should return widget format if it\'s graph', () => {
|
2425
2997
|
aggregatorObject.widget_values_format = '\"$\"#,###.###';
|
2426
2998
|
expect(aggregatorObject.format(2, false)).toBe('$2.000');
|
2999
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2427
3000
|
});
|
2428
3001
|
|
2429
3002
|
it('Should return widget format if it\'s only_formats', () => {
|
@@ -2431,6 +3004,7 @@ describe('highcharts_renderer', () => {
|
|
2431
3004
|
aggregatorObject = aggregator({}, '', '');
|
2432
3005
|
aggregatorObject.widget_values_format = '\"$\"#,###.###';
|
2433
3006
|
expect(aggregatorObject.format(2, true)).toBe('$2.000');
|
3007
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2434
3008
|
});
|
2435
3009
|
|
2436
3010
|
it('Should return uniq values if it\'s table and not only_formats', () => {
|
@@ -2438,6 +3012,7 @@ describe('highcharts_renderer', () => {
|
|
2438
3012
|
aggregatorObject = aggregator({}, '', '');
|
2439
3013
|
aggregatorObject.formated_values = ['val1', 'val2'];
|
2440
3014
|
expect(aggregatorObject.format(aggregatorObject.formated_values, false)).toBe('val1<br>val2');
|
3015
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2441
3016
|
});
|
2442
3017
|
});
|
2443
3018
|
});
|
@@ -2616,17 +3191,20 @@ describe('highcharts_renderer', () => {
|
|
2616
3191
|
it('Should return General format if there are no widget_values_format', () => {
|
2617
3192
|
aggregatorObject.widget_values_format = null;
|
2618
3193
|
expect(aggregatorObject.format(123.4567, false)).toBe('123.46');
|
3194
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2619
3195
|
});
|
2620
3196
|
|
2621
3197
|
it('Should return widget format if it\'s not calculated value', () => {
|
2622
3198
|
aggregatorObject.widget_values_format = '\"$\"#,###.###';
|
2623
3199
|
expect(aggregatorObject.format(1123.4567, false)).toBe('$1,123.457');
|
3200
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2624
3201
|
});
|
2625
3202
|
|
2626
3203
|
it('Should return calculated value format if it\'s calculated value', () => {
|
2627
3204
|
aggregator = highchartsRenderer.rhPivotAggregatorAverage(arg, widget_values_format, is_graph, render_options, calculated_info);
|
2628
3205
|
aggregatorObject = aggregator({}, ['Region average'], '');
|
2629
3206
|
expect(aggregatorObject.format(1123.45678, false)).toBe('112345.68%');
|
3207
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2630
3208
|
});
|
2631
3209
|
|
2632
3210
|
it('if FF enabledNewWidgetValueFormatting is and some of secondaryAxis is true widget values format must be from seriesOptions and widget_value_format to equal first seriesOptions format', () => {
|
@@ -2643,6 +3221,7 @@ describe('highcharts_renderer', () => {
|
|
2643
3221
|
aggregatorObject = aggregator({}, ['Profit'], '');
|
2644
3222
|
aggregatorObject.push({ DR_Values: 'Profit', Profit: 123 });
|
2645
3223
|
expect(aggregatorObject.format(1123.45678, false)).toBe('$1,123.457');
|
3224
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2646
3225
|
});
|
2647
3226
|
});
|
2648
3227
|
});
|
@@ -2818,17 +3397,20 @@ describe('highcharts_renderer', () => {
|
|
2818
3397
|
it('Should return General format if there are no widget_values_format', () => {
|
2819
3398
|
aggregatorObject.widget_values_format = null;
|
2820
3399
|
expect(aggregatorObject.format(123.4567, false)).toBe('123.46');
|
3400
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2821
3401
|
});
|
2822
3402
|
|
2823
3403
|
it('Should return widget format if it\'s not calculated value', () => {
|
2824
3404
|
aggregatorObject.widget_values_format = '\"$\"#,###.###';
|
2825
3405
|
expect(aggregatorObject.format(1123.4567, false)).toBe('$1,123.457');
|
3406
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2826
3407
|
});
|
2827
3408
|
|
2828
3409
|
it('Should return calculated value format if it\'s calculated value', () => {
|
2829
3410
|
aggregator = highchartsRenderer.rhPivotAggregatorMin(arg, widget_values_format, is_graph, render_options, calculated_info);
|
2830
3411
|
aggregatorObject = aggregator({}, ['Region average'], '');
|
2831
3412
|
expect(aggregatorObject.format(1123.45678, false)).toBe('112345.68%');
|
3413
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2832
3414
|
});
|
2833
3415
|
|
2834
3416
|
it('if FF enabledNewWidgetValueFormatting is and some of secondaryAxis is true widget values format must be from seriesOptions and widget_value_format to equal first seriesOptions format', () => {
|
@@ -2845,6 +3427,7 @@ describe('highcharts_renderer', () => {
|
|
2845
3427
|
aggregatorObject = aggregator({}, ['Profit'], '');
|
2846
3428
|
aggregatorObject.push({ DR_Values: 'Profit', Profit: 123 });
|
2847
3429
|
expect(aggregatorObject.format(1123.45678, false)).toBe('$1,123.457');
|
3430
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
2848
3431
|
});
|
2849
3432
|
});
|
2850
3433
|
});
|
@@ -3020,17 +3603,20 @@ describe('highcharts_renderer', () => {
|
|
3020
3603
|
it('Should return General format if there are no widget_values_format', () => {
|
3021
3604
|
aggregatorObject.widget_values_format = null;
|
3022
3605
|
expect(aggregatorObject.format(123.4567, false)).toBe('123.46');
|
3606
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
3023
3607
|
});
|
3024
3608
|
|
3025
3609
|
it('Should return widget format if it\'s not calculated value', () => {
|
3026
3610
|
aggregatorObject.widget_values_format = '\"$\"#,###.###';
|
3027
3611
|
expect(aggregatorObject.format(1123.4567, false)).toBe('$1,123.457');
|
3612
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
3028
3613
|
});
|
3029
3614
|
|
3030
3615
|
it('Should return calculated value format if it\'s calculated value', () => {
|
3031
3616
|
aggregator = highchartsRenderer.rhPivotAggregatorMax(arg, widget_values_format, is_graph, render_options, calculated_info);
|
3032
3617
|
aggregatorObject = aggregator({}, ['Region average'], '');
|
3033
3618
|
expect(aggregatorObject.format(1123.45678, false)).toBe('112345.68%');
|
3619
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
3034
3620
|
});
|
3035
3621
|
|
3036
3622
|
it('if FF enabledNewWidgetValueFormatting is and some of secondaryAxis is true widget values format must be from seriesOptions and widget_value_format to equal first seriesOptions format', () => {
|
@@ -3047,6 +3633,7 @@ describe('highcharts_renderer', () => {
|
|
3047
3633
|
aggregatorObject = aggregator({}, ['Profit'], '');
|
3048
3634
|
aggregatorObject.push({ DR_Values: 'Profit', Profit: 123 });
|
3049
3635
|
expect(aggregatorObject.format(1123.45678, false)).toBe('$1,123.457');
|
3636
|
+
expect(getAggregatorPercentageValueIfRequiredMock).toHaveBeenCalled();
|
3050
3637
|
});
|
3051
3638
|
});
|
3052
3639
|
});
|
@@ -3054,6 +3641,115 @@ describe('highcharts_renderer', () => {
|
|
3054
3641
|
});
|
3055
3642
|
|
3056
3643
|
describe('function getDefaultValueForChart', () => {
|
3644
|
+
let originalRich, originalGetter, originalSubDefaults;
|
3645
|
+
|
3646
|
+
beforeEach(() => {
|
3647
|
+
originalRich = highchartsRenderer.richTextSubType;
|
3648
|
+
originalGetter = highchartsRenderer.getChartOptionsBySubType;
|
3649
|
+
originalSubDefaults = highchartsRenderer.getDefaultValueForSubOptions;
|
3650
|
+
|
3651
|
+
highchartsRenderer.richTextSubType = { type: 'rich_text', suboptions: [], default_options: null };
|
3652
|
+
});
|
3653
|
+
|
3654
|
+
afterEach(() => {
|
3655
|
+
highchartsRenderer.richTextSubType = originalRich;
|
3656
|
+
highchartsRenderer.getChartOptionsBySubType = originalGetter;
|
3657
|
+
highchartsRenderer.getDefaultValueForSubOptions = originalSubDefaults;
|
3658
|
+
});
|
3659
|
+
|
3660
|
+
it('returns {} for unknown subtype', () => {
|
3661
|
+
highchartsRenderer.getChartOptionsBySubType = jest.fn(() => null);
|
3662
|
+
expect(highchartsRenderer.getDefaultValueForChart('unknown-type', {})).toEqual({});
|
3663
|
+
});
|
3664
|
+
|
3665
|
+
it('deep-clones suboptions return value (no shared refs)', () => {
|
3666
|
+
const shared = { arr: [1, 2, 3], nested: { x: 1 } };
|
3667
|
+
highchartsRenderer.getDefaultValueForSubOptions = jest.fn(() => shared);
|
3668
|
+
|
3669
|
+
const chartOpt = {
|
3670
|
+
suboptions: [{ category_type: 'segments' }, { category_type: 'label' }],
|
3671
|
+
default_options: {},
|
3672
|
+
};
|
3673
|
+
highchartsRenderer.getChartOptionsBySubType = jest.fn(() => chartOpt);
|
3674
|
+
|
3675
|
+
const res = highchartsRenderer.getDefaultValueForChart('line-chart', {});
|
3676
|
+
expect(res.segments).toEqual(shared);
|
3677
|
+
expect(res.segments).not.toBe(shared);
|
3678
|
+
|
3679
|
+
res.segments.arr.push(99);
|
3680
|
+
res.segments.nested.x = 42;
|
3681
|
+
expect(shared.arr).toEqual([1, 2, 3]);
|
3682
|
+
expect(shared.nested.x).toBe(1);
|
3683
|
+
});
|
3684
|
+
|
3685
|
+
it('does not mutate chartOpt.default_options (base defaults stay immutable)', () => {
|
3686
|
+
const baseDefaults = {
|
3687
|
+
segments: [10, 20],
|
3688
|
+
label: { font_size: 12, style: { weight: 400 } },
|
3689
|
+
};
|
3690
|
+
highchartsRenderer.getDefaultValueForSubOptions = jest.fn(() => ({ label: { font_size: 8 } }));
|
3691
|
+
highchartsRenderer.getChartOptionsBySubType = jest.fn(() => ({
|
3692
|
+
suboptions: [{ category_type: 'label' }],
|
3693
|
+
default_options: baseDefaults,
|
3694
|
+
}));
|
3695
|
+
|
3696
|
+
const res = highchartsRenderer.getDefaultValueForChart('line-chart', {});
|
3697
|
+
res.label.style.weight = 700;
|
3698
|
+
res.segments.push(30);
|
3699
|
+
|
3700
|
+
expect(baseDefaults).toEqual({
|
3701
|
+
segments: [10, 20],
|
3702
|
+
label: { font_size: 12, style: { weight: 400 } },
|
3703
|
+
});
|
3704
|
+
});
|
3705
|
+
|
3706
|
+
it('default_options override suboptions (old semantics) and arrays are replaced, not merged', () => {
|
3707
|
+
highchartsRenderer.getDefaultValueForSubOptions = jest.fn((sub) => {
|
3708
|
+
if (sub.category_type === 'segments') return [1, 2, 3];
|
3709
|
+
if (sub.category_type === 'label') return { font_size: 8 };
|
3710
|
+
return {};
|
3711
|
+
});
|
3712
|
+
|
3713
|
+
highchartsRenderer.getChartOptionsBySubType = jest.fn(() => ({
|
3714
|
+
suboptions: [{ category_type: 'segments' }, { category_type: 'label' }],
|
3715
|
+
default_options: {
|
3716
|
+
segments: [100],
|
3717
|
+
label: { font_size: 12, color: '#000' },
|
3718
|
+
},
|
3719
|
+
}));
|
3720
|
+
|
3721
|
+
const res = highchartsRenderer.getDefaultValueForChart('line-chart', {});
|
3722
|
+
expect(res.segments).toEqual([100]); // массив заменён целиком
|
3723
|
+
expect(res.label).toEqual({ font_size: 12, color: '#000' }); // приоритет у default_options
|
3724
|
+
});
|
3725
|
+
|
3726
|
+
it('fresh object on each call (no cross-call leakage)', () => {
|
3727
|
+
highchartsRenderer.getDefaultValueForSubOptions = jest.fn(() => ({ a: 1 }));
|
3728
|
+
highchartsRenderer.getChartOptionsBySubType = jest.fn(() => ({
|
3729
|
+
suboptions: [{ category_type: 'cfg' }],
|
3730
|
+
default_options: {},
|
3731
|
+
}));
|
3732
|
+
|
3733
|
+
const res1 = highchartsRenderer.getDefaultValueForChart('line-chart', {});
|
3734
|
+
const res2 = highchartsRenderer.getDefaultValueForChart('line-chart', {});
|
3735
|
+
res1.cfg.a = 99;
|
3736
|
+
|
3737
|
+
expect(res2.cfg.a).toBe(1);
|
3738
|
+
expect(res1.cfg).not.toBe(res2.cfg);
|
3739
|
+
});
|
3740
|
+
|
3741
|
+
it('handles non-plain values inside defaults (e.g., Date) without throwing', () => {
|
3742
|
+
const dt = new Date('2025-01-01T00:00:00.000Z');
|
3743
|
+
highchartsRenderer.getDefaultValueForSubOptions = jest.fn(() => ({}));
|
3744
|
+
highchartsRenderer.getChartOptionsBySubType = jest.fn(() => ({
|
3745
|
+
suboptions: [],
|
3746
|
+
default_options: { createdAt: dt },
|
3747
|
+
}));
|
3748
|
+
|
3749
|
+
const res = highchartsRenderer.getDefaultValueForChart('line-chart', {});
|
3750
|
+
expect(new Date(res.createdAt).getTime()).toBe(dt.getTime());
|
3751
|
+
});
|
3752
|
+
|
3057
3753
|
it('should return empty value for rich_text type', () => {
|
3058
3754
|
expect(highchartsRenderer.getDefaultValueForChart('rich_text', {})).toEqual({});
|
3059
3755
|
});
|
@@ -6854,7 +7550,9 @@ describe('highcharts_renderer', () => {
|
|
6854
7550
|
]);
|
6855
7551
|
});
|
6856
7552
|
it('should prepare appropriate chart series for standard input', () => {
|
6857
|
-
opts = {
|
7553
|
+
opts = {
|
7554
|
+
chartOptions: {}
|
7555
|
+
};
|
6858
7556
|
chartType = null;
|
6859
7557
|
const value = highchartsRenderer.ptCreateColumnSeries(pivotDataMock, colors, onlyNumbers, isUniqueVals, isNotDrillDown, additionalOptionsMock, opts, chartOptions, chartType);
|
6860
7558
|
expect(value).toEqual([
|
@@ -7083,7 +7781,9 @@ describe('highcharts_renderer', () => {
|
|
7083
7781
|
"#b3060e",
|
7084
7782
|
"#70000a"
|
7085
7783
|
];
|
7086
|
-
opts = {
|
7784
|
+
opts = {
|
7785
|
+
chartOptions: {}
|
7786
|
+
};
|
7087
7787
|
chartOptions = {
|
7088
7788
|
chart: {
|
7089
7789
|
type: '',
|
@@ -7241,7 +7941,8 @@ describe('highcharts_renderer', () => {
|
|
7241
7941
|
|
7242
7942
|
it('should prepare appropriate chart series, when opts \'total\' configuration is set to true', ()=> {
|
7243
7943
|
opts = {
|
7244
|
-
total: true
|
7944
|
+
total: true,
|
7945
|
+
chartOptions: {}
|
7245
7946
|
};
|
7246
7947
|
pivotDataMock.colTotals = {
|
7247
7948
|
'col 1': {value: () => 123450},
|
@@ -7310,6 +8011,7 @@ describe('highcharts_renderer', () => {
|
|
7310
8011
|
|
7311
8012
|
it('should prepare appropriate chart series, when opts \'trendLine\' configuration is set to true', ()=> {
|
7312
8013
|
opts = {
|
8014
|
+
chartOptions: {},
|
7313
8015
|
trendLine: true
|
7314
8016
|
};
|
7315
8017
|
const value = highchartsRenderer.ptCreateBasicLineSeries(pivotDataMock, colors, onlyNumbers, isUniqueVals, additionalOptionsMock, opts, chartOptions);
|
@@ -7533,36 +8235,6 @@ describe('highcharts_renderer', () => {
|
|
7533
8235
|
);
|
7534
8236
|
});
|
7535
8237
|
|
7536
|
-
it('should return no data result if series is empty', () => {
|
7537
|
-
const chartOptions = {
|
7538
|
-
chart: {},
|
7539
|
-
series: [{ data: [] }, { data: [] }, {}],
|
7540
|
-
};
|
7541
|
-
const options = {};
|
7542
|
-
const noDataFnSpy = jest.spyOn(highchartsRenderer, 'getNoDataResult').mockImplementation(() => {});
|
7543
|
-
|
7544
|
-
highchartsRenderer.ptCreateElementAndDraw(chartOptions, options);
|
7545
|
-
|
7546
|
-
expect(noDataFnSpy).toHaveBeenCalled();
|
7547
|
-
expect(options.error_has_occurred).toBeTruthy();
|
7548
|
-
expect(options.error_params).toBe(highchartsRenderer.widgetPlaceholders.nodata);
|
7549
|
-
});
|
7550
|
-
|
7551
|
-
it('should return too much data result if series is too long', () => {
|
7552
|
-
const chartOptions = {
|
7553
|
-
chart: {},
|
7554
|
-
series: [{ data: new Array(1000) }, { data: new Array(1000) }, {}],
|
7555
|
-
};
|
7556
|
-
const options = {};
|
7557
|
-
const noDataFnSpy = jest.spyOn(highchartsRenderer, 'getNoDataResult').mockImplementation(() => {});
|
7558
|
-
|
7559
|
-
highchartsRenderer.ptCreateElementAndDraw(chartOptions, options);
|
7560
|
-
|
7561
|
-
expect(noDataFnSpy).toHaveBeenCalled();
|
7562
|
-
expect(options.error_has_occurred).toBeTruthy();
|
7563
|
-
expect(options.error_params).toBe(highchartsRenderer.widgetPlaceholders.tooMuchData);
|
7564
|
-
});
|
7565
|
-
|
7566
8238
|
it('should set hcInstance on options with chart object for graph table renderer to use', () => {
|
7567
8239
|
jest.useFakeTimers();
|
7568
8240
|
jest.spyOn(Highcharts, 'chart').mockImplementation(() => ({ chart: true }));
|
@@ -7580,40 +8252,330 @@ describe('highcharts_renderer', () => {
|
|
7580
8252
|
});
|
7581
8253
|
});
|
7582
8254
|
|
7583
|
-
describe('
|
7584
|
-
const
|
8255
|
+
describe('Error Throwing Functionality', () => {
|
8256
|
+
const {
|
8257
|
+
NoDataError,
|
8258
|
+
TooMuchDataError,
|
8259
|
+
DataConflictError,
|
8260
|
+
GaugeConfigurationError,
|
8261
|
+
BaseRendererError,
|
8262
|
+
GenericRenderingError,
|
8263
|
+
GenericComputationalError
|
8264
|
+
} = require('../src/errors');
|
7585
8265
|
|
7586
|
-
|
7587
|
-
|
7588
|
-
const expected = container.clone().html(highchartsRenderer.getWidgetPlaceholder(placeholderMeta));
|
7589
|
-
expect(highchartsRenderer.getNoDataResult()).toEqual(expected);
|
8266
|
+
beforeEach(() => {
|
8267
|
+
jest.clearAllMocks();
|
7590
8268
|
});
|
7591
8269
|
|
7592
|
-
|
7593
|
-
|
7594
|
-
const expected = container.clone().html(highchartsRenderer.getWidgetPlaceholder(placeholderMeta));
|
7595
|
-
expect(highchartsRenderer.getNoDataResult(true)).toEqual(expected);
|
8270
|
+
afterAll(() => {
|
8271
|
+
jest.restoreAllMocks();
|
7596
8272
|
});
|
7597
8273
|
|
7598
|
-
|
8274
|
+
describe('ptCreateElementAndDraw - Error Throwing', () => {
|
8275
|
+
it('should throw NoDataError when series is empty and not onlyText', () => {
|
8276
|
+
const chartOptions = {
|
8277
|
+
chart: {},
|
8278
|
+
series: [{ data: [] }, { data: [] }, {}],
|
8279
|
+
};
|
8280
|
+
const options = {};
|
8281
|
+
|
8282
|
+
expect(() => {
|
8283
|
+
highchartsRenderer.ptCreateElementAndDraw(chartOptions, options);
|
8284
|
+
}).toThrow(NoDataError);
|
8285
|
+
});
|
8286
|
+
|
8287
|
+
it('should throw TooMuchDataError when series has too much data', () => {
|
8288
|
+
const chartOptions = {
|
8289
|
+
chart: {},
|
8290
|
+
series: [{ data: new Array(1000) }, { data: new Array(1000) }, {}],
|
8291
|
+
};
|
8292
|
+
const options = {};
|
8293
|
+
|
8294
|
+
expect(() => {
|
8295
|
+
highchartsRenderer.ptCreateElementAndDraw(chartOptions, options);
|
8296
|
+
}).toThrow(TooMuchDataError);
|
8297
|
+
});
|
8298
|
+
|
8299
|
+
it('should not throw errors when onlyText is true even if no data', () => {
|
8300
|
+
const chartOptions = {
|
8301
|
+
chart: {},
|
8302
|
+
series: [{ data: [] }],
|
8303
|
+
onlyText: true,
|
8304
|
+
};
|
8305
|
+
const options = {};
|
8306
|
+
|
8307
|
+
expect(() => {
|
8308
|
+
highchartsRenderer.ptCreateElementAndDraw(chartOptions, options);
|
8309
|
+
}).not.toThrow();
|
8310
|
+
});
|
8311
|
+
|
8312
|
+
it('should process normally when series has valid data', () => {
|
8313
|
+
jest.spyOn(Highcharts, 'chart').mockImplementation(() => ({ chart: true }));
|
8314
|
+
jest.useFakeTimers();
|
8315
|
+
|
8316
|
+
const chartOptions = {
|
8317
|
+
chart: {},
|
8318
|
+
series: [{ data: [1, 2, 3] }],
|
8319
|
+
};
|
8320
|
+
const options = {};
|
7599
8321
|
|
7600
|
-
|
7601
|
-
|
7602
|
-
|
7603
|
-
|
8322
|
+
expect(() => {
|
8323
|
+
highchartsRenderer.ptCreateElementAndDraw(chartOptions, options);
|
8324
|
+
}).not.toThrow();
|
8325
|
+
|
8326
|
+
jest.runAllTimers();
|
8327
|
+
jest.restoreAllMocks();
|
8328
|
+
jest.useRealTimers();
|
8329
|
+
});
|
8330
|
+
});
|
8331
|
+
|
8332
|
+
describe('Data Conflict Error Throwing', () => {
|
8333
|
+
it('should throw DataConflictError when categories are below minimum in waterfall chart', () => {
|
8334
|
+
const options = {
|
8335
|
+
isBreakdown: true,
|
8336
|
+
uniqueCategories: ['A', 'B'], // Only 2 categories
|
8337
|
+
minCategories: 5,
|
8338
|
+
maxCategories: 10
|
8339
|
+
};
|
8340
|
+
|
8341
|
+
expect(() => {
|
8342
|
+
throw new DataConflictError({
|
8343
|
+
isBreakdown: options.isBreakdown,
|
8344
|
+
uniqueCategories: options.uniqueCategories,
|
8345
|
+
minCategories: options.minCategories,
|
8346
|
+
maxCategories: options.maxCategories
|
8347
|
+
});
|
8348
|
+
}).toThrow(DataConflictError);
|
8349
|
+
});
|
8350
|
+
|
8351
|
+
it('should throw DataConflictError when categories exceed maximum in waterfall chart', () => {
|
8352
|
+
const uniqueCategories = new Array(15).fill(0).map((_, i) => `Category${i}`); // 15 categories
|
8353
|
+
const options = {
|
8354
|
+
isBreakdown: false,
|
8355
|
+
uniqueCategories,
|
8356
|
+
minCategories: 3,
|
8357
|
+
maxCategories: 10
|
8358
|
+
};
|
8359
|
+
|
8360
|
+
expect(() => {
|
8361
|
+
throw new DataConflictError({
|
8362
|
+
isBreakdown: options.isBreakdown,
|
8363
|
+
uniqueCategories: options.uniqueCategories,
|
8364
|
+
minCategories: options.minCategories,
|
8365
|
+
maxCategories: options.maxCategories
|
8366
|
+
});
|
8367
|
+
}).toThrow(DataConflictError);
|
8368
|
+
});
|
8369
|
+
|
8370
|
+
it('should create DataConflictError with correct options for breakdown scenario', () => {
|
8371
|
+
const options = {
|
8372
|
+
isBreakdown: true,
|
8373
|
+
uniqueCategories: ['A'],
|
8374
|
+
minCategories: 5,
|
8375
|
+
maxCategories: 10
|
8376
|
+
};
|
8377
|
+
|
8378
|
+
try {
|
8379
|
+
throw new DataConflictError(options);
|
8380
|
+
} catch (error) {
|
8381
|
+
expect(error).toBeInstanceOf(DataConflictError);
|
8382
|
+
expect(error.code).toBe(5);
|
8383
|
+
expect(error.title).toBe('Data Conflict');
|
8384
|
+
expect(error.options).toEqual(options);
|
8385
|
+
expect(error.options.isBreakdown).toBe(true);
|
8386
|
+
expect(error.options.uniqueCategories).toEqual(['A']);
|
8387
|
+
expect(error.options.minCategories).toBe(5);
|
8388
|
+
expect(error.options.maxCategories).toBe(10);
|
8389
|
+
}
|
8390
|
+
});
|
8391
|
+
|
8392
|
+
it('should create DataConflictError with correct options for walkthrough scenario', () => {
|
8393
|
+
const options = {
|
8394
|
+
isBreakdown: false,
|
8395
|
+
uniqueCategories: ['A', 'B', 'C'],
|
8396
|
+
minCategories: 5,
|
8397
|
+
maxCategories: 8
|
8398
|
+
};
|
8399
|
+
|
8400
|
+
try {
|
8401
|
+
throw new DataConflictError(options);
|
8402
|
+
} catch (error) {
|
8403
|
+
expect(error).toBeInstanceOf(DataConflictError);
|
8404
|
+
expect(error.code).toBe(5);
|
8405
|
+
expect(error.title).toBe('Data Conflict');
|
8406
|
+
expect(error.options).toEqual(options);
|
8407
|
+
expect(error.options.isBreakdown).toBe(false);
|
8408
|
+
}
|
8409
|
+
});
|
8410
|
+
});
|
8411
|
+
|
8412
|
+
describe('Gauge Configuration Error Throwing', () => {
|
8413
|
+
it('should create GaugeConfigurationError with correct properties', () => {
|
8414
|
+
try {
|
8415
|
+
throw new GaugeConfigurationError();
|
8416
|
+
} catch (error) {
|
8417
|
+
expect(error).toBeInstanceOf(GaugeConfigurationError);
|
8418
|
+
expect(error.code).toBe(6);
|
8419
|
+
expect(error.title).toBe('Please configure goal and needle');
|
8420
|
+
expect(error.options).toEqual({});
|
8421
|
+
}
|
8422
|
+
});
|
7604
8423
|
|
7605
|
-
|
7606
|
-
|
7607
|
-
|
7608
|
-
|
8424
|
+
it('should be instance of BaseRendererError', () => {
|
8425
|
+
const error = new GaugeConfigurationError();
|
8426
|
+
expect(error).toBeInstanceOf(require('../src/errors').BaseRendererError);
|
8427
|
+
});
|
7609
8428
|
});
|
7610
8429
|
|
7611
|
-
|
7612
|
-
|
7613
|
-
|
7614
|
-
|
7615
|
-
|
7616
|
-
|
8430
|
+
describe('Error handling in rhPivotView functions (testing private _handleComputationalError and _handleRenderingError)', () => {
|
8431
|
+
beforeEach(() => {
|
8432
|
+
jest.spyOn(console, 'error').mockImplementation(() => {});
|
8433
|
+
});
|
8434
|
+
|
8435
|
+
afterEach(() => {
|
8436
|
+
jest.restoreAllMocks();
|
8437
|
+
});
|
8438
|
+
|
8439
|
+
describe('Natural error conditions (testing private error handlers indirectly)', () => {
|
8440
|
+
it('should handle too much data error correctly', () => {
|
8441
|
+
// Create dataset that exceeds MAX_ROWS_FOR_SHOW_RESULTS to trigger TooMuchDataError
|
8442
|
+
const largeRowData = new Array(highchartsRenderer.MAX_ROWS_FOR_SHOW_RESULTS + 1)
|
8443
|
+
.fill({ field1: 'value1', field2: 'value2' });
|
8444
|
+
|
8445
|
+
const options = {
|
8446
|
+
onlyOptions: false,
|
8447
|
+
rendererOptions: {},
|
8448
|
+
renderer: jest.fn()
|
8449
|
+
};
|
8450
|
+
|
8451
|
+
expect(() => {
|
8452
|
+
highchartsRenderer.rhPivotView(largeRowData, options);
|
8453
|
+
}).toThrow(TooMuchDataError);
|
8454
|
+
});
|
8455
|
+
|
8456
|
+
it('should return empty object for too much data when onlyOptions is true', () => {
|
8457
|
+
// Create dataset that exceeds MAX_ROWS_FOR_SHOW_RESULTS
|
8458
|
+
const largeRowData = new Array(highchartsRenderer.MAX_ROWS_FOR_SHOW_RESULTS + 1)
|
8459
|
+
.fill({ field1: 'value1', field2: 'value2' });
|
8460
|
+
|
8461
|
+
const options = {
|
8462
|
+
onlyOptions: true,
|
8463
|
+
rendererOptions: {},
|
8464
|
+
renderer: jest.fn()
|
8465
|
+
};
|
8466
|
+
|
8467
|
+
const result = highchartsRenderer.rhPivotView(largeRowData, options);
|
8468
|
+
expect(result).toEqual({});
|
8469
|
+
});
|
8470
|
+
|
8471
|
+
it('should handle no data error correctly', () => {
|
8472
|
+
const options = {
|
8473
|
+
onlyOptions: false,
|
8474
|
+
rendererOptions: {},
|
8475
|
+
renderer: jest.fn()
|
8476
|
+
};
|
8477
|
+
|
8478
|
+
// Empty rowData should trigger NoDataError
|
8479
|
+
expect(() => {
|
8480
|
+
highchartsRenderer.rhPivotView([], options);
|
8481
|
+
}).toThrow(NoDataError);
|
8482
|
+
});
|
8483
|
+
|
8484
|
+
it('should return empty object for no data when onlyOptions is true', () => {
|
8485
|
+
const options = {
|
8486
|
+
onlyOptions: true,
|
8487
|
+
rendererOptions: {},
|
8488
|
+
renderer: jest.fn()
|
8489
|
+
};
|
8490
|
+
|
8491
|
+
const result = highchartsRenderer.rhPivotView([], options);
|
8492
|
+
expect(result).toEqual({});
|
8493
|
+
});
|
8494
|
+
|
8495
|
+
it('should handle renderer errors correctly', () => {
|
8496
|
+
const rowData = [{ field1: 'value1', field2: 'value2' }];
|
8497
|
+
const genericError = new Error('Renderer failed');
|
8498
|
+
|
8499
|
+
const options = {
|
8500
|
+
onlyOptions: false,
|
8501
|
+
rendererOptions: {},
|
8502
|
+
renderer: jest.fn().mockImplementation(() => {
|
8503
|
+
throw genericError;
|
8504
|
+
})
|
8505
|
+
};
|
8506
|
+
|
8507
|
+
// Generic errors from renderer should be wrapped in GenericRenderingError
|
8508
|
+
expect(() => {
|
8509
|
+
highchartsRenderer.rhPivotView(rowData, options);
|
8510
|
+
}).toThrow(GenericRenderingError);
|
8511
|
+
});
|
8512
|
+
|
8513
|
+
it('should return empty object for renderer errors when onlyOptions is true', () => {
|
8514
|
+
const rowData = [{ field1: 'value1', field2: 'value2' }];
|
8515
|
+
const genericError = new Error('Renderer failed');
|
8516
|
+
|
8517
|
+
const options = {
|
8518
|
+
onlyOptions: true,
|
8519
|
+
rendererOptions: {},
|
8520
|
+
renderer: jest.fn().mockImplementation(() => {
|
8521
|
+
throw genericError;
|
8522
|
+
})
|
8523
|
+
};
|
8524
|
+
|
8525
|
+
const result = highchartsRenderer.rhPivotView(rowData, options);
|
8526
|
+
expect(result).toEqual({});
|
8527
|
+
});
|
8528
|
+
|
8529
|
+
it('should re-throw BaseRendererError instances from renderer unchanged', () => {
|
8530
|
+
const rowData = [{ field1: 'value1', field2: 'value2' }];
|
8531
|
+
const originalError = new NoDataError();
|
8532
|
+
|
8533
|
+
const options = {
|
8534
|
+
onlyOptions: false,
|
8535
|
+
rendererOptions: {},
|
8536
|
+
renderer: jest.fn().mockImplementation(() => {
|
8537
|
+
throw originalError;
|
8538
|
+
})
|
8539
|
+
};
|
8540
|
+
|
8541
|
+
// BaseRendererError instances should be re-thrown unchanged
|
8542
|
+
expect(() => {
|
8543
|
+
highchartsRenderer.rhPivotView(rowData, options);
|
8544
|
+
}).toThrow(NoDataError);
|
8545
|
+
|
8546
|
+
expect(() => {
|
8547
|
+
highchartsRenderer.rhPivotView(rowData, options);
|
8548
|
+
}).toThrow(originalError);
|
8549
|
+
});
|
8550
|
+
|
8551
|
+
it('should handle null/undefined from renderer as GenericRenderingError', () => {
|
8552
|
+
const rowData = [{ field1: 'value1', field2: 'value2' }];
|
8553
|
+
|
8554
|
+
// Test null
|
8555
|
+
const nullOptions = {
|
8556
|
+
onlyOptions: false,
|
8557
|
+
rendererOptions: {},
|
8558
|
+
renderer: jest.fn().mockImplementation(() => {
|
8559
|
+
throw null;
|
8560
|
+
})
|
8561
|
+
};
|
8562
|
+
|
8563
|
+
expect(() => {
|
8564
|
+
highchartsRenderer.rhPivotView(rowData, nullOptions);
|
8565
|
+
}).toThrow(GenericRenderingError);
|
8566
|
+
|
8567
|
+
const undefinedOptions = {
|
8568
|
+
onlyOptions: false,
|
8569
|
+
rendererOptions: {},
|
8570
|
+
renderer: jest.fn().mockImplementation(() => {
|
8571
|
+
throw undefined;
|
8572
|
+
})
|
8573
|
+
};
|
8574
|
+
|
8575
|
+
expect(() => {
|
8576
|
+
highchartsRenderer.rhPivotView(rowData, undefinedOptions);
|
8577
|
+
}).toThrow(GenericRenderingError);
|
8578
|
+
});
|
7617
8579
|
});
|
7618
8580
|
});
|
7619
8581
|
});
|