@netdata/charts
Version:
Netdata frontend SDK and chart utilities
217 lines (208 loc) • 8.24 kB
JavaScript
;
var _numeric = _interopRequireDefault(require("./numeric"));
var _units = require("../../../helpers/units");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
describe("numericTicker", function () {
var mockOpts;
var mockDygraph;
beforeEach(function () {
mockOpts = jest.fn(function (key) {
var options = {
pixelsPerLabel: 50,
axisLabelFormatter: jest.fn(function (value) {
return "".concat(value);
})
};
return options[key];
});
mockDygraph = {
yAxisRange: jest.fn(function () {
return [0, 100];
}),
getArea: jest.fn(function () {
return {
h: 300
};
})
};
});
it("generates ticks when vals parameter is provided", function () {
var vals = [0, 25, 50, 75, 100];
var ticks = (0, _numeric["default"])(0, 100, 200, mockOpts, mockDygraph, vals, {
units: [""]
});
expect(ticks.length).toBeGreaterThan(vals.length); // includes boundary ticks and anomaly SVG
// Check that vals are included in ticks
vals.forEach(function (val) {
expect(ticks.some(function (tick) {
return tick.v === val;
})).toBe(true);
});
});
it("generates ticks automatically when vals is not provided", function () {
var ticks = (0, _numeric["default"])(0, 100, 400, mockOpts, mockDygraph, null, {
units: [""]
});
expect(Array.isArray(ticks)).toBe(true);
expect(ticks.length).toBeGreaterThan(2); // At least boundary ticks
expect(ticks[0]).toHaveProperty("label_v"); // Anomaly SVG tick
expect(ticks[ticks.length - 1]).toHaveProperty("label_v"); // Bottom boundary
});
it("uses binary multiples for binary units", function () {
var ticks = (0, _numeric["default"])(0, 1024, 400, mockOpts, mockDygraph, null, {
units: ["By"]
});
expect(Array.isArray(ticks)).toBe(true);
expect(ticks.length).toBeGreaterThan(2);
// Verify ticks are using binary spacing (powers of 2)
var dataTicks = ticks.filter(function (tick) {
return tick.v !== undefined && !tick.label_v;
}).map(function (t) {
return t.v;
});
if (dataTicks.length > 2) {
var spacing = dataTicks[1] - dataTicks[0];
// Binary units should use nice round numbers, not necessarily exact powers of 2
expect(spacing).toBeGreaterThan(0);
expect(spacing).toBeLessThanOrEqual(512);
}
});
it("uses decimal multiples for non-binary units", function () {
var ticks = (0, _numeric["default"])(0, 100, 400, mockOpts, mockDygraph, null, {
units: ["percent"]
});
expect(Array.isArray(ticks)).toBe(true);
expect(ticks.length).toBeGreaterThan(2);
// Verify ticks are using decimal spacing
var dataTicks = ticks.filter(function (tick) {
return tick.v !== undefined && !tick.label_v;
}).map(function (t) {
return t.v;
});
if (dataTicks.length > 2) {
var spacing = dataTicks[1] - dataTicks[0];
// Decimal units typically use 1, 2, 5, 10, 20, 50, 100 spacing
expect([1, 2, 5, 10, 20, 25, 50, 100].some(function (v) {
return Math.abs(spacing - v) < 0.01;
})).toBe(true);
}
});
it("calculates appropriate number of ticks based on pixels", function () {
var pixels = 500;
var pixelsPerLabel = 50;
mockOpts.mockImplementation(function (key) {
if (key === "pixelsPerLabel") return pixelsPerLabel;
if (key === "axisLabelFormatter") return jest.fn(function (v) {
return "".concat(v);
});
});
var ticks = (0, _numeric["default"])(0, 100, pixels, mockOpts, mockDygraph, null, {
units: [""]
});
// Should not exceed maximum ticks based on pixel spacing
var maxTicks = Math.ceil(pixels / pixelsPerLabel);
var dataTicks = ticks.filter(function (tick) {
return tick.v !== undefined && !tick.label_v;
});
expect(dataTicks.length).toBeLessThanOrEqual(maxTicks + 2); // Allow some margin for boundaries
});
it("formats labels using axisLabelFormatter", function () {
var mockFormatter = jest.fn(function (value) {
return "formatted_".concat(value);
});
mockOpts.mockImplementation(function (key) {
if (key === "axisLabelFormatter") return mockFormatter;
if (key === "pixelsPerLabel") return 50;
});
var vals = [0, 50, 100];
(0, _numeric["default"])(0, 100, 200, mockOpts, mockDygraph, vals, {
units: [""]
});
expect(mockFormatter).toHaveBeenCalled();
expect(mockFormatter).toHaveBeenCalledWith(expect.any(Number), 0, mockOpts, mockDygraph);
});
it("includes anomaly SVG in first tick", function () {
var ticks = (0, _numeric["default"])(0, 100, 200, mockOpts, mockDygraph, null, {
units: [""]
});
expect(ticks[0]).toHaveProperty("label_v");
expect(ticks[0]).toHaveProperty("label");
expect(ticks[0].label).toContain("svg");
expect(ticks[0].label).toContain("Anomaly detection");
});
it("includes empty label in last tick", function () {
var ticks = (0, _numeric["default"])(0, 100, 200, mockOpts, mockDygraph, null, {
units: [""]
});
expect(ticks[ticks.length - 1]).toHaveProperty("label_v");
expect(ticks[ticks.length - 1]).toHaveProperty("label");
expect(ticks[ticks.length - 1].label).toBe("");
});
it("handles negative ranges correctly", function () {
var ticks = (0, _numeric["default"])(-50, 50, 400, mockOpts, mockDygraph, null, {
units: [""]
});
expect(Array.isArray(ticks)).toBe(true);
expect(ticks.length).toBeGreaterThan(2);
// Should include ticks in negative range
var dataTicks = ticks.filter(function (tick) {
return tick.v !== undefined && !tick.label_v;
});
expect(dataTicks.some(function (tick) {
return tick.v < 0;
})).toBe(true);
});
it("handles reverse axis correctly", function () {
// Test with b < a to simulate reverse axis
var ticks = (0, _numeric["default"])(100, 0, 400, mockOpts, mockDygraph, null, {
units: [""]
});
expect(Array.isArray(ticks)).toBe(true);
expect(ticks.length).toBeGreaterThan(2);
});
it("calculates point height for boundary ticks", function () {
mockDygraph.yAxisRange.mockReturnValue([10, 90]);
mockDygraph.getArea.mockReturnValue({
h: 150
});
var ticks = (0, _numeric["default"])(0, 100, 200, mockOpts, mockDygraph, null, {
units: [""]
});
// Point height should be calculated as (max - min) / 15 / area.h
var expectedPointHeight = (90 - 10) / 15 / 150;
expect(ticks[0].label_v).toBeCloseTo(90 - expectedPointHeight);
expect(ticks[ticks.length - 1].label_v).toBeCloseTo(10 + expectedPointHeight);
});
it("formats all generated ticks with labels", function () {
var vals = [0, 25, 50, 75, 100];
var ticks = (0, _numeric["default"])(0, 100, 200, mockOpts, mockDygraph, vals, {
units: [""]
});
// All ticks should have labels (including boundary ticks)
ticks.forEach(function (tick) {
expect(tick).toHaveProperty("label");
});
// Data ticks should have formatted labels
var dataTicks = ticks.filter(function (tick) {
return tick.v !== undefined && !tick.label_v;
});
dataTicks.forEach(function (tick) {
expect(_typeof(tick.label)).toBe("string");
});
});
it("handles edge case with very small range", function () {
var ticks = (0, _numeric["default"])(0, 0.001, 200, mockOpts, mockDygraph, null, {
units: [""]
});
expect(Array.isArray(ticks)).toBe(true);
expect(ticks.length).toBeGreaterThan(2);
});
it("handles edge case with very large range", function () {
var ticks = (0, _numeric["default"])(0, 1000000, 200, mockOpts, mockDygraph, null, {
units: [""]
});
expect(Array.isArray(ticks)).toBe(true);
expect(ticks.length).toBeGreaterThan(2);
});
});