@oneuptime/common 12.0.27 → 12.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/AnalyticsModels/RumSessionChunk.ts +52 -0
- package/Models/DatabaseModels/CloudResource.ts +17 -1
- package/Models/DatabaseModels/DashboardDomain.ts +43 -0
- package/Models/DatabaseModels/MonitorTemplate.ts +15 -3
- package/Models/DatabaseModels/RumApplication.ts +35 -2
- package/Models/DatabaseModels/StatusPageDomain.ts +43 -0
- package/Server/API/DashboardDomainAPI.ts +74 -0
- package/Server/API/MicrosoftTeamsAPI.ts +81 -180
- package/Server/API/StatusPageDomainAPI.ts +74 -0
- package/Server/API/TelemetryAPI.ts +104 -15
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.ts +43 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/ProjectAuthorization.ts +6 -1
- package/Server/Services/DashboardDomainService.ts +131 -0
- package/Server/Services/LogPipelineProcessorService.ts +88 -0
- package/Server/Services/MonitorTemplateService.ts +70 -11
- package/Server/Services/StatusPageDomainService.ts +163 -3
- package/Server/Services/StatusPagePrivateUserSessionService.ts +197 -56
- package/Server/Types/Domain.ts +17 -0
- package/Server/Utils/APIKey/AccessPermission.ts +24 -0
- package/Server/Utils/Greenlock/Greenlock.ts +156 -58
- package/Server/Utils/LogPipelineProcessorValidation.ts +112 -0
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +1094 -45
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +153 -17
- package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +12 -5
- package/Server/Utils/SessionReplay/SessionReplayIdentity.ts +102 -0
- package/Server/Utils/SessionReplay/SessionReplayReadService.ts +11 -2
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +23 -11
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +0 -170
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.ts +158 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +23 -11
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +35 -13
- package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +505 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +106 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeUtil.test.ts +84 -0
- package/Tests/App/StatusPage/StatusPageRedirectSafety.test.ts +119 -0
- package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +81 -0
- package/Tests/Models/MonitorTemplateMonitorNameColumn.test.ts +258 -0
- package/Tests/Server/API/CustomDomainReissueSslAPI.test.ts +469 -0
- package/Tests/Server/API/MicrosoftTeamsBotMessagesEndpoint.test.ts +729 -0
- package/Tests/Server/API/MicrosoftTeamsBotTestEndpoint.test.ts +26 -0
- package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +402 -0
- package/Tests/Server/API/SessionReplayAPI.test.ts +232 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -0
- package/Tests/Server/Services/AddCertificateReissueRequestedAtToDomainsMigration.test.ts +287 -0
- package/Tests/Server/Services/CustomDomainCertificateReissue.test.ts +477 -0
- package/Tests/Server/Services/LogPipelineProcessorSaveValidation.test.ts +249 -0
- package/Tests/Server/Services/MonitorTemplateServiceBulkSync.test.ts +549 -0
- package/Tests/Server/Services/MonitorTemplateServiceNetworkDeviceSync.test.ts +16 -3
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +57 -0
- package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +151 -15
- package/Tests/Server/Services/StatusPagePrivateUserSessionService.test.ts +587 -0
- package/Tests/Server/Types/Database/Permissions/ApiKeyFileAccess.test.ts +286 -0
- package/Tests/Server/Types/Database/Permissions/MonitorTemplateColumnAccess.test.ts +328 -0
- package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAndIsNull.test.ts +83 -0
- package/Tests/Server/Utils/AI/SRE/ConfidenceSignal.test.ts +377 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +16 -1
- package/Tests/Server/Utils/Greenlock/CertificateRenewalSchedule.test.ts +250 -0
- package/Tests/Server/Utils/LogPipelineProcessorValidation.test.ts +198 -0
- package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +48 -9
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsAuth.test.ts +726 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +74 -11
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +1107 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +20 -3
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +51 -11
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +1246 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +961 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +653 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +880 -0
- package/Tests/Types/API/HTTPResponse.test.ts +175 -0
- package/Tests/Types/API/Route.test.ts +36 -4
- package/Tests/Types/Database/DatabaseProperty.test.ts +171 -0
- package/Tests/Types/DateLocalShortDateTimeString.test.ts +518 -0
- package/Tests/Types/DateLocalTimeString.test.ts +259 -0
- package/Tests/Types/DateUserPrefers12HourFormat.test.ts +316 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +41 -2
- package/Tests/UI/Components/LogsViewerEmptyStateTimeRange.test.tsx +212 -0
- package/Tests/UI/Components/TelemetryChartClockFormat.test.tsx +359 -0
- package/Tests/UI/Components/TimeRangePickerClockFormat.test.tsx +422 -0
- package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +146 -8
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +520 -0
- package/Tests/UI/Utils/Navigation.test.ts +82 -0
- package/Tests/Utils/Array.test.ts +100 -0
- package/Tests/Utils/CertificateReissue.test.ts +225 -0
- package/Tests/Utils/Grok/Grok.test.ts +492 -0
- package/Tests/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.test.ts +181 -13
- package/Types/API/Route.ts +20 -5
- package/Types/Date.ts +143 -13
- package/Types/Rum/SessionReplay.ts +76 -1
- package/UI/Components/Date/TimeRangePickerDropdown.tsx +9 -5
- package/UI/Components/LogsViewer/LogsViewer.tsx +12 -7
- package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +18 -17
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +21 -28
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +2 -4
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +2 -4
- package/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.tsx +17 -16
- package/UI/Utils/Navigation.ts +42 -0
- package/Utils/Array.ts +37 -0
- package/Utils/CertificateReissue.ts +128 -0
- package/Utils/Grok/Grok.ts +461 -0
- package/Utils/Grok/GrokPatterns.ts +118 -0
- package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +58 -6
- package/build/dist/Models/AnalyticsModels/RumSessionChunk.js +48 -0
- package/build/dist/Models/AnalyticsModels/RumSessionChunk.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CloudResource.js +19 -2
- package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DashboardDomain.js +44 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js +17 -4
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +37 -3
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js +44 -0
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
- package/build/dist/Server/API/DashboardDomainAPI.js +43 -1
- package/build/dist/Server/API/DashboardDomainAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +74 -118
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageDomainAPI.js +43 -1
- package/build/dist/Server/API/StatusPageDomainAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +67 -14
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js +28 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +6 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Services/DashboardDomainService.js +104 -0
- package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
- package/build/dist/Server/Services/LogPipelineProcessorService.js +65 -0
- package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +57 -11
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageDomainService.js +132 -3
- package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js +112 -32
- package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js.map +1 -1
- package/build/dist/Server/Types/Domain.js +16 -0
- package/build/dist/Server/Types/Domain.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +24 -0
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/Greenlock/Greenlock.js +110 -35
- package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
- package/build/dist/Server/Utils/LogPipelineProcessorValidation.js +77 -0
- package/build/dist/Server/Utils/LogPipelineProcessorValidation.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +777 -36
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +108 -17
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayIdentity.js +59 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayIdentity.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +9 -2
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +17 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +0 -159
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js +128 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +17 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -13
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Route.js +16 -5
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/Date.js +115 -13
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplay.js +38 -0
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +9 -5
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +12 -7
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +15 -14
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +17 -22
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +2 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +2 -4
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js +15 -14
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +30 -0
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/Utils/Array.js +25 -0
- package/build/dist/Utils/Array.js.map +1 -1
- package/build/dist/Utils/CertificateReissue.js +97 -0
- package/build/dist/Utils/CertificateReissue.js.map +1 -0
- package/build/dist/Utils/Grok/Grok.js +324 -0
- package/build/dist/Utils/Grok/Grok.js.map +1 -0
- package/build/dist/Utils/Grok/GrokPatterns.js +112 -0
- package/build/dist/Utils/Grok/GrokPatterns.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +63 -3
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
- package/package.json +1 -1
- package/UI/Utils/JsonWebToken.ts +0 -14
- package/build/dist/UI/Utils/JsonWebToken.js +0 -10
- package/build/dist/UI/Utils/JsonWebToken.js.map +0 -1
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
/** @timezone UTC */
|
|
2
|
+
/**
|
|
3
|
+
* The reported bug, exercised through the two pickers that actually sit above
|
|
4
|
+
* the Metrics, Traces and Logs explorers: pick a custom window, and the button
|
|
5
|
+
* summarising it must speak the clock and the timezone the person's computer is
|
|
6
|
+
* set to.
|
|
7
|
+
*
|
|
8
|
+
* It did not. The label was assembled by reading the digits off the Date
|
|
9
|
+
* (`date.getHours().toString().padStart(2, "0")`), which is a 24-hour clock in
|
|
10
|
+
* the browser process's zone and nothing else - so a user on an AM/PM machine
|
|
11
|
+
* picked 2:30 PM in the modal and got a button reading "Mar 1, 14:30", and a
|
|
12
|
+
* user who had set a timezone in User Settings got a button disagreeing with
|
|
13
|
+
* the modal they had just set the window in.
|
|
14
|
+
*
|
|
15
|
+
* These render the real components rather than calling the formatter, because
|
|
16
|
+
* the formatter being right is worth nothing if the button stops reading from
|
|
17
|
+
* it. The timezone is pinned to UTC by the docblock above and the 12/24-hour
|
|
18
|
+
* preference is pinned per test, so the assertions hold on any machine.
|
|
19
|
+
*/
|
|
20
|
+
import InBetween from "../../../Types/BaseDatabase/InBetween";
|
|
21
|
+
import OneUptimeDate from "../../../Types/Date";
|
|
22
|
+
import RangeStartAndEndDateTime from "../../../Types/Time/RangeStartAndEndDateTime";
|
|
23
|
+
import TimeRange from "../../../Types/Time/TimeRange";
|
|
24
|
+
import Timezone from "../../../Types/Timezone";
|
|
25
|
+
import LogTimeRangePicker, {
|
|
26
|
+
LOG_TIME_RANGE_PICKER_TEST_ID_PREFIX,
|
|
27
|
+
} from "../../../UI/Components/LogsViewer/components/LogTimeRangePicker";
|
|
28
|
+
import TelemetryTimeRangePicker, {
|
|
29
|
+
TELEMETRY_TIME_RANGE_PICKER_TEST_ID_PREFIX,
|
|
30
|
+
} from "../../../UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker";
|
|
31
|
+
import "@testing-library/jest-dom";
|
|
32
|
+
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
33
|
+
import React from "react";
|
|
34
|
+
import { afterEach, describe, expect, jest, test } from "@jest/globals";
|
|
35
|
+
import fs from "fs";
|
|
36
|
+
import path from "path";
|
|
37
|
+
|
|
38
|
+
/*
|
|
39
|
+
* An afternoon window, so a 12-hour clock has to do real work: 14:30 becoming
|
|
40
|
+
* "2:30 PM" is the exact translation the bug was skipping.
|
|
41
|
+
*/
|
|
42
|
+
const AFTERNOON_WINDOW: InBetween<Date> = new InBetween<Date>(
|
|
43
|
+
new Date("2024-03-01T14:30:00.000Z"),
|
|
44
|
+
new Date("2024-03-01T18:45:00.000Z"),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const CUSTOM_AFTERNOON: RangeStartAndEndDateTime = {
|
|
48
|
+
range: TimeRange.CUSTOM,
|
|
49
|
+
startAndEndDate: AFTERNOON_WINDOW,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
interface Surface {
|
|
53
|
+
name: string;
|
|
54
|
+
prefix: string;
|
|
55
|
+
render: (value: RangeStartAndEndDateTime) => void;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const SURFACES: Array<Surface> = [
|
|
59
|
+
{
|
|
60
|
+
name: "metrics and traces",
|
|
61
|
+
prefix: TELEMETRY_TIME_RANGE_PICKER_TEST_ID_PREFIX,
|
|
62
|
+
render: (value: RangeStartAndEndDateTime): void => {
|
|
63
|
+
render(
|
|
64
|
+
<TelemetryTimeRangePicker
|
|
65
|
+
value={value}
|
|
66
|
+
onChange={() => {
|
|
67
|
+
// The label under test does not depend on the change handler.
|
|
68
|
+
}}
|
|
69
|
+
/>,
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "logs",
|
|
75
|
+
prefix: LOG_TIME_RANGE_PICKER_TEST_ID_PREFIX,
|
|
76
|
+
render: (value: RangeStartAndEndDateTime): void => {
|
|
77
|
+
render(
|
|
78
|
+
<LogTimeRangePicker
|
|
79
|
+
value={value}
|
|
80
|
+
onChange={() => {
|
|
81
|
+
// The label under test does not depend on the change handler.
|
|
82
|
+
}}
|
|
83
|
+
/>,
|
|
84
|
+
);
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
function pin(use12HourFormat: boolean): void {
|
|
90
|
+
jest
|
|
91
|
+
.spyOn(OneUptimeDate, "getUserPrefers12HourFormat")
|
|
92
|
+
.mockReturnValue(use12HourFormat);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function buttonText(prefix: string): string {
|
|
96
|
+
return (screen.getByTestId(`${prefix}-button`).textContent || "").trim();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
afterEach(() => {
|
|
100
|
+
cleanup();
|
|
101
|
+
jest.restoreAllMocks();
|
|
102
|
+
OneUptimeDate.setUserTimezone(null);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe.each(SURFACES)(
|
|
106
|
+
"the $name time range picker button",
|
|
107
|
+
(surface: Surface) => {
|
|
108
|
+
describe("on a machine set to a 12-hour clock", () => {
|
|
109
|
+
test("writes the custom window with AM/PM", () => {
|
|
110
|
+
pin(true);
|
|
111
|
+
surface.render(CUSTOM_AFTERNOON);
|
|
112
|
+
|
|
113
|
+
expect(buttonText(surface.prefix)).toBe(
|
|
114
|
+
"Mar 1, 2:30 PM – Mar 1, 6:45 PM",
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("never leaves a bare 24-hour reading on the button", () => {
|
|
119
|
+
pin(true);
|
|
120
|
+
surface.render(CUSTOM_AFTERNOON);
|
|
121
|
+
|
|
122
|
+
const label: string = buttonText(surface.prefix);
|
|
123
|
+
|
|
124
|
+
expect(label).toContain("PM");
|
|
125
|
+
expect(label).not.toContain("14:30");
|
|
126
|
+
expect(label).not.toContain("18:45");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("marks a window that straddles noon AM then PM", () => {
|
|
130
|
+
pin(true);
|
|
131
|
+
surface.render({
|
|
132
|
+
range: TimeRange.CUSTOM,
|
|
133
|
+
startAndEndDate: new InBetween<Date>(
|
|
134
|
+
new Date("2024-03-01T08:00:00.000Z"),
|
|
135
|
+
new Date("2024-03-01T20:00:00.000Z"),
|
|
136
|
+
),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(buttonText(surface.prefix)).toBe(
|
|
140
|
+
"Mar 1, 8:00 AM – Mar 1, 8:00 PM",
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("writes a whole day from 12 AM to 11:59 PM", () => {
|
|
145
|
+
pin(true);
|
|
146
|
+
surface.render({
|
|
147
|
+
range: TimeRange.CUSTOM,
|
|
148
|
+
startAndEndDate: new InBetween<Date>(
|
|
149
|
+
new Date("2024-03-01T00:00:00.000Z"),
|
|
150
|
+
new Date("2024-03-01T23:59:00.000Z"),
|
|
151
|
+
),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
expect(buttonText(surface.prefix)).toBe(
|
|
155
|
+
"Mar 1, 12:00 AM – Mar 1, 11:59 PM",
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe("on a machine set to a 24-hour clock", () => {
|
|
161
|
+
test("writes the custom window on a 24-hour clock", () => {
|
|
162
|
+
pin(false);
|
|
163
|
+
surface.render(CUSTOM_AFTERNOON);
|
|
164
|
+
|
|
165
|
+
expect(buttonText(surface.prefix)).toBe("Mar 1, 14:30 – Mar 1, 18:45");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("puts no day period on the button", () => {
|
|
169
|
+
pin(false);
|
|
170
|
+
surface.render(CUSTOM_AFTERNOON);
|
|
171
|
+
|
|
172
|
+
expect(buttonText(surface.prefix)).not.toMatch(/AM|PM/);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("pads a single-digit hour", () => {
|
|
176
|
+
pin(false);
|
|
177
|
+
surface.render({
|
|
178
|
+
range: TimeRange.CUSTOM,
|
|
179
|
+
startAndEndDate: new InBetween<Date>(
|
|
180
|
+
new Date("2024-03-01T08:00:00.000Z"),
|
|
181
|
+
new Date("2024-03-01T09:05:00.000Z"),
|
|
182
|
+
),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(buttonText(surface.prefix)).toBe("Mar 1, 08:00 – Mar 1, 09:05");
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe("timezone", () => {
|
|
190
|
+
test("reads the window in the timezone set in User Settings", () => {
|
|
191
|
+
pin(true);
|
|
192
|
+
OneUptimeDate.setUserTimezone(Timezone.AsiaKolkata);
|
|
193
|
+
surface.render(CUSTOM_AFTERNOON);
|
|
194
|
+
|
|
195
|
+
// 14:30 and 18:45 UTC are 20:00 and 00:15 (next day) at +05:30.
|
|
196
|
+
expect(buttonText(surface.prefix)).toBe(
|
|
197
|
+
"Mar 1, 8:00 PM – Mar 2, 12:15 AM",
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("rolls the date back when the configured zone is behind UTC", () => {
|
|
202
|
+
pin(false);
|
|
203
|
+
OneUptimeDate.setUserTimezone(Timezone.AmericaLos_Angeles);
|
|
204
|
+
surface.render({
|
|
205
|
+
range: TimeRange.CUSTOM,
|
|
206
|
+
startAndEndDate: new InBetween<Date>(
|
|
207
|
+
new Date("2024-03-01T02:00:00.000Z"),
|
|
208
|
+
new Date("2024-03-01T10:00:00.000Z"),
|
|
209
|
+
),
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// 02:00 UTC is 18:00 the previous evening in Los Angeles (PST).
|
|
213
|
+
expect(buttonText(surface.prefix)).toBe("Feb 29, 18:00 – Mar 1, 02:00");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("agrees with the summary inside the modal that set the window", () => {
|
|
217
|
+
/*
|
|
218
|
+
* The two used to disagree outright: the modal summary already went
|
|
219
|
+
* through the timezone- and clock-aware formatter while the button did
|
|
220
|
+
* not, so the same window read two different ways on one screen.
|
|
221
|
+
*/
|
|
222
|
+
pin(true);
|
|
223
|
+
OneUptimeDate.setUserTimezone(Timezone.AsiaKolkata);
|
|
224
|
+
surface.render(CUSTOM_AFTERNOON);
|
|
225
|
+
|
|
226
|
+
fireEvent.click(screen.getByTestId(`${surface.prefix}-button`));
|
|
227
|
+
fireEvent.click(screen.getByTestId(`${surface.prefix}-custom-option`));
|
|
228
|
+
|
|
229
|
+
const summary: string =
|
|
230
|
+
screen.getByTestId("custom-time-range-summary").textContent || "";
|
|
231
|
+
|
|
232
|
+
/*
|
|
233
|
+
* The summary carries seconds and a year the compact button label
|
|
234
|
+
* drops, so compare the parts they both claim: the wall clock.
|
|
235
|
+
*/
|
|
236
|
+
expect(summary).toContain("8:00:00 PM");
|
|
237
|
+
expect(summary).toContain("12:15:00 AM");
|
|
238
|
+
expect(buttonText(surface.prefix)).toContain("8:00 PM");
|
|
239
|
+
expect(buttonText(surface.prefix)).toContain("12:15 AM");
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe("presets are unaffected", () => {
|
|
244
|
+
test("shows the preset's own label on a 12-hour machine", () => {
|
|
245
|
+
pin(true);
|
|
246
|
+
surface.render({ range: TimeRange.PAST_ONE_HOUR });
|
|
247
|
+
|
|
248
|
+
expect(buttonText(surface.prefix)).toBe("Past 1 Hour");
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("shows the preset's own label on a 24-hour machine", () => {
|
|
252
|
+
pin(false);
|
|
253
|
+
surface.render({ range: TimeRange.PAST_ONE_HOUR });
|
|
254
|
+
|
|
255
|
+
expect(buttonText(surface.prefix)).toBe("Past 1 Hour");
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("shows no time at all for a preset", () => {
|
|
259
|
+
pin(true);
|
|
260
|
+
surface.render({ range: TimeRange.PAST_THIRTY_MINS });
|
|
261
|
+
|
|
262
|
+
expect(buttonText(surface.prefix)).not.toMatch(/\d{1,2}:\d{2}/);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
},
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
describe("applying a window from the modal", () => {
|
|
269
|
+
test("re-labels the button on the machine's clock once applied", () => {
|
|
270
|
+
/*
|
|
271
|
+
* End to end: open the picker, pick the custom editor, type a window,
|
|
272
|
+
* apply, and read back what the button now says. This is the path the bug
|
|
273
|
+
* report walks.
|
|
274
|
+
*/
|
|
275
|
+
pin(true);
|
|
276
|
+
|
|
277
|
+
const emitted: Array<RangeStartAndEndDateTime> = [];
|
|
278
|
+
const prefix: string = TELEMETRY_TIME_RANGE_PICKER_TEST_ID_PREFIX;
|
|
279
|
+
|
|
280
|
+
const view: { rerender: (ui: React.ReactElement) => void } = render(
|
|
281
|
+
<TelemetryTimeRangePicker
|
|
282
|
+
value={{ range: TimeRange.PAST_ONE_HOUR }}
|
|
283
|
+
onChange={(next: RangeStartAndEndDateTime) => {
|
|
284
|
+
emitted.push(next);
|
|
285
|
+
}}
|
|
286
|
+
/>,
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
fireEvent.click(screen.getByTestId(`${prefix}-button`));
|
|
290
|
+
fireEvent.click(screen.getByTestId(`${prefix}-custom-option`));
|
|
291
|
+
|
|
292
|
+
fireEvent.change(screen.getByTestId("custom-time-range-start"), {
|
|
293
|
+
target: { value: "2024-03-01T14:30:00" },
|
|
294
|
+
});
|
|
295
|
+
fireEvent.change(screen.getByTestId("custom-time-range-end"), {
|
|
296
|
+
target: { value: "2024-03-01T18:45:00" },
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
fireEvent.click(screen.getByText("Apply"));
|
|
300
|
+
|
|
301
|
+
expect(emitted).toHaveLength(1);
|
|
302
|
+
expect(emitted[0]?.range).toBe(TimeRange.CUSTOM);
|
|
303
|
+
|
|
304
|
+
// The parent re-renders the picker with the window it was handed.
|
|
305
|
+
view.rerender(
|
|
306
|
+
<TelemetryTimeRangePicker
|
|
307
|
+
value={emitted[0] as RangeStartAndEndDateTime}
|
|
308
|
+
onChange={() => {
|
|
309
|
+
// Not exercised again.
|
|
310
|
+
}}
|
|
311
|
+
/>,
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
expect(buttonText(prefix)).toBe("Mar 1, 2:30 PM – Mar 1, 6:45 PM");
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test("re-labels the same applied window on a 24-hour machine", () => {
|
|
318
|
+
pin(false);
|
|
319
|
+
|
|
320
|
+
const emitted: Array<RangeStartAndEndDateTime> = [];
|
|
321
|
+
const prefix: string = LOG_TIME_RANGE_PICKER_TEST_ID_PREFIX;
|
|
322
|
+
|
|
323
|
+
const view: { rerender: (ui: React.ReactElement) => void } = render(
|
|
324
|
+
<LogTimeRangePicker
|
|
325
|
+
value={{ range: TimeRange.PAST_ONE_HOUR }}
|
|
326
|
+
onChange={(next: RangeStartAndEndDateTime) => {
|
|
327
|
+
emitted.push(next);
|
|
328
|
+
}}
|
|
329
|
+
/>,
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
fireEvent.click(screen.getByTestId(`${prefix}-button`));
|
|
333
|
+
fireEvent.click(screen.getByTestId(`${prefix}-custom-option`));
|
|
334
|
+
|
|
335
|
+
fireEvent.change(screen.getByTestId("custom-time-range-start"), {
|
|
336
|
+
target: { value: "2024-03-01T14:30:00" },
|
|
337
|
+
});
|
|
338
|
+
fireEvent.change(screen.getByTestId("custom-time-range-end"), {
|
|
339
|
+
target: { value: "2024-03-01T18:45:00" },
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
fireEvent.click(screen.getByText("Apply"));
|
|
343
|
+
|
|
344
|
+
view.rerender(
|
|
345
|
+
<LogTimeRangePicker
|
|
346
|
+
value={emitted[0] as RangeStartAndEndDateTime}
|
|
347
|
+
onChange={() => {
|
|
348
|
+
// Not exercised again.
|
|
349
|
+
}}
|
|
350
|
+
/>,
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
expect(buttonText(prefix)).toBe("Mar 1, 14:30 – Mar 1, 18:45");
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
/*
|
|
358
|
+
* The defect was not a wrong format string - it was a formatter hand-rolled out
|
|
359
|
+
* of Date's own getters, which cannot see either the configured timezone or the
|
|
360
|
+
* machine's clock preference. A rendering test cannot catch that coming back on
|
|
361
|
+
* some future surface, so this reads the source and refuses the idiom outright.
|
|
362
|
+
*/
|
|
363
|
+
describe("the picker never hand-rolls a date format again", () => {
|
|
364
|
+
/*
|
|
365
|
+
* Comments are stripped first: the banned tokens are exactly the words this
|
|
366
|
+
* file's own comments use to explain what went wrong, so a guard that reads
|
|
367
|
+
* them would fail on a prose edit that changes no behaviour.
|
|
368
|
+
*/
|
|
369
|
+
const PICKER_SOURCE: string = fs
|
|
370
|
+
.readFileSync(
|
|
371
|
+
path.join(
|
|
372
|
+
__dirname,
|
|
373
|
+
"..",
|
|
374
|
+
"..",
|
|
375
|
+
"..",
|
|
376
|
+
"UI",
|
|
377
|
+
"Components",
|
|
378
|
+
"Date",
|
|
379
|
+
"TimeRangePickerDropdown.tsx",
|
|
380
|
+
),
|
|
381
|
+
"utf-8",
|
|
382
|
+
)
|
|
383
|
+
.replace(/\/\*[\s\S]*?\*\//g, " ")
|
|
384
|
+
.replace(/\/\/.*$/gm, " ");
|
|
385
|
+
|
|
386
|
+
test("reads no wall-clock field off the Date itself", () => {
|
|
387
|
+
/*
|
|
388
|
+
* getHours/getMinutes/getDate report the browser process's zone, never the
|
|
389
|
+
* one the user configured, so none of them belongs in a rendered label.
|
|
390
|
+
*/
|
|
391
|
+
for (const banned of [
|
|
392
|
+
".getHours()",
|
|
393
|
+
".getMinutes()",
|
|
394
|
+
".getDate()",
|
|
395
|
+
".getMonth()",
|
|
396
|
+
]) {
|
|
397
|
+
expect(PICKER_SOURCE).not.toContain(banned);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
test("pins no locale of its own", () => {
|
|
402
|
+
/*
|
|
403
|
+
* Match the date-shaped calls only. A bare "toLocaleString" ban would also
|
|
404
|
+
* catch Number.prototype.toLocaleString, which formats counts and has
|
|
405
|
+
* nothing to do with this bug.
|
|
406
|
+
*/
|
|
407
|
+
expect(PICKER_SOURCE).not.toContain('"en-US"');
|
|
408
|
+
expect(PICKER_SOURCE).not.toMatch(
|
|
409
|
+
/toLocale(Date|Time)?String\s*\(\s*["'`[]/,
|
|
410
|
+
);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test("hardcodes no hour cycle", () => {
|
|
414
|
+
expect(PICKER_SOURCE).not.toContain("hour12");
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
test("goes through the shared timezone- and clock-aware formatter", () => {
|
|
418
|
+
expect(PICKER_SOURCE).toContain(
|
|
419
|
+
"OneUptimeDate.getDateAsLocalShortDateTimeString",
|
|
420
|
+
);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
@@ -8,10 +8,11 @@ import InBetween from "../../../Types/BaseDatabase/InBetween";
|
|
|
8
8
|
import OneUptimeDate from "../../../Types/Date";
|
|
9
9
|
import RangeStartAndEndDateTime from "../../../Types/Time/RangeStartAndEndDateTime";
|
|
10
10
|
import TimeRange from "../../../Types/Time/TimeRange";
|
|
11
|
+
import Timezone from "../../../Types/Timezone";
|
|
11
12
|
import "@testing-library/jest-dom";
|
|
12
13
|
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
13
14
|
import React from "react";
|
|
14
|
-
import { afterEach, describe, expect, test } from "@jest/globals";
|
|
15
|
+
import { afterEach, describe, expect, jest, test } from "@jest/globals";
|
|
15
16
|
|
|
16
17
|
const PREFIX: string = "test-range-picker";
|
|
17
18
|
const DROPDOWN_WIDTH_IN_PX: number = 288;
|
|
@@ -71,8 +72,31 @@ function minutesBetweenFields(): number {
|
|
|
71
72
|
|
|
72
73
|
afterEach(() => {
|
|
73
74
|
cleanup();
|
|
75
|
+
jest.restoreAllMocks();
|
|
76
|
+
OneUptimeDate.setUserTimezone(null);
|
|
74
77
|
});
|
|
75
78
|
|
|
79
|
+
/*
|
|
80
|
+
* The label writes times the way the machine running the browser writes them,
|
|
81
|
+
* so a test that does not pin the preference reads "Mar 1, 10:00" on a European
|
|
82
|
+
* laptop and "Mar 1, 10:00 AM" on a US one. Pinning it is what lets these
|
|
83
|
+
* assert the digits rather than a regex loose enough to accept the time going
|
|
84
|
+
* missing altogether.
|
|
85
|
+
*/
|
|
86
|
+
function pin(use12HourFormat: boolean): void {
|
|
87
|
+
jest
|
|
88
|
+
.spyOn(OneUptimeDate, "getUserPrefers12HourFormat")
|
|
89
|
+
.mockReturnValue(use12HourFormat);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const CUSTOM_RANGE: RangeStartAndEndDateTime = {
|
|
93
|
+
range: TimeRange.CUSTOM,
|
|
94
|
+
startAndEndDate: new InBetween<Date>(
|
|
95
|
+
new Date("2024-03-01T10:00:00.000Z"),
|
|
96
|
+
new Date("2024-03-02T13:30:00.000Z"),
|
|
97
|
+
),
|
|
98
|
+
};
|
|
99
|
+
|
|
76
100
|
describe("getTimeRangeButtonLabel", () => {
|
|
77
101
|
test("uses the preset label for a preset range", () => {
|
|
78
102
|
expect(getTimeRangeButtonLabel({ range: TimeRange.PAST_THIRTY_MINS })).toBe(
|
|
@@ -87,15 +111,129 @@ describe("getTimeRangeButtonLabel", () => {
|
|
|
87
111
|
});
|
|
88
112
|
|
|
89
113
|
test("summarises a custom range as a start–end pair", () => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
114
|
+
pin(false);
|
|
115
|
+
|
|
116
|
+
expect(getTimeRangeButtonLabel(CUSTOM_RANGE)).toBe(
|
|
117
|
+
"Mar 1, 10:00 – Mar 2, 13:30",
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
/*
|
|
122
|
+
* The bug these cover: the label was built by reading the digits off the
|
|
123
|
+
* Date - `date.getHours().toString().padStart(2, "0")` - so it was always a
|
|
124
|
+
* 24-hour clock in the browser process's own zone. A user whose computer is
|
|
125
|
+
* set to AM/PM picked an afternoon window and got "Mar 1, 14:30" back, and a
|
|
126
|
+
* user with a timezone set in User Settings got a label that disagreed with
|
|
127
|
+
* the modal they had just picked the window in.
|
|
128
|
+
*/
|
|
129
|
+
describe("clock format", () => {
|
|
130
|
+
test("writes both ends with AM/PM on a 12-hour machine", () => {
|
|
131
|
+
pin(true);
|
|
132
|
+
|
|
133
|
+
expect(getTimeRangeButtonLabel(CUSTOM_RANGE)).toBe(
|
|
134
|
+
"Mar 1, 10:00 AM – Mar 2, 1:30 PM",
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("writes both ends on a 24-hour clock on a 24-hour machine", () => {
|
|
139
|
+
pin(false);
|
|
140
|
+
|
|
141
|
+
const label: string = getTimeRangeButtonLabel(CUSTOM_RANGE);
|
|
142
|
+
|
|
143
|
+
expect(label).toBe("Mar 1, 10:00 – Mar 2, 13:30");
|
|
144
|
+
expect(label).not.toMatch(/AM|PM/);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("marks a morning start AM and an afternoon end PM", () => {
|
|
148
|
+
pin(true);
|
|
149
|
+
|
|
150
|
+
expect(
|
|
151
|
+
getTimeRangeButtonLabel({
|
|
152
|
+
range: TimeRange.CUSTOM,
|
|
153
|
+
startAndEndDate: new InBetween<Date>(
|
|
154
|
+
new Date("2024-03-01T09:15:00.000Z"),
|
|
155
|
+
new Date("2024-03-01T17:45:00.000Z"),
|
|
156
|
+
),
|
|
157
|
+
}),
|
|
158
|
+
).toBe("Mar 1, 9:15 AM – Mar 1, 5:45 PM");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("writes a midnight-to-noon window as 12 AM to 12 PM", () => {
|
|
162
|
+
pin(true);
|
|
163
|
+
|
|
164
|
+
expect(
|
|
165
|
+
getTimeRangeButtonLabel({
|
|
166
|
+
range: TimeRange.CUSTOM,
|
|
167
|
+
startAndEndDate: new InBetween<Date>(
|
|
168
|
+
new Date("2024-03-01T00:00:00.000Z"),
|
|
169
|
+
new Date("2024-03-01T12:00:00.000Z"),
|
|
170
|
+
),
|
|
171
|
+
}),
|
|
172
|
+
).toBe("Mar 1, 12:00 AM – Mar 1, 12:00 PM");
|
|
96
173
|
});
|
|
97
174
|
|
|
98
|
-
|
|
175
|
+
test("keeps the en dash separator on both clocks", () => {
|
|
176
|
+
pin(true);
|
|
177
|
+
expect(getTimeRangeButtonLabel(CUSTOM_RANGE)).toContain(" – ");
|
|
178
|
+
|
|
179
|
+
pin(false);
|
|
180
|
+
expect(getTimeRangeButtonLabel(CUSTOM_RANGE)).toContain(" – ");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("leaves preset labels untouched by the clock preference", () => {
|
|
184
|
+
pin(true);
|
|
185
|
+
const on12Hour: string = getTimeRangeButtonLabel({
|
|
186
|
+
range: TimeRange.PAST_ONE_HOUR,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
pin(false);
|
|
190
|
+
const on24Hour: string = getTimeRangeButtonLabel({
|
|
191
|
+
range: TimeRange.PAST_ONE_HOUR,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
expect(on12Hour).toBe("Past 1 Hour");
|
|
195
|
+
expect(on24Hour).toBe("Past 1 Hour");
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("timezone", () => {
|
|
200
|
+
test("reads the window in the timezone configured in User Settings", () => {
|
|
201
|
+
pin(true);
|
|
202
|
+
OneUptimeDate.setUserTimezone(Timezone.AsiaKolkata);
|
|
203
|
+
|
|
204
|
+
// 10:00 and 13:30 UTC are 15:30 and 19:00 at +05:30.
|
|
205
|
+
expect(getTimeRangeButtonLabel(CUSTOM_RANGE)).toBe(
|
|
206
|
+
"Mar 1, 3:30 PM – Mar 2, 7:00 PM",
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("moves the date too when the configured zone crosses midnight", () => {
|
|
211
|
+
pin(false);
|
|
212
|
+
OneUptimeDate.setUserTimezone(Timezone.AmericaNew_York);
|
|
213
|
+
|
|
214
|
+
expect(
|
|
215
|
+
getTimeRangeButtonLabel({
|
|
216
|
+
range: TimeRange.CUSTOM,
|
|
217
|
+
startAndEndDate: new InBetween<Date>(
|
|
218
|
+
new Date("2024-03-01T02:00:00.000Z"),
|
|
219
|
+
new Date("2024-03-01T06:00:00.000Z"),
|
|
220
|
+
),
|
|
221
|
+
}),
|
|
222
|
+
).toBe("Feb 29, 21:00 – Mar 1, 01:00");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("relabels the same window when the configured zone changes", () => {
|
|
226
|
+
pin(false);
|
|
227
|
+
|
|
228
|
+
OneUptimeDate.setUserTimezone(Timezone.UTC);
|
|
229
|
+
const inUtc: string = getTimeRangeButtonLabel(CUSTOM_RANGE);
|
|
230
|
+
|
|
231
|
+
OneUptimeDate.setUserTimezone(Timezone.AsiaKolkata);
|
|
232
|
+
const inKolkata: string = getTimeRangeButtonLabel(CUSTOM_RANGE);
|
|
233
|
+
|
|
234
|
+
expect(inUtc).toBe("Mar 1, 10:00 – Mar 2, 13:30");
|
|
235
|
+
expect(inKolkata).toBe("Mar 1, 15:30 – Mar 2, 19:00");
|
|
236
|
+
});
|
|
99
237
|
});
|
|
100
238
|
});
|
|
101
239
|
|