| |
| describe('TimeSeriesChart', () => { |
| |
| it('should be constructible with an empty sourec list and an empty options', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| new TimeSeriesChart([], {}); |
| }); |
| |
| describe('computeTimeGrid', () => { |
| it('should return an empty array when the start and the end times are identical', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| |
| const someTime = Date.now(); |
| const labels = TimeSeriesChart.computeTimeGrid(someTime, someTime, 0); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(0); |
| }); |
| |
| const millisecondsPerHour = 3600 * 1000; |
| const millisecondsPerDay = 24 * millisecondsPerHour; |
| |
| it('should return an empty array when maxLabels is 0', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = Date.now(); |
| const labels = TimeSeriesChart.computeTimeGrid(endTime - millisecondsPerDay, endTime, 0); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(0); |
| }); |
| |
| it('should return an empty array when maxLabels is 0 even when the interval spans multiple months', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = Date.now(); |
| const labels = TimeSeriesChart.computeTimeGrid(endTime - 120 * millisecondsPerDay, endTime, 0); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(0); |
| }); |
| |
| function checkGridItem(item, label, expectedDate) |
| { |
| expect(item.label).to.be(label); |
| expect(item.time.__proto__.constructor.name).to.be('Date'); |
| expect(+item.time).to.be(+new Date(expectedDate)); |
| } |
| |
| it('should generate one hour label with just day for two hour interval when maxLabels is 1', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T07:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(endTime - 2 * millisecondsPerHour, +endTime, 1); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(1); |
| checkGridItem(labels[0], '6AM', '2017-01-15T06:00:00Z'); |
| }); |
| |
| it('should generate two two-hour labels for four hour interval when maxLabels is 2', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T07:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(endTime - 4 * millisecondsPerHour, +endTime, 2); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(2); |
| checkGridItem(labels[0], '4AM', '2017-01-15T04:00:00Z'); |
| checkGridItem(labels[1], '6AM', '2017-01-15T06:00:00Z'); |
| }); |
| |
| it('should generate six two-hour labels for twelve hour interval when maxLabels is 6', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T07:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 12 * millisecondsPerHour, 6); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(6); |
| checkGridItem(labels[0], '8AM', '2017-01-15T08:00:00Z'); |
| checkGridItem(labels[1], '10AM', '2017-01-15T10:00:00Z'); |
| checkGridItem(labels[2], '12PM', '2017-01-15T12:00:00Z'); |
| checkGridItem(labels[3], '2PM', '2017-01-15T14:00:00Z'); |
| checkGridItem(labels[4], '4PM', '2017-01-15T16:00:00Z'); |
| checkGridItem(labels[5], '6PM', '2017-01-15T18:00:00Z'); |
| }); |
| |
| it('should generate six two-hour labels with one date label for twelve hour interval that cross a day when maxLabels is 6', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T16:12:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 12 * millisecondsPerHour, 6); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(6); |
| checkGridItem(labels[0], '6PM', '2017-01-15T18:00:00Z'); |
| checkGridItem(labels[1], '8PM', '2017-01-15T20:00:00Z'); |
| checkGridItem(labels[2], '10PM', '2017-01-15T22:00:00Z'); |
| checkGridItem(labels[3], '1/16', '2017-01-16T00:00:00Z'); |
| checkGridItem(labels[4], '2AM', '2017-01-16T02:00:00Z'); |
| checkGridItem(labels[5], '4AM', '2017-01-16T04:00:00Z'); |
| }); |
| |
| it('should generate three two-hour labels for six hour interval that cross a year when maxLabels is 5', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2016-12-31T21:37:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 6 * millisecondsPerHour, 5); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(3); |
| checkGridItem(labels[0], '10PM', '2016-12-31T22:00:00Z'); |
| checkGridItem(labels[1], '1/1', '2017-01-01T00:00:00Z'); |
| checkGridItem(labels[2], '2AM', '2017-01-01T02:00:00Z'); |
| }); |
| |
| it('should generate one one-day label for one day interval when maxLabels is 1', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T07:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(endTime - millisecondsPerDay, +endTime, 1); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(1); |
| checkGridItem(labels[0], '1/15', '2017-01-15T00:00:00Z'); |
| }); |
| |
| it('should generate two one-day labels for one day interval when maxLabels is 2', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T07:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(endTime - millisecondsPerDay, +endTime, 2); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(2); |
| checkGridItem(labels[0], '1/14 12PM', '2017-01-14T12:00:00Z'); |
| checkGridItem(labels[1], '1/15', '2017-01-15T00:00:00Z'); |
| }); |
| |
| it('should generate four half-day labels for two day interval when maxLabels is 5', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T16:12:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 2 * millisecondsPerDay, 5); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(4); |
| checkGridItem(labels[0], '1/16', '2017-01-16T00:00:00Z'); |
| checkGridItem(labels[1], '12PM', '2017-01-16T12:00:00Z'); |
| checkGridItem(labels[2], '1/17', '2017-01-17T00:00:00Z'); |
| checkGridItem(labels[3], '12PM', '2017-01-17T12:00:00Z'); |
| }); |
| |
| it('should generate four half-day labels for two day interval that cross a year when maxLabels is 5', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2016-12-31T09:12:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 2 * millisecondsPerDay, 5); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(4); |
| checkGridItem(labels[0], '12/31 12PM', '2016-12-31T12:00:00Z'); |
| checkGridItem(labels[1], '1/1', '2017-01-01T00:00:00Z'); |
| checkGridItem(labels[2], '12PM', '2017-01-01T12:00:00Z'); |
| checkGridItem(labels[3], '1/2', '2017-01-02T00:00:00Z'); |
| }); |
| |
| it('should generate seven per-day labels for one week interval when maxLabels is 10', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T07:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(endTime - 7 * millisecondsPerDay, endTime, 10); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(7); |
| checkGridItem(labels[0], '1/9', '2017-01-09T00:00:00Z'); |
| checkGridItem(labels[1], '1/10', '2017-01-10T00:00:00Z'); |
| checkGridItem(labels[2], '1/11', '2017-01-11T00:00:00Z'); |
| checkGridItem(labels[3], '1/12', '2017-01-12T00:00:00Z'); |
| checkGridItem(labels[4], '1/13', '2017-01-13T00:00:00Z'); |
| checkGridItem(labels[5], '1/14', '2017-01-14T00:00:00Z'); |
| checkGridItem(labels[6], '1/15', '2017-01-15T00:00:00Z'); |
| }); |
| |
| it('should generate three two-day labels for one week interval when maxLabels is 4', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T07:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(endTime - 7 * millisecondsPerDay, endTime, 4); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(3); |
| checkGridItem(labels[0], '1/10', '2017-01-10T00:00:00Z'); |
| checkGridItem(labels[1], '1/12', '2017-01-12T00:00:00Z'); |
| checkGridItem(labels[2], '1/14', '2017-01-14T00:00:00Z'); |
| }); |
| |
| it('should generate seven one-day labels for two week interval when maxLabels is 8', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T18:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 14 * millisecondsPerDay, 8); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(7); |
| checkGridItem(labels[0], '1/17', '2017-01-17T00:00:00Z'); |
| checkGridItem(labels[1], '1/19', '2017-01-19T00:00:00Z'); |
| checkGridItem(labels[2], '1/21', '2017-01-21T00:00:00Z'); |
| checkGridItem(labels[3], '1/23', '2017-01-23T00:00:00Z'); |
| checkGridItem(labels[4], '1/25', '2017-01-25T00:00:00Z'); |
| checkGridItem(labels[5], '1/27', '2017-01-27T00:00:00Z'); |
| checkGridItem(labels[6], '1/29', '2017-01-29T00:00:00Z'); |
| }); |
| |
| it('should generate two one-week labels for two week interval when maxLabels is 3', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T18:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 14 * millisecondsPerDay, 3); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(2); |
| checkGridItem(labels[0], '1/22', '2017-01-22T00:00:00Z'); |
| checkGridItem(labels[1], '1/29', '2017-01-29T00:00:00Z'); |
| }); |
| |
| it('should generate seven one-month labels for six and half months interval starting before 15th when maxLabels is 7', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T18:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-12T18:53:00Z'), new Date('2017-01-18T08:17:53Z'), 7); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(7); |
| checkGridItem(labels[0], '7/15', '2016-07-15T00:00:00Z'); |
| checkGridItem(labels[1], '8/15', '2016-08-15T00:00:00Z'); |
| checkGridItem(labels[2], '9/15', '2016-09-15T00:00:00Z'); |
| checkGridItem(labels[3], '10/15', '2016-10-15T00:00:00Z'); |
| checkGridItem(labels[4], '11/15', '2016-11-15T00:00:00Z'); |
| checkGridItem(labels[5], '12/15', '2016-12-15T00:00:00Z'); |
| checkGridItem(labels[6], '1/15', '2017-01-15T00:00:00Z'); |
| }); |
| |
| it('should generate seven one-month labels for six months interval staring after 15th when maxLabels is 7', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T18:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-18T18:53:00Z'), new Date('2017-01-18T08:17:53Z'), 7); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(6); |
| checkGridItem(labels[0], '8/1', '2016-08-01T00:00:00Z'); |
| checkGridItem(labels[1], '9/1', '2016-09-01T00:00:00Z'); |
| checkGridItem(labels[2], '10/1', '2016-10-01T00:00:00Z'); |
| checkGridItem(labels[3], '11/1', '2016-11-01T00:00:00Z'); |
| checkGridItem(labels[4], '12/1', '2016-12-01T00:00:00Z'); |
| }); |
| |
| it('should generate six two-months labels for one year interval when maxLabels is 7', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T18:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-11T18:53:00Z'), new Date('2017-07-27T08:17:53Z'), 7); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(6); |
| checkGridItem(labels[0], '9/1', '2016-09-01T00:00:00Z'); |
| checkGridItem(labels[1], '11/1', '2016-11-01T00:00:00Z'); |
| checkGridItem(labels[2], '1/1', '2017-01-01T00:00:00Z'); |
| checkGridItem(labels[3], '3/1', '2017-03-01T00:00:00Z'); |
| checkGridItem(labels[4], '5/1', '2017-05-01T00:00:00Z'); |
| checkGridItem(labels[5], '7/1', '2017-07-01T00:00:00Z'); |
| }); |
| |
| it('should generate four three-months labels for one year interval when maxLabels is 5', async () => { |
| const TimeSeriesChart = await ChartTest.importChartScripts(new BrowsingContext); |
| const endTime = new Date('2017-01-15T18:53:00Z'); |
| const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-11T18:53:00Z'), new Date('2017-07-27T08:17:53Z'), 5); |
| expect(labels).to.be.a('array'); |
| expect(labels.length).to.be(4); |
| checkGridItem(labels[0], '10/1', '2016-10-01T00:00:00Z'); |
| checkGridItem(labels[1], '1/1', '2017-01-01T00:00:00Z'); |
| checkGridItem(labels[2], '4/1', '2017-04-01T00:00:00Z'); |
| checkGridItem(labels[3], '7/1', '2017-07-01T00:00:00Z'); |
| }); |
| }); |
| |
| describe('computeValueGrid', () => { |
| |
| function checkValueGrid(actual, expected) |
| { |
| expect(actual).to.be.a('array'); |
| expect(JSON.stringify(actual)).to.be(JSON.stringify(expected)); |
| } |
| |
| function approximate(number) |
| { |
| return Math.round(number * 100000000) / 100000000; |
| } |
| |
| it('should generate [0.5, 1.0, 1.5, 2.0] for [0.3, 2.3] when maxLabels is 5', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(0.3, 2.3, 5, Metric.makeFormatter('pt', 2)); |
| expect(grid.map((item) => approximate(item.value))).to.eql([0.5, 1.0, 1.5, 2.0]); |
| expect(grid.map((item) => item.label)).to.eql(['0.5 pt', '1.0 pt', '1.5 pt', '2.0 pt']); |
| }); |
| |
| it('should generate [0.4, 0.6, 0.8, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.2] for [0.3, 2.3] when maxLabels is 10', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(0.3, 2.3, 10, Metric.makeFormatter('pt', 2)); |
| expect(grid.map((item) => approximate(item.value))).to.eql([0.4, 0.6, 0.8, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.2]); |
| expect(grid.map((item) => item.label)).to.eql(['0.4 pt', '0.6 pt', '0.8 pt', '1.0 pt', '1.2 pt', '1.4 pt', '1.6 pt', '1.8 pt', '2.0 pt', '2.2 pt']); |
| }); |
| |
| it('should generate [1, 2] for [0.3, 2.3] when maxLabels is 2', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(0.3, 2.3, 2, Metric.makeFormatter('pt', 2)); |
| expect(grid.map((item) => item.value)).to.eql([1, 2]); |
| expect(grid.map((item) => item.label)).to.eql(['1.0 pt', '2.0 pt']); |
| }); |
| |
| it('should generate [0.4, 0.6, 0.8, 1.0, 1.2] for [0.3, 1.3] when maxLabels is 5', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(0.3, 1.3, 5, Metric.makeFormatter('pt', 2)); |
| expect(grid.map((item) => approximate(item.value))).to.eql([0.4, 0.6, 0.8, 1.0, 1.2]); |
| expect(grid.map((item) => item.label)).to.eql(['0.4 pt', '0.6 pt', '0.8 pt', '1.0 pt', '1.2 pt']); |
| }); |
| |
| it('should generate [0.2, 0.4, 0.6, 0.8, 1, 1.2] for [0.2, 1.3] when maxLabels is 10', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(0.2, 1.3, 10, Metric.makeFormatter('pt', 2)); |
| expect(grid.map((item) => approximate(item.value))).to.eql([0.2, 0.4, 0.6, 0.8, 1, 1.2]); |
| expect(grid.map((item) => item.label)).to.eql(['0.2 pt', '0.4 pt', '0.6 pt', '0.8 pt', '1.0 pt', '1.2 pt']); |
| }); |
| |
| it('should generate [0.5, 1.0] for [0.3, 1.3] when maxLabels is 4', () => { |
| const context = new BrowsingContext; |
| return ChartTest.importChartScripts(context).then((TimeSeriesChart) => { |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(0.3, 1.3, 4, Metric.makeFormatter('pt', 2)); |
| expect(grid.map((item) => approximate(item.value))).to.eql([0.5, 1.0]); |
| expect(grid.map((item) => item.label)).to.eql(['0.5 pt', '1.0 pt']); |
| }); |
| }); |
| |
| it('should generate [10, 20, 30] for [4, 35] when maxLabels is 4', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(4, 35, 4, Metric.makeFormatter('pt', 2)); |
| expect(grid.map((item) => item.value)).to.eql([10, 20, 30]); |
| expect(grid.map((item) => item.label)).to.eql(['10 pt', '20 pt', '30 pt']); |
| }); |
| |
| it('should generate [10, 20, 30] for [4, 35] when maxLabels is 6', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(4, 35, 6, Metric.makeFormatter('pt', 2)); |
| expect(grid.map((item) => item.value)).to.eql([10, 20, 30]); |
| expect(grid.map((item) => item.label)).to.eql(['10 pt', '20 pt', '30 pt']); |
| }); |
| |
| it('should generate [10, 15, 20, 25, 30, 35] for [6, 35] when maxLabels is 6', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(6, 35, 6, Metric.makeFormatter('pt', 2)); |
| expect(grid.map((item) => item.value)).to.eql([10, 15, 20, 25, 30, 35]); |
| expect(grid.map((item) => item.label)).to.eql(['10 pt', '15 pt', '20 pt', '25 pt', '30 pt', '35 pt']); |
| }); |
| |
| it('should generate [110, 115, 120, 125, 130] for [107, 134] when maxLabels is 6', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(107, 134, 6, Metric.makeFormatter('pt', 3)); |
| expect(grid.map((item) => item.value)).to.eql([110, 115, 120, 125, 130]); |
| expect(grid.map((item) => item.label)).to.eql(['110 pt', '115 pt', '120 pt', '125 pt', '130 pt']); |
| }); |
| |
| it('should generate [5e7, 10e7] for [1e7, 1e8] when maxLabels is 4', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(1e7, 1e8, 4, Metric.makeFormatter('pt', 3)); |
| expect(grid.map((item) => item.value)).to.eql([5e7, 10e7]); |
| expect(grid.map((item) => item.label)).to.eql(['50.0 Mpt', '100 Mpt']); |
| }); |
| |
| it('should generate [2e7, 4e7, 6e7, 8e7, 10e7] for [1e7, 1e8] when maxLabels is 5', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(1e7, 1e8, 5, Metric.makeFormatter('pt', 3)); |
| expect(grid.map((item) => item.value)).to.eql([2e7, 4e7, 6e7, 8e7, 10e7]); |
| expect(grid.map((item) => item.label)).to.eql(['20.0 Mpt', '40.0 Mpt', '60.0 Mpt', '80.0 Mpt', '100 Mpt']); |
| }); |
| |
| it('should generate [-1.5, -1.0, -0.5, 0.0, 0.5] for [-1.8, 0.7] when maxLabels is 5', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(-1.8, 0.7, 5, Metric.makeFormatter('pt', 2)); |
| expect(grid.map((item) => approximate(item.value))).to.eql([-1.5, -1.0, -0.5, 0.0, 0.5]); |
| expect(grid.map((item) => item.label)).to.eql(['-1.5 pt', '-1.0 pt', '-0.5 pt', '0.0 pt', '0.5 pt']); |
| }); |
| |
| it('should generate [200ms, 400ms, 600ms, 800ms, 1.00s, 1.20s] for [0.2, 1.3] when maxLabels is 10 and unit is seconds', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const grid = TimeSeriesChart.computeValueGrid(0.2, 1.3, 10, Metric.makeFormatter('s', 3)); |
| expect(grid.map((item) => approximate(item.value))).to.eql([0.2, 0.4, 0.6, 0.8, 1, 1.2]); |
| expect(grid.map((item) => item.label)).to.eql(['200 ms', '400 ms', '600 ms', '800 ms', '1.00 s', '1.20 s']); |
| }); |
| |
| it('should generate [2.0GB, 4.0GB, 6.0GB] for [1.2GB, 7.2GB] when maxLabels is 4 and unit is bytes', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const gigabytes = Math.pow(1024, 3); |
| const grid = TimeSeriesChart.computeValueGrid(1.2 * gigabytes, 7.2 * gigabytes, 4, Metric.makeFormatter('B', 2)); |
| expect(grid.map((item) => approximate(item.value))).to.eql([2 * gigabytes, 4 * gigabytes, 6 * gigabytes]); |
| expect(grid.map((item) => item.label)).to.eql(['2.0 GB', '4.0 GB', '6.0 GB']); |
| }); |
| |
| it('should generate [0.6GB, 0.8GB, 1.0GB, 1.2GB] for [0.53GB, 1.23GB] when maxLabels is 4 and unit is bytes', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const Metric = context.symbols.Metric; |
| const gigabytes = Math.pow(1024, 3); |
| const grid = TimeSeriesChart.computeValueGrid(0.53 * gigabytes, 1.23 * gigabytes, 4, Metric.makeFormatter('B', 2)); |
| expect(grid.map((item) => item.label)).to.eql(['0.6 GB', '0.8 GB', '1.0 GB', '1.2 GB']); |
| }); |
| |
| }); |
| |
| describe('fetchMeasurementSets', () => { |
| |
| it('should fetch the measurement set and create a canvas element upon receiving the data', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const chart = ChartTest.createChartWithSampleCluster(context); |
| |
| chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chart.fetchMeasurementSets(); |
| |
| const requests = context.symbols.MockRemoteAPI.requests; |
| expect(requests.length).to.be(1); |
| ChartTest.respondWithSampleCluster(requests[0]); |
| |
| expect(chart.content().querySelector('canvas')).to.be(null); |
| |
| await waitForComponentsToRender(context); |
| |
| expect(chart.content().querySelector('canvas')).to.not.be(null); |
| }); |
| |
| it('should immediately enqueue to render when the measurement set had already been fetched', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const chart = ChartTest.createChartWithSampleCluster(context); |
| |
| let set = context.symbols.MeasurementSet.findSet(1, 1, 0); |
| let promise = set.fetchBetween(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| |
| const requests = context.symbols.MockRemoteAPI.requests; |
| expect(requests.length).to.be(1); |
| ChartTest.respondWithSampleCluster(requests[0]); |
| |
| await promise; |
| |
| expect(chart.content().querySelector('canvas')).to.be(null); |
| chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chart.fetchMeasurementSets(); |
| |
| await waitForComponentsToRender(context); |
| |
| expect(requests.length).to.be(1); |
| expect(chart.content().querySelector('canvas')).to.not.be(null); |
| }); |
| |
| it('should dispatch "dataChange" action once the fetched data becomes available', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const chart = ChartTest.createChartWithSampleCluster(context); |
| |
| let dataChangeCount = 0; |
| chart.listenToAction('dataChange', () => dataChangeCount++); |
| |
| chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chart.fetchMeasurementSets(); |
| |
| const requests = context.symbols.MockRemoteAPI.requests; |
| expect(requests.length).to.be(1); |
| ChartTest.respondWithSampleCluster(requests[0]); |
| |
| expect(dataChangeCount).to.be(0); |
| expect(chart.sampledTimeSeriesData('current')).to.be(null); |
| expect(chart.content().querySelector('canvas')).to.be(null); |
| |
| await waitForComponentsToRender(context); |
| |
| expect(dataChangeCount).to.be(1); |
| expect(chart.sampledTimeSeriesData('current')).to.not.be(null); |
| expect(chart.content().querySelector('canvas')).to.not.be(null); |
| }); |
| }); |
| |
| describe('sampledTimeSeriesData', () => { |
| it('should not contain an outlier when includeOutliers is false', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| |
| const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: false}]) |
| |
| chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chart.fetchMeasurementSets(); |
| ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]); |
| |
| await waitForComponentsToRender(context); |
| |
| const view = chart.sampledTimeSeriesData('current'); |
| expect(view.length()).to.be(5); |
| for (let point of view) |
| expect(point.markedOutlier).to.be(false); |
| }); |
| |
| it('should contain every outlier when includeOutliers is true', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: true}]) |
| |
| chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chart.fetchMeasurementSets(); |
| ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]); |
| |
| await waitForComponentsToRender(context); |
| |
| const view = chart.sampledTimeSeriesData('current'); |
| expect(view.length()).to.be(7); |
| expect(view.findPointByIndex(1).markedOutlier).to.be(true); |
| expect(view.findPointByIndex(5).markedOutlier).to.be(true); |
| }); |
| |
| it('should only contain data points in the domain and one preceding point when there are no succeeding points', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: true}]) |
| |
| chart.setDomain(posixTime('2016-01-06T00:00:00Z'), posixTime('2016-01-07T00:00:00Z')); |
| chart.fetchMeasurementSets(); |
| ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]); |
| |
| await waitForComponentsToRender(context); |
| |
| const view = chart.sampledTimeSeriesData('current'); |
| expect([...view].map((point) => point.id)).to.be.eql([1003, 1004, 1005, 1006]); |
| }); |
| |
| it('should only contain data points in the domain and one succeeding point when there are no preceding points', () => { |
| const context = new BrowsingContext(); |
| return ChartTest.importChartScripts(context).then(() => { |
| const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: true}]) |
| |
| chart.setDomain(posixTime('2016-01-05T00:00:00Z'), posixTime('2016-01-06T00:00:00Z')); |
| chart.fetchMeasurementSets(); |
| chart.fetchMeasurementSets(); |
| ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]); |
| |
| return waitForComponentsToRender(context).then(() => { |
| const view = chart.sampledTimeSeriesData('current'); |
| expect([...view].map((point) => point.id)).to.be.eql([1000, 1001, 1002, 1003, 1004]); |
| }); |
| }); |
| }); |
| |
| it('should only contain data points in the domain and one preceding point and one succeeding point', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: true}]) |
| |
| chart.setDomain(posixTime('2016-01-05T21:00:00Z'), posixTime('2016-01-06T02:00:00Z')); |
| chart.fetchMeasurementSets(); |
| ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]); |
| |
| await waitForComponentsToRender(context); |
| |
| const view = chart.sampledTimeSeriesData('current'); |
| expect([...view].map((point) => point.id)).to.be.eql([1002, 1003, 1004, 1005]); |
| }); |
| }); |
| |
| describe('render', () => { |
| it('should update the canvas size and its content after the window has been resized', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const chart = ChartTest.createChartWithSampleCluster(context, null, {width: '100%', height: '100%'}); |
| |
| let dataChangeCount = 0; |
| chart.listenToAction('dataChange', () => dataChangeCount++); |
| expect(chart.sampledTimeSeriesData('current')).to.be(null); |
| expect(chart.content().querySelector('canvas')).to.be(null); |
| |
| chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| await waitForComponentsToRender(context); |
| |
| chart.fetchMeasurementSets(); |
| const requests = context.symbols.MockRemoteAPI.requests; |
| expect(requests.length).to.be(1); |
| ChartTest.respondWithSampleCluster(requests[0]); |
| |
| await requests[0].promise; |
| await waitForComponentsToRender(context); |
| |
| expect(dataChangeCount).to.be(2); |
| expect(chart.sampledTimeSeriesData('current')).to.not.be(null); |
| const canvas = chart.content().querySelector('canvas'); |
| expect(canvas).to.not.be(null); |
| |
| const originalWidth = canvas.offsetWidth; |
| const originalHeight = canvas.offsetHeight; |
| expect(originalWidth).to.be(context.document.body.offsetWidth); |
| expect(originalHeight).to.be(context.document.body.offsetHeight); |
| |
| CanvasTest.fillCanvasBeforeRedrawCheck(canvas); |
| context.iframe.style.width = context.iframe.offsetWidth * 2 + 'px'; |
| |
| expect(canvas.offsetWidth).to.be(originalWidth); |
| expect(canvas.offsetHeight).to.be(originalHeight); |
| |
| await waitForElementResize(chart.element()); |
| await waitForComponentsToRender(context); |
| |
| expect(dataChangeCount).to.be(3); |
| expect(canvas.offsetWidth).to.be.greaterThan(originalWidth); |
| expect(canvas.offsetWidth).to.be(context.document.body.offsetWidth); |
| expect(canvas.offsetHeight).to.be(originalHeight); |
| expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true); |
| }); |
| |
| it('should not update update the canvas when the window has been resized but its dimensions stays the same', async () => { |
| const context = new BrowsingContext; |
| const TimeSeriesChart = await ChartTest.importChartScripts(context); |
| const chart = ChartTest.createChartWithSampleCluster(context, null, {width: '100px', height: '100px'}); |
| |
| let dataChangeCount = 0; |
| chart.listenToAction('dataChange', () => dataChangeCount++); |
| |
| chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chart.fetchMeasurementSets(); |
| |
| const requests = context.symbols.MockRemoteAPI.requests; |
| expect(requests.length).to.be(1); |
| ChartTest.respondWithSampleCluster(requests[0]); |
| expect(dataChangeCount).to.be(0); |
| |
| await waitForComponentsToRender(context); |
| |
| expect(dataChangeCount).to.be(1); |
| const data = chart.sampledTimeSeriesData('current'); |
| expect(data).to.not.be(null); |
| const canvas = chart.content().querySelector('canvas'); |
| expect(canvas).to.not.be(null); |
| |
| expect(canvas.offsetWidth).to.be(100); |
| expect(canvas.offsetHeight).to.be(100); |
| |
| CanvasTest.fillCanvasBeforeRedrawCheck(canvas); |
| context.iframe.style.width = context.iframe.offsetWidth * 2 + 'px'; |
| |
| expect(canvas.offsetWidth).to.be(100); |
| expect(canvas.offsetHeight).to.be(100); |
| |
| await waitForElementResize(chart.element()); |
| await waitForComponentsToRender(context); |
| |
| expect(dataChangeCount).to.be(1); |
| expect(chart.sampledTimeSeriesData('current')).to.be(data); |
| expect(canvas.offsetWidth).to.be(100); |
| expect(canvas.offsetHeight).to.be(100); |
| expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false); |
| }); |
| |
| it('should render Y-axis', async () => { |
| const context = new BrowsingContext; |
| await ChartTest.importChartScripts(context); |
| |
| const chartWithoutYAxis = ChartTest.createChartWithSampleCluster(context, null, {axis: |
| { |
| gridStyle: '#ccc', |
| fontSize: 1, |
| valueFormatter: context.symbols.Metric.makeFormatter('ms', 3), |
| } |
| }); |
| const chartWithYAxis1 = ChartTest.createChartWithSampleCluster(context, null, {axis: |
| { |
| yAxisWidth: 4, |
| gridStyle: '#ccc', |
| fontSize: 1, |
| valueFormatter: context.symbols.Metric.makeFormatter('ms', 3), |
| } |
| }); |
| const chartWithYAxis2 = ChartTest.createChartWithSampleCluster(context, null, {axis: |
| { |
| yAxisWidth: 4, |
| gridStyle: '#ccc', |
| fontSize: 1, |
| valueFormatter: context.symbols.Metric.makeFormatter('B', 3), |
| } |
| }); |
| |
| chartWithoutYAxis.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chartWithoutYAxis.fetchMeasurementSets(); |
| chartWithYAxis1.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chartWithYAxis1.fetchMeasurementSets(); |
| chartWithYAxis2.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chartWithYAxis2.fetchMeasurementSets(); |
| |
| const requests = context.symbols.MockRemoteAPI.requests; |
| expect(requests.length).to.be(1); |
| ChartTest.respondWithSampleCluster(requests[0]); |
| |
| await waitForComponentsToRender(context); |
| |
| let canvasWithoutYAxis = chartWithoutYAxis.content().querySelector('canvas'); |
| let canvasWithYAxis1 = chartWithYAxis1.content().querySelector('canvas'); |
| let canvasWithYAxis2 = chartWithYAxis2.content().querySelector('canvas'); |
| CanvasTest.expectCanvasesMismatch(canvasWithoutYAxis, canvasWithYAxis1); |
| CanvasTest.expectCanvasesMismatch(canvasWithoutYAxis, canvasWithYAxis1); |
| CanvasTest.expectCanvasesMismatch(canvasWithYAxis1, canvasWithYAxis2); |
| |
| expect(CanvasTest.canvasContainsColor(canvasWithYAxis1, {r: 204, g: 204, b: 204}, |
| {x: canvasWithYAxis1.width - 1, width: 1, y: 0, height: canvasWithYAxis1.height})).to.be(true); |
| }); |
| |
| it('should render the sampled time series', async () => { |
| const context = new BrowsingContext; |
| await ChartTest.importChartScripts(context); |
| |
| const lineStyle = 'rgb(0, 128, 255)'; |
| const lineColor = {r: 0, g: 128, b: 255}; |
| const chartOptions = {width: '100px', height: '100px'}; |
| const chartWithoutSampling = ChartTest.createChartWithSampleCluster(context, [{lineStyle, sampleData: false}], chartOptions);0 |
| const chartWithSampling = ChartTest.createChartWithSampleCluster(context, [{lineStyle, sampleData: true}], chartOptions); |
| |
| chartWithoutSampling.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chartWithoutSampling.fetchMeasurementSets(); |
| ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]); |
| |
| chartWithSampling.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chartWithSampling.fetchMeasurementSets(); |
| |
| for (const source of chartWithoutSampling.sourceList()) |
| await source.measurementSet.fetchBetween(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| |
| await waitForComponentsToRender(context); |
| |
| const canvasWithoutSampling = chartWithoutSampling.content().querySelector('canvas'); |
| const canvasWithSampling = chartWithSampling.content().querySelector('canvas'); |
| |
| CanvasTest.expectCanvasesMatch(canvasWithSampling, canvasWithoutSampling); |
| expect(CanvasTest.canvasContainsColor(canvasWithoutSampling, lineColor)).to.be(true); |
| expect(CanvasTest.canvasContainsColor(canvasWithSampling, lineColor)).to.be(true); |
| |
| const diff = ChartTest.sampleCluster.endTime - ChartTest.sampleCluster.startTime; |
| const expandedStartTime = ChartTest.sampleCluster.startTime - 2 * diff; |
| chartWithoutSampling.setDomain(expandedStartTime, ChartTest.sampleCluster.endTime); |
| chartWithSampling.setDomain(expandedStartTime, ChartTest.sampleCluster.endTime); |
| |
| for (const source of chartWithoutSampling.sourceList()) |
| await source.measurementSet.fetchBetween(expandedStartTime, ChartTest.sampleCluster.endTime); |
| |
| CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithoutSampling); |
| CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithSampling); |
| |
| await waitForComponentsToRender(context); |
| |
| expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithoutSampling)).to.be(true); |
| expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithSampling)).to.be(true); |
| |
| expect(CanvasTest.canvasContainsColor(canvasWithoutSampling, lineColor)).to.be(true); |
| expect(CanvasTest.canvasContainsColor(canvasWithSampling, lineColor)).to.be(true); |
| |
| CanvasTest.expectCanvasesMismatch(canvasWithSampling, canvasWithoutSampling); |
| }); |
| |
| it('should render annotations', async () => { |
| const context = new BrowsingContext; |
| await ChartTest.importChartScripts(context); |
| |
| const options = {annotations: { textStyle: '#000', textBackground: '#fff', minWidth: 3, barHeight: 7, barSpacing: 2, fillStyle: '#ccc'}}; |
| const chartWithoutAnnotations = ChartTest.createChartWithSampleCluster(context, null, options); |
| const chartWithAnnotations = ChartTest.createChartWithSampleCluster(context, null, options); |
| |
| chartWithoutAnnotations.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chartWithoutAnnotations.fetchMeasurementSets(); |
| ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]); |
| |
| chartWithAnnotations.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime); |
| chartWithAnnotations.fetchMeasurementSets(); |
| |
| await waitForComponentsToRender(context); |
| |
| const diff = ChartTest.sampleCluster.endTime - ChartTest.sampleCluster.startTime; |
| chartWithAnnotations.setAnnotations([{ |
| startTime: ChartTest.sampleCluster.startTime + diff / 4, |
| endTime: ChartTest.sampleCluster.startTime + diff / 2, |
| label: 'hello, world', |
| }]); |
| |
| const canvasWithAnnotations = chartWithAnnotations.content().querySelector('canvas'); |
| CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithAnnotations); |
| |
| await waitForComponentsToRender(context); |
| |
| expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithAnnotations)).to.be(true); |
| |
| const canvasWithoutAnnotations = chartWithoutAnnotations.content().querySelector('canvas'); |
| CanvasTest.expectCanvasesMismatch(canvasWithAnnotations, canvasWithoutAnnotations); |
| }); |
| }); |
| |
| }); |