From 3cb3ed6990a2e59872a78fe580c6808afd08f67d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Tue, 4 Sep 2018 13:35:35 +0100 Subject: [PATCH] feat(ui): validate silence form data before submiting --- ui/src/Components/SilenceModal/SilenceForm.js | 3 +- .../SilenceModal/SilenceForm.test.js | 19 ++++- ui/src/Stores/SilenceFormStore.js | 18 +++++ ui/src/Stores/SilenceFormStore.test.js | 79 ++++++++++++++++++- 4 files changed, 115 insertions(+), 4 deletions(-) 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]);