diff --git a/ui/src/Components/Grid/ReloadNeeded/__snapshots__/index.test.js.snap b/ui/src/Components/Grid/ReloadNeeded/__snapshots__/index.test.js.snap new file mode 100644 index 000000000..74e31e607 --- /dev/null +++ b/ui/src/Components/Grid/ReloadNeeded/__snapshots__/index.test.js.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` matches snapshot 1`] = ` +" +

+
+ + + + +

+ + + + + All API connection attempts failed. This migth be caused by authentication middleware, will try to reload. +

+
+

+" +`; diff --git a/ui/src/Components/Grid/ReloadNeeded/index.js b/ui/src/Components/Grid/ReloadNeeded/index.js new file mode 100644 index 000000000..1b1539792 --- /dev/null +++ b/ui/src/Components/Grid/ReloadNeeded/index.js @@ -0,0 +1,48 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; + +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons/faExclamationCircle"; +import { faSpinner } from "@fortawesome/free-solid-svg-icons/faSpinner"; + +import { CenteredMessage } from "Components/CenteredMessage"; + +class ReloadNeeded extends Component { + static propTypes = { + reloadAfter: PropTypes.number.isRequired + }; + + reloadApp = () => { + window.location.reload(); + }; + + componentDidMount() { + const { reloadAfter } = this.props; + this.timer = setTimeout(this.reloadApp, reloadAfter); + } + + componentWillUnmount() { + clearTimeout(this.timer); + this.timer = null; + } + + render() { + return ( + +
+ +

+ + All API connection attempts failed. This migth be caused by + authentication middleware, will try to reload. +

+
+
+ ); + } +} + +export { ReloadNeeded }; diff --git a/ui/src/Components/Grid/ReloadNeeded/index.test.js b/ui/src/Components/Grid/ReloadNeeded/index.test.js new file mode 100644 index 000000000..91fdc7c5a --- /dev/null +++ b/ui/src/Components/Grid/ReloadNeeded/index.test.js @@ -0,0 +1,43 @@ +import React from "react"; + +import { mount, shallow } from "enzyme"; + +import toDiffableHtml from "diffable-html"; + +import { ReloadNeeded } from "."; + +beforeEach(() => { + jest.useFakeTimers(); + jest.clearAllTimers(); +}); + +afterEach(() => { + jest.clearAllTimers(); + jest.restoreAllMocks(); +}); + +describe("", () => { + it("matches snapshot", () => { + const tree = shallow( + + ); + expect(toDiffableHtml(tree.html())).toMatchSnapshot(); + }); + + it("calls window.location.reload after timer is done", () => { + const reloadSpy = jest + .spyOn(global.window.location, "reload") + .mockImplementation(() => {}); + mount(); + jest.runOnlyPendingTimers(); + expect(reloadSpy).toBeCalled(); + }); + + it("timer is cleared on unmount", () => { + const tree = mount(); + const instance = tree.instance(); + + instance.componentWillUnmount(); + expect(instance.timer).toBeNull(); + }); +}); diff --git a/ui/src/Components/Grid/UpgradeNeeded/__snapshots__/index.test.js.snap b/ui/src/Components/Grid/UpgradeNeeded/__snapshots__/index.test.js.snap index 6a55d3cea..1948febd8 100644 --- a/ui/src/Components/Grid/UpgradeNeeded/__snapshots__/index.test.js.snap +++ b/ui/src/Components/Grid/UpgradeNeeded/__snapshots__/index.test.js.snap @@ -25,7 +25,7 @@ exports[` matches snapshot 1`] = ` focusable=\\"false\\" data-prefix=\\"fas\\" data-icon=\\"spinner\\" - class=\\"svg-inline--fa fa-spinner fa-w-16 fa-spin mr-1\\" + class=\\"svg-inline--fa fa-spinner fa-w-16 fa-spin mr-2\\" role=\\"img\\" xmlns=\\"http://www.w3.org/2000/svg\\" viewbox=\\"0 0 512 512\\" diff --git a/ui/src/Components/Grid/UpgradeNeeded/index.js b/ui/src/Components/Grid/UpgradeNeeded/index.js index f2ebe92a1..768b812e3 100644 --- a/ui/src/Components/Grid/UpgradeNeeded/index.js +++ b/ui/src/Components/Grid/UpgradeNeeded/index.js @@ -41,7 +41,7 @@ class UpgradeNeeded extends Component { />

- + Upgrading to a new version: {newVersion}

diff --git a/ui/src/Components/Grid/index.js b/ui/src/Components/Grid/index.js index 83818de40..77cf3c233 100644 --- a/ui/src/Components/Grid/index.js +++ b/ui/src/Components/Grid/index.js @@ -10,6 +10,7 @@ import { AlertGrid } from "./AlertGrid"; import { FatalError } from "./FatalError"; import { UpstreamError } from "./UpstreamError"; import { UpgradeNeeded } from "./UpgradeNeeded"; +import { ReloadNeeded } from "./ReloadNeeded"; import { EmptyGrid } from "./EmptyGrid"; const Grid = observer( @@ -23,10 +24,6 @@ const Grid = observer( render() { const { alertStore, settingsStore, silenceFormStore } = this.props; - if (alertStore.status.error) { - return ; - } - if (alertStore.info.upgradeNeeded) { return ( ; + } + + if (alertStore.status.error) { + return ; + } + if ( alertStore.data.upstreams.counters && alertStore.data.upstreams.counters.total === 1 && diff --git a/ui/src/Components/Grid/index.stories.js b/ui/src/Components/Grid/index.stories.js index d5ec644e1..e4f977ad7 100644 --- a/ui/src/Components/Grid/index.stories.js +++ b/ui/src/Components/Grid/index.stories.js @@ -8,6 +8,7 @@ import { Settings } from "Stores/Settings"; import { SilenceFormStore } from "Stores/SilenceFormStore"; import { FatalError } from "./FatalError"; import { UpgradeNeeded } from "./UpgradeNeeded"; +import { ReloadNeeded } from "./ReloadNeeded"; import { EmptyGrid } from "./EmptyGrid"; import { Grid } from "."; import { InternalError } from "../../ErrorBoundary"; @@ -32,6 +33,9 @@ storiesOf("Grid", module) .add("UpgradeNeeded", () => { return ; }) + .add("ReloadNeeded", () => { + return ; + }) .add("EmptyGrid", () => { return (
diff --git a/ui/src/Components/Grid/index.test.js b/ui/src/Components/Grid/index.test.js index 49503e643..722eb8545 100644 --- a/ui/src/Components/Grid/index.test.js +++ b/ui/src/Components/Grid/index.test.js @@ -94,6 +94,12 @@ describe("", () => { expect(tree.text()).toBe(""); }); + it("renders ReloadNeeded when alertStore.info.reloadNeeded=true", () => { + alertStore.info.reloadNeeded = true; + const tree = ShallowGrid(); + expect(tree.text()).toBe(""); + }); + it("renders AlertGrid before any fetch finished when totalAlerts is 0", () => { alertStore.info.version = "unknown"; alertStore.info.totalAlerts = 0; diff --git a/ui/src/Components/NavBar/FilterInput/index.test.js b/ui/src/Components/NavBar/FilterInput/index.test.js index d5a15c2ad..b8f831c9e 100644 --- a/ui/src/Components/NavBar/FilterInput/index.test.js +++ b/ui/src/Components/NavBar/FilterInput/index.test.js @@ -203,7 +203,7 @@ describe("", () => { tree.find("input").simulate("change", { target: { value: "bar" } }); await WaitForFetch(tree); - expect(fetch.mock.calls).toHaveLength(6); + expect(fetch.mock.calls).toHaveLength(11); expect(fetch.mock.calls[0]).toContain("./autocomplete.json?term=bar"); expect(instance.inputStore.suggestions).toHaveLength(0); }); diff --git a/ui/src/Components/SilenceModal/SilencePreview/index.test.js b/ui/src/Components/SilenceModal/SilencePreview/index.test.js index 18c123d9b..ef907878e 100644 --- a/ui/src/Components/SilenceModal/SilencePreview/index.test.js +++ b/ui/src/Components/SilenceModal/SilencePreview/index.test.js @@ -90,6 +90,7 @@ describe("", () => { { method: "GET", credentials: "include", + mode: "cors", redirect: "follow" } ); @@ -108,6 +109,7 @@ describe("", () => { { method: "GET", credentials: "include", + mode: "cors", redirect: "follow" } );