mirror of
https://github.com/prymitive/karma
synced 2026-08-23 11:56:20 +00:00
fix(ui): enable mobx strict mode in tests
This commit is contained in:
committed by
Łukasz Mierzwa
parent
b47089cbe8
commit
e45debed58
+1
-1
@@ -151,7 +151,7 @@ describe("<App />", () => {
|
||||
m2.values = [StringToOption("foo"), StringToOption("baz")];
|
||||
const store = new SilenceFormStore();
|
||||
store.data.setMatchers([m1, m2]);
|
||||
store.data.comment = "base64";
|
||||
store.data.setComment("base64");
|
||||
const m = store.data.toBase64;
|
||||
|
||||
global.window.location = {
|
||||
|
||||
@@ -26,12 +26,18 @@ beforeEach(() => {
|
||||
alertStore = new AlertStore([]);
|
||||
silenceFormStore = new SilenceFormStore();
|
||||
|
||||
alertStore.settings.values.alertAcknowledgement = {
|
||||
enabled: true,
|
||||
durationSeconds: 123,
|
||||
author: "default author",
|
||||
comment: "COMMENT",
|
||||
};
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{
|
||||
alertAcknowledgement: {
|
||||
enabled: true,
|
||||
durationSeconds: 123,
|
||||
author: "default author",
|
||||
comment: "COMMENT",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
alertStore.data.setUpstreams({
|
||||
counters: { total: 1, healthy: 1, failed: 0 },
|
||||
clusters: { default: ["default"] },
|
||||
@@ -100,7 +106,17 @@ const MountAndClick = async () => {
|
||||
|
||||
describe("<AlertAck />", () => {
|
||||
it("is null when acks are disabled", () => {
|
||||
alertStore.settings.values.alertAcknowledgement.enabled = false;
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{
|
||||
alertAcknowledgement: {
|
||||
enabled: false,
|
||||
durationSeconds: 123,
|
||||
author: "default author",
|
||||
comment: "COMMENT",
|
||||
},
|
||||
},
|
||||
});
|
||||
const tree = MountedAlertAck();
|
||||
expect(tree.html()).toBe("");
|
||||
});
|
||||
@@ -295,9 +311,17 @@ describe("<AlertAck />", () => {
|
||||
});
|
||||
|
||||
it("uses settings when generating payload", async () => {
|
||||
alertStore.settings.values.alertAcknowledgement.durationSeconds = 237;
|
||||
alertStore.settings.values.alertAcknowledgement.author = "me";
|
||||
alertStore.settings.values.alertAcknowledgement.comment = "comment";
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{
|
||||
alertAcknowledgement: {
|
||||
enabled: true,
|
||||
durationSeconds: 237,
|
||||
author: "me",
|
||||
comment: "comment",
|
||||
},
|
||||
},
|
||||
});
|
||||
await MountAndClick();
|
||||
expect(JSON.parse((fetchMock.lastOptions() as any).body)).toEqual({
|
||||
comment: "comment",
|
||||
@@ -312,10 +336,17 @@ describe("<AlertAck />", () => {
|
||||
});
|
||||
|
||||
it("injects timestamp when configured", async () => {
|
||||
alertStore.settings.values.alertAcknowledgement.durationSeconds = 237;
|
||||
alertStore.settings.values.alertAcknowledgement.author = "me";
|
||||
alertStore.settings.values.alertAcknowledgement.comment =
|
||||
"ACK! This alert was acknowledged using karma on %NOW%";
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{
|
||||
alertAcknowledgement: {
|
||||
enabled: true,
|
||||
durationSeconds: 237,
|
||||
author: "me",
|
||||
comment: "ACK! This alert was acknowledged using karma on %NOW%",
|
||||
},
|
||||
},
|
||||
});
|
||||
await MountAndClick();
|
||||
expect(JSON.parse((fetchMock.lastOptions() as any).body)).toEqual({
|
||||
comment:
|
||||
@@ -331,11 +362,18 @@ describe("<AlertAck />", () => {
|
||||
});
|
||||
|
||||
it("uses author from authentication info when auth is enabled", async () => {
|
||||
alertStore.info.authentication.enabled = true;
|
||||
alertStore.info.authentication.username = "auth@example.com";
|
||||
alertStore.settings.values.alertAcknowledgement.durationSeconds = 222;
|
||||
alertStore.settings.values.alertAcknowledgement.author = "me";
|
||||
alertStore.settings.values.alertAcknowledgement.comment = "FOO: bar";
|
||||
alertStore.info.setAuthentication(true, "auth@example.com");
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{
|
||||
alertAcknowledgement: {
|
||||
enabled: true,
|
||||
durationSeconds: 222,
|
||||
author: "me",
|
||||
comment: "FOO: bar",
|
||||
},
|
||||
},
|
||||
});
|
||||
await MountAndClick();
|
||||
expect(JSON.parse((fetchMock.lastOptions() as any).body)).toEqual({
|
||||
comment: "FOO: bar",
|
||||
@@ -350,11 +388,18 @@ describe("<AlertAck />", () => {
|
||||
});
|
||||
|
||||
it("uses author from silenceFormStore if authentication is disabled", async () => {
|
||||
alertStore.info.authentication.enabled = false;
|
||||
alertStore.info.authentication.username = "wrong";
|
||||
alertStore.settings.values.alertAcknowledgement.durationSeconds = 222;
|
||||
alertStore.settings.values.alertAcknowledgement.author = "me";
|
||||
alertStore.settings.values.alertAcknowledgement.comment = "FOO: bar";
|
||||
alertStore.info.setAuthentication(false, "wrong");
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{
|
||||
alertAcknowledgement: {
|
||||
enabled: true,
|
||||
durationSeconds: 222,
|
||||
author: "me",
|
||||
comment: "FOO: bar",
|
||||
},
|
||||
},
|
||||
});
|
||||
silenceFormStore.data.setAuthor("bob@example.com");
|
||||
await MountAndClick();
|
||||
expect(JSON.parse((fetchMock.lastOptions() as any).body)).toEqual({
|
||||
@@ -370,9 +415,17 @@ describe("<AlertAck />", () => {
|
||||
});
|
||||
|
||||
it("uses default author as fallback", async () => {
|
||||
alertStore.settings.values.alertAcknowledgement.durationSeconds = 222;
|
||||
alertStore.settings.values.alertAcknowledgement.author = "me";
|
||||
alertStore.settings.values.alertAcknowledgement.comment = "FOO: bar";
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{
|
||||
alertAcknowledgement: {
|
||||
enabled: true,
|
||||
durationSeconds: 222,
|
||||
author: "me",
|
||||
comment: "FOO: bar",
|
||||
},
|
||||
},
|
||||
});
|
||||
silenceFormStore.data.setAuthor("");
|
||||
await MountAndClick();
|
||||
expect(JSON.parse((fetchMock.lastOptions() as any).body)).toEqual({
|
||||
|
||||
@@ -36,7 +36,7 @@ describe("<FaviconBadge />", () => {
|
||||
});
|
||||
|
||||
it("badge is updated when alertStore.status.error changes", () => {
|
||||
alertStore.status.error = "foo";
|
||||
alertStore.status.setError("foo");
|
||||
MountedFaviconBadge();
|
||||
expect(Favico.badge).toHaveBeenCalledTimes(1);
|
||||
expect(Favico.badge).toHaveBeenCalledWith("?");
|
||||
|
||||
@@ -321,7 +321,7 @@ describe("<Fetcher /> children", () => {
|
||||
|
||||
it("doesn't render any children when upgrade is needed", () => {
|
||||
act(() => {
|
||||
alertStore.info.upgradeNeeded = true;
|
||||
alertStore.info.setUpgradeNeeded(true);
|
||||
});
|
||||
const tree = mount(
|
||||
<Fetcher alertStore={alertStore} settingsStore={settingsStore} />
|
||||
|
||||
@@ -3,7 +3,11 @@ import { act } from "react-dom/test-utils";
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import { MockAlertGroup, MockAlert } from "__fixtures__/Alerts";
|
||||
import { APIAlertGroupT, APIAlertT } from "Models/APITypes";
|
||||
import {
|
||||
APIAlertGroupT,
|
||||
APIAlertT,
|
||||
APIAlertsResponseUpstreamsT,
|
||||
} from "Models/APITypes";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { AlertMenu, MenuContent } from "./AlertMenu";
|
||||
@@ -16,6 +20,49 @@ let group: APIAlertGroupT;
|
||||
let MockAfterClick: () => void;
|
||||
let MockSetIsMenuOpen: () => void;
|
||||
|
||||
const generateUpstreams = (): APIAlertsResponseUpstreamsT => ({
|
||||
counters: { total: 1, healthy: 1, failed: 0 },
|
||||
clusters: { default: ["am1"], ro: ["ro"], am2: ["am2"] },
|
||||
instances: [
|
||||
{
|
||||
name: "am1",
|
||||
uri: "http://localhost:8080",
|
||||
publicURI: "http://example.com",
|
||||
readonly: false,
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "default",
|
||||
clusterMembers: ["am1"],
|
||||
},
|
||||
{
|
||||
name: "ro",
|
||||
uri: "http://localhost:8080",
|
||||
publicURI: "http://example.com",
|
||||
readonly: true,
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "ro",
|
||||
clusterMembers: ["ro"],
|
||||
},
|
||||
{
|
||||
name: "am2",
|
||||
uri: "http://localhost:8080",
|
||||
publicURI: "http://example.com",
|
||||
readonly: false,
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "am2",
|
||||
clusterMembers: ["am2"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
@@ -28,48 +75,7 @@ beforeEach(() => {
|
||||
alert = MockAlert([], { foo: "bar" }, "active");
|
||||
group = MockAlertGroup({ alertname: "Fake Alert" }, [alert], [], {}, {});
|
||||
|
||||
alertStore.data.setUpstreams({
|
||||
counters: { total: 1, healthy: 1, failed: 0 },
|
||||
clusters: { default: ["am1"], ro: ["ro"], am2: ["am2"] },
|
||||
instances: [
|
||||
{
|
||||
name: "am1",
|
||||
uri: "http://localhost:8080",
|
||||
publicURI: "http://example.com",
|
||||
readonly: false,
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "default",
|
||||
clusterMembers: ["am1"],
|
||||
},
|
||||
{
|
||||
name: "ro",
|
||||
uri: "http://localhost:8080",
|
||||
publicURI: "http://example.com",
|
||||
readonly: true,
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "ro",
|
||||
clusterMembers: ["ro"],
|
||||
},
|
||||
{
|
||||
name: "am2",
|
||||
uri: "http://localhost:8080",
|
||||
publicURI: "http://example.com",
|
||||
readonly: false,
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "am2",
|
||||
clusterMembers: ["am2"],
|
||||
},
|
||||
],
|
||||
});
|
||||
alertStore.data.setUpstreams(generateUpstreams());
|
||||
});
|
||||
|
||||
const MountedAlertMenu = (group: APIAlertGroupT) => {
|
||||
@@ -171,8 +177,10 @@ describe("<MenuContent />", () => {
|
||||
});
|
||||
|
||||
it("'Silence' menu entry is disabled when all Alertmanager instances are read-only", () => {
|
||||
alertStore.data.upstreams.instances[0].readonly = true;
|
||||
alertStore.data.upstreams.instances[2].readonly = true;
|
||||
const upstreams = generateUpstreams();
|
||||
upstreams.instances[0].readonly = true;
|
||||
upstreams.instances[2].readonly = true;
|
||||
alertStore.data.setUpstreams(upstreams);
|
||||
const tree = MountedMenuContent(group);
|
||||
const button = tree.find(".dropdown-item").at(1);
|
||||
expect(button.hasClass("disabled")).toBe(true);
|
||||
|
||||
@@ -173,11 +173,11 @@ describe("<Alert />", () => {
|
||||
it("renders a silence if alert is silenced", () => {
|
||||
const alert = MockedAlert();
|
||||
alert.alertmanager[0].silencedBy = ["silence123456789"];
|
||||
alertStore.data.silences = {
|
||||
alertStore.data.setSilences({
|
||||
default: {
|
||||
silence123456789: MockSilence(),
|
||||
},
|
||||
};
|
||||
});
|
||||
const group = MockAlertGroup({}, [alert], [], {}, { default: [] });
|
||||
const tree = MountedAlert(alert, group, false, false, false);
|
||||
const silence = tree.find("ManagedSilence");
|
||||
@@ -188,11 +188,11 @@ describe("<Alert />", () => {
|
||||
it("renders a fallback silence if the silence is not found in alertStore", () => {
|
||||
const alert = MockedAlert();
|
||||
alert.alertmanager[0].silencedBy = ["silence123456789"];
|
||||
alertStore.data.silences = {
|
||||
alertStore.data.setSilences({
|
||||
default: {
|
||||
"123": MockSilence(),
|
||||
},
|
||||
};
|
||||
});
|
||||
const group = MockAlertGroup({}, [alert], [], {}, { default: [] });
|
||||
const tree = MountedAlert(alert, group, false, false, false);
|
||||
const silence = tree.find("FallbackSilenceDesciption");
|
||||
@@ -203,11 +203,11 @@ describe("<Alert />", () => {
|
||||
it("renders a fallback silence if the cluster is not found in alertStore", () => {
|
||||
const alert = MockedAlert();
|
||||
alert.alertmanager[0].silencedBy = ["silence123456789"];
|
||||
alertStore.data.silences = {
|
||||
alertStore.data.setSilences({
|
||||
foo: {
|
||||
"123": MockSilence(),
|
||||
},
|
||||
};
|
||||
});
|
||||
const group = MockAlertGroup({}, [alert], [], {}, { default: [] });
|
||||
const tree = MountedAlert(alert, group, false, false, false);
|
||||
const silence = tree.find("FallbackSilenceDesciption");
|
||||
@@ -239,11 +239,11 @@ describe("<Alert />", () => {
|
||||
inhibitedBy: [],
|
||||
},
|
||||
];
|
||||
alertStore.data.silences = {
|
||||
alertStore.data.setSilences({
|
||||
ha: {
|
||||
silence123456789: MockSilence(),
|
||||
},
|
||||
};
|
||||
});
|
||||
const group = MockAlertGroup({}, [alert], [], {}, {});
|
||||
const tree = MountedAlert(alert, group, false, false, false);
|
||||
const silence = tree.find("ManagedSilence");
|
||||
|
||||
@@ -54,7 +54,7 @@ beforeEach(() => {
|
||||
group = MockGroup();
|
||||
advanceTo(new Date(Date.UTC(2000, 0, 1, 15, 0, 0)));
|
||||
|
||||
alertStore.data.receivers = ["by-cluster-service", "by-name"];
|
||||
alertStore.data.setReceivers(["by-cluster-service", "by-name"]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -90,11 +90,11 @@ describe("<GroupFooter />", () => {
|
||||
group.alerts[index].alertmanager[0].silencedBy = ["123456789"];
|
||||
}
|
||||
group.shared.silences = { default: ["123456789"] };
|
||||
alertStore.data.silences = {
|
||||
alertStore.data.setSilences({
|
||||
default: {
|
||||
"123456789": MockSilence(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const tree = MountedGroupFooter().find("GroupFooter");
|
||||
expect(tree.find("ManagedSilence")).toHaveLength(1);
|
||||
@@ -105,9 +105,9 @@ describe("<GroupFooter />", () => {
|
||||
group.alerts[index].alertmanager[0].silencedBy = ["123456789"];
|
||||
}
|
||||
group.shared.silences = { default: ["123456789"] };
|
||||
alertStore.data.silences = {
|
||||
alertStore.data.setSilences({
|
||||
default: {},
|
||||
};
|
||||
});
|
||||
|
||||
const tree = MountedGroupFooter().find("GroupFooter");
|
||||
expect(tree.find("FallbackSilenceDesciption")).toHaveLength(1);
|
||||
@@ -118,9 +118,9 @@ describe("<GroupFooter />", () => {
|
||||
group.alerts[index].alertmanager[0].silencedBy = ["123456789"];
|
||||
}
|
||||
group.shared.silences = { default: ["123456789"] };
|
||||
alertStore.data.silences = {
|
||||
alertStore.data.setSilences({
|
||||
foo: {},
|
||||
};
|
||||
});
|
||||
|
||||
const tree = MountedGroupFooter().find("GroupFooter");
|
||||
expect(tree.find("FallbackSilenceDesciption")).toHaveLength(1);
|
||||
@@ -132,25 +132,26 @@ describe("<GroupFooter />", () => {
|
||||
}
|
||||
group.shared.silences = { default: ["123456789"] };
|
||||
|
||||
alertStore.data.silences = {
|
||||
const silence = MockSilence();
|
||||
silence.id = "123456789";
|
||||
alertStore.data.setSilences({
|
||||
default: {
|
||||
"123456789": MockSilence(),
|
||||
"123456789": silence,
|
||||
},
|
||||
};
|
||||
alertStore.data.silences["default"]["123456789"].id = "123456789";
|
||||
});
|
||||
|
||||
const tree = MountedGroupFooter().find("GroupFooter");
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders @receiver label when alertStore.data.receivers.length > 1", () => {
|
||||
alertStore.data.receivers = ["foo", "bar"];
|
||||
alertStore.data.setReceivers(["foo", "bar"]);
|
||||
const tree = MountedGroupFooter();
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/@receiver:/);
|
||||
});
|
||||
|
||||
it("doesn't render @receiver label when alertStore.data.receivers.length == 0", () => {
|
||||
alertStore.data.receivers = [];
|
||||
alertStore.data.setReceivers([]);
|
||||
const tree = MountedGroupFooter();
|
||||
expect(toDiffableHtml(tree.html())).not.toMatch(/@receiver:/);
|
||||
});
|
||||
@@ -161,12 +162,13 @@ describe("<GroupFooter />", () => {
|
||||
}
|
||||
group.shared.silences = { default: ["123456789"] };
|
||||
|
||||
alertStore.data.silences = {
|
||||
const silence = MockSilence();
|
||||
silence.id = "123456789";
|
||||
alertStore.data.setSilences({
|
||||
default: {
|
||||
"123456789": MockSilence(),
|
||||
"123456789": silence,
|
||||
},
|
||||
};
|
||||
alertStore.data.silences["default"]["123456789"].id = "123456789";
|
||||
});
|
||||
|
||||
const tree = mount(
|
||||
<GroupFooter
|
||||
|
||||
@@ -5,7 +5,7 @@ import { mount } from "enzyme";
|
||||
import copy from "copy-to-clipboard";
|
||||
|
||||
import { MockAlertGroup } from "__fixtures__/Alerts";
|
||||
import { APIAlertGroupT } from "Models/APITypes";
|
||||
import { APIAlertGroupT, APIAlertsResponseUpstreamsT } from "Models/APITypes";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { GroupMenu, MenuContent } from "./GroupMenu";
|
||||
@@ -16,6 +16,49 @@ let silenceFormStore: SilenceFormStore;
|
||||
let MockAfterClick: () => void;
|
||||
let MockSetIsMenuOpen: () => void;
|
||||
|
||||
const generateUpstreams = (): APIAlertsResponseUpstreamsT => ({
|
||||
counters: { total: 3, healthy: 3, failed: 0 },
|
||||
clusters: { default: ["am1"], ro: ["ro"], am2: ["am2"] },
|
||||
instances: [
|
||||
{
|
||||
name: "am1",
|
||||
uri: "http://localhost:8080",
|
||||
publicURI: "http://example.com",
|
||||
readonly: false,
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "default",
|
||||
clusterMembers: ["am1"],
|
||||
},
|
||||
{
|
||||
name: "ro",
|
||||
uri: "http://localhost:8080",
|
||||
publicURI: "http://example.com",
|
||||
readonly: true,
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "ro",
|
||||
clusterMembers: ["ro"],
|
||||
},
|
||||
{
|
||||
name: "am2",
|
||||
uri: "http://localhost:8080",
|
||||
publicURI: "http://example.com",
|
||||
readonly: false,
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "am2",
|
||||
clusterMembers: ["am2"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
alertStore = new AlertStore([]);
|
||||
silenceFormStore = new SilenceFormStore();
|
||||
@@ -25,48 +68,7 @@ beforeEach(() => {
|
||||
MockAfterClick = jest.fn();
|
||||
MockSetIsMenuOpen = jest.fn();
|
||||
|
||||
alertStore.data.setUpstreams({
|
||||
counters: { total: 3, healthy: 3, failed: 0 },
|
||||
clusters: { default: ["am1"], ro: ["ro"], am2: ["am2"] },
|
||||
instances: [
|
||||
{
|
||||
name: "am1",
|
||||
uri: "http://localhost:8080",
|
||||
publicURI: "http://example.com",
|
||||
readonly: false,
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "default",
|
||||
clusterMembers: ["am1"],
|
||||
},
|
||||
{
|
||||
name: "ro",
|
||||
uri: "http://localhost:8080",
|
||||
publicURI: "http://example.com",
|
||||
readonly: true,
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "ro",
|
||||
clusterMembers: ["ro"],
|
||||
},
|
||||
{
|
||||
name: "am2",
|
||||
uri: "http://localhost:8080",
|
||||
publicURI: "http://example.com",
|
||||
readonly: false,
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "am2",
|
||||
clusterMembers: ["am2"],
|
||||
},
|
||||
],
|
||||
});
|
||||
alertStore.data.setUpstreams(generateUpstreams());
|
||||
});
|
||||
|
||||
const MountedGroupMenu = (group: APIAlertGroupT, themed: boolean) => {
|
||||
@@ -180,8 +182,10 @@ describe("<MenuContent />", () => {
|
||||
});
|
||||
|
||||
it("'Silence' menu entry is disabled when all Alertmanager instances are read-only", () => {
|
||||
alertStore.data.upstreams.instances[0].readonly = true;
|
||||
alertStore.data.upstreams.instances[2].readonly = true;
|
||||
const upstreams = generateUpstreams();
|
||||
upstreams.instances[0].readonly = true;
|
||||
upstreams.instances[2].readonly = true;
|
||||
alertStore.data.setUpstreams(upstreams);
|
||||
|
||||
const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {}, {});
|
||||
const tree = MountedMenuContent(group);
|
||||
|
||||
@@ -33,7 +33,7 @@ describe("<RenderSilence />", () => {
|
||||
it("renders ManagedSilence if silence is present in AlertStore", () => {
|
||||
const silence = MockSilence();
|
||||
|
||||
alertStore.data.silences = { fakeCluster: { [silence.id]: silence } };
|
||||
alertStore.data.setSilences({ fakeCluster: { [silence.id]: silence } });
|
||||
|
||||
const tree = mount(
|
||||
<RenderSilence
|
||||
@@ -52,7 +52,7 @@ describe("<RenderSilence />", () => {
|
||||
it("re-render when silence was removed AlertStore is a no-op", () => {
|
||||
const silence = MockSilence();
|
||||
|
||||
alertStore.data.silences = { fakeCluster: { [silence.id]: silence } };
|
||||
alertStore.data.setSilences({ fakeCluster: { [silence.id]: silence } });
|
||||
|
||||
const tree = mount(
|
||||
<RenderSilence
|
||||
@@ -66,7 +66,7 @@ describe("<RenderSilence />", () => {
|
||||
expect(tree.find("ManagedSilence")).toHaveLength(1);
|
||||
const snapshot = toDiffableHtml(tree.html());
|
||||
|
||||
alertStore.data.silences = {};
|
||||
alertStore.data.setSilences({});
|
||||
|
||||
tree.setProps({});
|
||||
expect(tree.find("ManagedSilence")).toHaveLength(1);
|
||||
|
||||
@@ -42,7 +42,7 @@ beforeEach(() => {
|
||||
silenceFormStore = new SilenceFormStore();
|
||||
group = MockGroup("fakeGroup");
|
||||
|
||||
alertStore.data.receivers = ["by-cluster-service", "by-name"];
|
||||
alertStore.data.setReceivers(["by-cluster-service", "by-name"]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -91,7 +91,7 @@ const ValidateCollapse = (
|
||||
) => {
|
||||
global.innerWidth = innerWidth;
|
||||
|
||||
settingsStore.alertGroupConfig.config.defaultCollapseState = defaultCollapseState;
|
||||
settingsStore.alertGroupConfig.setDefaultCollapseState(defaultCollapseState);
|
||||
|
||||
MockAlerts(3);
|
||||
const tree = MountedAlertGroup(jest.fn(), false);
|
||||
@@ -242,14 +242,14 @@ describe("<AlertGroup />", () => {
|
||||
});
|
||||
|
||||
it("renders @receiver label when alertStore.data.receivers.length > 1", () => {
|
||||
alertStore.data.receivers = ["foo", "bar"];
|
||||
alertStore.data.setReceivers(["foo", "bar"]);
|
||||
MockAlerts(10);
|
||||
const tree = MountedAlertGroup(jest.fn(), false);
|
||||
expect(tree.html()).toMatch(/@receiver:/);
|
||||
});
|
||||
|
||||
it("doesn't render @receiver label when alertStore.data.receivers.length == 0", () => {
|
||||
alertStore.data.receivers = [];
|
||||
alertStore.data.setReceivers([]);
|
||||
MockAlerts(10);
|
||||
const tree = MountedAlertGroup(jest.fn(), false);
|
||||
expect(tree.html()).not.toMatch(/@receiver:/);
|
||||
@@ -426,14 +426,20 @@ describe("<AlertGroup /> card theme", () => {
|
||||
});
|
||||
|
||||
it("renders AlertHistory when enabled", () => {
|
||||
alertStore.settings.values.historyEnabled = true;
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{ historyEnabled: true },
|
||||
});
|
||||
group.stateCount = { active: 5, suppressed: 0, unprocessed: 0 };
|
||||
const tree = MountedAlertGroup(jest.fn(), false);
|
||||
expect(tree.find("AlertHistory")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("doesn't render AlertHistory when disabled", () => {
|
||||
alertStore.settings.values.historyEnabled = false;
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{ historyEnabled: false },
|
||||
});
|
||||
group.stateCount = { active: 5, suppressed: 0, unprocessed: 0 };
|
||||
const tree = MountedAlertGroup(jest.fn(), false);
|
||||
expect(tree.find("AlertHistory")).toHaveLength(0);
|
||||
|
||||
@@ -73,7 +73,7 @@ describe("<GridLabelSelect />", () => {
|
||||
toggle.simulate("click");
|
||||
expect(tree.find("div.components-grid-label-select-menu")).toHaveLength(1);
|
||||
|
||||
settingsStore.multiGridConfig.config.gridLabel = "foo";
|
||||
settingsStore.multiGridConfig.setGridLabel("foo");
|
||||
const options = tree.find("div.react-select__option");
|
||||
options.at(5).simulate("click");
|
||||
expect(settingsStore.multiGridConfig.config.gridLabel).toBe("cluster");
|
||||
|
||||
@@ -437,7 +437,7 @@ describe("<AlertGrid />", () => {
|
||||
{ canvas: 4000, columns: 10 },
|
||||
].map((t) =>
|
||||
it(`renders ${t.columns} column(s) on ${t.canvas} breakpoint`, () => {
|
||||
settingsStore.gridConfig.config.groupWidth = 400;
|
||||
settingsStore.gridConfig.setGroupWidth(400);
|
||||
VerifyColumnCount(t.canvas - 1, t.canvas - 1, Math.max(1, t.columns - 1));
|
||||
VerifyColumnCount(t.canvas, t.canvas, t.columns);
|
||||
VerifyColumnCount(t.canvas + 1, t.canvas + 1, t.columns);
|
||||
@@ -459,7 +459,7 @@ describe("<AlertGrid />", () => {
|
||||
{ canvas: 3840, columns: 9 },
|
||||
].map((t) =>
|
||||
it(`renders ${t.columns} column(s) with ${t.canvas} resolution`, () => {
|
||||
settingsStore.gridConfig.config.groupWidth = 400;
|
||||
settingsStore.gridConfig.setGroupWidth(400);
|
||||
VerifyColumnCount(t.canvas, t.canvas, t.columns);
|
||||
})
|
||||
);
|
||||
@@ -522,7 +522,7 @@ describe("<AlertGrid />", () => {
|
||||
});
|
||||
|
||||
it("scrollbar render doesn't resize alert groups", () => {
|
||||
settingsStore.gridConfig.config.groupWidth = 400;
|
||||
settingsStore.gridConfig.setGroupWidth(400);
|
||||
|
||||
MockGroupList(20, 1);
|
||||
// set initial width
|
||||
@@ -551,7 +551,7 @@ describe("<AlertGrid />", () => {
|
||||
});
|
||||
|
||||
it("viewport resize doesn't allow loops", () => {
|
||||
settingsStore.gridConfig.config.groupWidth = 400;
|
||||
settingsStore.gridConfig.setGroupWidth(400);
|
||||
|
||||
MockGroupList(10, 1);
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ describe("<Grid />", () => {
|
||||
});
|
||||
|
||||
it("renders only FatalError on failed fetch", () => {
|
||||
alertStore.status.error = "error";
|
||||
alertStore.status.setError("error");
|
||||
alertStore.data.setUpstreams({
|
||||
counters: { total: 0, healthy: 0, failed: 1 },
|
||||
instances: [
|
||||
@@ -110,33 +110,33 @@ describe("<Grid />", () => {
|
||||
});
|
||||
|
||||
it("renders UpgradeNeeded when alertStore.info.upgradeNeeded=true", () => {
|
||||
alertStore.info.upgradeNeeded = true;
|
||||
alertStore.info.setUpgradeNeeded(true);
|
||||
const tree = ShallowGrid();
|
||||
expect(tree.text()).toBe("<UpgradeNeeded />");
|
||||
});
|
||||
|
||||
it("renders ReloadNeeded when alertStore.info.reloadNeeded=true", () => {
|
||||
alertStore.info.reloadNeeded = true;
|
||||
alertStore.info.setReloadNeeded(true);
|
||||
const tree = ShallowGrid();
|
||||
expect(tree.text()).toBe("<ReloadNeeded />");
|
||||
});
|
||||
|
||||
it("renders AlertGrid before any fetch finished when totalAlerts is 0", () => {
|
||||
alertStore.info.version = "unknown";
|
||||
alertStore.info.setVersion("unknown");
|
||||
alertStore.info.setTotalAlerts(0);
|
||||
const tree = ShallowGrid();
|
||||
expect(tree.find("AlertGrid")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("renders EmptyGrid after first fetch when totalAlerts is 0", () => {
|
||||
alertStore.info.version = "1.2.3";
|
||||
alertStore.info.setVersion("1.2.3");
|
||||
alertStore.info.setTotalAlerts(0);
|
||||
const tree = ShallowGrid();
|
||||
expect(tree.text()).toBe("<EmptyGrid />");
|
||||
});
|
||||
|
||||
it("renders AlertGrid after first fetch finished when totalAlerts is >0", () => {
|
||||
alertStore.info.version = "unknown";
|
||||
alertStore.info.setVersion("unknown");
|
||||
alertStore.info.setTotalAlerts(1);
|
||||
const tree = ShallowGrid();
|
||||
expect(tree.find("AlertGrid")).toHaveLength(1);
|
||||
|
||||
@@ -84,12 +84,18 @@ describe("<FilterInputLabel /> className", () => {
|
||||
});
|
||||
|
||||
it("applied filter included in staticColorLabels with '=' matcher should use 'btn-info' class", () => {
|
||||
alertStore.settings.values.staticColorLabels = ["foo"];
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{ staticColorLabels: ["foo"] },
|
||||
});
|
||||
ValidateClass("=", true, "btn-info");
|
||||
});
|
||||
|
||||
it("applied filter included in staticColorLabels with any matcher other than '=' should use 'btn-default' class", () => {
|
||||
alertStore.settings.values.staticColorLabels = ["foo"];
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{ staticColorLabels: ["foo"] },
|
||||
});
|
||||
for (const matcher of NonEqualMatchers) {
|
||||
ValidateClass(matcher, true, "btn-default");
|
||||
}
|
||||
|
||||
@@ -15,14 +15,20 @@ beforeEach(() => {
|
||||
|
||||
describe("<GetClassAndStyle />", () => {
|
||||
it("static label uses StaticColorLabelClassMap.badge", () => {
|
||||
alertStore.settings.values.staticColorLabels = ["foo", "job", "bar"];
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{ staticColorLabels: ["foo", "job", "bar"] },
|
||||
});
|
||||
const cs = GetClassAndStyle(alertStore, "foo", "bar");
|
||||
expect(cs.colorClassNames).toContain(StaticColorLabelClassMap.badge);
|
||||
});
|
||||
|
||||
Object.entries(StaticColorLabelClassMap).map(([key, _]) =>
|
||||
it(`non-static label doesn't use StaticColorLabelClassMap.${key}`, () => {
|
||||
alertStore.settings.values.staticColorLabels = [];
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{ staticColorLabels: [] },
|
||||
});
|
||||
const cs = GetClassAndStyle(alertStore, "foo", "bar");
|
||||
expect(cs.colorClassNames).not.toContain(StaticColorLabelClassMap.badge);
|
||||
})
|
||||
@@ -63,13 +69,19 @@ describe("<GetClassAndStyle />", () => {
|
||||
});
|
||||
|
||||
it("style prop on a label included in staticColorLabels should be empty", () => {
|
||||
alertStore.settings.values.staticColorLabels = ["foo", "job", "bar"];
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{ staticColorLabels: ["foo", "job", "bar"] },
|
||||
});
|
||||
const cs = GetClassAndStyle(alertStore, "foo", "bar");
|
||||
expect(cs.style).toEqual({});
|
||||
});
|
||||
|
||||
it("style prop on a label without any color information should be empty", () => {
|
||||
alertStore.settings.values.staticColorLabels = [];
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{ staticColorLabels: [] },
|
||||
});
|
||||
const cs = GetClassAndStyle(alertStore, "foo", "bar");
|
||||
expect(cs.style).toEqual({});
|
||||
});
|
||||
|
||||
@@ -30,8 +30,7 @@ describe("<AlertGroupCollapseConfiguration />", () => {
|
||||
});
|
||||
|
||||
it("resets stored config to defaults if it is invalid", (done) => {
|
||||
(settingsStore.alertGroupConfig.config
|
||||
.defaultCollapseState as string) = "foo";
|
||||
settingsStore.alertGroupConfig.setDefaultCollapseState("foo" as any);
|
||||
const tree = FakeConfiguration();
|
||||
const select = tree.find("div.react-select__value-container");
|
||||
expect(select.text()).toBe(
|
||||
|
||||
@@ -41,7 +41,7 @@ describe("<AlertGroupConfiguration />", () => {
|
||||
});
|
||||
|
||||
it("custom interval value is rendered correctly", () => {
|
||||
settingsStore.alertGroupConfig.config.defaultRenderCount = 4;
|
||||
settingsStore.alertGroupConfig.setDefaultRenderCount(4);
|
||||
const component = FakeConfiguration();
|
||||
expect(component.find("Range").props().values).toContain(4);
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ describe("<AlertGroupSortConfiguration />", () => {
|
||||
});
|
||||
|
||||
it("invalid sortOrder value is reset on mount", () => {
|
||||
(settingsStore.gridConfig.config.sortOrder as string) = "badValue";
|
||||
settingsStore.gridConfig.setSortOrder("badValue" as any);
|
||||
FakeConfiguration();
|
||||
expect(settingsStore.gridConfig.config.sortOrder).toBe(
|
||||
settingsStore.gridConfig.options.default.value
|
||||
|
||||
@@ -41,7 +41,7 @@ describe("<AlertGroupWidthConfiguration />", () => {
|
||||
});
|
||||
|
||||
it("custom interval value is rendered correctly", () => {
|
||||
settingsStore.gridConfig.config.groupWidth = 460;
|
||||
settingsStore.gridConfig.setGroupWidth(460);
|
||||
const component = FakeConfiguration();
|
||||
expect(component.find("Range").props().values).toContain(460);
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ const AlertGroupWidthConfiguration: FC<{
|
||||
]);
|
||||
|
||||
const onChangeComplete = debounce((value: number) => {
|
||||
settingsStore.gridConfig.config.groupWidth = value as number;
|
||||
settingsStore.gridConfig.setGroupWidth(value);
|
||||
}, 200);
|
||||
|
||||
return (
|
||||
|
||||
@@ -49,19 +49,19 @@ describe("<MultiGridConfiguration />", () => {
|
||||
});
|
||||
|
||||
it("correctly renders default option when multi-grid is disabled", () => {
|
||||
settingsStore.multiGridConfig.config.gridLabel = "";
|
||||
settingsStore.multiGridConfig.setGridLabel("");
|
||||
const tree = FakeConfiguration();
|
||||
expect(tree.find("Creatable").text()).toBe("Disable multi-grid");
|
||||
});
|
||||
|
||||
it("correctly renders default option when multi-grid is set to @auto", () => {
|
||||
settingsStore.multiGridConfig.config.gridLabel = "@auto";
|
||||
settingsStore.multiGridConfig.setGridLabel("@auto");
|
||||
const tree = FakeConfiguration();
|
||||
expect(tree.find("Creatable").text()).toBe("Automatic selection");
|
||||
});
|
||||
|
||||
it("correctly renders default option when multi-grid is enabled", () => {
|
||||
settingsStore.multiGridConfig.config.gridLabel = "cluster";
|
||||
settingsStore.multiGridConfig.setGridLabel("cluster");
|
||||
const tree = FakeConfiguration();
|
||||
expect(tree.find("Creatable").text()).toBe("cluster");
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ const SortLabelName: FC<{
|
||||
);
|
||||
|
||||
if (!settingsStore.gridConfig.config.sortLabel) {
|
||||
settingsStore.gridConfig.config.sortLabel = StaticLabels.AlertName;
|
||||
settingsStore.gridConfig.setSortLabel(StaticLabels.AlertName);
|
||||
}
|
||||
|
||||
const context = React.useContext(ThemeContext);
|
||||
@@ -30,13 +30,16 @@ const SortLabelName: FC<{
|
||||
classNamePrefix="react-select"
|
||||
instanceId="configuration-sort-label"
|
||||
formatCreateLabel={NewLabelName}
|
||||
defaultValue={StringToOption(settingsStore.gridConfig.config.sortLabel)}
|
||||
defaultValue={StringToOption(
|
||||
settingsStore.gridConfig.config.sortLabel as string
|
||||
)}
|
||||
options={
|
||||
response ? response.map((value: string) => StringToOption(value)) : []
|
||||
}
|
||||
onChange={(option) => {
|
||||
settingsStore.gridConfig.config.sortLabel = (option as OptionT)
|
||||
.value as string;
|
||||
settingsStore.gridConfig.setSortLabel(
|
||||
(option as OptionT).value as string
|
||||
);
|
||||
}}
|
||||
components={{ Menu: AnimatedMenu }}
|
||||
/>
|
||||
|
||||
@@ -93,8 +93,7 @@ describe("<MainModalContent />", () => {
|
||||
});
|
||||
|
||||
it("shows username when alertStore.info.authentication.enabled=true", () => {
|
||||
alertStore.info.authentication.enabled = true;
|
||||
alertStore.info.authentication.username = "me@example.com";
|
||||
alertStore.info.setAuthentication(true, "me@example.com");
|
||||
const tree = mount(
|
||||
<span>
|
||||
{Wrapped(
|
||||
|
||||
@@ -34,8 +34,7 @@ storiesOf("MainModal", module)
|
||||
}
|
||||
);
|
||||
|
||||
alertStore.info.authentication.enabled = true;
|
||||
alertStore.info.authentication.username = "me@example.com";
|
||||
alertStore.info.setAuthentication(true, "me@example.com");
|
||||
return (
|
||||
<MainModalContent
|
||||
alertStore={alertStore}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { advanceTo, clear } from "jest-date-mock";
|
||||
import { MockSilence } from "__fixtures__/Alerts";
|
||||
import { PressKey } from "__fixtures__/PressKey";
|
||||
import { useFetchDelete } from "Hooks/useFetchDelete";
|
||||
import { APISilenceT } from "Models/APITypes";
|
||||
import { APISilenceT, APIAlertsResponseUpstreamsT } from "Models/APITypes";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { DeleteSilence, DeleteSilenceModalContent } from "./DeleteSilence";
|
||||
@@ -19,6 +19,25 @@ let silenceFormStore: SilenceFormStore;
|
||||
let cluster: string;
|
||||
let silence: APISilenceT;
|
||||
|
||||
const generateUpstreams = (): APIAlertsResponseUpstreamsT => ({
|
||||
counters: { total: 1, healthy: 1, failed: 0 },
|
||||
instances: [
|
||||
{
|
||||
name: "am1",
|
||||
cluster: "am",
|
||||
uri: "http://localhost:9093",
|
||||
publicURI: "http://localhost:9093",
|
||||
readonly: false,
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
clusterMembers: ["am1"],
|
||||
},
|
||||
],
|
||||
clusters: { am: ["am1"] },
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 30, 0)));
|
||||
jest.useFakeTimers();
|
||||
@@ -28,24 +47,7 @@ beforeEach(() => {
|
||||
cluster = "am";
|
||||
silence = MockSilence();
|
||||
|
||||
alertStore.data.setUpstreams({
|
||||
counters: { total: 1, healthy: 1, failed: 0 },
|
||||
instances: [
|
||||
{
|
||||
name: "am1",
|
||||
cluster: "am",
|
||||
uri: "http://localhost:9093",
|
||||
publicURI: "http://localhost:9093",
|
||||
readonly: false,
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
clusterMembers: ["am1"],
|
||||
},
|
||||
],
|
||||
clusters: { am: ["am1"] },
|
||||
});
|
||||
alertStore.data.setUpstreams(generateUpstreams());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -137,7 +139,10 @@ describe("<DeleteSilence />", () => {
|
||||
});
|
||||
|
||||
it("button is disabled when all alertmanager instances are read-only", () => {
|
||||
alertStore.data.upstreams.instances[0].readonly = true;
|
||||
const upstreams = generateUpstreams();
|
||||
upstreams.instances[0].readonly = true;
|
||||
alertStore.data.setUpstreams(upstreams);
|
||||
|
||||
const tree = mount(
|
||||
<DeleteSilence
|
||||
alertStore={alertStore}
|
||||
@@ -197,9 +202,11 @@ describe("<DeleteSilenceModalContent />", () => {
|
||||
typeof useFetchDelete
|
||||
>).mockReturnValue({ response: "success", error: null, isDeleting: false });
|
||||
|
||||
alertStore.data.upstreams.instances[0].headers = {
|
||||
const upstreams = generateUpstreams();
|
||||
upstreams.instances[0].headers = {
|
||||
Authorization: "Basic ***",
|
||||
};
|
||||
alertStore.data.setUpstreams(upstreams);
|
||||
|
||||
const tree = MountedDeleteSilenceModalContent();
|
||||
tree.find(".btn-danger").simulate("click");
|
||||
@@ -224,7 +231,9 @@ describe("<DeleteSilenceModalContent />", () => {
|
||||
typeof useFetchDelete
|
||||
>).mockReturnValue({ response: "success", error: null, isDeleting: false });
|
||||
|
||||
alertStore.data.upstreams.instances[0].corsCredentials = "omit";
|
||||
const upstreams = generateUpstreams();
|
||||
upstreams.instances[0].corsCredentials = "omit";
|
||||
alertStore.data.setUpstreams(upstreams);
|
||||
|
||||
const tree = MountedDeleteSilenceModalContent();
|
||||
tree.find(".btn-danger").simulate("click");
|
||||
|
||||
@@ -7,7 +7,7 @@ import copy from "copy-to-clipboard";
|
||||
import { advanceTo, clear } from "jest-date-mock";
|
||||
|
||||
import { MockSilence } from "__fixtures__/Alerts";
|
||||
import { APISilenceT } from "Models/APITypes";
|
||||
import { APIAlertsResponseUpstreamsT, APISilenceT } from "Models/APITypes";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { SilenceDetails } from "./SilenceDetails";
|
||||
@@ -19,30 +19,32 @@ let silence: APISilenceT;
|
||||
|
||||
const MockEditSilence = jest.fn();
|
||||
|
||||
const generateUpstreams = (): APIAlertsResponseUpstreamsT => ({
|
||||
counters: { total: 1, healthy: 1, failed: 0 },
|
||||
instances: [
|
||||
{
|
||||
name: "am1",
|
||||
cluster: "am",
|
||||
clusterMembers: ["am"],
|
||||
uri: "http://localhost:9093",
|
||||
publicURI: "http://example.com",
|
||||
readonly: false,
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
},
|
||||
],
|
||||
clusters: { am: ["am1"] },
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
alertStore = new AlertStore([]);
|
||||
silenceFormStore = new SilenceFormStore();
|
||||
cluster = "am";
|
||||
silence = MockSilence();
|
||||
|
||||
alertStore.data.setUpstreams({
|
||||
counters: { total: 1, healthy: 1, failed: 0 },
|
||||
instances: [
|
||||
{
|
||||
name: "am1",
|
||||
cluster: "am",
|
||||
clusterMembers: ["am"],
|
||||
uri: "http://localhost:9093",
|
||||
publicURI: "http://example.com",
|
||||
readonly: false,
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
},
|
||||
],
|
||||
clusters: { am: ["am1"] },
|
||||
});
|
||||
alertStore.data.setUpstreams(generateUpstreams());
|
||||
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
@@ -97,7 +99,9 @@ describe("<SilenceDetails />", () => {
|
||||
});
|
||||
|
||||
it("Edit silence button is disabled when all alertmanager instances are read-only", () => {
|
||||
alertStore.data.upstreams.instances[0].readonly = true;
|
||||
const upstreams = generateUpstreams();
|
||||
upstreams.instances[0].readonly = true;
|
||||
alertStore.data.setUpstreams(upstreams);
|
||||
const tree = MountedSilenceDetails();
|
||||
expect(tree.find("button").prop("disabled")).toBe(true);
|
||||
|
||||
|
||||
@@ -138,7 +138,9 @@ describe("<HistoryMenu />", () => {
|
||||
it("renders correctly when rendered with a filter in history", async () => {
|
||||
const promise = Promise.resolve();
|
||||
const tree = MountedHistory();
|
||||
PopulateHistory(tree, 1);
|
||||
act(() => {
|
||||
PopulateHistory(tree, 1);
|
||||
});
|
||||
tree.find("button.cursor-pointer").simulate("click");
|
||||
|
||||
expect(tree.text()).toBe(
|
||||
@@ -155,7 +157,9 @@ describe("<HistoryMenu />", () => {
|
||||
it("clicking on a filter set in history populates alertStore", async () => {
|
||||
const promise = Promise.resolve();
|
||||
const tree = MountedHistory();
|
||||
PopulateHistory(tree, 1);
|
||||
act(() => {
|
||||
PopulateHistory(tree, 1);
|
||||
});
|
||||
tree.find("button.cursor-pointer").simulate("click");
|
||||
|
||||
const button = tree.find("button.dropdown-item").at(0);
|
||||
@@ -179,7 +183,9 @@ describe("<HistoryMenu />", () => {
|
||||
|
||||
const promise = Promise.resolve();
|
||||
const tree = MountedHistory();
|
||||
PopulateHistory(tree, 16);
|
||||
act(() => {
|
||||
PopulateHistory(tree, 16);
|
||||
});
|
||||
tree.find("button.cursor-pointer").simulate("click");
|
||||
expect(tree.find("button.dropdown-item")).toHaveLength(8);
|
||||
|
||||
@@ -205,7 +211,9 @@ describe("<HistoryMenu />", () => {
|
||||
|
||||
const promise = Promise.resolve();
|
||||
const tree = MountedHistory();
|
||||
PopulateHistory(tree, 16);
|
||||
act(() => {
|
||||
PopulateHistory(tree, 16);
|
||||
});
|
||||
tree.find("button.cursor-pointer").simulate("click");
|
||||
expect(tree.find("button.dropdown-item")).toHaveLength(4);
|
||||
|
||||
@@ -247,7 +255,7 @@ describe("<HistoryMenu />", () => {
|
||||
});
|
||||
|
||||
it("clicking on 'Reset filters' clears current filter set in Settings", () => {
|
||||
settingsStore.savedFilters.config.filters = ["foo=bar"];
|
||||
settingsStore.savedFilters.save(["foo=bar"]);
|
||||
const tree = MountedHistory();
|
||||
tree.find("button.cursor-pointer").simulate("click");
|
||||
|
||||
@@ -260,9 +268,12 @@ describe("<HistoryMenu />", () => {
|
||||
expect(settingsStore.savedFilters.config.filters).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("clicking on 'Clear history' clears the history", () => {
|
||||
it("clicking on 'Clear history' clears the history", async () => {
|
||||
const promise = Promise.resolve();
|
||||
const tree = MountedHistory();
|
||||
PopulateHistory(tree, 5);
|
||||
act(() => {
|
||||
PopulateHistory(tree, 5);
|
||||
});
|
||||
tree.find("button.cursor-pointer").simulate("click");
|
||||
expect(tree.find("button.dropdown-item")).toHaveLength(5);
|
||||
|
||||
@@ -274,5 +285,6 @@ describe("<HistoryMenu />", () => {
|
||||
});
|
||||
tree.update();
|
||||
expect(tree.find("button.dropdown-item")).toHaveLength(0);
|
||||
await act(() => promise);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ReactNode,
|
||||
} from "react";
|
||||
|
||||
import { action } from "mobx";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { localStored } from "mobx-stored";
|
||||
|
||||
@@ -161,21 +162,28 @@ interface HistoryStorageT {
|
||||
filters: ReduceFilterT[][];
|
||||
}
|
||||
|
||||
const History: FC<{
|
||||
alertStore: AlertStore;
|
||||
settingsStore: Settings;
|
||||
}> = observer(({ alertStore, settingsStore }) => {
|
||||
// this will be dumped to local storage via mobx-stored
|
||||
const history: HistoryStorageT = localStored(
|
||||
"history.filters",
|
||||
class HistoryStorage {
|
||||
config: HistoryStorageT = localStored(
|
||||
"filters",
|
||||
{
|
||||
filters: [],
|
||||
filters: [] as ReduceFilterT[][],
|
||||
},
|
||||
{
|
||||
delay: 100,
|
||||
}
|
||||
);
|
||||
|
||||
setFilters = action((newFilters: ReduceFilterT[][]) => {
|
||||
this.config.filters = newFilters;
|
||||
});
|
||||
}
|
||||
|
||||
const History: FC<{
|
||||
alertStore: AlertStore;
|
||||
settingsStore: Settings;
|
||||
}> = observer(({ alertStore, settingsStore }) => {
|
||||
// this will be dumped to local storage via mobx-stored
|
||||
const [history] = useState<HistoryStorage>(new HistoryStorage());
|
||||
const [isVisible, setIsVisible] = useState<boolean>(false);
|
||||
const hide = useCallback(() => setIsVisible(false), []);
|
||||
const toggle = useCallback(() => setIsVisible(!isVisible), [isVisible]);
|
||||
@@ -203,9 +211,11 @@ const History: FC<{
|
||||
// it up if user selects a filter set that was already in history
|
||||
const newHistory = [
|
||||
...[validAppliedFilters],
|
||||
...history.filters.filter((f) => JSON.stringify(f) !== filtersJSON),
|
||||
...history.config.filters.filter(
|
||||
(f) => JSON.stringify(f) !== filtersJSON
|
||||
),
|
||||
].slice(0, 8);
|
||||
history.filters = newHistory;
|
||||
history.setFilters(newHistory);
|
||||
} else {
|
||||
mountRef.current = true;
|
||||
}
|
||||
@@ -251,9 +261,9 @@ const History: FC<{
|
||||
popperPlacement={placement}
|
||||
popperRef={ref}
|
||||
popperStyle={style}
|
||||
filters={history.filters}
|
||||
filters={history.config.filters}
|
||||
onClear={() => {
|
||||
history.filters = [];
|
||||
history.setFilters([]);
|
||||
}}
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
|
||||
@@ -39,7 +39,7 @@ storiesOf("NavBar", module).add("NavBar", () => {
|
||||
const silenceFormStore = new SilenceFormStore();
|
||||
|
||||
alertStore.info.setTotalAlerts(197);
|
||||
alertStore.data.colors = {
|
||||
alertStore.data.setColors({
|
||||
cluster: {
|
||||
staging: {
|
||||
brightness: 205,
|
||||
@@ -52,7 +52,7 @@ storiesOf("NavBar", module).add("NavBar", () => {
|
||||
background: "rgba(115,101,152,255)",
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
alertStore.filters.setFilterValues([
|
||||
NewFilter("cluster=staging", "cluster", "=", "staging", true, true, 15),
|
||||
|
||||
@@ -33,7 +33,7 @@ describe("<OverviewModalContent />", () => {
|
||||
NewUnappliedFilter("abc=xyz"),
|
||||
NewUnappliedFilter("foo=bar"),
|
||||
]);
|
||||
alertStore.data.counters = [
|
||||
alertStore.data.setCounters([
|
||||
{
|
||||
name: "foo",
|
||||
hits: 16,
|
||||
@@ -67,21 +67,21 @@ describe("<OverviewModalContent />", () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
const tree = MountedOverviewModalContent();
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("matches snapshot with no labels to show", () => {
|
||||
alertStore.data.counters = [];
|
||||
alertStore.data.setCounters([]);
|
||||
const tree = MountedOverviewModalContent();
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders all labels after expand button click", () => {
|
||||
alertStore.info.setTotalAlerts(5);
|
||||
alertStore.data.counters = [
|
||||
alertStore.data.setCounters([
|
||||
{
|
||||
name: "foo",
|
||||
hits: 5,
|
||||
@@ -102,7 +102,7 @@ describe("<OverviewModalContent />", () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
]);
|
||||
const tree = MountedOverviewModalContent();
|
||||
|
||||
expect(tree.find("span.components-label")).toHaveLength(2 + 1); // +1 for toggle icon
|
||||
|
||||
@@ -7,17 +7,14 @@ import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { AlertManagerInput } from ".";
|
||||
import { APIAlertsResponseUpstreamsT } from "Models/APITypes";
|
||||
|
||||
let alertStore: AlertStore;
|
||||
let silenceFormStore: SilenceFormStore;
|
||||
|
||||
beforeEach(() => {
|
||||
alertStore = new AlertStore([]);
|
||||
alertStore.data.upstreams.clusters = {
|
||||
HA: ["am1", "am2"],
|
||||
am3: ["am3"],
|
||||
};
|
||||
alertStore.data.setInstances([
|
||||
const generateUpstreams = (): APIAlertsResponseUpstreamsT => ({
|
||||
counters: { total: 3, healthy: 3, failed: 0 },
|
||||
instances: [
|
||||
{
|
||||
name: "am1",
|
||||
uri: "http://am1.example.com",
|
||||
@@ -54,7 +51,13 @@ beforeEach(() => {
|
||||
cluster: "am3",
|
||||
clusterMembers: ["am3"],
|
||||
},
|
||||
]);
|
||||
],
|
||||
clusters: { HA: ["am1", "am2"], am3: ["am3"] },
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
alertStore = new AlertStore([]);
|
||||
alertStore.data.setUpstreams(generateUpstreams());
|
||||
silenceFormStore = new SilenceFormStore();
|
||||
});
|
||||
|
||||
@@ -166,7 +169,7 @@ describe("<AlertManagerInput />", () => {
|
||||
});
|
||||
|
||||
it("is enabled when silenceFormStore.data.silenceID is null", () => {
|
||||
silenceFormStore.data.silenceID = null;
|
||||
silenceFormStore.data.setSilenceID(null);
|
||||
const tree = MountedAlertManagerInput();
|
||||
const select = tree.find("StateManager");
|
||||
expect((select.props() as any).isDisabled).toBeFalsy();
|
||||
@@ -192,9 +195,11 @@ describe("<AlertManagerInput />", () => {
|
||||
});
|
||||
|
||||
it("doesn't include readonly instances", () => {
|
||||
alertStore.data.upstreams.instances[0].readonly = true;
|
||||
alertStore.data.upstreams.instances[2].readonly = true;
|
||||
const upstreams = generateUpstreams();
|
||||
upstreams.instances[0].readonly = true;
|
||||
upstreams.instances[2].readonly = true;
|
||||
MountedAlertManagerInput();
|
||||
alertStore.data.setUpstreams(upstreams);
|
||||
expect(silenceFormStore.data.alertmanagers).toHaveLength(1);
|
||||
expect(silenceFormStore.data.alertmanagers).toContainEqual({
|
||||
label: "am2",
|
||||
|
||||
@@ -9,20 +9,19 @@ import { SilenceFormStore, NewEmptyMatcher } from "Stores/SilenceFormStore";
|
||||
import { QueryOperators, StaticLabels } from "Common/Query";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import SilenceForm from "./SilenceForm";
|
||||
import { APIAlertsResponseUpstreamsT } from "Models/APITypes";
|
||||
|
||||
let alertStore: AlertStore;
|
||||
let settingsStore: Settings;
|
||||
let silenceFormStore: SilenceFormStore;
|
||||
|
||||
beforeEach(() => {
|
||||
alertStore = new AlertStore([]);
|
||||
settingsStore = new Settings(null);
|
||||
silenceFormStore = new SilenceFormStore();
|
||||
|
||||
alertStore.data.upstreams.clusters = {
|
||||
am1: ["am1"],
|
||||
};
|
||||
alertStore.data.setInstances([
|
||||
const generateUpstreams = (): APIAlertsResponseUpstreamsT => ({
|
||||
counters: {
|
||||
healthy: 1,
|
||||
failed: 0,
|
||||
total: 1,
|
||||
},
|
||||
instances: [
|
||||
{
|
||||
name: "am1",
|
||||
uri: "http://am1.example.com",
|
||||
@@ -35,7 +34,17 @@ beforeEach(() => {
|
||||
cluster: "am1",
|
||||
clusterMembers: ["am1"],
|
||||
},
|
||||
]);
|
||||
],
|
||||
clusters: {
|
||||
am1: ["am1"],
|
||||
},
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
alertStore = new AlertStore([]);
|
||||
settingsStore = new Settings(null);
|
||||
silenceFormStore = new SilenceFormStore();
|
||||
alertStore.data.setUpstreams(generateUpstreams());
|
||||
});
|
||||
|
||||
const MountedSilenceForm = () => {
|
||||
@@ -84,7 +93,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
...filterCombos("cluster"),
|
||||
...filterCombos("foo"),
|
||||
]);
|
||||
silenceFormStore.data.autofillMatchers = true;
|
||||
silenceFormStore.data.setAutofillMatchers(true);
|
||||
const tree = MountedSilenceForm();
|
||||
const matchers = tree.find("SilenceMatch");
|
||||
expect(matchers).toHaveLength(6);
|
||||
@@ -174,7 +183,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
...filterCombos("cluster"),
|
||||
...filterCombos("foo"),
|
||||
]);
|
||||
silenceFormStore.data.autofillMatchers = false;
|
||||
silenceFormStore.data.setAutofillMatchers(false);
|
||||
const tree = MountedSilenceForm();
|
||||
const matchers = tree.find("SilenceMatch");
|
||||
expect(matchers).toHaveLength(1);
|
||||
@@ -204,7 +213,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
});
|
||||
|
||||
it("trash icon is visible when there are two matchers", () => {
|
||||
silenceFormStore.data.autofillMatchers = false;
|
||||
silenceFormStore.data.setAutofillMatchers(false);
|
||||
silenceFormStore.data.addEmptyMatcher();
|
||||
silenceFormStore.data.addEmptyMatcher();
|
||||
const tree = MountedSilenceForm();
|
||||
@@ -216,7 +225,7 @@ describe("<SilenceForm /> matchers", () => {
|
||||
});
|
||||
|
||||
it("clicking trash icon on a matcher select removes it", () => {
|
||||
silenceFormStore.data.autofillMatchers = false;
|
||||
silenceFormStore.data.setAutofillMatchers(false);
|
||||
silenceFormStore.data.addEmptyMatcher();
|
||||
silenceFormStore.data.addEmptyMatcher();
|
||||
silenceFormStore.data.addEmptyMatcher();
|
||||
@@ -261,7 +270,7 @@ describe("<SilenceForm /> preview", () => {
|
||||
silenceFormStore.data.setAlertmanagers([{ label: "am1", value: ["am1"] }]);
|
||||
silenceFormStore.data.setAuthor("me@example.com");
|
||||
silenceFormStore.data.setComment("fake silence");
|
||||
silenceFormStore.data.autofillMatchers = false;
|
||||
silenceFormStore.data.setAutofillMatchers(false);
|
||||
|
||||
const tree = MountedSilenceForm();
|
||||
tree.find(".badge.cursor-pointer.text-muted").simulate("click");
|
||||
@@ -280,7 +289,7 @@ describe("<SilenceForm /> preview", () => {
|
||||
silenceFormStore.data.setAlertmanagers([{ label: "am1", value: ["am1"] }]);
|
||||
silenceFormStore.data.setAuthor("me@example.com");
|
||||
silenceFormStore.data.setComment("fake silence");
|
||||
silenceFormStore.data.autofillMatchers = false;
|
||||
silenceFormStore.data.setAutofillMatchers(false);
|
||||
|
||||
const tree = MountedSilenceForm();
|
||||
tree.find(".badge.cursor-pointer.text-muted").simulate("click");
|
||||
@@ -296,8 +305,7 @@ describe("<SilenceForm /> preview", () => {
|
||||
|
||||
describe("<SilenceForm /> inputs", () => {
|
||||
it("author is read-only when info.authentication.enabled is true", () => {
|
||||
alertStore.info.authentication.enabled = true;
|
||||
alertStore.info.authentication.username = "auth@example.com";
|
||||
alertStore.info.setAuthentication(true, "auth@example.com");
|
||||
const tree = MountedSilenceForm();
|
||||
const input = tree.find("input[placeholder='Author']");
|
||||
expect(input.props().readOnly).toBe(true);
|
||||
@@ -306,7 +314,7 @@ describe("<SilenceForm /> inputs", () => {
|
||||
});
|
||||
|
||||
it("default author value comes from Settings store", () => {
|
||||
settingsStore.silenceFormConfig.config.author = "foo@example.com";
|
||||
settingsStore.silenceFormConfig.saveAuthor("foo@example.com");
|
||||
const tree = MountedSilenceForm();
|
||||
const input = tree.find("input[placeholder='Author']");
|
||||
expect(input.props().value).toBe("foo@example.com");
|
||||
@@ -314,7 +322,7 @@ describe("<SilenceForm /> inputs", () => {
|
||||
});
|
||||
|
||||
it("default author value is empty if nothing is stored in Settings", () => {
|
||||
settingsStore.silenceFormConfig.config.author = "";
|
||||
settingsStore.silenceFormConfig.saveAuthor("");
|
||||
const tree = MountedSilenceForm();
|
||||
const input = tree.find("input[placeholder='Author']");
|
||||
expect(input.text()).toBe("");
|
||||
|
||||
@@ -102,7 +102,7 @@ const SilenceForm: FC<{
|
||||
}
|
||||
|
||||
// reset cluster request state
|
||||
silenceFormStore.data.requestsByCluster = {};
|
||||
silenceFormStore.data.setRequestsByCluster({});
|
||||
|
||||
if (silenceFormStore.data.autofillMatchers) {
|
||||
silenceFormStore.data.setMatchers([]);
|
||||
@@ -124,7 +124,7 @@ const SilenceForm: FC<{
|
||||
} else {
|
||||
matcher.values = [StringToOption(f.value)];
|
||||
}
|
||||
silenceFormStore.data.matchers.push(matcher);
|
||||
silenceFormStore.data.addMatcherWithID(matcher);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -168,12 +168,12 @@ const SilenceForm: FC<{
|
||||
silenceFormStore.data.alertmanagers.forEach((am) => {
|
||||
rbc[am.label] = NewClusterRequest(am.label, am.value);
|
||||
});
|
||||
silenceFormStore.data.requestsByCluster = rbc;
|
||||
silenceFormStore.data.setRequestsByCluster(rbc);
|
||||
|
||||
settingsStore.silenceFormConfig.saveAuthor(silenceFormStore.data.author);
|
||||
|
||||
if (silenceFormStore.data.isValid)
|
||||
silenceFormStore.data.currentStage = "preview";
|
||||
silenceFormStore.data.setStage("preview");
|
||||
|
||||
silenceFormStore.data.setWasValidated(true);
|
||||
};
|
||||
|
||||
@@ -8,36 +8,39 @@ import { Settings } from "Stores/Settings";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { SilenceModalContent } from "./SilenceModalContent";
|
||||
import { APIAlertsResponseUpstreamsT } from "Models/APITypes";
|
||||
|
||||
let alertStore: AlertStore;
|
||||
let settingsStore: Settings;
|
||||
let silenceFormStore: SilenceFormStore;
|
||||
|
||||
const generateUpstreams = (): APIAlertsResponseUpstreamsT => ({
|
||||
counters: { total: 1, healthy: 1, failed: 0 },
|
||||
instances: [
|
||||
{
|
||||
name: "am1",
|
||||
cluster: "am",
|
||||
clusterMembers: ["am1"],
|
||||
uri: "http://localhost:9093",
|
||||
publicURI: "http://localhost:9093",
|
||||
readonly: false,
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
},
|
||||
],
|
||||
clusters: { am: ["am1"] },
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
alertStore = new AlertStore([]);
|
||||
settingsStore = new Settings(null);
|
||||
silenceFormStore = new SilenceFormStore();
|
||||
|
||||
alertStore.data.setUpstreams({
|
||||
counters: { total: 1, healthy: 1, failed: 0 },
|
||||
instances: [
|
||||
{
|
||||
name: "am1",
|
||||
cluster: "am",
|
||||
clusterMembers: ["am1"],
|
||||
uri: "http://localhost:9093",
|
||||
publicURI: "http://localhost:9093",
|
||||
readonly: false,
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
headers: {},
|
||||
corsCredentials: "include",
|
||||
},
|
||||
],
|
||||
clusters: { am: ["am1"] },
|
||||
});
|
||||
alertStore.data.setUpstreams(generateUpstreams());
|
||||
|
||||
silenceFormStore.tab.current = "editor";
|
||||
silenceFormStore.tab.setTab("editor");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -63,7 +66,9 @@ const MountedSilenceModalContent = () => {
|
||||
|
||||
describe("<SilenceModalContent />", () => {
|
||||
it("Renders ReadOnlyPlaceholder when there are no writable Alertmanager upstreams", () => {
|
||||
alertStore.data.upstreams.instances[0].readonly = true;
|
||||
const upstreams = generateUpstreams();
|
||||
upstreams.instances[0].readonly = true;
|
||||
alertStore.data.setUpstreams(upstreams);
|
||||
const tree = MountedSilenceModalContent();
|
||||
const placeholder = tree.find("ReadOnlyPlaceholder");
|
||||
expect(placeholder).toHaveLength(1);
|
||||
@@ -79,7 +84,7 @@ describe("<SilenceModalContent />", () => {
|
||||
});
|
||||
|
||||
it("Clicking on the Editor tab changes content", () => {
|
||||
silenceFormStore.tab.current = "browser";
|
||||
silenceFormStore.tab.setTab("browser");
|
||||
const tree = MountedSilenceModalContent();
|
||||
const tabs = tree.find("Tab");
|
||||
tabs.at(0).simulate("click");
|
||||
@@ -88,13 +93,13 @@ describe("<SilenceModalContent />", () => {
|
||||
});
|
||||
|
||||
it("Content is not blurred when silenceFormStore.toggle.blurred is false", () => {
|
||||
silenceFormStore.toggle.blurred = false;
|
||||
silenceFormStore.toggle.setBlur(false);
|
||||
const tree = MountedSilenceModalContent();
|
||||
expect(tree.find("div.modal-body.modal-content-blur")).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("Content is blurred when silenceFormStore.toggle.blurred is true", () => {
|
||||
silenceFormStore.toggle.blurred = true;
|
||||
silenceFormStore.toggle.setBlur(true);
|
||||
const tree = MountedSilenceModalContent();
|
||||
expect(tree.find("div.modal-body.modal-content-blur")).toHaveLength(1);
|
||||
});
|
||||
@@ -102,28 +107,28 @@ describe("<SilenceModalContent />", () => {
|
||||
|
||||
describe("<SilenceModalContent /> Editor", () => {
|
||||
it("title is 'New silence' when creating new silence", () => {
|
||||
silenceFormStore.data.currentStage = "form";
|
||||
silenceFormStore.data.silenceID = null;
|
||||
silenceFormStore.data.setStage("form");
|
||||
silenceFormStore.data.setSilenceID(null);
|
||||
const tree = MountedSilenceModalContent();
|
||||
const tab = tree.find("Tab").at(0);
|
||||
expect(tab.props().title).toBe("New silence");
|
||||
});
|
||||
it("title is 'Editing silence' when editing exiting silence", () => {
|
||||
silenceFormStore.data.currentStage = "form";
|
||||
silenceFormStore.data.setStage("form");
|
||||
silenceFormStore.data.setSilenceID("1234");
|
||||
const tree = MountedSilenceModalContent();
|
||||
const tab = tree.find("Tab").at(0);
|
||||
expect(tab.props().title).toBe("Editing silence");
|
||||
});
|
||||
it("title is 'Preview silenced alerts' when previewing silenced alerts", () => {
|
||||
silenceFormStore.data.currentStage = "preview";
|
||||
silenceFormStore.data.setStage("preview");
|
||||
silenceFormStore.data.setSilenceID("1234");
|
||||
const tree = MountedSilenceModalContent();
|
||||
const tab = tree.find("Tab").at(0);
|
||||
expect(tab.props().title).toBe("Preview silenced alerts");
|
||||
});
|
||||
it("title is 'Silence submitted' after sending silence to Alertmanager", () => {
|
||||
silenceFormStore.data.currentStage = "submit";
|
||||
silenceFormStore.data.setStage("submit");
|
||||
silenceFormStore.data.setSilenceID("1234");
|
||||
const tree = MountedSilenceModalContent();
|
||||
const tab = tree.find("Tab").at(0);
|
||||
@@ -131,21 +136,21 @@ describe("<SilenceModalContent /> Editor", () => {
|
||||
});
|
||||
|
||||
it("renders SilenceForm when silenceFormStore.data.currentStage is 'UserInput'", () => {
|
||||
silenceFormStore.data.currentStage = "form";
|
||||
silenceFormStore.data.setStage("form");
|
||||
const tree = MountedSilenceModalContent();
|
||||
const form = tree.find("SilenceForm");
|
||||
expect(form).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("renders SilencePreview when silenceFormStore.data.currentStage is 'Preview'", () => {
|
||||
silenceFormStore.data.currentStage = "preview";
|
||||
silenceFormStore.data.setStage("preview");
|
||||
const tree = MountedSilenceModalContent();
|
||||
const ctrl = tree.find("SilencePreview");
|
||||
expect(ctrl).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("renders SilenceSubmitController when silenceFormStore.data.currentStage is 'Submit'", () => {
|
||||
silenceFormStore.data.currentStage = "submit";
|
||||
silenceFormStore.data.setStage("submit");
|
||||
const tree = MountedSilenceModalContent();
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
@@ -153,7 +158,7 @@ describe("<SilenceModalContent /> Editor", () => {
|
||||
|
||||
describe("<SilenceModalContent /> Browser", () => {
|
||||
it("renders silence browser when tab is set to Browser", () => {
|
||||
silenceFormStore.tab.current = "browser";
|
||||
silenceFormStore.tab.setTab("browser");
|
||||
const tree = MountedSilenceModalContent();
|
||||
const form = tree.find("Browser");
|
||||
expect(form).toHaveLength(1);
|
||||
|
||||
@@ -31,7 +31,7 @@ const SilencePreview: FC<{
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
onClick={silenceFormStore.data.setStageSubmit}
|
||||
onClick={() => silenceFormStore.data.setStage("submit")}
|
||||
>
|
||||
<FontAwesomeIcon icon={faCheckCircle} className="pr-1" />
|
||||
Submit
|
||||
|
||||
@@ -59,10 +59,10 @@ beforeEach(() => {
|
||||
|
||||
describe("<SilenceSubmitController />", () => {
|
||||
it("renders MultiClusterStatus when multiple clusters are used", () => {
|
||||
silenceFormStore.data.requestsByCluster = {
|
||||
silenceFormStore.data.setRequestsByCluster({
|
||||
ha: NewClusterRequest("ha", ["am1", "am2"]),
|
||||
single: NewClusterRequest("single", ["single"]),
|
||||
};
|
||||
});
|
||||
|
||||
const tree = shallow(
|
||||
<SilenceSubmitController
|
||||
@@ -75,9 +75,9 @@ describe("<SilenceSubmitController />", () => {
|
||||
});
|
||||
|
||||
it("renders SingleClusterStatus when multiple clusters are used", () => {
|
||||
silenceFormStore.data.requestsByCluster = {
|
||||
silenceFormStore.data.setRequestsByCluster({
|
||||
ha: NewClusterRequest("ha", ["am1", "am2"]),
|
||||
};
|
||||
});
|
||||
|
||||
const tree = shallow(
|
||||
<SilenceSubmitController
|
||||
@@ -90,7 +90,7 @@ describe("<SilenceSubmitController />", () => {
|
||||
});
|
||||
|
||||
it("resets the form on 'Back' button click", () => {
|
||||
silenceFormStore.data.currentStage = "submit";
|
||||
silenceFormStore.data.setStage("submit");
|
||||
const tree = shallow(
|
||||
<SilenceSubmitController
|
||||
alertStore={alertStore}
|
||||
@@ -105,10 +105,10 @@ describe("<SilenceSubmitController />", () => {
|
||||
|
||||
describe("<MultiClusterStatus />", () => {
|
||||
it("renders all passed SilenceSubmitProgress", () => {
|
||||
silenceFormStore.data.requestsByCluster = {
|
||||
silenceFormStore.data.setRequestsByCluster({
|
||||
ha: NewClusterRequest("ha", ["am1", "am2"]),
|
||||
single: NewClusterRequest("single", ["single"]),
|
||||
};
|
||||
});
|
||||
const tree = shallow(
|
||||
<MultiClusterStatus
|
||||
alertStore={alertStore}
|
||||
@@ -120,7 +120,7 @@ describe("<MultiClusterStatus />", () => {
|
||||
|
||||
it("renders spinner for pending requests", () => {
|
||||
const single = NewClusterRequest("single", ["single"]);
|
||||
silenceFormStore.data.requestsByCluster = { single: single };
|
||||
silenceFormStore.data.setRequestsByCluster({ single: single });
|
||||
const tree = shallow(
|
||||
<MultiClusterStatus
|
||||
alertStore={alertStore}
|
||||
@@ -137,7 +137,7 @@ describe("<MultiClusterStatus />", () => {
|
||||
const single = NewClusterRequest("single", ["single"]);
|
||||
single.isDone = true;
|
||||
single.error = "fake error";
|
||||
silenceFormStore.data.requestsByCluster = { single: single };
|
||||
silenceFormStore.data.setRequestsByCluster({ single: single });
|
||||
const tree = shallow(
|
||||
<MultiClusterStatus
|
||||
alertStore={alertStore}
|
||||
@@ -155,7 +155,7 @@ describe("<MultiClusterStatus />", () => {
|
||||
single.isDone = true;
|
||||
single.silenceID = "123456789";
|
||||
single.silenceLink = "http://localhost";
|
||||
silenceFormStore.data.requestsByCluster = { single: single };
|
||||
silenceFormStore.data.setRequestsByCluster({ single: single });
|
||||
const tree = shallow(
|
||||
<MultiClusterStatus
|
||||
alertStore={alertStore}
|
||||
@@ -175,7 +175,7 @@ describe("<MultiClusterStatus />", () => {
|
||||
describe("<SingleClusterStatus />", () => {
|
||||
it("renders spinner for pending requests", () => {
|
||||
const single = NewClusterRequest("single", ["single"]);
|
||||
silenceFormStore.data.requestsByCluster = { single: single };
|
||||
silenceFormStore.data.setRequestsByCluster({ single: single });
|
||||
const tree = shallow(
|
||||
<SingleClusterStatus
|
||||
alertStore={alertStore}
|
||||
@@ -191,7 +191,7 @@ describe("<SingleClusterStatus />", () => {
|
||||
const single = NewClusterRequest("single", ["single"]);
|
||||
single.isDone = true;
|
||||
single.error = "fake error";
|
||||
silenceFormStore.data.requestsByCluster = { single: single };
|
||||
silenceFormStore.data.setRequestsByCluster({ single: single });
|
||||
const tree = shallow(
|
||||
<SingleClusterStatus
|
||||
alertStore={alertStore}
|
||||
@@ -210,7 +210,7 @@ describe("<SingleClusterStatus />", () => {
|
||||
single.isDone = true;
|
||||
single.silenceID = "123456789";
|
||||
single.silenceLink = "http://localhost";
|
||||
silenceFormStore.data.requestsByCluster = { single: single };
|
||||
silenceFormStore.data.setRequestsByCluster({ single: single });
|
||||
const tree = shallow(
|
||||
<SingleClusterStatus
|
||||
alertStore={alertStore}
|
||||
|
||||
@@ -7,38 +7,41 @@ import fetchMock from "fetch-mock";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore, NewClusterRequest } from "Stores/SilenceFormStore";
|
||||
import { SilenceSubmitProgress } from "./SilenceSubmitProgress";
|
||||
import { APIAlertsResponseUpstreamsT } from "Models/APITypes";
|
||||
|
||||
let alertStore: AlertStore;
|
||||
let silenceFormStore: SilenceFormStore;
|
||||
|
||||
const generateUpstreams = (): APIAlertsResponseUpstreamsT => ({
|
||||
counters: { total: 1, healthy: 1, failed: 0 },
|
||||
clusters: { mockAlertmanager: ["mockAlertmanager"] },
|
||||
instances: [
|
||||
{
|
||||
name: "mockAlertmanager",
|
||||
uri: "http://localhost",
|
||||
publicURI: "http://example.com",
|
||||
readonly: false,
|
||||
headers: { foo: "bar" },
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "mockAlertmanager",
|
||||
clusterMembers: ["mockAlertmanager"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
alertStore = new AlertStore([]);
|
||||
silenceFormStore = new SilenceFormStore();
|
||||
|
||||
alertStore.data.setUpstreams({
|
||||
counters: { total: 1, healthy: 1, failed: 0 },
|
||||
clusters: { mockAlertmanager: ["mockAlertmanager"] },
|
||||
instances: [
|
||||
{
|
||||
name: "mockAlertmanager",
|
||||
uri: "http://localhost",
|
||||
publicURI: "http://example.com",
|
||||
readonly: false,
|
||||
headers: { foo: "bar" },
|
||||
corsCredentials: "include",
|
||||
error: "",
|
||||
version: "0.17.0",
|
||||
cluster: "mockAlertmanager",
|
||||
clusterMembers: ["mockAlertmanager"],
|
||||
},
|
||||
],
|
||||
});
|
||||
alertStore.data.setUpstreams(generateUpstreams());
|
||||
|
||||
silenceFormStore.data.requestsByCluster = {
|
||||
silenceFormStore.data.setRequestsByCluster({
|
||||
mockAlertmanager: NewClusterRequest("mockAlertmanager", [
|
||||
"mockAlertmanager",
|
||||
]),
|
||||
};
|
||||
});
|
||||
|
||||
fetchMock.resetHistory();
|
||||
fetchMock.mock(
|
||||
@@ -114,7 +117,9 @@ describe("<SilenceSubmitProgress />", () => {
|
||||
});
|
||||
|
||||
it("uses CORS credentials from alertmanager config", async () => {
|
||||
alertStore.data.upstreams.instances[0].corsCredentials = "same-origin";
|
||||
const upstreams = generateUpstreams();
|
||||
upstreams.instances[0].corsCredentials = "same-origin";
|
||||
alertStore.data.setUpstreams(upstreams);
|
||||
MountedSilenceSubmitProgress();
|
||||
await act(async () => {
|
||||
await fetchMock.flush(true);
|
||||
@@ -165,9 +170,9 @@ describe("<SilenceSubmitProgress />", () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
silenceFormStore.data.requestsByCluster = {
|
||||
silenceFormStore.data.setRequestsByCluster({
|
||||
ha: NewClusterRequest("ha", ["am1", "am2"]),
|
||||
};
|
||||
});
|
||||
|
||||
mount(
|
||||
<SilenceSubmitProgress
|
||||
@@ -234,9 +239,9 @@ describe("<SilenceSubmitProgress />", () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
silenceFormStore.data.requestsByCluster = {
|
||||
silenceFormStore.data.setRequestsByCluster({
|
||||
ha: NewClusterRequest("ha", ["am1", "am2"]),
|
||||
};
|
||||
});
|
||||
|
||||
const tree = mount(
|
||||
<SilenceSubmitProgress
|
||||
@@ -288,9 +293,9 @@ describe("<SilenceSubmitProgress />", () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
silenceFormStore.data.requestsByCluster = {
|
||||
silenceFormStore.data.setRequestsByCluster({
|
||||
ha: NewClusterRequest("ha", ["am1", "am2"]),
|
||||
};
|
||||
});
|
||||
|
||||
mount(
|
||||
<SilenceSubmitProgress
|
||||
@@ -327,9 +332,9 @@ describe("<SilenceSubmitProgress />", () => {
|
||||
clusters: { ha: ["am1", "am2"] },
|
||||
instances: [],
|
||||
});
|
||||
silenceFormStore.data.requestsByCluster = {
|
||||
silenceFormStore.data.setRequestsByCluster({
|
||||
ha: NewClusterRequest("ha", ["am1", "am2"]),
|
||||
};
|
||||
});
|
||||
|
||||
mount(
|
||||
<SilenceSubmitProgress
|
||||
@@ -393,9 +398,9 @@ describe("<SilenceSubmitProgress />", () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
silenceFormStore.data.requestsByCluster = {
|
||||
silenceFormStore.data.setRequestsByCluster({
|
||||
ha: NewClusterRequest("ha", ["am1", "am2"]),
|
||||
};
|
||||
});
|
||||
|
||||
mount(
|
||||
<SilenceSubmitProgress
|
||||
@@ -463,9 +468,9 @@ describe("<SilenceSubmitProgress />", () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
silenceFormStore.data.requestsByCluster = {
|
||||
silenceFormStore.data.setRequestsByCluster({
|
||||
ha: NewClusterRequest("ha", ["am1", "am2"]),
|
||||
};
|
||||
});
|
||||
|
||||
mount(
|
||||
<SilenceSubmitProgress
|
||||
|
||||
@@ -64,15 +64,18 @@ const SilenceSubmitProgress: FC<{
|
||||
|
||||
useEffect(() => {
|
||||
if (!inProgress && error !== null) {
|
||||
silenceFormStore.data.requestsByCluster[cluster].isDone = true;
|
||||
silenceFormStore.data.requestsByCluster[cluster].error = error;
|
||||
silenceFormStore.data.setRequestsByClusterUpdate(cluster, {
|
||||
isDone: true,
|
||||
error: error,
|
||||
});
|
||||
} else if (!inProgress && response !== null) {
|
||||
silenceFormStore.data.requestsByCluster[cluster].isDone = true;
|
||||
silenceFormStore.data.requestsByCluster[cluster].silenceID =
|
||||
response.silenceID;
|
||||
silenceFormStore.data.requestsByCluster[cluster].silenceLink = `${
|
||||
publicURIs[responseURI as string]
|
||||
}/#/silences/${response.silenceID}`;
|
||||
silenceFormStore.data.setRequestsByClusterUpdate(cluster, {
|
||||
isDone: true,
|
||||
silenceID: response.silenceID,
|
||||
silenceLink: `${publicURIs[responseURI as string]}/#/silences/${
|
||||
response.silenceID
|
||||
}`,
|
||||
});
|
||||
}
|
||||
}, [cluster, error, inProgress, publicURIs, response, responseURI]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
|
||||
@@ -47,8 +47,7 @@ storiesOf("SilenceModal", module)
|
||||
const settingsStore = new Settings(null);
|
||||
const silenceFormStore = new SilenceFormStore();
|
||||
|
||||
alertStore.info.authentication.enabled = true;
|
||||
alertStore.info.authentication.username = "me@example.com";
|
||||
alertStore.info.setAuthentication(true, "me@example.com");
|
||||
|
||||
alertStore.data.setUpstreams({
|
||||
counters: { healthy: 3, failed: 0, total: 3 },
|
||||
@@ -94,7 +93,7 @@ storiesOf("SilenceModal", module)
|
||||
});
|
||||
|
||||
silenceFormStore.toggle.visible = true;
|
||||
silenceFormStore.data.autofillMatchers = false;
|
||||
silenceFormStore.data.setAutofillMatchers(false);
|
||||
silenceFormStore.data.setMatchers([
|
||||
MockMatcher("cluster", ["prod"], false),
|
||||
MockMatcher("instance", ["server1", "server3"], true),
|
||||
|
||||
@@ -143,7 +143,7 @@ describe("<SilenceModal />", () => {
|
||||
// mark form as dirty, resetProgress() should change this value to false
|
||||
silenceFormStore.data.setWasValidated(true);
|
||||
// disable autofill, closing modal should re-enable it
|
||||
silenceFormStore.data.autofillMatchers = false;
|
||||
silenceFormStore.data.setAutofillMatchers(false);
|
||||
|
||||
// click to hide
|
||||
toggle.simulate("click");
|
||||
|
||||
@@ -46,7 +46,7 @@ const SilenceModal: FC<{
|
||||
toggleOpen={silenceFormStore.toggle.toggle}
|
||||
onExited={() => {
|
||||
silenceFormStore.data.resetProgress();
|
||||
silenceFormStore.data.autofillMatchers = true;
|
||||
silenceFormStore.data.setAutofillMatchers(true);
|
||||
}}
|
||||
>
|
||||
<React.Suspense
|
||||
|
||||
@@ -58,7 +58,7 @@ const makeErrors = () => {
|
||||
|
||||
describe("<AppToasts />", () => {
|
||||
it("doesn't render anything when alertStore.info.upgradeNeeded=true", () => {
|
||||
alertStore.info.upgradeNeeded = true;
|
||||
alertStore.info.setUpgradeNeeded(true);
|
||||
const tree = mount(<AppToasts alertStore={alertStore} />);
|
||||
expect(tree.html()).toBe("");
|
||||
});
|
||||
@@ -128,8 +128,8 @@ describe("<AppToasts />", () => {
|
||||
|
||||
it("clicking navbar icon toggles all notifications", () => {
|
||||
makeErrors();
|
||||
alertStore.info.upgradeNeeded = false;
|
||||
alertStore.info.upgradeReady = false;
|
||||
alertStore.info.setUpgradeNeeded(false);
|
||||
alertStore.info.setUpgradeReady(false);
|
||||
const tree = mount(<AppToasts alertStore={alertStore} />);
|
||||
expect(tree.find("div.bg-toast")).toHaveLength(2);
|
||||
expect(tree.find("span.badge.cursor-pointer.with-click")).toHaveLength(2);
|
||||
@@ -143,7 +143,7 @@ describe("<AppToasts />", () => {
|
||||
});
|
||||
|
||||
it("renders UpgradeToastMessage when alertStore.info.upgradeReady=true", () => {
|
||||
alertStore.info.upgradeReady = true;
|
||||
alertStore.info.setUpgradeReady(true);
|
||||
const tree = mount(<AppToasts alertStore={alertStore} />);
|
||||
expect(tree.find("UpgradeToastMessage")).toHaveLength(1);
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
|
||||
@@ -9,7 +9,7 @@ let alertStore: AlertStore;
|
||||
|
||||
beforeEach(() => {
|
||||
alertStore = new AlertStore([]);
|
||||
alertStore.info.version = "1.2.3";
|
||||
alertStore.info.setVersion("1.2.3");
|
||||
});
|
||||
|
||||
describe("<ToastMessage />", () => {
|
||||
@@ -46,4 +46,10 @@ describe("<UpgradeToastMessage />", () => {
|
||||
tree.find("button").simulate("click");
|
||||
expect(alertStore.info.upgradeNeeded).toBe(true);
|
||||
});
|
||||
|
||||
it("upgradeNeeded=true after onAnimationEnd is called", () => {
|
||||
const tree = mount(<UpgradeToastMessage alertStore={alertStore} />);
|
||||
tree.find("div.toast-upgrade-progressbar").simulate("animationend");
|
||||
expect(alertStore.info.upgradeNeeded).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ const UpgradeToastMessage: FC<{
|
||||
|
||||
const setPause = useCallback(() => {
|
||||
if (isPaused) {
|
||||
alertStore.info.setUpgradeNeeded();
|
||||
alertStore.info.setUpgradeNeeded(true);
|
||||
} else {
|
||||
setIsPaused(true);
|
||||
}
|
||||
@@ -56,7 +56,7 @@ const UpgradeToastMessage: FC<{
|
||||
className={`progress-bar bg-white ${
|
||||
isPaused ? "" : "toast-upgrade-progressbar"
|
||||
}`}
|
||||
onAnimationEnd={alertStore.info.setUpgradeNeeded}
|
||||
onAnimationEnd={() => alertStore.info.setUpgradeNeeded(true)}
|
||||
role="progressbar"
|
||||
style={{ width: 100 }}
|
||||
></div>
|
||||
|
||||
@@ -11,7 +11,7 @@ import "Styles/Percy.scss";
|
||||
|
||||
storiesOf("AppToasts", module).add("AppToasts", () => {
|
||||
const alertStore = new AlertStore([]);
|
||||
alertStore.info.version = "999.99.0";
|
||||
alertStore.info.setVersion("999.99.0");
|
||||
|
||||
return (
|
||||
<div className="d-flex flex-column">
|
||||
|
||||
@@ -177,9 +177,9 @@ describe("AlertStore.info", () => {
|
||||
it("setUpgradeNeeded() sets upgradeNeeded to true", () => {
|
||||
const store = new AlertStore([]);
|
||||
expect(store.info.upgradeNeeded).toBe(false);
|
||||
store.info.setUpgradeNeeded();
|
||||
store.info.setUpgradeNeeded(true);
|
||||
expect(store.info.upgradeNeeded).toBe(true);
|
||||
store.info.setUpgradeNeeded();
|
||||
store.info.setUpgradeNeeded(true);
|
||||
expect(store.info.upgradeNeeded).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -575,8 +575,8 @@ describe("AlertStore.fetch", () => {
|
||||
});
|
||||
|
||||
it("unapplied filters are marked as applied on fetch error", async () => {
|
||||
const store = new AlertStore(["foo"]);
|
||||
store.filters.values[0].applied = false;
|
||||
const store = new AlertStore([]);
|
||||
store.filters.setFilterValues([NewUnappliedFilter("foo")]);
|
||||
|
||||
jest.spyOn(console, "trace").mockImplementation(() => {});
|
||||
fetchMock.reset();
|
||||
@@ -600,7 +600,7 @@ describe("AlertStore.fetch", () => {
|
||||
const store = new AlertStore(["label=value"]);
|
||||
|
||||
// initial fetch, should update settings
|
||||
store.settings.values = { foo: "bar" } as any;
|
||||
store.settings.setValues({ foo: "bar" } as any);
|
||||
await expect(store.fetch("", false, "", "", "")).resolves.toBeUndefined();
|
||||
expect(store.settings.values).toMatchObject({
|
||||
staticColorLabels: ["job"],
|
||||
@@ -664,14 +664,14 @@ describe("AlertStore.fetch", () => {
|
||||
const g1 = MockGroup("group1", 1, 1, 0);
|
||||
const g2 = MockGroup("group2", 1, 1, 0);
|
||||
const g3 = MockGroup("group3", 1, 1, 0);
|
||||
store.data.grids = [
|
||||
store.data.setGrids([
|
||||
{
|
||||
labelName: "",
|
||||
labelValue: "",
|
||||
alertGroups: [g1, g2, g3],
|
||||
stateCount: { unprocessed: 0, active: 3, suppressed: 0 },
|
||||
},
|
||||
];
|
||||
]);
|
||||
expect(store.data.grids).toHaveLength(1);
|
||||
expect(Object.keys(store.data.grids[0].alertGroups)).toHaveLength(3);
|
||||
|
||||
|
||||
+55
-14
@@ -150,8 +150,11 @@ interface AlertStoreDataT {
|
||||
getColorData: (name: string, value: string) => APILabelColorT | undefined;
|
||||
setGrids: (g: APIGridT[]) => void;
|
||||
setUpstreams: (u: APIAlertsResponseUpstreamsT) => void;
|
||||
setInstances: (i: APIAlertmanagerUpstreamT[]) => void;
|
||||
setClusters: (c: APIAlertsResponseUpstreamsClusterMapT) => void;
|
||||
setSilences: (s: APIAlertsResponseSilenceMapT) => void;
|
||||
setCounters: (c: APILabelCounterT[]) => void;
|
||||
setReceivers: (r: string[]) => void;
|
||||
setColors: (c: APIAlertsResponseColorsT) => void;
|
||||
readonly upstreamsWithErrors: APIAlertmanagerUpstreamT[];
|
||||
}
|
||||
|
||||
@@ -168,13 +171,17 @@ interface AlertStoreInfoT {
|
||||
reloadNeeded: boolean;
|
||||
setIsRetrying: () => void;
|
||||
clearIsRetrying: () => void;
|
||||
setUpgradeNeeded: () => void;
|
||||
setReloadNeeded: () => void;
|
||||
setUpgradeNeeded: (v: boolean) => void;
|
||||
setUpgradeReady: (v: boolean) => void;
|
||||
setReloadNeeded: (v: boolean) => void;
|
||||
setTotalAlerts: (n: number) => void;
|
||||
setAuthentication: (enabled: boolean, username: string) => void;
|
||||
setVersion: (v: string) => void;
|
||||
}
|
||||
|
||||
interface AlertStoreSettingsT {
|
||||
values: APISettingsT;
|
||||
setValues: (v: APISettingsT) => void;
|
||||
}
|
||||
|
||||
interface AlertStoreStatusT {
|
||||
@@ -191,6 +198,7 @@ interface AlertStoreStatusT {
|
||||
resume: () => void;
|
||||
togglePause: () => void;
|
||||
stop: () => void;
|
||||
setError: (e: null | string) => void;
|
||||
}
|
||||
|
||||
interface AlertStoreUIT {
|
||||
@@ -338,12 +346,21 @@ class AlertStore {
|
||||
setUpstreams(u: APIAlertsResponseUpstreamsT) {
|
||||
this.upstreams = u;
|
||||
},
|
||||
setInstances(i: APIAlertmanagerUpstreamT[]) {
|
||||
this.upstreams.instances = i;
|
||||
},
|
||||
setClusters(c: APIAlertsResponseUpstreamsClusterMapT) {
|
||||
this.upstreams.clusters = c;
|
||||
},
|
||||
setSilences(s: APIAlertsResponseSilenceMapT) {
|
||||
this.silences = s;
|
||||
},
|
||||
setCounters(c: APILabelCounterT[]) {
|
||||
this.counters = c;
|
||||
},
|
||||
setReceivers(r: string[]) {
|
||||
this.receivers = r;
|
||||
},
|
||||
setColors(c: APIAlertsResponseColorsT) {
|
||||
this.colors = c;
|
||||
},
|
||||
get upstreamsWithErrors(): APIAlertmanagerUpstreamT[] {
|
||||
return this.upstreams.instances.filter(
|
||||
(upstream) => upstream.error !== ""
|
||||
@@ -357,8 +374,11 @@ class AlertStore {
|
||||
clustersWithoutReadOnly: computed,
|
||||
setGrids: action.bound,
|
||||
setUpstreams: action.bound,
|
||||
setInstances: action.bound,
|
||||
setClusters: action.bound,
|
||||
setSilences: action.bound,
|
||||
setCounters: action.bound,
|
||||
setReceivers: action.bound,
|
||||
setColors: action.bound,
|
||||
},
|
||||
{ name: "API Response data" }
|
||||
);
|
||||
@@ -381,15 +401,25 @@ class AlertStore {
|
||||
clearIsRetrying() {
|
||||
this.isRetrying = false;
|
||||
},
|
||||
setUpgradeNeeded() {
|
||||
this.upgradeNeeded = true;
|
||||
setUpgradeNeeded(v: boolean) {
|
||||
this.upgradeNeeded = v;
|
||||
},
|
||||
setReloadNeeded() {
|
||||
this.reloadNeeded = true;
|
||||
setUpgradeReady(v: boolean) {
|
||||
this.upgradeReady = v;
|
||||
},
|
||||
setReloadNeeded(v: boolean) {
|
||||
this.reloadNeeded = v;
|
||||
},
|
||||
setTotalAlerts(n: number) {
|
||||
this.totalAlerts = n;
|
||||
},
|
||||
setAuthentication(enabled: boolean, username: string) {
|
||||
this.authentication.enabled = enabled;
|
||||
this.authentication.username = username;
|
||||
},
|
||||
setVersion(v: string) {
|
||||
this.version = v;
|
||||
},
|
||||
},
|
||||
{
|
||||
setIsRetrying: action.bound,
|
||||
@@ -397,6 +427,8 @@ class AlertStore {
|
||||
setReloadNeeded: action.bound,
|
||||
setUpgradeNeeded: action.bound,
|
||||
setTotalAlerts: action.bound,
|
||||
setAuthentication: action.bound,
|
||||
setVersion: action.bound,
|
||||
},
|
||||
{ name: "API response info" }
|
||||
);
|
||||
@@ -429,9 +461,14 @@ class AlertStore {
|
||||
comment: "ACK! This alert was acknowledged using karma",
|
||||
},
|
||||
historyEnabled: true,
|
||||
} as APISettingsT,
|
||||
setValues(v: APISettingsT) {
|
||||
this.values = v;
|
||||
},
|
||||
},
|
||||
{},
|
||||
{
|
||||
setValues: action.bound,
|
||||
},
|
||||
{
|
||||
name: "Global settings",
|
||||
}
|
||||
@@ -474,6 +511,9 @@ class AlertStore {
|
||||
this.paused = true;
|
||||
this.stopped = true;
|
||||
},
|
||||
setError(e: null | string) {
|
||||
this.error = e;
|
||||
},
|
||||
},
|
||||
{
|
||||
setIdle: action,
|
||||
@@ -484,6 +524,7 @@ class AlertStore {
|
||||
resume: action.bound,
|
||||
togglePause: action.bound,
|
||||
stop: action.bound,
|
||||
setError: action.bound,
|
||||
},
|
||||
{ name: "Store status" }
|
||||
);
|
||||
@@ -532,7 +573,7 @@ class AlertStore {
|
||||
// if that request comes back as type=opaque then we might be getting
|
||||
// redirected by an auth proxy
|
||||
if (result.type === "opaque") {
|
||||
this.info.setReloadNeeded();
|
||||
this.info.setReloadNeeded(true);
|
||||
}
|
||||
this.info.clearIsRetrying();
|
||||
this.status.setProcessing();
|
||||
@@ -607,7 +648,7 @@ class AlertStore {
|
||||
this.info.version !== "unknown" &&
|
||||
this.info.version !== result.version
|
||||
) {
|
||||
this.info.upgradeReady = true;
|
||||
this.info.setUpgradeReady(true);
|
||||
this.status.stop();
|
||||
}
|
||||
// update extra root level keys that are stored under 'info'
|
||||
|
||||
@@ -138,6 +138,7 @@ class GridConfig {
|
||||
setSortOrder: (o: SortOrderT) => void;
|
||||
setSortLabel: (l: string) => void;
|
||||
setSortReverse: (v: boolean | null) => void;
|
||||
setGroupWidth: (w: number) => void;
|
||||
|
||||
constructor(groupWidth: number) {
|
||||
this.config = localStored(
|
||||
@@ -160,6 +161,9 @@ class GridConfig {
|
||||
this.setSortReverse = action((v: boolean | null) => {
|
||||
this.config.reverseSort = v;
|
||||
});
|
||||
this.setGroupWidth = action((w: number) => {
|
||||
this.config.groupWidth = w;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ describe("SilenceFormStore.toggle", () => {
|
||||
|
||||
it("hide() set 'visible' to false", () => {
|
||||
expect(store.toggle.visible).toBe(false);
|
||||
store.toggle.visible = true;
|
||||
store.toggle.show();
|
||||
expect(store.toggle.visible).toBe(true);
|
||||
store.toggle.hide();
|
||||
expect(store.toggle.visible).toBe(false);
|
||||
@@ -89,7 +89,7 @@ describe("SilenceFormStore.data", () => {
|
||||
});
|
||||
|
||||
it("resetProgress() sets currentStage to UserInput", () => {
|
||||
store.data.currentStage = "submit";
|
||||
store.data.setStage("submit");
|
||||
store.data.resetProgress();
|
||||
expect(store.data.currentStage).toBe("form");
|
||||
});
|
||||
@@ -292,7 +292,7 @@ describe("SilenceFormStore.data", () => {
|
||||
});
|
||||
|
||||
it("fillMatchersFromGroup() resets silenceID if set", () => {
|
||||
store.data.silenceID = "12345";
|
||||
store.data.setSilenceID("12345");
|
||||
const group = MockGroup();
|
||||
store.data.fillMatchersFromGroup(group, [], [], [group.alerts[0]]);
|
||||
expect(store.data.silenceID).toBeNull();
|
||||
@@ -401,14 +401,14 @@ describe("SilenceFormStore.data", () => {
|
||||
}
|
||||
|
||||
it("toAlertmanagerPayload constains id when store.data.silenceID is set", () => {
|
||||
store.data.silenceID = "12345";
|
||||
store.data.setSilenceID("12345");
|
||||
expect(store.data.toAlertmanagerPayload).toMatchObject({
|
||||
id: "12345",
|
||||
});
|
||||
});
|
||||
|
||||
it("toAlertmanagerPayload doesn't contain id when store.data.silenceID is null", () => {
|
||||
store.data.silenceID = null;
|
||||
store.data.setSilenceID(null);
|
||||
expect(store.data.toAlertmanagerPayload.id).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -437,7 +437,7 @@ describe("SilenceFormStore.data", () => {
|
||||
const b64 = store.data.toBase64;
|
||||
|
||||
store.data.setMatchers([]);
|
||||
store.data.comment = "";
|
||||
store.data.setComment("");
|
||||
|
||||
store.data.fromBase64(b64);
|
||||
expect(store.data.matchers).toMatchObject([
|
||||
@@ -550,7 +550,7 @@ describe("SilenceFormStore.data.isValid", () => {
|
||||
MockMatcher("foo", [{ label: "bar", value: "bar" }]),
|
||||
]);
|
||||
store.data.setAuthor("me@example.com");
|
||||
store.data.comment = "fake silence";
|
||||
store.data.setComment("fake silence");
|
||||
expect(store.data.isValid).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -237,13 +237,14 @@ interface SilenceFormStoreDataT {
|
||||
resetStartEnd: () => void;
|
||||
resetProgress: () => void;
|
||||
resetSilenceID: () => void;
|
||||
setSilenceID: (id: string) => void;
|
||||
setSilenceID: (id: string | null) => void;
|
||||
setAlertmanagers: (val: MultiValueOptionT[]) => void;
|
||||
setAutofillMatchers: (v: boolean) => void;
|
||||
setResetInputs: (v: boolean) => void;
|
||||
setStageSubmit: () => void;
|
||||
setStage: (val: SilenceFormStageT) => void;
|
||||
setMatchers: (m: MatcherWithIDT[]) => void;
|
||||
addEmptyMatcher: () => void;
|
||||
addMatcherWithID: (m: MatcherWithIDT) => void;
|
||||
deleteMatcher: (id: string) => void;
|
||||
fillMatchersFromGroup: (
|
||||
group: APIAlertGroupT,
|
||||
@@ -265,6 +266,11 @@ interface SilenceFormStoreDataT {
|
||||
incEnd: (minutes: number) => void;
|
||||
decEnd: (minutes: number) => void;
|
||||
setWasValidated: (v: boolean) => void;
|
||||
setRequestsByCluster: (val: { [key: string]: ClusterRequestT }) => void;
|
||||
setRequestsByClusterUpdate: (
|
||||
key: string,
|
||||
v: Partial<ClusterRequestT>
|
||||
) => void;
|
||||
readonly toAlertmanagerPayload: AlertmanagerSilencePayloadT;
|
||||
readonly toDuration: DurationT;
|
||||
}
|
||||
@@ -411,7 +417,7 @@ class SilenceFormStore {
|
||||
this.silenceID = null;
|
||||
},
|
||||
|
||||
setSilenceID(id: string) {
|
||||
setSilenceID(id: string | null) {
|
||||
this.silenceID = id;
|
||||
},
|
||||
|
||||
@@ -426,8 +432,8 @@ class SilenceFormStore {
|
||||
this.resetInputs = v;
|
||||
},
|
||||
|
||||
setStageSubmit() {
|
||||
this.currentStage = "submit";
|
||||
setStage(val: SilenceFormStageT) {
|
||||
this.currentStage = val;
|
||||
},
|
||||
|
||||
setMatchers(m: MatcherWithIDT[]) {
|
||||
@@ -438,6 +444,9 @@ class SilenceFormStore {
|
||||
addEmptyMatcher() {
|
||||
this.matchers.push(NewEmptyMatcher());
|
||||
},
|
||||
addMatcherWithID(m: MatcherWithIDT) {
|
||||
this.matchers.push(m);
|
||||
},
|
||||
|
||||
deleteMatcher(id: string) {
|
||||
// only delete matchers if we have more than 1
|
||||
@@ -541,6 +550,16 @@ class SilenceFormStore {
|
||||
this.wasValidated = v;
|
||||
},
|
||||
|
||||
setRequestsByCluster(val: { [key: string]: ClusterRequestT }) {
|
||||
this.requestsByCluster = val;
|
||||
},
|
||||
setRequestsByClusterUpdate(key: string, v: Partial<ClusterRequestT>) {
|
||||
this.requestsByCluster[key] = {
|
||||
...this.requestsByCluster[key],
|
||||
...v,
|
||||
};
|
||||
},
|
||||
|
||||
get toAlertmanagerPayload() {
|
||||
const startsAt = new Date(this.startsAt);
|
||||
startsAt.setSeconds(0);
|
||||
@@ -577,9 +596,10 @@ class SilenceFormStore {
|
||||
setAlertmanagers: action.bound,
|
||||
setAutofillMatchers: action.bound,
|
||||
setResetInputs: action.bound,
|
||||
setStageSubmit: action.bound,
|
||||
setStage: action.bound,
|
||||
setMatchers: action.bound,
|
||||
addEmptyMatcher: action.bound,
|
||||
addMatcherWithID: action.bound,
|
||||
deleteMatcher: action.bound,
|
||||
fillMatchersFromGroup: action.bound,
|
||||
fillFormFromSilence: action.bound,
|
||||
@@ -594,6 +614,8 @@ class SilenceFormStore {
|
||||
decEnd: action.bound,
|
||||
isValid: computed,
|
||||
setWasValidated: action.bound,
|
||||
setRequestsByCluster: action.bound,
|
||||
setRequestsByClusterUpdate: action.bound,
|
||||
toAlertmanagerPayload: computed,
|
||||
toDuration: computed,
|
||||
},
|
||||
|
||||
@@ -81,16 +81,23 @@ const MockGroup = (
|
||||
};
|
||||
|
||||
const MockGrid = (alertStore: AlertStore): void => {
|
||||
alertStore.settings.values.alertAcknowledgement.enabled = true;
|
||||
alertStore.data.receivers = ["by-cluster-service", "by-name"];
|
||||
alertStore.settings.setValues({
|
||||
...alertStore.settings.values,
|
||||
...{
|
||||
alertAcknowledgement: {
|
||||
...alertStore.settings.values.alertAcknowledgement,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
alertStore.data.setReceivers(["by-cluster-service", "by-name"]);
|
||||
|
||||
const silence = MockSilence();
|
||||
silence.startsAt = "2018-08-14T12:00:00Z";
|
||||
silence.endsAt = "2018-08-14T18:00:00Z";
|
||||
alertStore.data.silences = { prod: {} };
|
||||
alertStore.data.silences["prod"][silence.id] = silence;
|
||||
alertStore.data.setSilences({ prod: { [silence.id]: silence } });
|
||||
|
||||
alertStore.data.colors = {
|
||||
alertStore.data.setColors({
|
||||
group: {
|
||||
group1: {
|
||||
brightness: 50,
|
||||
@@ -123,9 +130,9 @@ const MockGrid = (alertStore: AlertStore): void => {
|
||||
background: "rgba(66,250,123,255)",
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
alertStore.data.counters = [
|
||||
alertStore.data.setCounters([
|
||||
{
|
||||
name: "@receiver",
|
||||
hits: 2,
|
||||
@@ -193,7 +200,7 @@ const MockGrid = (alertStore: AlertStore): void => {
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
const groups = [];
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
|
||||
@@ -8,11 +8,20 @@ import { useInView } from "react-intersection-observer";
|
||||
// react-idle-timer >= 4.6.0
|
||||
import "regenerator-runtime/runtime";
|
||||
|
||||
import { configure } from "mobx";
|
||||
|
||||
import { FetchRetryConfig } from "Common/Fetch";
|
||||
|
||||
import { useFetchGetMock } from "__fixtures__/useFetchGet";
|
||||
import { useFetchGet } from "Hooks/useFetchGet";
|
||||
|
||||
configure({
|
||||
enforceActions: "always",
|
||||
//computedRequiresReaction: true,
|
||||
//reactionRequiresObservable: true,
|
||||
//observableRequiresReaction: true,
|
||||
});
|
||||
|
||||
jest.mock("Hooks/useFetchGet");
|
||||
|
||||
jest.mock("react-intersection-observer");
|
||||
|
||||
Reference in New Issue
Block a user