@netdata/charts
Version:
Netdata frontend SDK and chart utilities
277 lines (276 loc) • 11.1 kB
JavaScript
;
var _makeNode = _interopRequireDefault(require("./makeNode"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
describe("makeNode", function () {
var mockSdk;
var mockParent;
var node;
beforeEach(function () {
mockSdk = {
trigger: jest.fn()
};
mockParent = {
getAttributes: jest.fn(function () {
return {
timezone: "UTC"
};
}),
removeChild: jest.fn()
};
var initialAttributes = {
id: "test-chart",
chartLibrary: "dygraph",
pristine: {},
drawer: {
action: "values",
tab: "window",
showAdvancedStats: false
}
};
node = (0, _makeNode["default"])({
sdk: mockSdk,
parent: mockParent,
attributes: initialAttributes
});
});
describe("basic attribute operations", function () {
it("gets simple attributes", function () {
expect(node.getAttribute("id")).toBe("test-chart");
expect(node.getAttribute("chartLibrary")).toBe("dygraph");
});
it("sets simple attributes", function () {
node.setAttribute("newAttr", "newValue");
expect(node.getAttribute("newAttr")).toBe("newValue");
});
it("returns default value for non-existent attributes", function () {
expect(node.getAttribute("nonExistent", "default")).toBe("default");
});
});
describe("nested attribute operations", function () {
it("gets nested attributes with dot notation", function () {
expect(node.getAttribute("drawer.action")).toBe("values");
expect(node.getAttribute("drawer.tab")).toBe("window");
expect(node.getAttribute("drawer.showAdvancedStats")).toBe(false);
});
it("sets nested attributes with dot notation", function () {
node.setAttribute("drawer.newProp", "newValue");
expect(node.getAttribute("drawer.newProp")).toBe("newValue");
expect(node.getAttribute("drawer.action")).toBe("values");
});
it("returns default for non-existent nested attributes", function () {
expect(node.getAttribute("drawer.nonExistent", "default")).toBe("default");
expect(node.getAttribute("nonExistent.prop", "default")).toBe("default");
});
it("creates nested structure when setting deep paths", function () {
node.setAttribute("deep.nested.path", "value");
expect(node.getAttribute("deep.nested.path")).toBe("value");
});
});
describe("updateAttribute", function () {
it("updates simple attributes and triggers listeners", function () {
var listener = jest.fn();
node.onAttributeChange("testAttr", listener);
node.updateAttribute("testAttr", "newValue");
expect(node.getAttribute("testAttr")).toBe("newValue");
expect(listener).toHaveBeenCalledWith("newValue", undefined, "testAttr");
});
it("updates nested attributes and triggers listeners", function () {
var listener = jest.fn();
node.onAttributeChange("drawer.showAdvancedStats", listener);
node.updateAttribute("drawer.showAdvancedStats", true);
expect(node.getAttribute("drawer.showAdvancedStats")).toBe(true);
expect(listener).toHaveBeenCalledWith(true, false, "drawer.showAdvancedStats");
});
it("does not trigger listeners if value unchanged", function () {
var listener = jest.fn();
node.onAttributeChange("drawer.action", listener);
node.updateAttribute("drawer.action", "values");
expect(listener).not.toHaveBeenCalled();
});
it("handles undefined to defined transitions", function () {
var listener = jest.fn();
node.onAttributeChange("newAttr", listener);
node.updateAttribute("newAttr", "value");
expect(listener).toHaveBeenCalledWith("value", undefined, "newAttr");
});
});
describe("updateAttributes", function () {
it("updates multiple simple attributes", function () {
var listener1 = jest.fn();
var listener2 = jest.fn();
node.onAttributeChange("attr1", listener1);
node.onAttributeChange("attr2", listener2);
node.updateAttributes({
attr1: "value1",
attr2: "value2"
});
expect(node.getAttribute("attr1")).toBe("value1");
expect(node.getAttribute("attr2")).toBe("value2");
expect(listener1).toHaveBeenCalledWith("value1", undefined, "attr1");
expect(listener2).toHaveBeenCalledWith("value2", undefined, "attr2");
});
it("handles nested objects in updateAttributes", function () {
var listener = jest.fn();
node.onAttributeChange("drawer", listener);
node.updateAttributes({
drawer: {
showAdvancedStats: true,
action: "compare"
}
});
expect(node.getAttribute("drawer.showAdvancedStats")).toBe(true);
expect(node.getAttribute("drawer.action")).toBe("compare");
expect(listener).toHaveBeenCalledWith({
action: "compare",
showAdvancedStats: true
}, {
action: "values",
showAdvancedStats: false,
tab: "window"
}, "drawer");
});
it("handles mixed flat and nested updates", function () {
node.updateAttributes({
simpleAttr: "value",
nested: {
prop: "nested value"
},
dot: {
notation: "dot value"
}
});
expect(node.getAttribute("simpleAttr")).toBe("value");
expect(node.getAttribute("nested.prop")).toBe("nested value");
expect(node.getAttribute("dot.notation")).toBe("dot value");
});
});
describe("attribute listeners", function () {
it("supports multiple listeners on same attribute", function () {
var listener1 = jest.fn();
var listener2 = jest.fn();
node.onAttributeChange("testAttr", listener1);
node.onAttributeChange("testAttr", listener2);
node.updateAttribute("testAttr", "value");
expect(listener1).toHaveBeenCalledWith("value", undefined, "testAttr");
expect(listener2).toHaveBeenCalledWith("value", undefined, "testAttr");
});
it("supports listening to multiple attributes", function () {
var listener = jest.fn();
node.onAttributesChange(["attr1", "attr2"], listener);
node.updateAttribute("attr1", "value1");
node.updateAttribute("attr2", "value2");
expect(listener).toHaveBeenCalledTimes(2);
});
it("supports once listeners", function () {
var listener = jest.fn();
node.onceAttributeChange("testAttr", listener);
node.updateAttribute("testAttr", "value1");
node.updateAttribute("testAttr", "value2");
expect(listener).toHaveBeenCalledTimes(1);
expect(listener).toHaveBeenCalledWith("value1", undefined, "testAttr");
});
});
describe("pristine functionality", function () {
it("tracks pristine state changes", function () {
node.updateAttribute("chartType", "table");
expect(mockSdk.trigger).toHaveBeenCalledWith("pristineChanged", "pristine", node, "table", undefined);
});
});
describe("edge cases", function () {
it("handles null/undefined values in nested attributes", function () {
node.setAttribute("test.null", null);
node.setAttribute("test.undefined", undefined);
expect(node.getAttribute("test.null")).toBe(null);
expect(node.getAttribute("test.undefined", "default")).toBe("default");
});
it("handles empty string paths", function () {
node.setAttribute("", "value");
expect(node.getAttribute("", "default")).toBe("default");
});
it("handles null parent gracefully", function () {
var nodeWithoutParent = (0, _makeNode["default"])({
sdk: mockSdk,
parent: null,
attributes: {
id: "test"
}
});
expect(nodeWithoutParent.getAttribute("id")).toBe("test");
});
});
describe("pause reasons", function () {
it("starts with no reasons and paused=false (assuming blur is off)", function () {
expect(node.getAttribute("paused")).toBeUndefined();
});
it("addPauseReason flips paused to true", function () {
node.addPauseReason("hover");
expect(node.getAttribute("paused")).toBe(true);
});
it("removePauseReason after the only reason flips paused back to false", function () {
node.addPauseReason("hover");
node.removePauseReason("hover");
expect(node.getAttribute("paused")).toBe(false);
});
it("composes multiple reasons — removing one keeps paused true while others remain", function () {
node.addPauseReason("hover");
node.addPauseReason("modal");
expect(node.getAttribute("paused")).toBe(true);
node.removePauseReason("hover");
expect(node.getAttribute("paused")).toBe(true);
node.removePauseReason("modal");
expect(node.getAttribute("paused")).toBe(false);
});
it("is idempotent — same reason id added twice acts as one", function () {
node.addPauseReason("hover");
node.addPauseReason("hover");
node.removePauseReason("hover");
expect(node.getAttribute("paused")).toBe(false);
});
it("ignores empty/missing reason ids", function () {
node.addPauseReason("");
node.addPauseReason(null);
node.addPauseReason(undefined);
expect(node.getAttribute("paused")).toBeUndefined();
});
it("recomputes when the blur attributes flip — window blur fires pause without an explicit reason", function () {
node.updateAttribute("autofetchOnWindowBlur", false);
node.updateAttribute("blurred", true);
expect(node.getAttribute("paused")).toBe(true);
node.updateAttribute("blurred", false);
expect(node.getAttribute("paused")).toBe(false);
});
it("blur and explicit reasons combine — un-blurring keeps paused true if a reason is still registered", function () {
node.updateAttribute("autofetchOnWindowBlur", false);
node.updateAttribute("blurred", true);
node.addPauseReason("modal");
expect(node.getAttribute("paused")).toBe(true);
node.updateAttribute("blurred", false);
expect(node.getAttribute("paused")).toBe(true);
node.removePauseReason("modal");
expect(node.getAttribute("paused")).toBe(false);
});
it("autofetchOnWindowBlur=true disables the blur reason — paused stays false while blurred", function () {
node.updateAttribute("autofetchOnWindowBlur", true);
node.updateAttribute("blurred", true);
expect(node.getAttribute("paused")).toBe(false);
});
});
describe("inheritance", function () {
it("inherits parent attributes", function () {
mockParent.getAttributes.mockReturnValue({
timezone: "America/New_York",
inherited: "value"
});
var childNode = (0, _makeNode["default"])({
sdk: mockSdk,
parent: mockParent,
attributes: {
id: "child"
}
});
expect(childNode.getAttribute("timezone")).toBe("America/New_York");
expect(childNode.getAttribute("inherited")).toBe("value");
expect(childNode.getAttribute("id")).toBe("child");
});
});
});