mirror of
https://github.com/prymitive/karma
synced 2026-05-07 03:26:52 +00:00
Merge pull request #90 from prymitive/silence-form-validation
Silence form validation
This commit is contained in:
12
ui/src/Components/MultiSelect/ValidationError.js
Normal file
12
ui/src/Components/MultiSelect/ValidationError.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from "react";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons/faExclamationCircle";
|
||||
|
||||
const ValidationError = () => (
|
||||
<span className="text-danger">
|
||||
<FontAwesomeIcon icon={faExclamationCircle} /> Required
|
||||
</span>
|
||||
);
|
||||
|
||||
export { ValidationError };
|
||||
14
ui/src/Components/MultiSelect/ValidationError.test.js
Normal file
14
ui/src/Components/MultiSelect/ValidationError.test.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from "react";
|
||||
|
||||
import { shallow } from "enzyme";
|
||||
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { ValidationError } from "./ValidationError";
|
||||
|
||||
describe("<ValidationError />", () => {
|
||||
it("matches snapshot", () => {
|
||||
const tree = shallow(<ValidationError />);
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<ValidationError /> matches snapshot 1`] = `
|
||||
"
|
||||
<span class=\\"text-danger\\">
|
||||
<svg aria-hidden=\\"true\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"exclamation-circle\\"
|
||||
class=\\"svg-inline--fa fa-exclamation-circle fa-w-16 \\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 512 512\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
Required
|
||||
</span>
|
||||
"
|
||||
`;
|
||||
@@ -7,6 +7,7 @@ import { observer } from "mobx-react";
|
||||
import ReactSelect from "react-select";
|
||||
|
||||
import { MultiSelect, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { ValidationError } from "Components/MultiSelect/ValidationError";
|
||||
|
||||
const AlertmanagerInstancesToOptions = instances =>
|
||||
instances.map(i => ({
|
||||
@@ -75,7 +76,13 @@ const AlertManagerInput = observer(
|
||||
options={AlertmanagerInstancesToOptions(
|
||||
alertStore.data.upstreams.instances
|
||||
)}
|
||||
placeholder="Alertmanager"
|
||||
placeholder={
|
||||
silenceFormStore.data.wasValidated ? (
|
||||
<ValidationError />
|
||||
) : (
|
||||
"Alertmanager"
|
||||
)
|
||||
}
|
||||
isMulti
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
|
||||
@@ -60,6 +60,21 @@ describe("<AlertManagerInput />", () => {
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("doesn't render ValidationError after passed validation", () => {
|
||||
const tree = ShallowAlertManagerInput();
|
||||
silenceFormStore.data.wasValidated = true;
|
||||
expect(tree.html()).not.toMatch(/fa-exclamation-circle/);
|
||||
expect(tree.html()).not.toMatch(/Required/);
|
||||
});
|
||||
|
||||
it("renders ValidationError after failed validation", () => {
|
||||
const tree = ShallowAlertManagerInput();
|
||||
silenceFormStore.data.alertmanagers = [];
|
||||
silenceFormStore.data.wasValidated = true;
|
||||
expect(tree.html()).toMatch(/fa-exclamation-circle/);
|
||||
expect(tree.html()).toMatch(/Required/);
|
||||
});
|
||||
|
||||
it("all available Alertmanager instances are selected by default", () => {
|
||||
ShallowAlertManagerInput();
|
||||
expect(silenceFormStore.data.alertmanagers).toHaveLength(3);
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { action } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { MultiSelect } from "Components/MultiSelect";
|
||||
import { ValidationError } from "Components/MultiSelect/ValidationError";
|
||||
import { FormatUnseeBackendURI } from "Stores/AlertStore";
|
||||
|
||||
const LabelNameInput = observer(
|
||||
class LabelNameInput extends MultiSelect {
|
||||
static propTypes = {
|
||||
matcher: PropTypes.object.isRequired
|
||||
matcher: PropTypes.object.isRequired,
|
||||
isValid: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
populateNameSuggestions = action(() => {
|
||||
@@ -70,7 +73,7 @@ const LabelNameInput = observer(
|
||||
}
|
||||
|
||||
renderProps = () => {
|
||||
const { matcher } = this.props;
|
||||
const { matcher, isValid } = this.props;
|
||||
|
||||
const value = matcher.name
|
||||
? { label: matcher.name, value: matcher.name }
|
||||
@@ -80,7 +83,7 @@ const LabelNameInput = observer(
|
||||
instanceId: `silence-input-label-name-${matcher.id}`,
|
||||
defaultValue: value,
|
||||
options: matcher.suggestions.names,
|
||||
placeholder: "Label name",
|
||||
placeholder: isValid ? "Label name" : <ValidationError />,
|
||||
onChange: this.onChange
|
||||
};
|
||||
};
|
||||
|
||||
@@ -20,16 +20,16 @@ beforeEach(() => {
|
||||
];
|
||||
});
|
||||
|
||||
const ShallowLabelNameInput = () => {
|
||||
return shallow(<LabelNameInput matcher={matcher} />);
|
||||
const ShallowLabelNameInput = isValid => {
|
||||
return shallow(<LabelNameInput matcher={matcher} isValid={isValid} />);
|
||||
};
|
||||
|
||||
const MountedLabelNameInput = () => {
|
||||
return mount(<LabelNameInput matcher={matcher} />);
|
||||
const MountedLabelNameInput = isValid => {
|
||||
return mount(<LabelNameInput matcher={matcher} isValid={isValid} />);
|
||||
};
|
||||
|
||||
const ValidateSuggestions = () => {
|
||||
const tree = MountedLabelNameInput();
|
||||
const tree = MountedLabelNameInput(true);
|
||||
// click on the react-select component doesn't seem to trigger options
|
||||
// rendering in tests, so change the input instead
|
||||
tree.find("input").simulate("change", { target: { value: "f" } });
|
||||
@@ -38,10 +38,28 @@ const ValidateSuggestions = () => {
|
||||
|
||||
describe("<LabelNameInput />", () => {
|
||||
it("matches snapshot", () => {
|
||||
const tree = ShallowLabelNameInput();
|
||||
const tree = ShallowLabelNameInput(true);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("doesn't renders ValidationError after passed validation", () => {
|
||||
// clear the name so placeholder is rendered
|
||||
matcher.name = "";
|
||||
const tree = ShallowLabelNameInput(true);
|
||||
expect(tree.html()).toMatch(/Label name/);
|
||||
expect(tree.html()).not.toMatch(/fa-exclamation-circle/);
|
||||
expect(tree.html()).not.toMatch(/Required/);
|
||||
});
|
||||
|
||||
it("renders ValidationError after failed validation", () => {
|
||||
// clear the name so placeholder is rendered
|
||||
matcher.name = "";
|
||||
const tree = ShallowLabelNameInput(false);
|
||||
expect(tree.html()).not.toMatch(/Label name/);
|
||||
expect(tree.html()).toMatch(/fa-exclamation-circle/);
|
||||
expect(tree.html()).toMatch(/Required/);
|
||||
});
|
||||
|
||||
it("renders suggestions", () => {
|
||||
const tree = ValidateSuggestions();
|
||||
const options = tree.find("[role='option']");
|
||||
@@ -61,7 +79,7 @@ describe("<LabelNameInput />", () => {
|
||||
fetch
|
||||
.once(JSON.stringify(["name1", "name2", "name3"]))
|
||||
.once(JSON.stringify(["value1", "value2", "value3"]));
|
||||
ShallowLabelNameInput();
|
||||
ShallowLabelNameInput(true);
|
||||
// use timeout since mount will call fetch
|
||||
setTimeout(() => {
|
||||
expect(matcher.suggestions.names).toHaveLength(3);
|
||||
@@ -79,7 +97,7 @@ describe("<LabelNameInput />", () => {
|
||||
|
||||
it("suggestions are emptied on failed fetch", done => {
|
||||
fetch.mockReject(new Error("fake error message"));
|
||||
ShallowLabelNameInput();
|
||||
ShallowLabelNameInput(true);
|
||||
// use timeout since mount will call fetch
|
||||
setTimeout(() => {
|
||||
expect(matcher.suggestions.names).toHaveLength(0);
|
||||
@@ -88,7 +106,7 @@ describe("<LabelNameInput />", () => {
|
||||
});
|
||||
|
||||
it("doesn't fetch suggestions if value is changed to empty string", () => {
|
||||
const tree = MountedLabelNameInput();
|
||||
const tree = MountedLabelNameInput(true);
|
||||
const instance = tree.instance();
|
||||
const fetchSpy = jest.spyOn(instance, "populateValueSuggestions");
|
||||
instance.onChange("");
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { action } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { MultiSelect } from "Components/MultiSelect";
|
||||
import { ValidationError } from "Components/MultiSelect/ValidationError";
|
||||
|
||||
const LabelValueInput = observer(
|
||||
class LabelValueInput extends MultiSelect {
|
||||
static propTypes = {
|
||||
matcher: PropTypes.object.isRequired
|
||||
matcher: PropTypes.object.isRequired,
|
||||
isValid: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
onChange = action((newValue, actionMeta) => {
|
||||
@@ -25,13 +28,13 @@ const LabelValueInput = observer(
|
||||
});
|
||||
|
||||
renderProps = () => {
|
||||
const { matcher } = this.props;
|
||||
const { matcher, isValid } = this.props;
|
||||
|
||||
return {
|
||||
instanceId: `silence-input-label-value-${matcher.id}`,
|
||||
defaultValue: matcher.values,
|
||||
options: matcher.suggestions.values,
|
||||
placeholder: "Label value",
|
||||
placeholder: isValid ? "Label value" : <ValidationError />,
|
||||
isMulti: true,
|
||||
onChange: this.onChange
|
||||
};
|
||||
|
||||
@@ -20,16 +20,16 @@ beforeEach(() => {
|
||||
];
|
||||
});
|
||||
|
||||
const ShallowLabelValueInput = () => {
|
||||
return shallow(<LabelValueInput matcher={matcher} />);
|
||||
const ShallowLabelValueInput = isValid => {
|
||||
return shallow(<LabelValueInput matcher={matcher} isValid={isValid} />);
|
||||
};
|
||||
|
||||
const MountedLabelValueInput = () => {
|
||||
return mount(<LabelValueInput matcher={matcher} />);
|
||||
const MountedLabelValueInput = isValid => {
|
||||
return mount(<LabelValueInput matcher={matcher} isValid={isValid} />);
|
||||
};
|
||||
|
||||
const ValidateSuggestions = () => {
|
||||
const tree = MountedLabelValueInput();
|
||||
const tree = MountedLabelValueInput(true);
|
||||
// click on the react-select component doesn't seem to trigger options
|
||||
// rendering in tests, so change the input instead
|
||||
tree.find("input").simulate("change", { target: { value: "f" } });
|
||||
@@ -38,10 +38,22 @@ const ValidateSuggestions = () => {
|
||||
|
||||
describe("<LabelValueInput />", () => {
|
||||
it("matches snapshot", () => {
|
||||
const tree = ShallowLabelValueInput();
|
||||
const tree = ShallowLabelValueInput(true);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("doesn't renders ValidationError after passed validation", () => {
|
||||
const tree = ShallowLabelValueInput(true);
|
||||
expect(tree.html()).not.toMatch(/fa-exclamation-circle/);
|
||||
expect(tree.html()).not.toMatch(/Required/);
|
||||
});
|
||||
|
||||
it("renders ValidationError after failed validation", () => {
|
||||
const tree = ShallowLabelValueInput(false);
|
||||
expect(tree.html()).toMatch(/fa-exclamation-circle/);
|
||||
expect(tree.html()).toMatch(/Required/);
|
||||
});
|
||||
|
||||
it("renders suggestions", () => {
|
||||
const tree = ValidateSuggestions();
|
||||
const options = tree.find("[role='option']");
|
||||
|
||||
@@ -102,7 +102,10 @@ const SilenceForm = observer(
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
silenceFormStore.data.inProgress = true;
|
||||
if (silenceFormStore.data.isValid)
|
||||
silenceFormStore.data.inProgress = true;
|
||||
|
||||
silenceFormStore.data.wasValidated = true;
|
||||
});
|
||||
|
||||
render() {
|
||||
@@ -124,6 +127,7 @@ const SilenceForm = observer(
|
||||
silenceFormStore.data.deleteMatcher(matcher.id);
|
||||
}}
|
||||
showDelete={silenceFormStore.data.matchers.length > 1}
|
||||
isValid={!silenceFormStore.data.wasValidated}
|
||||
/>
|
||||
))}
|
||||
<button
|
||||
|
||||
@@ -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("<SilenceForm /> inputs", () => {
|
||||
});
|
||||
|
||||
describe("<SilenceForm />", () => {
|
||||
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);
|
||||
|
||||
@@ -20,7 +20,8 @@ const SilenceMatch = observer(
|
||||
isRegex: PropTypes.bool.isRequired
|
||||
}),
|
||||
showDelete: PropTypes.bool.isRequired,
|
||||
onDelete: PropTypes.func.isRequired
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
isValid: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
onIsRegexChange = action(event => {
|
||||
@@ -33,15 +34,15 @@ const SilenceMatch = observer(
|
||||
});
|
||||
|
||||
render() {
|
||||
const { matcher, showDelete, onDelete } = this.props;
|
||||
const { matcher, showDelete, onDelete, isValid } = this.props;
|
||||
|
||||
return (
|
||||
<div className="d-flex flex-fill flex-lg-row flex-column mb-3">
|
||||
<div className="flex-shrink-0 flex-grow-0 flex-basis-25 pr-lg-2 pb-2 pb-lg-0">
|
||||
<LabelNameInput matcher={matcher} />
|
||||
<LabelNameInput matcher={matcher} isValid={isValid} />
|
||||
</div>
|
||||
<div className="flex-shrink-0 flex-grow-0 flex-basis-50 pr-lg-2 pb-2 pb-lg-0">
|
||||
<LabelValueInput matcher={matcher} />
|
||||
<LabelValueInput matcher={matcher} isValid={isValid} />
|
||||
</div>
|
||||
<div className="flex-shrink-0 flex-grow-1 flex-basis-auto form-check form-check-inline d-flex justify-content-between m-0">
|
||||
<span>
|
||||
|
||||
@@ -2,17 +2,25 @@ import React from "react";
|
||||
|
||||
import { shallow } from "enzyme";
|
||||
|
||||
import { NewEmptyMatcher, MatcherValueToObject } from "Stores/SilenceFormStore";
|
||||
import {
|
||||
SilenceFormStore,
|
||||
NewEmptyMatcher,
|
||||
MatcherValueToObject
|
||||
} from "Stores/SilenceFormStore";
|
||||
import { SilenceMatch } from "./SilenceMatch";
|
||||
|
||||
let silenceFormStore;
|
||||
let matcher;
|
||||
|
||||
beforeEach(() => {
|
||||
silenceFormStore = new SilenceFormStore();
|
||||
matcher = NewEmptyMatcher();
|
||||
});
|
||||
|
||||
const ShallowLabelValueInput = () => {
|
||||
return shallow(<SilenceMatch matcher={matcher} />);
|
||||
return shallow(
|
||||
<SilenceMatch matcher={matcher} silenceFormStore={silenceFormStore} />
|
||||
);
|
||||
};
|
||||
|
||||
describe("<SilenceMatch />", () => {
|
||||
|
||||
@@ -44,6 +44,7 @@ class SilenceFormStore {
|
||||
data = observable(
|
||||
{
|
||||
inProgress: false,
|
||||
wasValidated: false,
|
||||
alertmanagers: [],
|
||||
matchers: [],
|
||||
startsAt: moment(),
|
||||
@@ -51,8 +52,26 @@ 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;
|
||||
this.wasValidated = false;
|
||||
},
|
||||
|
||||
// append a new empty matcher to the list
|
||||
@@ -181,6 +200,7 @@ class SilenceFormStore {
|
||||
decStart: action.bound,
|
||||
incEnd: action.bound,
|
||||
decEnd: action.bound,
|
||||
isValid: computed,
|
||||
toAlertmanagerPayload: computed,
|
||||
toDuration: computed
|
||||
},
|
||||
|
||||
@@ -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(() => {
|
||||
@@ -52,6 +52,13 @@ describe("SilenceFormStore.data", () => {
|
||||
expect(store.data.inProgress).toBe(false);
|
||||
});
|
||||
|
||||
it("resetProgress() sets 'wasValidated' to false", () => {
|
||||
store.data.wasValidated = true;
|
||||
expect(store.data.wasValidated).toBe(true);
|
||||
store.data.resetProgress();
|
||||
expect(store.data.wasValidated).toBe(false);
|
||||
});
|
||||
|
||||
it("addEmptyMatcher() appends a matcher", () => {
|
||||
expect(store.data.matchers).toHaveLength(0);
|
||||
store.data.addEmptyMatcher();
|
||||
@@ -129,6 +136,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]);
|
||||
|
||||
Reference in New Issue
Block a user