diff --git a/ui/src/Components/SilenceModal/AlertManagerInput.test.js b/ui/src/Components/SilenceModal/AlertManagerInput.test.js
new file mode 100644
index 000000000..1e7257fb9
--- /dev/null
+++ b/ui/src/Components/SilenceModal/AlertManagerInput.test.js
@@ -0,0 +1,110 @@
+import React from "react";
+
+import { shallow, mount } from "enzyme";
+
+import { AlertStore } from "Stores/AlertStore";
+import { SilenceFormStore } from "Stores/SilenceFormStore";
+import { AlertManagerInput } from "./AlertManagerInput";
+
+let alertStore;
+let silenceFormStore;
+
+const AlertmanagerOption = index => ({
+ label: `am${index}`,
+ value: `http://am${index}.example.com`
+});
+
+beforeEach(() => {
+ alertStore = new AlertStore([]);
+ alertStore.data.upstreams.instances = [
+ { name: "am1", uri: "http://am1.example.com", error: "" },
+ { name: "am2", uri: "http://am2.example.com", error: "" },
+ { name: "am3", uri: "http://am3.example.com", error: "" }
+ ];
+ silenceFormStore = new SilenceFormStore();
+});
+
+const ShallowAlertManagerInput = () => {
+ return shallow(
+
+ );
+};
+
+const MountedAlertManagerInput = () => {
+ return mount(
+
+ );
+};
+
+const ValidateSuggestions = () => {
+ const tree = MountedAlertManagerInput();
+ // clear all selected instances, they are selected by default
+ const clear = tree.find("ClearIndicator");
+ // https://github.com/JedWatson/react-select/blob/c22d296d50917e210836fb011ae3e565895e6440/src/__tests__/Select.test.js#L1873
+ clear.simulate("mousedown", { button: 0 });
+ // 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: "am" } });
+ return tree;
+};
+
+describe("", () => {
+ it("matches snapshot", () => {
+ const tree = ShallowAlertManagerInput();
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("all available Alertmanager instances are selected by default", () => {
+ ShallowAlertManagerInput();
+ expect(silenceFormStore.data.alertmanagers).toHaveLength(3);
+ for (let i = 1; i <= 3; i++) {
+ expect(silenceFormStore.data.alertmanagers).toContainEqual(
+ AlertmanagerOption(i)
+ );
+ }
+ });
+
+ it("renders all 3 suggestions", () => {
+ const tree = ValidateSuggestions();
+ const options = tree.find("[role='option']");
+ expect(options).toHaveLength(3);
+ expect(options.at(0).text()).toBe("am1");
+ expect(options.at(1).text()).toBe("am2");
+ expect(options.at(2).text()).toBe("am3");
+ });
+
+ it("clicking on options appends them to silenceFormStore.data.alertmanagers", () => {
+ const tree = ValidateSuggestions();
+ const options = tree.find("[role='option']");
+ options.at(0).simulate("click");
+ options.at(2).simulate("click");
+ expect(silenceFormStore.data.alertmanagers).toHaveLength(2);
+ expect(silenceFormStore.data.alertmanagers).toContainEqual(
+ AlertmanagerOption(1)
+ );
+ expect(silenceFormStore.data.alertmanagers).toContainEqual(
+ AlertmanagerOption(3)
+ );
+ });
+
+ it("silenceFormStore.data.alertmanagers gets updated from alertStore.data.upstreams.instances on mismatch", () => {
+ const tree = ShallowAlertManagerInput();
+ alertStore.data.upstreams.instances[0] = {
+ name: "am1",
+ uri: "http://am1.example.com/new",
+ error: ""
+ };
+ // force update since this is where the mismatch check lives
+ tree.instance().componentDidUpdate();
+ expect(silenceFormStore.data.alertmanagers).toContainEqual({
+ label: "am1",
+ value: "http://am1.example.com/new"
+ });
+ });
+});
diff --git a/ui/src/Components/SilenceModal/__snapshots__/AlertManagerInput.test.js.snap b/ui/src/Components/SilenceModal/__snapshots__/AlertManagerInput.test.js.snap
new file mode 100644
index 000000000..0a6937c7b
--- /dev/null
+++ b/ui/src/Components/SilenceModal/__snapshots__/AlertManagerInput.test.js.snap
@@ -0,0 +1,155 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` matches snapshot 1`] = `
+ false,
+ "nodeType" => false,
+ "$$typeof" => false,
+ "@@__IMMUTABLE_LIST__@@" => false,
+ "@@__IMMUTABLE_SET__@@" => false,
+ "@@__IMMUTABLE_MAP__@@" => false,
+ "@@__IMMUTABLE_STACK__@@" => false,
+ "toJSON" => false,
+ },
+ "proxy": [Circular],
+ "target": Object {
+ "label": "am1",
+ "value": "http://am1.example.com",
+ Symbol(mobx administration): [Circular],
+ },
+ "values": Map {
+ "label" => "am1",
+ "value" => "http://am1.example.com",
+ },
+ },
+ },
+ Object {
+ "label": "am2",
+ "value": "http://am2.example.com",
+ Symbol(mobx administration): ObservableObjectAdministration$$1 {
+ "defaultEnhancer": [Function],
+ "keysAtom": Atom$$1 {
+ "diffValue": 0,
+ "isBeingObserved": false,
+ "isPendingUnobservation": false,
+ "lastAccessedBy": 0,
+ "lowestObserverState": 2,
+ "name": "Silence form store.alertmanagers[..].keys",
+ "observers": Set {},
+ },
+ "name": "Silence form store.alertmanagers[..]",
+ "pendingKeys": Map {
+ "cheerio" => false,
+ "nodeType" => false,
+ "$$typeof" => false,
+ "@@__IMMUTABLE_LIST__@@" => false,
+ "@@__IMMUTABLE_SET__@@" => false,
+ "@@__IMMUTABLE_MAP__@@" => false,
+ "@@__IMMUTABLE_STACK__@@" => false,
+ "toJSON" => false,
+ },
+ "proxy": [Circular],
+ "target": Object {
+ "label": "am2",
+ "value": "http://am2.example.com",
+ Symbol(mobx administration): [Circular],
+ },
+ "values": Map {
+ "label" => "am2",
+ "value" => "http://am2.example.com",
+ },
+ },
+ },
+ Object {
+ "label": "am3",
+ "value": "http://am3.example.com",
+ Symbol(mobx administration): ObservableObjectAdministration$$1 {
+ "defaultEnhancer": [Function],
+ "keysAtom": Atom$$1 {
+ "diffValue": 0,
+ "isBeingObserved": false,
+ "isPendingUnobservation": false,
+ "lastAccessedBy": 0,
+ "lowestObserverState": 2,
+ "name": "Silence form store.alertmanagers[..].keys",
+ "observers": Set {},
+ },
+ "name": "Silence form store.alertmanagers[..]",
+ "pendingKeys": Map {
+ "cheerio" => false,
+ "nodeType" => false,
+ "$$typeof" => false,
+ "@@__IMMUTABLE_LIST__@@" => false,
+ "@@__IMMUTABLE_SET__@@" => false,
+ "@@__IMMUTABLE_MAP__@@" => false,
+ "@@__IMMUTABLE_STACK__@@" => false,
+ "toJSON" => false,
+ },
+ "proxy": [Circular],
+ "target": Object {
+ "label": "am3",
+ "value": "http://am3.example.com",
+ Symbol(mobx administration): [Circular],
+ },
+ "values": Map {
+ "label" => "am3",
+ "value" => "http://am3.example.com",
+ },
+ },
+ },
+ ]
+ }
+ instanceId="silence-input-alertmanagers"
+ isMulti={true}
+ onChange={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "am1",
+ "value": "http://am1.example.com",
+ },
+ Object {
+ "label": "am2",
+ "value": "http://am2.example.com",
+ },
+ Object {
+ "label": "am3",
+ "value": "http://am3.example.com",
+ },
+ ]
+ }
+ placeholder="Alertmanager"
+ styles={
+ Object {
+ "control": [Function],
+ "indicatorsContainer": [Function],
+ "multiValue": [Function],
+ "multiValueLabel": [Function],
+ "multiValueRemove": [Function],
+ "option": [Function],
+ "valueContainer": [Function],
+ "valueLabel": [Function],
+ }
+ }
+/>
+`;