diff --git a/ui/src/Components/SilenceModal/SilenceForm.js b/ui/src/Components/SilenceModal/SilenceForm.js
index fa231932d..7215d62e4 100644
--- a/ui/src/Components/SilenceModal/SilenceForm.js
+++ b/ui/src/Components/SilenceModal/SilenceForm.js
@@ -102,7 +102,8 @@ const SilenceForm = observer(
event.preventDefault();
- silenceFormStore.data.inProgress = true;
+ if (silenceFormStore.data.isValid)
+ silenceFormStore.data.inProgress = true;
});
render() {
diff --git a/ui/src/Components/SilenceModal/SilenceForm.test.js b/ui/src/Components/SilenceModal/SilenceForm.test.js
index f0d189c80..538c30756 100644
--- a/ui/src/Components/SilenceModal/SilenceForm.test.js
+++ b/ui/src/Components/SilenceModal/SilenceForm.test.js
@@ -3,7 +3,7 @@ import React from "react";
import { mount, shallow } from "enzyme";
import { AlertStore } from "Stores/AlertStore";
-import { SilenceFormStore } from "Stores/SilenceFormStore";
+import { SilenceFormStore, NewEmptyMatcher } from "Stores/SilenceFormStore";
import { SilenceForm } from "./SilenceForm";
let alertStore;
@@ -121,7 +121,22 @@ describe(" inputs", () => {
});
describe("", () => {
- it("calling submit marks form as in progress", () => {
+ it("calling submit doesn't mark form as in progress when form is invalid", () => {
+ const tree = ShallowSilenceForm();
+ tree.simulate("submit", { preventDefault: jest.fn() });
+ expect(silenceFormStore.data.inProgress).toBe(false);
+ });
+
+ it("calling submit marks form as in progress when form is valid", () => {
+ const matcher = NewEmptyMatcher();
+ matcher.name = "job";
+ matcher.values = ["node_exporter"];
+ silenceFormStore.data.matchers = [matcher];
+ silenceFormStore.data.alertmanagers = [
+ { label: "am1", value: "http://example.com" }
+ ];
+ silenceFormStore.data.author = "me@example.com";
+ silenceFormStore.data.comment = "fake silence";
const tree = ShallowSilenceForm();
tree.simulate("submit", { preventDefault: jest.fn() });
expect(silenceFormStore.data.inProgress).toBe(true);
diff --git a/ui/src/Stores/SilenceFormStore.js b/ui/src/Stores/SilenceFormStore.js
index 8ef4d3a31..0d42ac3b6 100644
--- a/ui/src/Stores/SilenceFormStore.js
+++ b/ui/src/Stores/SilenceFormStore.js
@@ -51,6 +51,23 @@ class SilenceFormStore {
comment: "",
author: "",
+ get isValid() {
+ if (this.alertmanagers.length === 0) return false;
+ if (this.matchers.length === 0) return false;
+ if (
+ this.matchers.filter(
+ m =>
+ m.name === "" ||
+ m.values.length === 0 ||
+ m.values.filter(v => v === "").length > 0
+ ).length > 0
+ )
+ return false;
+ if (this.comment === "") return false;
+ if (this.author === "") return false;
+ return true;
+ },
+
resetProgress() {
this.inProgress = false;
},
@@ -181,6 +198,7 @@ class SilenceFormStore {
decStart: action.bound,
incEnd: action.bound,
decEnd: action.bound,
+ isValid: computed,
toAlertmanagerPayload: computed,
toDuration: computed
},
diff --git a/ui/src/Stores/SilenceFormStore.test.js b/ui/src/Stores/SilenceFormStore.test.js
index 8568e3389..ea690a1d9 100644
--- a/ui/src/Stores/SilenceFormStore.test.js
+++ b/ui/src/Stores/SilenceFormStore.test.js
@@ -1,7 +1,7 @@
import moment from "moment";
import { MockAlert, MockAlertGroup } from "__mocks__/Alerts.js";
-import { SilenceFormStore } from "./SilenceFormStore";
+import { SilenceFormStore, NewEmptyMatcher } from "./SilenceFormStore";
let store;
beforeEach(() => {
@@ -129,6 +129,83 @@ describe("SilenceFormStore.data", () => {
});
});
+const MockAlertmanager = () => ({
+ label: "default",
+ value: "http://localhost"
+});
+
+const MockMatcher = (name, values) => {
+ const matcher = NewEmptyMatcher();
+ matcher.name = name;
+ matcher.values = values;
+ return matcher;
+};
+
+describe("SilenceFormStore.data.isValid", () => {
+ it("isValid returns 'false' if alertmanagers list is empty", () => {
+ store.data.matchers = [MockMatcher("foo", ["bar"])];
+ store.data.author = "me@example.com";
+ store.data.comment = "fake silence";
+ expect(store.data.isValid).toBe(false);
+ });
+
+ it("isValid returns 'false' if matchers list is empty", () => {
+ store.data.alertmanagers = [MockAlertmanager];
+ store.data.matchers = [];
+ store.data.author = "me@example.com";
+ store.data.comment = "fake silence";
+ expect(store.data.isValid).toBe(false);
+ });
+
+ it("isValid returns 'false' if matchers list is pupulated when a matcher without any name", () => {
+ store.data.alertmanagers = [MockAlertmanager];
+ store.data.matchers = [MockMatcher("", ["bar"])];
+ store.data.author = "me@example.com";
+ store.data.comment = "fake silence";
+ expect(store.data.isValid).toBe(false);
+ });
+
+ it("isValid returns 'false' if matchers list is pupulated when a matcher without any value ([])", () => {
+ store.data.alertmanagers = [MockAlertmanager];
+ store.data.matchers = [MockMatcher("foo", [])];
+ store.data.author = "me@example.com";
+ store.data.comment = "fake silence";
+ expect(store.data.isValid).toBe(false);
+ });
+
+ it("isValid returns 'false' if matchers list is pupulated when a matcher with empty value ([''])", () => {
+ store.data.alertmanagers = [MockAlertmanager];
+ store.data.matchers = [MockMatcher("foo", [])];
+ store.data.author = "me@example.com";
+ store.data.comment = "fake silence";
+ expect(store.data.isValid).toBe(false);
+ });
+
+ it("isValid returns 'false' if author is empty", () => {
+ store.data.alertmanagers = [MockAlertmanager];
+ store.data.matchers = [MockMatcher("foo", ["bar"])];
+ store.data.author = "";
+ store.data.comment = "fake silence";
+ expect(store.data.isValid).toBe(false);
+ });
+
+ it("isValid returns 'false' if comment is empty", () => {
+ store.data.alertmanagers = [MockAlertmanager];
+ store.data.matchers = [MockMatcher("foo", ["bar"])];
+ store.data.author = "me@example.com";
+ store.data.comment = "";
+ expect(store.data.isValid).toBe(false);
+ });
+
+ it("isValid returns 'true' if all fileds are set", () => {
+ store.data.alertmanagers = [MockAlertmanager];
+ store.data.matchers = [MockMatcher("foo", ["bar"])];
+ store.data.author = "me@example.com";
+ store.data.comment = "fake silence";
+ expect(store.data.isValid).toBe(true);
+ });
+});
+
describe("SilenceFormStore.data startsAt & endsAt validation", () => {
it("toDuration returns correct duration for 5d 0h 1m", () => {
store.data.startsAt = moment([2000, 1, 1, 0, 0, 0]);