diff --git a/ui/package-lock.json b/ui/package-lock.json
index e09004ed4..33326b5d6 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -6355,6 +6355,12 @@
"p-map": "1.2.0"
}
},
+ "jest-localstorage-mock": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/jest-localstorage-mock/-/jest-localstorage-mock-2.2.0.tgz",
+ "integrity": "sha512-x+P0vcwr4540bCAYzTEpiD9rs+zh/QZzyiABV+MU6yM2OPwPlrrLyUx/6gValMyt6tg5lX6Z53o2rHWfUht5Xw==",
+ "dev": true
+ },
"jest-matcher-utils": {
"version": "20.0.3",
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-20.0.3.tgz",
diff --git a/ui/package.json b/ui/package.json
index 372070f65..01417fb43 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -52,6 +52,7 @@
},
"devDependencies": {
"eslint-plugin-react": "7.11.1",
+ "jest-localstorage-mock": "2.2.0",
"markdownlint-cli": "0.13.0",
"node-sass-chokidar": "1.3.3",
"onchange": "4.1.0",
diff --git a/ui/src/Components/Fetcher/index.test.js b/ui/src/Components/Fetcher/index.test.js
new file mode 100644
index 000000000..02c2c7cfa
--- /dev/null
+++ b/ui/src/Components/Fetcher/index.test.js
@@ -0,0 +1,124 @@
+import React from "react";
+import renderer from "react-test-renderer";
+
+import { FetchMock, EmptyAPIResponse } from "__mocks__/Fetch";
+
+import { AlertStore } from "Stores/AlertStore";
+import { Settings } from "Stores/Settings";
+
+import { Fetcher } from ".";
+
+beforeAll(() => {
+ jest.useFakeTimers();
+});
+
+let alertStore;
+let settingsStore;
+
+beforeEach(() => {
+ const response = EmptyAPIResponse();
+ global.fetch = FetchMock(response);
+
+ alertStore = new AlertStore(["label=value"]);
+ settingsStore = new Settings();
+});
+
+afterEach(() => {
+ jest.clearAllTimers();
+
+ global.fetch.mockRestore();
+});
+
+describe("", () => {
+ it("renders correctly with 'label=value' filter", () => {
+ const tree = renderer
+ .create()
+ .toJSON();
+
+ expect(tree.props["data-filters"]).toBe("label=value");
+ expect(tree.props["data-interval"]).toBe(30);
+ });
+
+ it("re-renders on fetch interval change", () => {
+ const fetcher = renderer.create(
+
+ );
+
+ expect(fetcher.toJSON().props["data-interval"]).toBe(30);
+ settingsStore.fetchConfig.config.interval = 60;
+ expect(fetcher.toJSON().props["data-interval"]).toBe(60);
+ });
+
+ it("re-renders on filters change", () => {
+ const fetcher = renderer.create(
+
+ );
+
+ expect(fetcher.toJSON().props["data-filters"]).toBe("label=value");
+ alertStore.filters.values = [];
+ expect(fetcher.toJSON().props["data-filters"]).toBe("");
+ });
+
+ it("calls alertStore.fetchWithThrottle on mount", () => {
+ const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle");
+
+ renderer.create(
+
+ );
+
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
+ });
+
+ it("calls alertStore.fetchWithThrottle again after interval change", () => {
+ const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle");
+
+ renderer.create(
+
+ );
+ settingsStore.fetchConfig.config.interval = 60;
+
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
+ });
+
+ it("calls alertStore.fetchWithThrottle again after filter change", () => {
+ const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle");
+
+ renderer.create(
+
+ );
+ alertStore.filters.values = [];
+
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
+ });
+
+ it("keeps calling alertStore.fetchWithThrottle after running pending timers", () => {
+ const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle");
+
+ renderer.create(
+
+ );
+ jest.runOnlyPendingTimers();
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
+ jest.runOnlyPendingTimers();
+ expect(fetchSpy).toHaveBeenCalledTimes(3);
+ jest.runOnlyPendingTimers();
+ expect(fetchSpy).toHaveBeenCalledTimes(4);
+ });
+
+ it("internal timer is armed after render", () => {
+ const fetcher = renderer.create(
+
+ );
+ const instance = fetcher.getInstance();
+ expect(instance.timer).toBeGreaterThanOrEqual(0);
+ });
+
+ it("internal timer is null after unmount", () => {
+ const fetcher = renderer.create(
+
+ );
+ const instance = fetcher.getInstance();
+ instance.componentWillUnmount();
+ expect(instance.timer).toBeNull();
+ });
+});
diff --git a/ui/src/Stores/AlertStore.js b/ui/src/Stores/AlertStore.js
index d3674de43..419ebd4e1 100644
--- a/ui/src/Stores/AlertStore.js
+++ b/ui/src/Stores/AlertStore.js
@@ -220,18 +220,20 @@ class AlertStore {
return;
}
- const queryFilters = new Set(
- this.filters.values
- .map(f => f.raw)
- .slice()
- .sort()
- );
- const responseFilters = new Set(result.filters.map(m => m.text).sort());
- if (
- JSON.stringify([...queryFilters]) !== JSON.stringify([...responseFilters])
- ) {
+ const queryFilters = [
+ ...new Set(
+ this.filters.values
+ .map(f => f.raw)
+ .slice()
+ .sort()
+ )
+ ];
+ const responseFilters = [
+ ...new Set(result.filters.map(m => m.text).sort())
+ ];
+ if (JSON.stringify(queryFilters) !== JSON.stringify(responseFilters)) {
console.info(
- `Got response with filters=${responseFilters} while expecting results for ${queryFilters}, ignoring`
+ `Got response with filters '${responseFilters}' while expecting results for '${queryFilters}', ignoring`
);
return;
}
diff --git a/ui/src/setupTests.js b/ui/src/setupTests.js
new file mode 100644
index 000000000..5b9840092
--- /dev/null
+++ b/ui/src/setupTests.js
@@ -0,0 +1 @@
+require("jest-localstorage-mock");