feat(ui): drop moment.js to reduce bundle size

This commit is contained in:
Łukasz Mierzwa
2020-06-17 18:57:13 +01:00
committed by Łukasz Mierzwa
parent fdf8824a60
commit 6a7cc1c72f
34 changed files with 344 additions and 262 deletions
+5 -10
View File
@@ -8769,6 +8769,11 @@
}
}
},
"date-fns": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.14.0.tgz",
"integrity": "sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw=="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
@@ -15834,11 +15839,6 @@
"traverse": "^0.6.6"
}
},
"moment": {
"version": "2.26.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz",
"integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw=="
},
"moo": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz",
@@ -19251,11 +19251,6 @@
"prop-types": "^15.5.10"
}
},
"react-moment": {
"version": "0.9.7",
"resolved": "https://registry.npmjs.org/react-moment/-/react-moment-0.9.7.tgz",
"integrity": "sha512-ifzUrUGF6KRsUN2pRG5k56kO0mJBr8kRkWb0wNvtFIsBIxOuPxhUpL1YlXwpbQCbHq23hUu6A0VEk64HsFxk9g=="
},
"react-popper": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.2.3.tgz",
+1 -2
View File
@@ -21,6 +21,7 @@
"bricks.js": "1.8.0",
"copy-to-clipboard": "3.3.1",
"csshake": "1.5.3",
"date-fns": "2.14.0",
"favico.js": "0.3.10",
"fontfaceobserver": "2.1.0",
"lodash.debounce": "4.0.8",
@@ -30,7 +31,6 @@
"mobx": "5.15.4",
"mobx-react-lite": "2.0.7",
"mobx-stored": "1.1.0",
"moment": "2.26.0",
"promise-retry": "2.0.1",
"prop-types": "15.7.2",
"qs": "6.9.4",
@@ -47,7 +47,6 @@
"react-json-pretty": "2.2.0",
"react-linkify": "0.2.2",
"react-media": "1.10.0",
"react-moment": "0.9.7",
"react-popper": "2.2.3",
"react-resize-detector": "5.0.6",
"react-reveal": "1.2.2",
-7
View File
@@ -45,12 +45,6 @@ const FaviconBadge = React.lazy(() =>
}))
);
const MomentPolledTimer = React.lazy(() =>
import("Components/MomentPolledTimer").then((module) => ({
default: module.MomentPolledTimer,
}))
);
interface UIDefaults {
Refresh: number;
HideFiltersWhenIdle: boolean;
@@ -162,7 +156,6 @@ const App: FunctionComponent<AppProps> = ({ defaultFilters, uiDefaults }) => {
<React.Suspense fallback={null}>
<FaviconBadge alertStore={alertStore} />
<Fetcher alertStore={alertStore} settingsStore={settingsStore} />
<MomentPolledTimer />
</React.Suspense>
</ErrorBoundary>
));
+5 -6
View File
@@ -4,7 +4,7 @@ import PropTypes from "prop-types";
import { toJS } from "mobx";
import { useObserver } from "mobx-react-lite";
import moment from "moment";
import addSeconds from "date-fns/addSeconds";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheck } from "@fortawesome/free-solid-svg-icons/faCheck";
@@ -62,17 +62,16 @@ const AlertAck = ({ alertStore, silenceFormStore, group }) => {
const commentPrefix = toJS(
alertStore.settings.values.alertAcknowledgement.commentPrefix
);
const now = new Date();
c.push({
payload: GenerateAlertmanagerSilenceData(
moment.utc(),
moment.utc().add(durationSeconds, "seconds"),
now,
addSeconds(now, durationSeconds),
MatchersFromGroup(group, [], group.alerts, true),
author,
`${
commentPrefix ? commentPrefix + " " : ""
}This alert was acknowledged using karma on ${moment
.utc()
.toString()}`
}This alert was acknowledged using karma on ${now.toUTCString()}`
),
clusterName: clusterName,
members: clusterMembers,
+5 -5
View File
@@ -250,7 +250,7 @@ describe("<AlertAck />", () => {
await MountAndClick();
expect(JSON.parse(fetchMock.calls()[0][1].body)).toEqual({
comment:
"PREFIX This alert was acknowledged using karma on Tue Feb 01 2000 00:00:00 GMT+0000",
"PREFIX This alert was acknowledged using karma on Tue, 01 Feb 2000 00:00:00 GMT",
createdBy: "karma/ui",
endsAt: "2000-02-01T00:02:03.000Z",
matchers: [
@@ -268,7 +268,7 @@ describe("<AlertAck />", () => {
await MountAndClick();
expect(JSON.parse(fetchMock.calls()[0][1].body)).toEqual({
comment:
"This alert was acknowledged using karma on Tue Feb 01 2000 00:00:00 GMT+0000",
"This alert was acknowledged using karma on Tue, 01 Feb 2000 00:00:00 GMT",
createdBy: "me",
endsAt: "2000-02-01T00:03:57.000Z",
matchers: [
@@ -288,7 +288,7 @@ describe("<AlertAck />", () => {
await MountAndClick();
expect(JSON.parse(fetchMock.calls()[0][1].body)).toEqual({
comment:
"FOO: This alert was acknowledged using karma on Tue Feb 01 2000 00:00:00 GMT+0000",
"FOO: This alert was acknowledged using karma on Tue, 01 Feb 2000 00:00:00 GMT",
createdBy: "auth@example.com",
endsAt: "2000-02-01T00:03:42.000Z",
matchers: [
@@ -309,7 +309,7 @@ describe("<AlertAck />", () => {
await MountAndClick();
expect(JSON.parse(fetchMock.calls()[0][1].body)).toEqual({
comment:
"FOO: This alert was acknowledged using karma on Tue Feb 01 2000 00:00:00 GMT+0000",
"FOO: This alert was acknowledged using karma on Tue, 01 Feb 2000 00:00:00 GMT",
createdBy: "bob@example.com",
endsAt: "2000-02-01T00:03:42.000Z",
matchers: [
@@ -328,7 +328,7 @@ describe("<AlertAck />", () => {
await MountAndClick();
expect(JSON.parse(fetchMock.calls()[0][1].body)).toEqual({
comment:
"FOO: This alert was acknowledged using karma on Tue Feb 01 2000 00:00:00 GMT+0000",
"FOO: This alert was acknowledged using karma on Tue, 01 Feb 2000 00:00:00 GMT",
createdBy: "me",
endsAt: "2000-02-01T00:03:42.000Z",
matchers: [
@@ -0,0 +1,43 @@
import React from "react";
import { mount } from "enzyme";
import addSeconds from "date-fns/addSeconds";
import subSeconds from "date-fns/subSeconds";
import { DateFromNow } from ".";
describe("<DateFromNow />", () => {
it("renders 'just now' for now", () => {
const tree = mount(<DateFromNow timestamp={new Date().toISOString()} />);
expect(tree.text()).toBe("just now");
});
it("renders 'a few seconds ago' for 35 seconds old timestamp", () => {
const tree = mount(
<DateFromNow timestamp={subSeconds(new Date(), 35).toISOString()} />
);
expect(tree.text()).toBe("a few seconds ago");
});
it("renders 'in a few seconds' for a timestamp 35 seconds away", () => {
const tree = mount(
<DateFromNow timestamp={addSeconds(new Date(), 35).toISOString()} />
);
expect(tree.text()).toBe("in a few seconds");
});
it("renders '1 minute ago' for 65 seconds old timestamp", () => {
const tree = mount(
<DateFromNow timestamp={subSeconds(new Date(), 65).toISOString()} />
);
expect(tree.text()).toBe("1 minute ago");
});
it("renders 'in 1 minute' for a timestamp 65 seconds away", () => {
const tree = mount(
<DateFromNow timestamp={addSeconds(new Date(), 65).toISOString()} />
);
expect(tree.text()).toBe("in 1 minute");
});
});
+32
View File
@@ -0,0 +1,32 @@
import React, { FunctionComponent, useState, useEffect } from "react";
import parseISO from "date-fns/parseISO";
import differenceInSeconds from "date-fns/differenceInSeconds";
import formatDistanceToNowStrict from "date-fns/formatDistanceToNowStrict";
const formatLabel = (timestamp: string) => {
const ts = parseISO(timestamp);
const diff = differenceInSeconds(new Date(), ts);
if (diff > 0 && diff < 60) return "a few seconds ago";
if (diff < 0 && diff >= -60) return "in a few seconds";
if (diff === 0) return "just now";
return formatDistanceToNowStrict(ts, {
addSuffix: true,
});
};
export const DateFromNow: FunctionComponent<{ timestamp: string }> = ({
timestamp,
}) => {
const [label, setLabel] = useState(formatLabel(timestamp));
useEffect(() => {
const timer = setInterval(
() => setLabel(formatLabel(timestamp)),
30 * 1000
);
return () => clearInterval(timer);
}, [timestamp]);
return <React.Fragment>{label}</React.Fragment>;
};
+5 -5
View File
@@ -3,7 +3,7 @@ import PropTypes from "prop-types";
import { reaction } from "mobx";
import moment from "moment";
import addSeconds from "date-fns/addSeconds";
import { AlertStore, AlertStoreStatuses } from "Stores/AlertStore";
import { Settings } from "Stores/Settings";
@@ -50,12 +50,12 @@ const Fetcher = ({ alertStore, settingsStore }) => {
};
const fetchIfIdle = () => {
const nextTick = moment(alertStore.status.lastUpdateAt).add(
settingsStore.fetchConfig.config.interval,
"seconds"
const nextTick = addSeconds(
alertStore.status.lastUpdateAt,
settingsStore.fetchConfig.config.interval
);
const pastDeadline = moment().isSameOrAfter(nextTick);
const pastDeadline = new Date() >= nextTick;
const status = alertStore.status.value.toString();
const updateInProgress =
@@ -5,8 +5,6 @@ import { useObserver } from "mobx-react-lite";
import { Manager, Reference, Popper } from "react-popper";
import Moment from "react-moment";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCaretDown } from "@fortawesome/free-solid-svg-icons/faCaretDown";
import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash";
@@ -17,6 +15,7 @@ import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore, SilenceTabNames } from "Stores/SilenceFormStore";
import { FetchPauser } from "Components/FetchPauser";
import { DropdownSlide } from "Components/Animations/DropdownSlide";
import { DateFromNow } from "Components/DateFromNow";
import { useOnClickOutside } from "Hooks/useOnClickOutside";
const onSilenceClick = (alertStore, silenceFormStore, group, alert) => {
@@ -130,7 +129,7 @@ const AlertMenu = ({
style={{ width: "0.8rem" }}
icon={faCaretDown}
/>
<Moment fromNow>{alert.startsAt}</Moment>
<DateFromNow timestamp={alert.startsAt} />
</span>
)}
</Reference>
@@ -75,9 +75,7 @@ exports[`<Alert /> matches snapshot when inhibited 1`] = `
>
</path>
</svg>
<time datetime=\\"1534268200017\\">
a day ago
</time>
1 day ago
</span>
</span>
<div style=\\"display: inline-block; max-width: 100%;\\"
@@ -224,9 +222,7 @@ exports[`<Alert /> matches snapshot with showAlertmanagers=false showReceiver=fa
>
</path>
</svg>
<time datetime=\\"1534268200017\\">
a day ago
</time>
1 day ago
</span>
</span>
<div style=\\"display: inline-block; max-width: 100%;\\"
@@ -7,8 +7,6 @@ import { advanceTo, clear } from "jest-date-mock";
import toDiffableHtml from "diffable-html";
import Moment from "react-moment";
import {
MockAlert,
MockAnnotation,
@@ -291,25 +289,62 @@ describe("<Alert />", () => {
it("alert timestamp is updated every minute", () => {
jest.useFakeTimers();
Moment.startPooledTimer();
advanceTo(new Date(Date.UTC(2018, 7, 14, 17, 36, 41)));
const alert = MockedAlert();
const group = MockAlertGroup({}, [alert], [], {}, {});
const tree = MountedAlert(alert, group, false, false);
expect(tree.find("time").text()).toBe("a few seconds ago");
expect(
tree
.find("span.components-label.badge.badge-secondary.cursor-pointer")
.at(0)
.text()
).toBe("just now");
advanceTo(new Date(Date.UTC(2018, 7, 14, 17, 36, 42)));
act(() => jest.advanceTimersByTime(31 * 1000));
expect(
tree
.find("span.components-label.badge.badge-secondary.cursor-pointer")
.at(0)
.text()
).toBe("a few seconds ago");
advanceTo(new Date(Date.UTC(2018, 7, 14, 17, 37, 41)));
act(() => jest.advanceTimersByTime(61 * 1000));
expect(tree.find("time").text()).toBe("a minute ago");
act(() => jest.advanceTimersByTime(31 * 1000));
expect(
tree
.find("span.components-label.badge.badge-secondary.cursor-pointer")
.at(0)
.text()
).toBe("1 minute ago");
advanceTo(new Date(Date.UTC(2018, 7, 14, 18, 36, 41)));
jest.advanceTimersByTime(61 * 1000);
expect(tree.find("time").text()).toBe("an hour ago");
act(() => jest.advanceTimersByTime(31 * 1000));
expect(
tree
.find("span.components-label.badge.badge-secondary.cursor-pointer")
.at(0)
.text()
).toBe("1 hour ago");
advanceTo(new Date(Date.UTC(2018, 7, 14, 19, 36, 41)));
jest.advanceTimersByTime(61 * 1000);
expect(tree.find("time").text()).toBe("2 hours ago");
act(() => jest.advanceTimersByTime(31 * 1000));
expect(
tree
.find("span.components-label.badge.badge-secondary.cursor-pointer")
.at(0)
.text()
).toBe("2 hours ago");
advanceTo(new Date(Date.UTC(2018, 7, 16, 19, 36, 41)));
act(() => jest.advanceTimersByTime(31 * 1000));
expect(
tree
.find("span.components-label.badge.badge-secondary.cursor-pointer")
.at(0)
.text()
).toBe("2 days ago");
});
});
@@ -285,10 +285,7 @@ exports[`<GroupFooter /> mathes snapshot when silence is rendered 1`] = `
— me@example.com
</span>
<span class=\\"badge badge-danger align-text-bottom p-1\\">
Expired
<time datetime=\\"946688400000\\">
14 hours ago
</time>
Expired 14 hours ago
</span>
</div>
</div>
@@ -4,7 +4,6 @@ import { mount } from "enzyme";
import toDiffableHtml from "diffable-html";
import moment from "moment";
import { advanceTo, clear } from "jest-date-mock";
import {
@@ -48,7 +47,7 @@ beforeEach(() => {
alertStore = new AlertStore([]);
silenceFormStore = new SilenceFormStore();
group = MockGroup();
advanceTo(moment.utc([2000, 0, 1, 15, 0, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 15, 0, 0)));
alertStore.data.receivers = ["by-cluster-service", "by-name"];
});
@@ -3,8 +3,6 @@ import { act } from "react-dom/test-utils";
import { mount } from "enzyme";
import moment from "moment";
import { MockAlert, MockAlertGroup } from "__mocks__/Alerts";
import { MockThemeContext } from "__mocks__/Theme";
import { AlertStore } from "Stores/AlertStore";
@@ -52,9 +50,9 @@ afterEach(() => {
const MockAlerts = (alertCount) => {
for (let i = 1; i <= alertCount; i++) {
let alert = MockAlert([], { instance: `instance${i}` }, "active");
const startsAt = moment().toISOString();
alert.startsAt = startsAt;
alert.alertmanager[0].startsAt = startsAt;
const startsAt = new Date();
alert.startsAt = startsAt.toISOString();
alert.alertmanager[0].startsAt = startsAt.toISOString();
group.alerts.push(alert);
}
};
@@ -5,8 +5,6 @@ import { mount } from "enzyme";
import { advanceTo, clear } from "jest-date-mock";
import moment from "moment";
import { EmptyAPIResponse } from "__mocks__/Fetch";
import { MockSilence } from "__mocks__/Alerts";
import { PressKey } from "__mocks__/PressKey";
@@ -22,7 +20,7 @@ let cluster;
let silence;
beforeEach(() => {
advanceTo(moment.utc([2000, 0, 1, 0, 30, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 30, 0)));
jest.useFakeTimers();
alertStore = new AlertStore([]);
@@ -297,7 +295,7 @@ describe("<DeleteSilenceModalContent />", () => {
tree.find(".btn-danger").simulate("click");
expect(useFetchDelete).toHaveBeenCalledTimes(1);
advanceTo(moment.utc([2000, 0, 1, 0, 30, 1]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 30, 1)));
tree.find(".btn-danger").simulate("click");
expect(useFetchDelete).toHaveBeenCalledTimes(2);
});
@@ -1,8 +1,7 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import moment from "moment";
import Moment from "react-moment";
import parseISO from "date-fns/parseISO";
import copy from "copy-to-clipboard";
@@ -22,6 +21,7 @@ import { SilenceFormStore } from "Stores/SilenceFormStore";
import { QueryOperators } from "Common/Query";
import { TooltipWrapper } from "Components/TooltipWrapper";
import { RenderLinkAnnotation } from "Components/Grid/AlertGrid/AlertGroup/Annotation";
import { DateFromNow } from "Components/DateFromNow";
import { DeleteSilence } from "./DeleteSilence";
const SilenceIDCopyButton = ({ id }) => {
@@ -51,7 +51,7 @@ const SilenceDetails = ({
onEditSilence,
isUpper,
}) => {
let isExpired = moment(silence.endsAt) < moment();
const isExpired = parseISO(silence.endsAt) < new Date();
let expiresClass = "";
let expiresLabel = "Expires";
if (isExpired) {
@@ -78,7 +78,7 @@ const SilenceDetails = ({
icon={faCalendarCheck}
fixedWidth
/>
Started <Moment fromNow>{silence.startsAt}</Moment>
Started <DateFromNow timestamp={silence.startsAt} />
</span>
<span
className={`badge ${expiresClass} px-1 mr-1 components-label`}
@@ -88,7 +88,7 @@ const SilenceDetails = ({
icon={faCalendarTimes}
fixedWidth
/>
{expiresLabel} <Moment fromNow>{silence.endsAt}</Moment>
{expiresLabel} <DateFromNow timestamp={silence.endsAt} />
</span>
</div>
<div className="my-1 d-flex flex-row">
@@ -6,7 +6,6 @@ import toDiffableHtml from "diffable-html";
import copy from "copy-to-clipboard";
import moment from "moment";
import { advanceTo, clear } from "jest-date-mock";
import { MockSilence } from "__mocks__/Alerts";
@@ -67,14 +66,14 @@ const MountedSilenceDetails = () => {
describe("<SilenceDetails />", () => {
it("unexpired silence endsAt label doesn't use 'danger' class", () => {
advanceTo(moment.utc([2000, 0, 1, 0, 30, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 30, 0)));
const tree = MountedSilenceDetails();
const endsAt = tree.find("span.badge").at(1);
expect(toDiffableHtml(endsAt.html())).not.toMatch(/text-danger/);
});
it("expired silence endsAt label uses 'danger' class", () => {
advanceTo(moment.utc([2000, 0, 1, 23, 0, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 23, 0, 0)));
const tree = MountedSilenceDetails();
const endsAt = tree.find("span.badge").at(1);
expect(toDiffableHtml(endsAt.html())).toMatch(/text-danger/);
@@ -2,16 +2,19 @@ import React, { useEffect, useState } from "react";
import { useObserver } from "mobx-react-lite";
import moment from "moment";
import Moment from "react-moment";
import parseISO from "date-fns/parseISO";
import getUnixTime from "date-fns/getUnixTime";
import { APISilence } from "Models/API";
import { DateFromNow } from "Components/DateFromNow";
import "./SilenceProgress.scss";
const calculatePercent = (startsAt, endsAt) => {
const durationDone = moment().unix() - moment(startsAt).unix();
const durationTotal = moment(endsAt).unix() - moment(startsAt).unix();
const durationDone =
getUnixTime(new Date()) - getUnixTime(parseISO(startsAt));
const durationTotal =
getUnixTime(parseISO(endsAt)) - getUnixTime(parseISO(startsAt));
return Math.floor((durationDone / durationTotal) * 100);
};
@@ -28,13 +31,13 @@ const SilenceProgress = ({ silence }) => {
}, [silence.startsAt, silence.endsAt]);
return useObserver(() =>
moment(silence.endsAt) < moment() ? (
parseISO(silence.endsAt) < new Date() ? (
<span className="badge badge-danger align-text-bottom p-1">
Expired <Moment fromNow>{silence.endsAt}</Moment>
Expired <DateFromNow timestamp={silence.endsAt} />
</span>
) : (
<span className="badge badge-light nmb-05 align-text-bottom p-1">
Expires <Moment fromNow>{silence.endsAt}</Moment>
Expires <DateFromNow timestamp={silence.endsAt} />
<div className="progress silence-progress">
<div
className={
@@ -5,7 +5,6 @@ import { mount } from "enzyme";
import toDiffableHtml from "diffable-html";
import moment from "moment";
import { advanceTo, clear } from "jest-date-mock";
import { MockSilence } from "__mocks__/Alerts";
@@ -35,40 +34,40 @@ const MountedSilenceProgress = () => {
describe("<SilenceProgress />", () => {
it("renders with class 'danger' and no progressbar when expired", () => {
advanceTo(moment.utc([2001, 0, 1, 23, 0, 0]));
advanceTo(new Date(Date.UTC(2001, 0, 1, 23, 0, 0)));
const tree = MountedSilenceProgress();
expect(toDiffableHtml(tree.html())).toMatch(/badge-danger/);
expect(tree.text()).toMatch(/Expired a year ago/);
expect(tree.text()).toMatch(/Expired 1 year ago/);
});
it("progressbar uses class 'danger' when > 90%", () => {
advanceTo(moment.utc([2000, 0, 1, 0, 55, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 55, 0)));
const tree = MountedSilenceProgress();
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-danger/);
});
it("progressbar uses class 'danger' when > 75%", () => {
advanceTo(moment.utc([2000, 0, 1, 0, 50, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 50, 0)));
const tree = MountedSilenceProgress();
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-warning/);
});
it("progressbar uses class 'success' when <= 75%", () => {
advanceTo(moment.utc([2000, 0, 1, 0, 30, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 30, 0)));
const tree = MountedSilenceProgress();
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-success/);
});
it("progressbar is updated every 30 seconds", () => {
advanceTo(moment.utc([2000, 0, 1, 0, 30, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 30, 0)));
const tree = MountedSilenceProgress();
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-success/);
advanceTo(moment.utc([2000, 0, 1, 0, 50, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 50, 0)));
act(() => jest.runOnlyPendingTimers());
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-warning/);
advanceTo(moment.utc([2000, 0, 1, 0, 55, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 55, 0)));
act(() => jest.runOnlyPendingTimers());
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-danger/);
});
@@ -28,10 +28,7 @@ exports[`<SilenceComment /> Matches snapshot when collapsed 1`] = `
— me@example.com
</span>
<span class=\\"badge badge-danger align-text-bottom p-1\\">
Expired
<time datetime=\\"946688400000\\">
20 years ago
</time>
Expired 20 years ago
</span>
</div>
</div>
@@ -98,10 +95,7 @@ exports[`<SilenceComment /> Matches snapshot when collapsed and multiple cluster
ha
</span>
<span class=\\"badge badge-danger align-text-bottom p-1\\">
Expired
<time datetime=\\"946688400000\\">
20 years ago
</time>
Expired 20 years ago
</span>
</div>
</div>
@@ -32,10 +32,7 @@ exports[`<ManagedSilence /> matches snapshot when collapsed 1`] = `
— me@example.com
</span>
<span class=\\"badge badge-light nmb-05 align-text-bottom p-1\\">
Expires
<time datetime=\\"946688400000\\">
in 30 minutes
</time>
Expires in 30 minutes
<div class=\\"progress silence-progress\\">
<div class=\\"progress-bar bg-success\\"
role=\\"progressbar\\"
@@ -166,10 +163,7 @@ exports[`<ManagedSilence /> matches snapshot with expaned details 1`] = `
>
</path>
</svg>
Started
<time datetime=\\"946684800000\\">
30 minutes ago
</time>
Started 30 minutes ago
</span>
<span class=\\"badge px-1 mr-1 components-label\\">
<svg aria-hidden=\\"true\\"
@@ -186,10 +180,7 @@ exports[`<ManagedSilence /> matches snapshot with expaned details 1`] = `
>
</path>
</svg>
Expires
<time datetime=\\"946688400000\\">
in 30 minutes
</time>
Expires in 30 minutes
</span>
</div>
<div class=\\"my-1 d-flex flex-row\\">
@@ -4,7 +4,6 @@ import { mount } from "enzyme";
import toDiffableHtml from "diffable-html";
import moment from "moment";
import { advanceTo, clear } from "jest-date-mock";
import { MockSilence } from "__mocks__/Alerts";
@@ -20,7 +19,7 @@ let cluster;
let silence;
beforeEach(() => {
advanceTo(moment.utc([2000, 0, 1, 0, 30, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 30, 0)));
alertStore = new AlertStore([]);
silenceFormStore = new SilenceFormStore();
@@ -169,7 +168,7 @@ describe("<ManagedSilence />", () => {
});
it("shows Recreate button on expired silence", () => {
advanceTo(moment.utc([2000, 0, 1, 23, 30, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 23, 30, 0)));
const tree = MountedManagedSilence();
tree.find("svg.text-muted.cursor-pointer").simulate("click");
@@ -1,12 +0,0 @@
import React, { useEffect } from "react";
import Moment from "react-moment";
export function MomentPolledTimer() {
useEffect(() => {
Moment.startPooledTimer();
return () => Moment.clearPooledTimer();
}, []);
return <span id="moment-polled-timer" />;
}
+2 -4
View File
@@ -3,8 +3,6 @@ import { act } from "react-dom/test-utils";
import { mount } from "enzyme";
import moment from "moment";
import { MockThemeContext } from "__mocks__/Theme";
import { AlertStore, NewUnappliedFilter } from "Stores/AlertStore";
import { Settings } from "Stores/Settings";
@@ -26,8 +24,8 @@ beforeEach(() => {
silenceFormStore = new SilenceFormStore();
settingsStore.filterBarConfig.config.autohide = true;
// fix startsAt & endsAt dates so they don't change between tests
silenceFormStore.data.startsAt = moment([2018, 1, 30, 10, 25, 50]).utc();
silenceFormStore.data.endsAt = moment([2018, 1, 30, 11, 25, 50]).utc();
silenceFormStore.data.startsAt = new Date(Date.UTC(2018, 1, 30, 10, 25, 50));
silenceFormStore.data.endsAt = new Date(Date.UTC(2018, 1, 30, 11, 25, 50));
});
afterEach(() => {
@@ -5,7 +5,6 @@ import { mount } from "enzyme";
import toDiffableHtml from "diffable-html";
import moment from "moment";
import { advanceTo, clear } from "jest-date-mock";
import { MockSilence } from "__mocks__/Alerts";
@@ -25,7 +24,7 @@ let cluster;
let silence;
beforeEach(() => {
advanceTo(moment.utc([2000, 0, 1, 0, 30, 0]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 30, 0)));
jest.useFakeTimers();
alertStore = new AlertStore([]);
@@ -108,13 +107,13 @@ describe("<Browser />", () => {
settingsStore.fetchConfig.config.interval = 1;
MountedBrowser();
advanceTo(moment.utc([2000, 0, 1, 0, 30, 2]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 30, 2)));
act(() => jest.runOnlyPendingTimers());
advanceTo(moment.utc([2000, 0, 1, 0, 30, 4]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 30, 4)));
act(() => jest.runOnlyPendingTimers());
advanceTo(moment.utc([2000, 0, 1, 0, 30, 6]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 30, 6)));
act(() => jest.runOnlyPendingTimers());
expect(useFetchGet.fetch.calls).toHaveLength(4);
@@ -364,7 +363,7 @@ describe("<Browser />", () => {
tree.unmount();
act(() => {
advanceTo(moment.utc([2000, 0, 1, 0, 30, 59]));
advanceTo(new Date(Date.UTC(2000, 0, 1, 0, 30, 59)));
jest.runOnlyPendingTimers();
});
@@ -3,8 +3,6 @@ import PropTypes from "prop-types";
import { observer } from "mobx-react-lite";
import moment from "moment";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAngleUp } from "@fortawesome/free-solid-svg-icons/faAngleUp";
import { faAngleDown } from "@fortawesome/free-solid-svg-icons/faAngleDown";
@@ -59,8 +57,8 @@ const HourMinute = observer(
}
};
const hour = dateValue.hour();
const minute = dateValue.minute();
const hour = dateValue.getHours();
const minute = dateValue.getMinutes();
return (
<div
@@ -117,7 +115,7 @@ const HourMinute = observer(
}
);
HourMinute.propTypes = {
dateValue: PropTypes.instanceOf(moment).isRequired,
dateValue: PropTypes.instanceOf(Date).isRequired,
onHourInc: PropTypes.func.isRequired,
onHourDec: PropTypes.func.isRequired,
onMinuteInc: PropTypes.func.isRequired,
@@ -3,7 +3,10 @@ import PropTypes from "prop-types";
import { useObserver } from "mobx-react-lite";
import moment from "moment";
import differenceInMinutes from "date-fns/differenceInMinutes";
import differenceInHours from "date-fns/differenceInHours";
import differenceInDays from "date-fns/differenceInDays";
import setSeconds from "date-fns/setSeconds";
import DayPicker from "react-day-picker";
import "react-day-picker/lib/style.css";
@@ -12,10 +15,16 @@ import { SilenceFormStore } from "Stores/SilenceFormStore";
import { Duration } from "./Duration";
import { HourMinute } from "./HourMinute";
const nowZeroSeconds = () => {
const now = new Date();
now.setSeconds(0);
return now;
};
const OffsetBadge = ({ startDate, endDate, prefixLabel }) => {
const days = endDate.diff(startDate, "days");
const hours = endDate.diff(startDate, "hours") % 24;
const minutes = endDate.diff(startDate, "minutes") % 60;
const days = differenceInDays(endDate, startDate);
const hours = differenceInHours(endDate, startDate) % 24;
const minutes = differenceInMinutes(endDate, startDate) % 60;
return (
<span className="badge badge-light">
@@ -27,8 +36,8 @@ const OffsetBadge = ({ startDate, endDate, prefixLabel }) => {
);
};
OffsetBadge.propTypes = {
startDate: PropTypes.instanceOf(moment).isRequired,
endDate: PropTypes.instanceOf(moment).isRequired,
startDate: PropTypes.instanceOf(Date).isRequired,
endDate: PropTypes.instanceOf(Date).isRequired,
prefixLabel: PropTypes.string.isRequired,
};
@@ -60,28 +69,26 @@ const TabContentStart = ({ silenceFormStore }) => {
<div className="d-flex justify-content-center align-items-center">
<DayPicker
className="components-date-range"
month={silenceFormStore.data.startsAt.toDate()}
month={silenceFormStore.data.startsAt}
disabledDays={{
before: moment().second(0).toDate(),
before: nowZeroSeconds(),
}}
todayButton="Today"
onDayClick={(val, ...mod) => {
const startsAt = moment(val);
startsAt.set({
hour: silenceFormStore.data.startsAt.hour(),
minute: silenceFormStore.data.startsAt.minute(),
second: 0,
});
const startsAt = new Date(val);
startsAt.setHours(silenceFormStore.data.startsAt.getHours());
startsAt.setMinutes(silenceFormStore.data.startsAt.getMinutes());
startsAt.setSeconds(0);
silenceFormStore.data.startsAt = startsAt;
silenceFormStore.data.verifyStarEnd();
}}
selectedDays={{
from: silenceFormStore.data.startsAt.toDate(),
to: silenceFormStore.data.endsAt.toDate(),
from: silenceFormStore.data.startsAt,
to: silenceFormStore.data.endsAt,
}}
modifiers={{
start: silenceFormStore.data.startsAt.toDate(),
end: silenceFormStore.data.endsAt.toDate(),
start: silenceFormStore.data.startsAt,
end: silenceFormStore.data.endsAt,
}}
/>
</div>
@@ -102,28 +109,26 @@ const TabContentEnd = ({ silenceFormStore }) => {
<div className="d-flex justify-content-center align-items-center">
<DayPicker
className="components-date-range"
month={silenceFormStore.data.endsAt.toDate()}
month={silenceFormStore.data.endsAt}
disabledDays={{
before: silenceFormStore.data.startsAt.second(0).toDate(),
before: setSeconds(silenceFormStore.data.startsAt, 0),
}}
todayButton="Today"
onDayClick={(val) => {
const endsAt = moment(val);
endsAt.set({
hour: silenceFormStore.data.endsAt.hour(),
minute: silenceFormStore.data.endsAt.minute(),
second: 0,
});
const endsAt = new Date(val);
endsAt.setHours(silenceFormStore.data.endsAt.getHours());
endsAt.setMinutes(silenceFormStore.data.endsAt.getMinutes());
endsAt.setSeconds(0);
silenceFormStore.data.endsAt = endsAt;
silenceFormStore.data.verifyStarEnd();
}}
selectedDays={{
from: silenceFormStore.data.startsAt.toDate(),
to: silenceFormStore.data.endsAt.toDate(),
from: silenceFormStore.data.startsAt,
to: silenceFormStore.data.endsAt,
}}
modifiers={{
start: silenceFormStore.data.startsAt.toDate(),
end: silenceFormStore.data.endsAt.toDate(),
start: silenceFormStore.data.startsAt,
end: silenceFormStore.data.endsAt,
}}
/>
</div>
@@ -199,10 +204,10 @@ TabContentDuration.propTypes = {
const DateTimeSelect = ({ silenceFormStore, openTab }) => {
const [currentTab, setCurrentTab] = useState(openTab);
const [timeNow, setTimeNow] = useState(moment().seconds(0));
const [timeNow, setTimeNow] = useState(nowZeroSeconds());
const updateTimeNow = useCallback(() => {
setTimeNow(moment().seconds(0));
setTimeNow(nowZeroSeconds());
}, []);
useEffect(() => {
@@ -7,7 +7,9 @@ import { advanceTo, clear } from "jest-date-mock";
import toDiffableHtml from "diffable-html";
import moment from "moment";
import addMinutes from "date-fns/addMinutes";
import addHours from "date-fns/addHours";
import differenceInMilliseconds from "date-fns/differenceInMilliseconds";
import { SilenceFormStore } from "Stores/SilenceFormStore";
import {
@@ -21,8 +23,8 @@ let silenceFormStore;
beforeEach(() => {
silenceFormStore = new SilenceFormStore();
silenceFormStore.data.startsAt = moment([2060, 1, 1, 0, 0, 0]);
silenceFormStore.data.endsAt = moment([2061, 1, 1, 0, 0, 0]);
silenceFormStore.data.startsAt = new Date(2060, 1, 1, 0, 0, 0);
silenceFormStore.data.endsAt = new Date(2061, 1, 1, 0, 0, 0);
});
afterEach(() => {
@@ -121,8 +123,8 @@ describe("<DateTimeSelect />", () => {
it("'Ends' tab offset badge is updated after 1 minute", () => {
jest.useFakeTimers();
advanceTo(new Date(2060, 1, 1, 12, 0, 0));
silenceFormStore.data.startsAt = moment([2060, 1, 1, 12, 0, 0]);
silenceFormStore.data.endsAt = moment([2060, 1, 1, 13, 0, 0]);
silenceFormStore.data.startsAt = new Date(2060, 1, 1, 12, 0, 0);
silenceFormStore.data.endsAt = new Date(2060, 1, 1, 13, 0, 0);
const tree = MountedDateTimeSelect();
expect(tree.find(".nav-link").at(1).text()).toBe("Endsin 1h ");
@@ -149,19 +151,22 @@ const ValidateTimeButton = (
const button = tab.find("td > span").at(elemIndex);
expect(button.html()).toMatch(iconMatch);
const oldTimeValue = moment(silenceFormStore.data[storeKey]);
const oldTimeValue = new Date(silenceFormStore.data[storeKey]);
button.simulate("click");
expect(silenceFormStore.data[storeKey].toISOString()).not.toBe(
oldTimeValue.toISOString()
);
const diffMS = silenceFormStore.data[storeKey].diff(oldTimeValue);
const diffMS = differenceInMilliseconds(
silenceFormStore.data[storeKey],
oldTimeValue
);
expect(diffMS).toBe(expectedDiff);
};
const ValidateTimeWheel = (tab, storeKey, className, deltaY, expectedDiff) => {
const elem = tab.find(className);
const oldTimeValue = moment(silenceFormStore.data[storeKey]);
const oldTimeValue = new Date(silenceFormStore.data[storeKey]);
elem.simulate("wheel", { deltaY: deltaY });
// fire real event so cancel listener will trigger
@@ -175,7 +180,10 @@ const ValidateTimeWheel = (tab, storeKey, className, deltaY, expectedDiff) => {
expect(silenceFormStore.data[storeKey].toISOString()).not.toBe(
oldTimeValue.toISOString()
);
const diffMS = silenceFormStore.data[storeKey].diff(oldTimeValue);
const diffMS = differenceInMilliseconds(
silenceFormStore.data[storeKey],
oldTimeValue
);
expect(diffMS).toBe(expectedDiff);
};
@@ -187,11 +195,11 @@ describe("<TabContentStart />", () => {
it("selecting date on DayPicker updates startsAt", () => {
const tree = MountedTabContentStart();
expect(silenceFormStore.data.startsAt.toISOString()).toBe(
moment([2060, 1, 1, 0, 0, 0]).toISOString()
new Date(2060, 1, 1, 0, 0, 0).toISOString()
);
tree.find('div[aria-label="Wed Feb 18 2060"]').simulate("click");
expect(silenceFormStore.data.startsAt.toISOString()).toBe(
moment([2060, 1, 18, 0, 0, 0]).toISOString()
new Date(2060, 1, 18, 0, 0, 0).toISOString()
);
});
@@ -300,11 +308,11 @@ describe("<TabContentEnd />", () => {
it("Selecting date on DayPicker updates endsAt", () => {
const tree = MountedTabContentEnd();
expect(silenceFormStore.data.endsAt.toISOString()).toBe(
moment([2061, 1, 1, 0, 0, 0]).toISOString()
new Date(2061, 1, 1, 0, 0, 0).toISOString()
);
tree.find('div[aria-label="Thu Feb 24 2061"]').simulate("click");
expect(silenceFormStore.data.endsAt.toISOString()).toBe(
moment([2061, 1, 24, 0, 0, 0]).toISOString()
new Date(2061, 1, 24, 0, 0, 0).toISOString()
);
});
@@ -406,12 +414,15 @@ const ValidateDurationButton = (elemIndex, iconMatch, expectedDiff) => {
const button = tree.find("td > span").at(elemIndex);
expect(button.html()).toMatch(iconMatch);
const oldEndsAt = moment(silenceFormStore.data.endsAt);
const oldEndsAt = new Date(silenceFormStore.data.endsAt);
button.simulate("click");
expect(silenceFormStore.data.endsAt.toISOString()).not.toBe(
oldEndsAt.toISOString()
);
const diffMS = silenceFormStore.data.endsAt.diff(oldEndsAt);
const diffMS = differenceInMilliseconds(
silenceFormStore.data.endsAt,
oldEndsAt
);
expect(diffMS).toBe(expectedDiff);
};
@@ -421,7 +432,7 @@ const ValidateDurationWheel = (elemIndex, deltaY, expectedDiff) => {
);
const elem = tree.find(".components-duration").at(elemIndex);
const oldEndsAt = moment(silenceFormStore.data.endsAt);
const oldEndsAt = new Date(silenceFormStore.data.endsAt);
elem.simulate("wheel", { deltaY: deltaY });
// fire real event so cancel listener will trigger
@@ -431,7 +442,10 @@ const ValidateDurationWheel = (elemIndex, deltaY, expectedDiff) => {
expect(silenceFormStore.data.endsAt.toISOString()).not.toBe(
oldEndsAt.toISOString()
);
const diffMS = silenceFormStore.data.endsAt.diff(oldEndsAt);
const diffMS = differenceInMilliseconds(
silenceFormStore.data.endsAt,
oldEndsAt
);
expect(diffMS).toBe(expectedDiff);
};
@@ -486,8 +500,8 @@ describe("<TabContentDuration />", () => {
});
const SetDurationTo = (hours, minutes) => {
const startsAt = moment([2060, 1, 1, 0, 0, 0]);
const endsAt = moment(startsAt).add(hours, "hours").add(minutes, "minutes");
const startsAt = new Date(2060, 1, 1, 0, 0, 0);
const endsAt = addMinutes(addHours(startsAt, hours), minutes);
silenceFormStore.data.startsAt = startsAt;
silenceFormStore.data.endsAt = endsAt;
};
@@ -4,16 +4,14 @@ import { render } from "enzyme";
import toDiffableHtml from "diffable-html";
import moment from "moment";
import { SilenceFormStore } from "Stores/SilenceFormStore";
import { PayloadPreview } from ".";
describe("<PayloadPreview />", () => {
it("matches snapshot", () => {
const silenceFormStore = new SilenceFormStore();
silenceFormStore.data.startsAt = moment.utc([2000, 1, 1, 0, 0, 0]);
silenceFormStore.data.endsAt = moment.utc([2000, 1, 1, 1, 0, 0]);
silenceFormStore.data.startsAt = new Date(Date.UTC(2000, 1, 1, 0, 0, 0));
silenceFormStore.data.endsAt = new Date(Date.UTC(2000, 1, 1, 1, 0, 0));
silenceFormStore.data.createdBy = "me@example.com";
silenceFormStore.data.comment = "PayloadPreview test";
@@ -4,7 +4,8 @@ import fetchMock from "fetch-mock";
import { storiesOf } from "@storybook/react";
import moment from "moment";
import addHours from "date-fns/addHours";
import addDays from "date-fns/addDays";
import { MockSilence } from "__mocks__/Alerts";
import { AlertStore } from "Stores/AlertStore";
@@ -105,8 +106,8 @@ storiesOf("SilenceModal", module)
silenceFormStore.data.comment = "fake silence";
silenceFormStore.data.silenceID = "1234567890";
silenceFormStore.data.startsAt = moment();
silenceFormStore.data.endsAt = moment().add(2, "hour").add(10, "day");
silenceFormStore.data.startsAt = new Date();
silenceFormStore.data.endsAt = addDays(addHours(new Date(), 2), 10);
silenceFormStore.tab.current = SilenceTabNames.Editor;
+33 -20
View File
@@ -2,7 +2,13 @@ import { observable, action, computed } from "mobx";
import uniqueId from "lodash.uniqueid";
import moment from "moment";
import parseISO from "date-fns/parseISO";
import addHours from "date-fns/addHours";
import addMinutes from "date-fns/addMinutes";
import subMinutes from "date-fns/subMinutes";
import differenceInDays from "date-fns/differenceInDays";
import differenceInHours from "date-fns/differenceInHours";
import differenceInMinutes from "date-fns/differenceInMinutes";
const NewEmptyMatcher = () => {
return {
@@ -178,8 +184,8 @@ class SilenceFormStore {
silenceID: null,
alertmanagers: [],
matchers: [],
startsAt: moment(),
endsAt: moment().add(1, "hour"),
startsAt: new Date(),
endsAt: addHours(new Date(), 1),
comment: "",
author: "",
@@ -201,8 +207,8 @@ class SilenceFormStore {
},
resetStartEnd() {
this.startsAt = moment();
this.endsAt = moment().add(1, "hour");
this.startsAt = new Date();
this.endsAt = addHours(new Date(), 1);
},
resetProgress() {
@@ -260,44 +266,51 @@ class SilenceFormStore {
}
this.matchers = matchers;
this.startsAt = moment(silence.startsAt);
this.endsAt = moment(silence.endsAt);
this.startsAt = parseISO(silence.startsAt);
this.endsAt = parseISO(silence.endsAt);
this.comment = silence.comment;
this.author = silence.createdBy;
},
verifyStarEnd() {
const now = moment().second(0);
if (this.startsAt.isBefore(now)) {
const now = new Date();
now.setSeconds(0);
if (this.startsAt < now) {
this.startsAt = now;
}
if (this.endsAt.isSameOrBefore(this.startsAt)) {
this.endsAt = moment(this.startsAt).add(1, "minutes");
if (this.endsAt <= this.startsAt) {
this.endsAt = addMinutes(this.startsAt, 1);
}
},
incStart(minutes) {
this.startsAt = moment(this.startsAt).add(minutes, "minutes");
this.startsAt = addMinutes(this.startsAt, minutes);
this.verifyStarEnd();
},
decStart(minutes) {
this.startsAt = moment(this.startsAt).subtract(minutes, "minutes");
this.startsAt = subMinutes(this.startsAt, minutes);
this.verifyStarEnd();
},
incEnd(minutes) {
this.endsAt = moment(this.endsAt).add(minutes, "minutes");
this.endsAt = addMinutes(this.endsAt, minutes);
this.verifyStarEnd();
},
decEnd(minutes) {
this.endsAt = moment(this.endsAt).subtract(minutes, "minutes");
this.endsAt = subMinutes(this.endsAt, minutes);
this.verifyStarEnd();
},
get toAlertmanagerPayload() {
const startsAt = new Date(this.startsAt);
startsAt.setSeconds(0);
startsAt.setMilliseconds(0);
const endsAt = new Date(this.endsAt);
endsAt.setSeconds(0);
endsAt.setMilliseconds(0);
return GenerateAlertmanagerSilenceData(
this.startsAt.second(0).millisecond(0),
this.endsAt.second(0).millisecond(0),
startsAt,
endsAt,
this.matchers,
this.author,
this.comment,
@@ -307,9 +320,9 @@ class SilenceFormStore {
get toDuration() {
const data = {
days: this.endsAt.diff(this.startsAt, "days"),
hours: this.endsAt.diff(this.startsAt, "hours") % 24,
minutes: this.endsAt.diff(this.startsAt, "minutes") % 60,
days: differenceInDays(this.endsAt, this.startsAt),
hours: differenceInHours(this.endsAt, this.startsAt) % 24,
minutes: differenceInMinutes(this.endsAt, this.startsAt) % 60,
};
return data;
},
+38 -36
View File
@@ -1,4 +1,5 @@
import moment from "moment";
import differenceInMilliseconds from "date-fns/differenceInMilliseconds";
import isSameDay from "date-fns/isSameDay";
import {
MockAlert,
@@ -74,13 +75,13 @@ describe("SilenceFormStore.toggle", () => {
describe("SilenceFormStore.data", () => {
it("resetStartEnd() sets startsAt and endsAt to defaults", () => {
store.data.startsAt = moment([2000, 1, 1, 0, 1, 0]);
store.data.endsAt = moment([2000, 1, 1, 1, 2, 0]);
expect(store.data.startsAt.isSame([2000, 1, 1], "day")).toBe(true);
expect(store.data.endsAt.isSame([2000, 1, 1], "day")).toBe(true);
store.data.startsAt = new Date(2000, 1, 1, 0, 1, 0);
store.data.endsAt = new Date(2000, 1, 1, 1, 2, 0);
expect(isSameDay(store.data.startsAt, new Date(2000, 1, 1))).toBe(true);
expect(isSameDay(store.data.endsAt, new Date(2000, 1, 1))).toBe(true);
store.data.resetStartEnd();
expect(store.data.startsAt.isSame([2000, 1, 1], "day")).toBe(false);
expect(store.data.endsAt.isSame([2000, 1, 1], "day")).toBe(false);
expect(isSameDay(store.data.startsAt, new Date(2000, 1, 1))).toBe(false);
expect(isSameDay(store.data.endsAt, new Date(2000, 1, 1))).toBe(false);
});
it("resetProgress() sets currentStage to UserInput", () => {
@@ -303,11 +304,11 @@ describe("SilenceFormStore.data", () => {
})
);
expect(store.data.startsAt.utc().toISOString()).toBe(
moment.utc([2000, 0, 1, 0, 0, 0]).toISOString()
expect(store.data.startsAt.toISOString()).toBe(
new Date(Date.UTC(2000, 0, 1, 0, 0, 0)).toISOString()
);
expect(store.data.endsAt.utc().toISOString()).toBe(
moment.utc([2000, 0, 1, 1, 0, 0]).toISOString()
expect(store.data.endsAt.toISOString()).toBe(
new Date(Date.UTC(2000, 0, 1, 1, 0, 0)).toISOString()
);
expect(store.data.author).toBe("me@example.com");
@@ -331,8 +332,8 @@ describe("SilenceFormStore.data", () => {
store.data.fillMatchersFromGroup(group, []);
// add empty matcher so we test empty string rendering
store.data.addEmptyMatcher();
store.data.startsAt = moment.utc([2000, 1, 1, 0, 0, 0]);
store.data.endsAt = moment.utc([2000, 1, 1, 1, 0, 0]);
store.data.startsAt = new Date(Date.UTC(2000, 1, 1, 0, 0, 0));
store.data.endsAt = new Date(Date.UTC(2000, 1, 1, 1, 0, 0));
store.data.author = "me@example.com";
store.data.comment = "toAlertmanagerPayload test";
expect(store.data.toAlertmanagerPayload).toMatchSnapshot();
@@ -406,8 +407,8 @@ describe("SilenceFormStore.data.isValid", () => {
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]);
store.data.endsAt = moment([2000, 1, 6, 0, 1, 15]);
store.data.startsAt = new Date(2000, 1, 1, 0, 0, 0);
store.data.endsAt = new Date(2000, 1, 6, 0, 1, 15);
expect(store.data.toDuration).toMatchObject({
days: 5,
hours: 0,
@@ -416,8 +417,8 @@ describe("SilenceFormStore.data startsAt & endsAt validation", () => {
});
it("toDuration returns correct duration for 2h 15m", () => {
store.data.startsAt = moment([2000, 1, 1, 0, 0, 0]);
store.data.endsAt = moment([2000, 1, 1, 2, 15, 0]);
store.data.startsAt = new Date(2000, 1, 1, 0, 0, 0);
store.data.endsAt = new Date(2000, 1, 1, 2, 15, 0);
expect(store.data.toDuration).toMatchObject({
days: 0,
hours: 2,
@@ -426,8 +427,8 @@ describe("SilenceFormStore.data startsAt & endsAt validation", () => {
});
it("toDuration returns correct duration for 59m", () => {
store.data.startsAt = moment([2000, 1, 1, 0, 10, 0]);
store.data.endsAt = moment([2000, 1, 1, 1, 9, 0]);
store.data.startsAt = new Date(2000, 1, 1, 0, 10, 0);
store.data.endsAt = new Date(2000, 1, 1, 1, 9, 0);
expect(store.data.toDuration).toMatchObject({
days: 0,
hours: 0,
@@ -436,8 +437,8 @@ describe("SilenceFormStore.data startsAt & endsAt validation", () => {
});
it("verifyStarEnd() doesn't do anything if endsAt if after startsAt", () => {
const startsAt = moment([2063, 1, 1, 0, 0, 0]);
const endsAt = moment([2063, 1, 1, 1, 1, 0]);
const startsAt = new Date(2063, 1, 1, 0, 0, 0);
const endsAt = new Date(2063, 1, 1, 1, 1, 0);
store.data.startsAt = startsAt;
store.data.endsAt = endsAt;
store.data.verifyStarEnd();
@@ -446,57 +447,58 @@ describe("SilenceFormStore.data startsAt & endsAt validation", () => {
});
it("verifyStarEnd() updates startsAt if it's before now()", () => {
const now = moment().second(0);
const startsAt = moment([2000, 1, 1, 0, 0, 1]);
const endsAt = moment([2063, 1, 1, 0, 0, 0]);
const now = new Date();
now.setSeconds(0);
const startsAt = new Date(2000, 1, 1, 0, 0, 1);
const endsAt = new Date(2063, 1, 1, 0, 0, 0);
store.data.startsAt = startsAt;
store.data.endsAt = endsAt;
store.data.verifyStarEnd();
expect(store.data.startsAt.isSameOrAfter(now)).toBeTruthy();
expect(store.data.startsAt >= now).toBeTruthy();
expect(store.data.endsAt.toISOString()).toBe(endsAt.toISOString());
});
it("verifyStarEnd() updates endsAt if it's before startsAt", () => {
const startsAt = moment([2063, 1, 1, 0, 0, 1]);
const endsAt = moment([2063, 1, 1, 0, 0, 0]);
const startsAt = new Date(2063, 1, 1, 0, 0, 1);
const endsAt = new Date(2063, 1, 1, 0, 0, 0);
store.data.startsAt = startsAt;
store.data.endsAt = endsAt;
store.data.verifyStarEnd();
expect(store.data.startsAt.toISOString()).toBe(startsAt.toISOString());
expect(store.data.endsAt.toISOString()).toBe(
moment([2063, 1, 1, 0, 1, 1]).toISOString()
new Date(2063, 1, 1, 0, 1, 1).toISOString()
);
});
it("incStart(7) adds 7 minutes to startsAt", () => {
const startsAt = moment([2063, 1, 1, 0, 0, 1]);
const startsAt = new Date(2063, 1, 1, 0, 0, 1);
store.data.startsAt = startsAt;
store.data.incStart(7);
const diffMS = store.data.startsAt.diff(startsAt);
const diffMS = differenceInMilliseconds(store.data.startsAt, startsAt);
expect(diffMS).toBe(7 * 60 * 1000);
});
it("decStart(14) subtracts 14 minutes from startsAt", () => {
const startsAt = moment([2063, 1, 1, 0, 0, 1]);
const startsAt = new Date(2063, 1, 1, 0, 0, 1);
store.data.startsAt = startsAt;
store.data.decStart(14);
const diffMS = store.data.startsAt.diff(startsAt);
const diffMS = differenceInMilliseconds(store.data.startsAt, startsAt);
expect(diffMS).toBe(-14 * 60 * 1000);
});
it("incEnd(120) adds 120 minutes to endsAt", () => {
const endsAt = moment([2063, 1, 1, 0, 0, 1]);
const endsAt = new Date(2063, 1, 1, 0, 0, 1);
store.data.endsAt = endsAt;
store.data.incEnd(120);
const diffMS = store.data.endsAt.diff(endsAt);
const diffMS = differenceInMilliseconds(store.data.endsAt, endsAt);
expect(diffMS).toBe(120 * 60 * 1000);
});
it("decEnd(1) subtracts 1 minute from endsAt", () => {
const endsAt = moment([2063, 1, 1, 0, 0, 1]);
const endsAt = new Date(2063, 1, 1, 0, 0, 1);
store.data.endsAt = endsAt;
store.data.decEnd(1);
const diffMS = store.data.endsAt.diff(endsAt);
const diffMS = differenceInMilliseconds(store.data.endsAt, endsAt);
expect(diffMS).toBe(-1 * 60 * 1000);
});
});
+1 -3
View File
@@ -1,10 +1,8 @@
import moment from "moment";
import { MockAlert, MockAlertGroup, MockSilence } from "./Alerts";
const EmptyAPIResponse = () => ({
status: "success",
timestamp: moment().toISOString(),
timestamp: new Date().toISOString(),
version: "fakeVersion",
upstreams: {
counters: { total: 1, healthy: 1, failed: 0 },
+2 -2
View File
@@ -1,4 +1,4 @@
import moment from "moment";
import subMinutes from "date-fns/subMinutes";
import { MockAlert, MockAlertGroup, MockSilence } from "./Alerts";
@@ -49,7 +49,7 @@ const MockGroup = (groupName, alertCount, active, suppressed, unprocessed) => {
{ instance: `instance${i}` },
state
);
alert.startsAt = moment().subtract(alertCount, "minutes").toISOString();
alert.startsAt = subMinutes(new Date(), alertCount).toISOString();
alerts.push(alert);
}
const group = MockAlertGroup(