{
describe("", () => {
it("doesn't render anything when alertStore.info.upgradeNeeded=true", () => {
- alertStore.info.upgradeNeeded = true;
+ alertStore.info.setUpgradeNeeded(true);
const tree = mount();
expect(tree.html()).toBe("");
});
@@ -128,8 +128,8 @@ describe("", () => {
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();
expect(tree.find("div.bg-toast")).toHaveLength(2);
expect(tree.find("span.badge.cursor-pointer.with-click")).toHaveLength(2);
@@ -143,7 +143,7 @@ describe("", () => {
});
it("renders UpgradeToastMessage when alertStore.info.upgradeReady=true", () => {
- alertStore.info.upgradeReady = true;
+ alertStore.info.setUpgradeReady(true);
const tree = mount();
expect(tree.find("UpgradeToastMessage")).toHaveLength(1);
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
diff --git a/ui/src/Components/Toast/ToastMessages.test.tsx b/ui/src/Components/Toast/ToastMessages.test.tsx
index 4360defb9..326087911 100644
--- a/ui/src/Components/Toast/ToastMessages.test.tsx
+++ b/ui/src/Components/Toast/ToastMessages.test.tsx
@@ -9,7 +9,7 @@ let alertStore: AlertStore;
beforeEach(() => {
alertStore = new AlertStore([]);
- alertStore.info.version = "1.2.3";
+ alertStore.info.setVersion("1.2.3");
});
describe("", () => {
@@ -46,4 +46,10 @@ describe("", () => {
tree.find("button").simulate("click");
expect(alertStore.info.upgradeNeeded).toBe(true);
});
+
+ it("upgradeNeeded=true after onAnimationEnd is called", () => {
+ const tree = mount();
+ tree.find("div.toast-upgrade-progressbar").simulate("animationend");
+ expect(alertStore.info.upgradeNeeded).toBe(true);
+ });
});
diff --git a/ui/src/Components/Toast/ToastMessages.tsx b/ui/src/Components/Toast/ToastMessages.tsx
index fe854df43..45dfddbf5 100644
--- a/ui/src/Components/Toast/ToastMessages.tsx
+++ b/ui/src/Components/Toast/ToastMessages.tsx
@@ -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 }}
>
diff --git a/ui/src/Components/Toast/index.stories.tsx b/ui/src/Components/Toast/index.stories.tsx
index 8185d2acb..695293c57 100644
--- a/ui/src/Components/Toast/index.stories.tsx
+++ b/ui/src/Components/Toast/index.stories.tsx
@@ -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 (
diff --git a/ui/src/Stores/AlertStore.test.ts b/ui/src/Stores/AlertStore.test.ts
index d85d020cd..6ce4f9417 100644
--- a/ui/src/Stores/AlertStore.test.ts
+++ b/ui/src/Stores/AlertStore.test.ts
@@ -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);
diff --git a/ui/src/Stores/AlertStore.ts b/ui/src/Stores/AlertStore.ts
index 3acafadc6..7da765940 100644
--- a/ui/src/Stores/AlertStore.ts
+++ b/ui/src/Stores/AlertStore.ts
@@ -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'
diff --git a/ui/src/Stores/Settings.ts b/ui/src/Stores/Settings.ts
index 28a7e8f61..cdc8d6617 100644
--- a/ui/src/Stores/Settings.ts
+++ b/ui/src/Stores/Settings.ts
@@ -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;
+ });
}
}
diff --git a/ui/src/Stores/SilenceFormStore.test.ts b/ui/src/Stores/SilenceFormStore.test.ts
index a657b19f8..bf28526cd 100644
--- a/ui/src/Stores/SilenceFormStore.test.ts
+++ b/ui/src/Stores/SilenceFormStore.test.ts
@@ -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);
});
});
diff --git a/ui/src/Stores/SilenceFormStore.ts b/ui/src/Stores/SilenceFormStore.ts
index a6dbbe962..e8fbb09c5 100644
--- a/ui/src/Stores/SilenceFormStore.ts
+++ b/ui/src/Stores/SilenceFormStore.ts
@@ -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
+ ) => 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) {
+ 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,
},
diff --git a/ui/src/__fixtures__/Stories.ts b/ui/src/__fixtures__/Stories.ts
index c565fdf21..f3a186349 100644
--- a/ui/src/__fixtures__/Stories.ts
+++ b/ui/src/__fixtures__/Stories.ts
@@ -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++) {
diff --git a/ui/src/setupTests.ts b/ui/src/setupTests.ts
index ff93747b1..0671f84ed 100644
--- a/ui/src/setupTests.ts
+++ b/ui/src/setupTests.ts
@@ -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");