diff --git a/ui/src/Components/MainModal/Help.test.js b/ui/src/Components/MainModal/Help.test.js new file mode 100644 index 000000000..6baa684ae --- /dev/null +++ b/ui/src/Components/MainModal/Help.test.js @@ -0,0 +1,11 @@ +import React from "react"; +import renderer from "react-test-renderer"; + +import { Help } from "./Help"; + +describe("", () => { + it("matches snapshot", () => { + const tree = renderer.create().toJSON(); + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/ui/src/Components/MainModal/MainModalContent.test.js b/ui/src/Components/MainModal/MainModalContent.test.js new file mode 100644 index 000000000..3c17fd0b9 --- /dev/null +++ b/ui/src/Components/MainModal/MainModalContent.test.js @@ -0,0 +1,80 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import renderer from "react-test-renderer"; + +import { AlertStore } from "Stores/AlertStore"; +import { Settings } from "Stores/Settings"; +import { MainModalContent } from "./MainModalContent"; + +let alertStore; +let settingsStore; +const onHide = jest.fn(); + +beforeAll(() => { + // modal renders into document.body using portals, but that isn't working + // out of the box with react-test-renderer, a workaround is needed based on + // https://github.com/facebook/react/issues/11565#issuecomment-380143358 + ReactDOM.createPortal = jest.fn((element, node) => { + return element; + }); +}); + +beforeEach(() => { + alertStore = new AlertStore([]); + settingsStore = new Settings(); + onHide.mockClear(); +}); + +afterEach(() => { + // https://github.com/facebook/react/issues/11565#issuecomment-380143358 + ReactDOM.createPortal.mockClear(); +}); + +const FakeModal = () => { + return renderer.create( + + ); +}; + +const ValidateSetTab = (title, callArg) => { + const component = FakeModal(); + const instance = component.getInstance(); + const setTabSpy = jest.spyOn(instance.tab, "setTab"); + + const helpTab = component.root.findByProps({ title: title }); + helpTab.props.onClick(); + + expect(setTabSpy).toHaveBeenCalledWith(callArg); +}; + +describe("", () => { + it("matches snapshot", () => { + const tree = FakeModal().toJSON(); + expect(tree).toMatchSnapshot(); + }); + + it("shows 'Configuration' tab by default", () => { + const component = FakeModal(); + const tabs = component.root.findAll(testInstance => { + if (!testInstance.props.className) return false; + const classNames = testInstance.props.className.split(" "); + return classNames.includes("nav-link") && classNames.includes("active"); + }); + expect(tabs).toHaveLength(1); + expect(tabs[0].children).toContain("Configuration"); + }); + + // modal makes it tricky to verify re-rendered content, so only check if we + // update the store for now + it("calls setTab('configuration') after clicking on the 'Configuration' tab", () => { + ValidateSetTab("Configuration", "configuration"); + }); + + it("calls setTab('help') after clicking on the 'Help' tab", () => { + ValidateSetTab("Help", "help"); + }); +}); diff --git a/ui/src/Components/MainModal/__snapshots__/Help.test.js.snap b/ui/src/Components/MainModal/__snapshots__/Help.test.js.snap new file mode 100644 index 000000000..f2742e4aa --- /dev/null +++ b/ui/src/Components/MainModal/__snapshots__/Help.test.js.snap @@ -0,0 +1,879 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` matches snapshot 1`] = ` +
+

+ Filter operators +

+
+
+ + = + + + Exact match +
+
+
+ Example: + + key + = + value + +
+
+ True if compared alert attribute value is equal to + + value + + . +
+
+
+ + != + + + Negative match +
+
+
+ Example: + + key + != + value + +
+
+ True if compared alert attribute is missing or have a value that is not equal to + + value + + . +
+
+
+ + =~ + + + Regular expression match +
+
+
+ Example: + + key + =~ + value + +
+
+ True if compared alert attribute value matches + + value + + regex. +
+
+
+ + !~ + + + Negative regular expression match +
+
+
+ Example: + + key + !~ + value + +
+
+ False if compared alert attribute value matches + + value + + + regex. +
+
+
+ + > + + + Greater than match +
+
+
+ Example: + + key + > + value + +
+
+ True if compared alert attribute value is greater than + + + value + + . +
+
+
+ + < + + + Less than match +
+
+
+ Example: + + key + < + value + +
+
+ True if compared alert attribue value is less than + + value + + . +
+
+
+
+

+ Filtering using alert labels +

+
+
+ Match alerts based on any label +
+
+
+ Supported operators: + + + = + + + != + + + =~ + + + !~ + + + > + + + < + +
+
+ Examples: +
+
    +
  • +
    + + alertname=UnableToPing + +
    +
    + Match alerts with label + + alertname + + equal to + + + UnableToPing + + . +
    +
  • +
  • +
    + + hostname=localhost + +
    +
    + Match alerts with label + + hostname + + equal to + + + localhost + + . +
    +
  • +
  • +
    + + service!=apache3 + +
    +
    + Match alerts with label + + service + + missing or not equal to + + + apache3 + + . +
    +
  • +
  • +
    + + service=~apache + +
    +
    + Match alerts with label + + service + + matching regular expression + + /.*apache.*/ + + . +
    +
  • +
  • +
    + + service=~apache[1-3] + +
    +
    + Match alerts with label + + service + + matching regular expression + + /.*apache[1-3].*/ + + . +
    +
  • +
  • +
    + + priority>4 + +
    +
    + Match alerts with label + + priority + + value + + + > + + than + + 4 + + . Value will be casted to integer if possible, string comparision will be used as fallback. +
    +
  • +
+
+
+
+
+

+ Filtering alerts using special filters +

+
+
+ Match alerts based on the Alertmanager instance name they were collected from +
+
+
+ Supported operators: + + + = + + + != + + + =~ + + + !~ + +
+
+ Examples: +
+
    +
  • +
    + + @alertmanager=prod + +
    +
    + Match alerts collected from Alertmanager instance named + + + prod + + . +
    +
  • +
  • +
    + + @alertmanager!=dev + +
    +
    + Match alerts collected from Alertmanager instances except for the one named + + dev + + . +
    +
  • +
  • +
    + + @alertmanager=~prod + +
    +
    + Match alerts collected from Alertmanager instances with names matching regular expression + + /.*prod.*/ + + . +
    +
  • +
+
+
+ Match alerts based on the receiver name +
+
+
+ Supported operators: + + + = + + + != + + + =~ + + + !~ + +
+
+ Examples: +
+
    +
  • +
    + + @receiver=default + +
    +
    + Match alerts sent to the default receiver. +
    +
  • +
  • +
    + + @receiver!=hipchat + +
    +
    + Match alerts not sent to the hipchat receiver. +
    +
  • +
  • +
    + + @receiver=~email + +
    +
    + Match alerts sent to any receiver with name matching regular expression + + /.*email.*/ + + . +
    +
  • +
+
+
+ Match alerts based on the state +
+
+
+ Supported operators: + + + = + + + != + +
+
+ Examples: +
+
    +
  • +
    + + @state=active + +
    +
    + Match only active alerts. +
    +
  • +
  • +
    + + @state!=active + +
    +
    + Match alerts that are not active, only suppressed and unprocessed will be matched. +
    +
  • +
  • +
    + + @state=suppressed + +
    +
    + Match only suppressed alerts. +
    +
  • +
  • +
    + + @state=unprocessed + +
    +
    + Match only unprocessed alerts. +
    +
  • +
+
+
+ Match alerts based on the author of silence +
+
+
+ Supported operators: + + + = + + + != + + + =~ + + + !~ + +
+
+ Examples: +
+
    +
  • +
    + + @silence_author=me@example.com + +
    +
    + Match alerts silenced by + + me@example.com + + . +
    +
  • +
  • +
    + + @silence_author!=me@example.com + +
    +
    + Match alerts silenced by everyone except + + + foo@example.com + + . +
    +
  • +
  • +
    + + @silence_author=~@example.com + +
    +
    + Match alerts silenced by author matching regular expression + + + /.*@example.com.*/ + + . +
    +
  • +
+
+
+ Match alerts based on the jira linked in the silence +
+
+
+ Supported operators: + + + = + + + != + + + =~ + + + !~ + +
+
+ Examples: +
+
    +
    + This is supported only if JIRA regexp are enabled and able to match JIRA ids in the silence comment body. +
    +
  • +
    + + @silence_jira=PROJECT-123 + +
    +
    + Match silenced alerts where detected JIRA issue id is equal to + + + PROJECT-123 + + . +
    +
  • +
  • +
    + + @silence_jira!=PROJECT-123 + +
    +
    + Match silenced alerts where detected JIRA issue id is different than + + + PROJECT-123 + + . +
    +
  • +
  • +
    + + @silence_jira=~PROJECT + +
    +
    + Match silenced alerts where detected JIRA issue id matches regular expression + + /.*PROJECT.*/ + + . +
    +
  • +
+
+
+ Limit number of displayed alerts +
+
+
+ Supported operators: + + + = + +
+
+ Examples: +
+
    +
    + Value must be a number >= 1. +
    +
  • +
    + + @limit=10 + +
    +
    + Limit number of displayed alerts to 10. +
    +
  • +
+
+
+ Match alerts based on creation timestamp +
+
+
+ Supported operators: + + + > + + + < + +
+
+ Examples: +
+
    +
  • +
    + + @age>15m + +
    +
    + Match alerts older than 15 minutes. +
    +
  • +
  • +
    + + @age>1h + +
    +
    + Match alerts older than 1 hour. +
    +
  • +
  • +
    + + @age<10h30m + +
    +
    + Match alerts more recent than 10 hours and 30 minutes. +
    +
  • +
+
+
+
+
+`; diff --git a/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap b/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap new file mode 100644 index 000000000..bb0db1bde --- /dev/null +++ b/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap @@ -0,0 +1,236 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` matches snapshot 1`] = ` +
+
+
+
+ +
+
+
+
+ +
+ + + 10s + + +
+
+ + + + 30s + + +
+ +
+ + + 120s + + +
+
+
+
+ +
+ + + 1 + + +
+
+ + + + 5 + + +
+ +
+ + + 10 + + +
+
+ +
+
+ + Version: + unknown + +
+
+
+
+`; diff --git a/ui/src/Components/MainModal/index.test.js b/ui/src/Components/MainModal/index.test.js new file mode 100644 index 000000000..2e19b794b --- /dev/null +++ b/ui/src/Components/MainModal/index.test.js @@ -0,0 +1,54 @@ +import React from "react"; +import sd from "skin-deep"; + +import { AlertStore } from "Stores/AlertStore"; +import { Settings } from "Stores/Settings"; +import { MainModal } from "."; + +let alertStore; +let settingsStore; + +beforeEach(() => { + alertStore = new AlertStore([]); + settingsStore = new Settings(); +}); + +const RenderMainModal = () => { + return sd.shallowRender( + + ); +}; + +describe("", () => { + it("only renders FontAwesomeIcon when modal is not shown", () => { + const tree = RenderMainModal(); + // is how React.Fragment gets rendered + expect(tree.text()).toBe(""); + }); + + it("renders the modal when it is shown", () => { + const tree = RenderMainModal(); + const instance = tree.getMountedInstance(); + instance.toggle.toggle(); + // is how React.Fragment gets rendered + expect(tree.text()).toBe( + "" + ); + }); + + it("hides the modal when toggle() is called twice", () => { + const tree = RenderMainModal(); + const instance = tree.getMountedInstance(); + instance.toggle.toggle(); + instance.toggle.toggle(); + expect(tree.text()).toBe(""); + }); + + it("hides the modal when hide() is called", () => { + const tree = RenderMainModal(); + const instance = tree.getMountedInstance(); + instance.toggle.show = true; + instance.toggle.hide(); + expect(tree.text()).toBe(""); + }); +});