@oneuptime/common 12.0.9 → 12.0.11
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/DatabaseModels/NetworkDevice.ts +44 -0
- package/Models/DatabaseModels/NetworkDeviceLink.ts +116 -0
- package/Models/DatabaseModels/RunnerJob.ts +17 -2
- package/Server/API/LlmProviderAPI.ts +8 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +240 -0
- package/Server/Services/NetworkDeviceLinkService.ts +338 -2
- package/Server/Services/NetworkDeviceService.ts +265 -12
- package/Server/Services/RunnerJobService.ts +24 -1
- package/Server/Types/Billing/MeteredPlan/AllMeteredPlans.ts +19 -5
- package/Server/Types/Database/JSONColumnQuery.ts +42 -4
- package/Server/Utils/LLM/LLMService.ts +108 -19
- package/Server/Utils/Monitor/MonitorAlert.ts +22 -115
- package/Server/Utils/Monitor/MonitorIncident.ts +7 -337
- package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +27 -2
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +32 -0
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +469 -0
- package/Server/Utils/SSRFProtection.ts +5 -4
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +186 -38
- package/Tests/App/AdminDashboard/AdminUsersBulkAddToProject.test.tsx +955 -0
- package/Tests/App/AdminDashboard/BulkAddUsersToProject.test.ts +778 -0
- package/Tests/App/AdminDashboard/BulkAddUsersToProjectModal.test.tsx +1051 -0
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +420 -112
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +81 -6
- package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +543 -0
- package/Tests/App/Dashboard/PayAsYouGoFreePlan.test.ts +125 -0
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +612 -0
- package/Tests/App/Dashboard/SloWidgetChartRendering.test.tsx +536 -0
- package/Tests/App/Dashboard/UserElementEmail.test.tsx +447 -0
- package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +284 -0
- package/Tests/Models/AnalyticsModels/CommonModel.test.ts +312 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +250 -0
- package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +46 -18
- package/Tests/Server/Services/NetworkDeviceLabelRuleRun.test.ts +618 -0
- package/Tests/Server/Services/NetworkDeviceLinkParentValidation.test.ts +1043 -0
- package/Tests/Server/Services/NetworkSiteAssignmentRuleRun.test.ts +706 -0
- package/Tests/Server/Services/RunnerJobEnqueue.test.ts +412 -0
- package/Tests/Server/Types/Billing/PayAsYouGoMeteredPricing.test.ts +125 -0
- package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +508 -0
- package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +2 -2
- package/Tests/Server/Utils/AI/LLMServiceRetryPolicy.test.ts +450 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +556 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +547 -0
- package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +30 -0
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +699 -0
- package/Tests/Server/Utils/SeriesResourceLabels.test.ts +55 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +668 -0
- package/Tests/Types/API/URLMalformedTolerance.test.ts +301 -0
- package/Tests/Types/API/URLTelScheme.test.ts +261 -0
- package/Tests/Types/API/URLUnsupportedScheme.test.ts +148 -0
- package/Tests/Types/Billing/PayAsYouGoPricing.test.ts +64 -0
- package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +107 -0
- package/Tests/Types/Monitor/MonitorType.test.ts +62 -0
- package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +184 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +124 -0
- package/Tests/Types/Workflow/BaseModelComponents.test.ts +17 -0
- package/Tests/UI/Components/Button.test.tsx +279 -0
- package/Tests/UI/Components/Charts/ChartXAxisDataKey.test.tsx +256 -0
- package/Tests/UI/Components/Charts/DataPointUtil.test.ts +323 -0
- package/Tests/UI/Components/DictionaryValuePlaceholderAndSuggestions.test.tsx +595 -0
- package/Tests/UI/Components/Icon.test.tsx +57 -0
- package/Tests/UI/Components/List.test.tsx +20 -3
- package/Tests/UI/Components/LogsSavedViewsDropdown.test.tsx +304 -0
- package/Tests/UI/Components/LogsViewerClearSavedView.test.tsx +179 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +349 -1
- package/Tests/UI/Components/Pagination.test.tsx +721 -146
- package/Tests/UI/Components/PaginationIntegration.test.tsx +386 -0
- package/Tests/UI/Components/PaginationUtil.test.ts +417 -0
- package/Tests/UI/Components/TelemetrySavedViewsDropdown.test.tsx +343 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnControl.test.ts +289 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnOperators.test.ts +271 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRow.test.ts +119 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.test.ts +968 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +766 -0
- package/Tests/UI/Utils/RunNetworkRule.test.ts +320 -0
- package/Tests/Utils/APIRetryPolicy.test.ts +823 -0
- package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +502 -0
- package/Tests/Utils/Monitor/NetworkTopologyDeclaredHierarchy.test.ts +759 -0
- package/Tests/Utils/NetworkAutomation/RuleRunSummary.test.ts +286 -0
- package/Tests/Utils/Schema/BaseSchema.test.ts +651 -0
- package/Types/API/Protocol.ts +9 -0
- package/Types/API/URL.ts +286 -20
- package/Types/Billing/PayAsYouGoPricing.ts +47 -0
- package/Types/CustomField/CustomFieldType.ts +28 -0
- package/Types/Monitor/MonitorType.ts +11 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
- package/Types/NetworkAutomation/RuleRunResult.ts +121 -0
- package/Types/Runbook/RunbookStepType.ts +23 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +7 -0
- package/UI/Components/Button/Button.tsx +14 -22
- package/UI/Components/Charts/Area/AreaChart.tsx +4 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +4 -2
- package/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.ts +13 -0
- package/UI/Components/Charts/Line/LineChart.tsx +4 -2
- package/UI/Components/Charts/Types/XAxis/XAxis.ts +6 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +15 -12
- package/UI/Components/Dictionary/Dictionary.tsx +4 -39
- package/UI/Components/Icon/Icon.tsx +1 -1
- package/UI/Components/LogsViewer/LogsViewer.tsx +5 -1
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +8 -0
- package/UI/Components/LogsViewer/components/LogsPagination.tsx +29 -91
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +2 -0
- package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +66 -1
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +55 -0
- package/UI/Components/Pagination/Pagination.tsx +373 -328
- package/UI/Components/Pagination/PaginationUtil.ts +263 -0
- package/UI/Components/Pagination/Screenshots/README.md +15 -0
- package/UI/Components/Pagination/Screenshots/pagination-before-after.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-go-to-page-modal.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-long-list.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-mobile.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-telemetry.png +0 -0
- package/UI/Components/TelemetryViewer/components/SavedViewsDropdown.tsx +66 -1
- package/UI/Components/TelemetryViewer/components/TelemetryPagination.tsx +38 -92
- package/UI/Components/TextArea/TextArea.tsx +3 -0
- package/UI/Components/Workflow/ArgumentsForm.tsx +18 -1
- package/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.tsx +177 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.tsx +243 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnControl.ts +255 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.tsx +178 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnOperators.ts +206 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnRow.ts +157 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.ts +517 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnValueInput.tsx +482 -0
- package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +131 -0
- package/UI/Components/Workflow/ColumnEditor/ModelRecordForm.tsx +163 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +284 -100
- package/UI/Utils/NetworkAutomation/RunNetworkRule.ts +122 -0
- package/Utils/API.ts +243 -36
- package/Utils/Monitor/NetworkDeviceRoleUtil.ts +45 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +42 -2
- package/Utils/NetworkAutomation/RuleRunSummary.ts +147 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +115 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +17 -2
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Server/API/LlmProviderAPI.js +9 -1
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js +33 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +202 -0
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLinkService.js +253 -1
- package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +223 -12
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +20 -2
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js +12 -5
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js.map +1 -1
- package/build/dist/Server/Types/Database/JSONColumnQuery.js +40 -4
- package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +78 -22
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -98
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +7 -254
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +14 -2
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +27 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +285 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -0
- package/build/dist/Server/Utils/SSRFProtection.js +5 -4
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +128 -35
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Types/API/Protocol.js +9 -0
- package/build/dist/Types/API/Protocol.js.map +1 -1
- package/build/dist/Types/API/URL.js +241 -18
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Billing/PayAsYouGoPricing.js +41 -0
- package/build/dist/Types/Billing/PayAsYouGoPricing.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldType.js +26 -0
- package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +10 -0
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +39 -0
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js +21 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/Button/Button.js +10 -16
- package/build/dist/UI/Components/Button/Button.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +2 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +2 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js +12 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +2 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +17 -10
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +4 -18
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +3 -3
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +5 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js +14 -44
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +20 -2
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +42 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/Pagination/Pagination.js +174 -199
- package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
- package/build/dist/UI/Components/Pagination/PaginationUtil.js +160 -0
- package/build/dist/UI/Components/Pagination/PaginationUtil.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js +20 -2
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js +23 -47
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js.map +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +14 -2
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js +86 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js +119 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js +206 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js +57 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js +150 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js +67 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js +309 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js +204 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +66 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js +83 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +187 -68
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
- package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js +75 -0
- package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js.map +1 -0
- package/build/dist/Utils/API.js +175 -34
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +37 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +25 -3
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js +78 -0
- package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @timezone UTC
|
|
3
|
+
*/
|
|
4
|
+
import "@testing-library/jest-dom";
|
|
5
|
+
import {
|
|
6
|
+
afterEach,
|
|
7
|
+
beforeEach,
|
|
8
|
+
describe,
|
|
9
|
+
expect,
|
|
10
|
+
jest,
|
|
11
|
+
test,
|
|
12
|
+
} from "@jest/globals";
|
|
13
|
+
import {
|
|
14
|
+
RenderResult,
|
|
15
|
+
cleanup,
|
|
16
|
+
render,
|
|
17
|
+
screen,
|
|
18
|
+
waitFor,
|
|
19
|
+
} from "@testing-library/react";
|
|
20
|
+
import * as React from "react";
|
|
21
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* The SLO widget's Chart display renders a real recharts line — and it is
|
|
25
|
+
* exactly there that it broke. Every sibling suite stubs LineChart out and
|
|
26
|
+
* asserts on the props handed down, which is why the bug survived: the props
|
|
27
|
+
* were right, the REQUEST was right, the data was right, and recharts still
|
|
28
|
+
* drew nothing, because the widget labelled its x-axis "" and the chart rows
|
|
29
|
+
* were keyed off that label while recharts was told to read "Time".
|
|
30
|
+
*
|
|
31
|
+
* So this suite deliberately renders the real chart and asserts on the SVG
|
|
32
|
+
* the viewer actually gets. It covers both surfaces the widget runs on: the
|
|
33
|
+
* anonymous public dashboard (where the bug was reported) and the
|
|
34
|
+
* authenticated one (where it was equally broken, just unnoticed).
|
|
35
|
+
*
|
|
36
|
+
* Pinned to UTC: the x-axis bucket labels are produced by local-time
|
|
37
|
+
* formatters.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
const getItemMock: MockFunction = getJestMockFunction();
|
|
41
|
+
const getListMock: MockFunction = getJestMockFunction();
|
|
42
|
+
const aggregateMock: MockFunction = getJestMockFunction();
|
|
43
|
+
const getCurrentProjectIdMock: MockFunction = getJestMockFunction();
|
|
44
|
+
|
|
45
|
+
/*
|
|
46
|
+
* The arrow wrappers are load bearing: jest.mock is hoisted above the compiled
|
|
47
|
+
* requires, so the mock variables above are still unassigned when the factory
|
|
48
|
+
* runs. Dereferencing them lazily, at call time, is what makes this work.
|
|
49
|
+
*/
|
|
50
|
+
jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
|
|
51
|
+
return {
|
|
52
|
+
__esModule: true,
|
|
53
|
+
default: {
|
|
54
|
+
getItem: (...args: Array<any>) => {
|
|
55
|
+
return getItemMock(...args);
|
|
56
|
+
},
|
|
57
|
+
getList: (...args: Array<any>) => {
|
|
58
|
+
return getListMock(...args);
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
jest.mock("../../../UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI", () => {
|
|
65
|
+
return {
|
|
66
|
+
__esModule: true,
|
|
67
|
+
default: {
|
|
68
|
+
aggregate: (...args: Array<any>) => {
|
|
69
|
+
return aggregateMock(...args);
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
jest.mock("../../../UI/Utils/Project", () => {
|
|
76
|
+
return {
|
|
77
|
+
__esModule: true,
|
|
78
|
+
default: {
|
|
79
|
+
getCurrentProjectId: (...args: Array<any>) => {
|
|
80
|
+
return getCurrentProjectIdMock(...args);
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
/*
|
|
87
|
+
* The ONLY thing stubbed in the chart stack. ResponsiveContainer measures its
|
|
88
|
+
* parent, which is always 0x0 in jsdom, and a 0x0 recharts chart draws no
|
|
89
|
+
* marks — which would make every assertion below pass for the wrong reason.
|
|
90
|
+
*/
|
|
91
|
+
jest.mock("recharts", () => {
|
|
92
|
+
const actual: Record<string, any> = jest.requireActual("recharts") as Record<
|
|
93
|
+
string,
|
|
94
|
+
any
|
|
95
|
+
>;
|
|
96
|
+
return {
|
|
97
|
+
...actual,
|
|
98
|
+
ResponsiveContainer: ({ children }: { children: React.ReactElement }) => {
|
|
99
|
+
return React.cloneElement(children, { width: 600, height: 300 });
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
import DashboardSloComponentElement from "../../../../App/FeatureSet/Dashboard/src/Components/Dashboard/Components/DashboardSloComponent";
|
|
105
|
+
import { DashboardBaseComponentProps } from "../../../../App/FeatureSet/Dashboard/src/Components/Dashboard/Components/DashboardBaseComponent";
|
|
106
|
+
import {
|
|
107
|
+
PublicDashboardContext,
|
|
108
|
+
setPublicDashboardContext,
|
|
109
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/Dashboard/Utils/PublicDashboardContext";
|
|
110
|
+
import ServiceLevelObjective from "../../../Models/DatabaseModels/ServiceLevelObjective";
|
|
111
|
+
import HTTPResponse from "../../../Types/API/HTTPResponse";
|
|
112
|
+
import AggregatedResult from "../../../Types/BaseDatabase/AggregatedResult";
|
|
113
|
+
import InBetween from "../../../Types/BaseDatabase/InBetween";
|
|
114
|
+
import DashboardComponentType from "../../../Types/Dashboard/DashboardComponentType";
|
|
115
|
+
import DashboardSloComponent, {
|
|
116
|
+
SloWidgetDisplayType,
|
|
117
|
+
SloWidgetMetric,
|
|
118
|
+
} from "../../../Types/Dashboard/DashboardComponents/DashboardSloComponent";
|
|
119
|
+
import DashboardViewConfig from "../../../Types/Dashboard/DashboardViewConfig";
|
|
120
|
+
import { JSONObject, ObjectType } from "../../../Types/JSON";
|
|
121
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
122
|
+
import SloStatus from "../../../Types/ServiceLevelObjective/SloStatus";
|
|
123
|
+
import RangeStartAndEndDateTime from "../../../Types/Time/RangeStartAndEndDateTime";
|
|
124
|
+
import TimeRange from "../../../Types/Time/TimeRange";
|
|
125
|
+
|
|
126
|
+
const COMPONENT_ID: ObjectID = new ObjectID(
|
|
127
|
+
"11111111-1111-4111-8111-111111111111",
|
|
128
|
+
);
|
|
129
|
+
const DASHBOARD_ID: ObjectID = new ObjectID(
|
|
130
|
+
"22222222-2222-4222-8222-222222222222",
|
|
131
|
+
);
|
|
132
|
+
const SLO_ID: ObjectID = new ObjectID("33333333-3333-4333-8333-333333333333");
|
|
133
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
134
|
+
"44444444-4444-4444-8444-444444444444",
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
/*
|
|
138
|
+
* A CUSTOM range pins the window to fixed instants; a relative range would
|
|
139
|
+
* resolve against the wall clock. Six hours buckets at five minutes, which
|
|
140
|
+
* is the cadence the evaluation worker writes at.
|
|
141
|
+
*/
|
|
142
|
+
const START_DATE: Date = new Date("2026-08-10T00:00:00.000Z");
|
|
143
|
+
const END_DATE: Date = new Date("2026-08-10T06:00:00.000Z");
|
|
144
|
+
|
|
145
|
+
const DASHBOARD_RANGE: RangeStartAndEndDateTime = {
|
|
146
|
+
range: TimeRange.CUSTOM,
|
|
147
|
+
startAndEndDate: new InBetween<Date>(START_DATE, END_DATE),
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const DASHBOARD_VIEW_CONFIG: DashboardViewConfig = {
|
|
151
|
+
_type: ObjectType.DashboardViewConfig,
|
|
152
|
+
components: [],
|
|
153
|
+
heightInDashboardUnits: 60,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/** SVG the viewer sees only when recharts actually plotted the series. */
|
|
157
|
+
const LINE_SELECTOR: string = "path.recharts-line-curve";
|
|
158
|
+
|
|
159
|
+
let publicResponse: JSONObject = { data: [] };
|
|
160
|
+
|
|
161
|
+
const PUBLIC_DASHBOARD_CONTEXT: PublicDashboardContext = {
|
|
162
|
+
dashboardId: DASHBOARD_ID,
|
|
163
|
+
apiUrl: {
|
|
164
|
+
toString: (): string => {
|
|
165
|
+
return "http://localhost/public-dashboard-api";
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
postJSON: () => {
|
|
169
|
+
return Promise.resolve({
|
|
170
|
+
data: publicResponse,
|
|
171
|
+
} as unknown as HTTPResponse<JSONObject>);
|
|
172
|
+
},
|
|
173
|
+
} as unknown as PublicDashboardContext;
|
|
174
|
+
|
|
175
|
+
/** One row every five minutes across the window, oldest first. */
|
|
176
|
+
type BuildHistoryFunction = (data?: {
|
|
177
|
+
rowCount?: number | undefined;
|
|
178
|
+
valueAt?: ((index: number) => number | string) | undefined;
|
|
179
|
+
}) => Array<JSONObject>;
|
|
180
|
+
|
|
181
|
+
const buildHistory: BuildHistoryFunction = (
|
|
182
|
+
data: {
|
|
183
|
+
rowCount?: number | undefined;
|
|
184
|
+
valueAt?: ((index: number) => number | string) | undefined;
|
|
185
|
+
} = {},
|
|
186
|
+
): Array<JSONObject> => {
|
|
187
|
+
const rowCount: number = data.rowCount ?? 12;
|
|
188
|
+
const rows: Array<JSONObject> = [];
|
|
189
|
+
|
|
190
|
+
for (let index: number = 0; index < rowCount; index++) {
|
|
191
|
+
rows.push({
|
|
192
|
+
timestamp: new Date(
|
|
193
|
+
START_DATE.getTime() + index * 5 * 60 * 1000,
|
|
194
|
+
).toISOString(),
|
|
195
|
+
value: data.valueAt ? data.valueAt(index) : 99.9 - index * 0.01,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return rows;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
type BuildSloFunction = (
|
|
203
|
+
overrides?: Partial<ServiceLevelObjective>,
|
|
204
|
+
) => ServiceLevelObjective;
|
|
205
|
+
|
|
206
|
+
const buildSlo: BuildSloFunction = (
|
|
207
|
+
overrides: Partial<ServiceLevelObjective> = {},
|
|
208
|
+
): ServiceLevelObjective => {
|
|
209
|
+
const slo: ServiceLevelObjective = new ServiceLevelObjective();
|
|
210
|
+
slo.name = "Checkout availability";
|
|
211
|
+
slo.targetPercentage = 99.9;
|
|
212
|
+
slo.currentSliPercentage = 99.95;
|
|
213
|
+
slo.errorBudgetRemainingPercentage = 42.5;
|
|
214
|
+
slo.errorBudgetRemainingSeconds = 3600;
|
|
215
|
+
slo.currentBurnRate = 1.25;
|
|
216
|
+
slo.sloStatus = SloStatus.Healthy;
|
|
217
|
+
return Object.assign(slo, overrides);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
type BuildPropsFunction = (
|
|
221
|
+
overrides?: Partial<DashboardBaseComponentProps>,
|
|
222
|
+
) => DashboardBaseComponentProps;
|
|
223
|
+
|
|
224
|
+
const buildBaseProps: BuildPropsFunction = (
|
|
225
|
+
overrides: Partial<DashboardBaseComponentProps> = {},
|
|
226
|
+
): DashboardBaseComponentProps => {
|
|
227
|
+
return {
|
|
228
|
+
componentId: COMPONENT_ID,
|
|
229
|
+
isEditMode: false,
|
|
230
|
+
isSelected: false,
|
|
231
|
+
key: "slo-widget",
|
|
232
|
+
onComponentUpdate: (): void => {
|
|
233
|
+
// The widget never writes back through this.
|
|
234
|
+
},
|
|
235
|
+
totalCurrentDashboardWidthInPx: 1200,
|
|
236
|
+
dashboardCanvasTopInPx: 0,
|
|
237
|
+
dashboardCanvasLeftInPx: 0,
|
|
238
|
+
dashboardCanvasWidthInPx: 1200,
|
|
239
|
+
dashboardCanvasHeightInPx: 800,
|
|
240
|
+
dashboardComponentHeightInPx: 320,
|
|
241
|
+
dashboardComponentWidthInPx: 480,
|
|
242
|
+
dashboardViewConfig: DASHBOARD_VIEW_CONFIG,
|
|
243
|
+
dashboardStartAndEndDate: DASHBOARD_RANGE,
|
|
244
|
+
metricTypes: [],
|
|
245
|
+
refreshTick: 0,
|
|
246
|
+
variables: undefined,
|
|
247
|
+
...overrides,
|
|
248
|
+
} as unknown as DashboardBaseComponentProps;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
type RenderWidgetFunction = (
|
|
252
|
+
args: DashboardSloComponent["arguments"],
|
|
253
|
+
propOverrides?: Partial<DashboardBaseComponentProps>,
|
|
254
|
+
) => RenderResult;
|
|
255
|
+
|
|
256
|
+
const renderWidget: RenderWidgetFunction = (
|
|
257
|
+
args: DashboardSloComponent["arguments"],
|
|
258
|
+
propOverrides: Partial<DashboardBaseComponentProps> = {},
|
|
259
|
+
): RenderResult => {
|
|
260
|
+
const component: DashboardSloComponent = {
|
|
261
|
+
_type: ObjectType.DashboardComponent,
|
|
262
|
+
componentId: COMPONENT_ID,
|
|
263
|
+
componentType: DashboardComponentType.Slo,
|
|
264
|
+
topInDashboardUnits: 0,
|
|
265
|
+
leftInDashboardUnits: 0,
|
|
266
|
+
widthInDashboardUnits: 3,
|
|
267
|
+
heightInDashboardUnits: 3,
|
|
268
|
+
minWidthInDashboardUnits: 2,
|
|
269
|
+
minHeightInDashboardUnits: 2,
|
|
270
|
+
arguments: args,
|
|
271
|
+
} as unknown as DashboardSloComponent;
|
|
272
|
+
|
|
273
|
+
return render(
|
|
274
|
+
<DashboardSloComponentElement
|
|
275
|
+
{...buildBaseProps(propOverrides)}
|
|
276
|
+
component={component}
|
|
277
|
+
/>,
|
|
278
|
+
);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
/*
|
|
282
|
+
* Settle in two stages.
|
|
283
|
+
*
|
|
284
|
+
* First the chart FRAME, which appears as soon as the widget's fetch resolves
|
|
285
|
+
* and it switches to its Chart branch. Under the bug the frame — surface,
|
|
286
|
+
* axes, grid, tooltip — rendered perfectly and only the series was missing,
|
|
287
|
+
* so reaching this point proves the failure below is about the LINE and not
|
|
288
|
+
* about a request that never came back.
|
|
289
|
+
*
|
|
290
|
+
* Then the series itself, on a short leash: the chart fills its rows in an
|
|
291
|
+
* effect one tick after mounting, so it is never there on the first paint,
|
|
292
|
+
* and a regression should fail in a second rather than sitting out the
|
|
293
|
+
* default five.
|
|
294
|
+
*/
|
|
295
|
+
const LINE_TIMEOUT_IN_MS: number = 1500;
|
|
296
|
+
|
|
297
|
+
type FindLinePathsFunction = (
|
|
298
|
+
container: HTMLElement,
|
|
299
|
+
) => Promise<Array<Element>>;
|
|
300
|
+
|
|
301
|
+
const findLinePaths: FindLinePathsFunction = async (
|
|
302
|
+
container: HTMLElement,
|
|
303
|
+
): Promise<Array<Element>> => {
|
|
304
|
+
await waitFor((): void => {
|
|
305
|
+
expect(container.querySelector("svg.recharts-surface")).not.toBeNull();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
await waitFor(
|
|
309
|
+
(): void => {
|
|
310
|
+
expect(container.querySelector(LINE_SELECTOR)).not.toBeNull();
|
|
311
|
+
},
|
|
312
|
+
{ timeout: LINE_TIMEOUT_IN_MS },
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
return Array.from(container.querySelectorAll(LINE_SELECTOR));
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/*
|
|
319
|
+
* recharts emits the curve more than once per series (the drawn stroke plus
|
|
320
|
+
* its hit-target twin), so assert on the GEOMETRY they all share rather than
|
|
321
|
+
* on how many nodes carry it.
|
|
322
|
+
*/
|
|
323
|
+
type ExpectPlottedFunction = (paths: Array<Element>) => string;
|
|
324
|
+
|
|
325
|
+
const expectPlotted: ExpectPlottedFunction = (
|
|
326
|
+
paths: Array<Element>,
|
|
327
|
+
): string => {
|
|
328
|
+
expect(paths.length).toBeGreaterThan(0);
|
|
329
|
+
|
|
330
|
+
const geometries: Array<string> = paths.map((path: Element): string => {
|
|
331
|
+
return path.getAttribute("d") || "";
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
for (const geometry of geometries) {
|
|
335
|
+
/*
|
|
336
|
+
* The reported bug produced a chart frame with NO curve at all; a curve
|
|
337
|
+
* present but empty (or full of NaN) would be the same blank widget, so
|
|
338
|
+
* pin the path data itself.
|
|
339
|
+
*/
|
|
340
|
+
expect(geometry.length).toBeGreaterThan(0);
|
|
341
|
+
expect(geometry.startsWith("M")).toBe(true);
|
|
342
|
+
expect(geometry).not.toContain("NaN");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Every copy is the same series, so they must agree.
|
|
346
|
+
expect(new Set(geometries).size).toBe(1);
|
|
347
|
+
|
|
348
|
+
return geometries[0]!;
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
beforeEach((): void => {
|
|
352
|
+
jest.clearAllMocks();
|
|
353
|
+
publicResponse = { data: buildHistory() };
|
|
354
|
+
getCurrentProjectIdMock.mockReturnValue(PROJECT_ID);
|
|
355
|
+
getItemMock.mockResolvedValue(buildSlo());
|
|
356
|
+
getListMock.mockResolvedValue({
|
|
357
|
+
data: [buildSlo()],
|
|
358
|
+
count: 1,
|
|
359
|
+
skip: 0,
|
|
360
|
+
limit: 1,
|
|
361
|
+
});
|
|
362
|
+
aggregateMock.mockResolvedValue({
|
|
363
|
+
data: buildHistory(),
|
|
364
|
+
} as unknown as AggregatedResult);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
afterEach((): void => {
|
|
368
|
+
cleanup();
|
|
369
|
+
setPublicDashboardContext(null);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
describe("SLO chart widget on a public dashboard", () => {
|
|
373
|
+
beforeEach((): void => {
|
|
374
|
+
setPublicDashboardContext(PUBLIC_DASHBOARD_CONTEXT);
|
|
375
|
+
// A public dashboard has no session, so no current project.
|
|
376
|
+
getCurrentProjectIdMock.mockReturnValue(null);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test("plots a line an anonymous viewer can actually see", async () => {
|
|
380
|
+
const { container }: RenderResult = renderWidget({
|
|
381
|
+
serviceLevelObjectiveId: SLO_ID.toString(),
|
|
382
|
+
sloMetric: SloWidgetMetric.Sli,
|
|
383
|
+
displayType: SloWidgetDisplayType.Chart,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const geometry: string = expectPlotted(await findLinePaths(container));
|
|
387
|
+
|
|
388
|
+
/*
|
|
389
|
+
* A drawn line visits every history row, so it carries one curve command
|
|
390
|
+
* per plotted point rather than a single degenerate move-to.
|
|
391
|
+
*/
|
|
392
|
+
expect(geometry.split("C").length).toBeGreaterThan(1);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
test.each([
|
|
396
|
+
[SloWidgetMetric.Sli],
|
|
397
|
+
[SloWidgetMetric.ErrorBudgetRemaining],
|
|
398
|
+
[SloWidgetMetric.BurnRate],
|
|
399
|
+
])("plots %s", async (sloMetric: SloWidgetMetric) => {
|
|
400
|
+
publicResponse = {
|
|
401
|
+
data: buildHistory({
|
|
402
|
+
valueAt: (index: number): number => {
|
|
403
|
+
return sloMetric === SloWidgetMetric.BurnRate
|
|
404
|
+
? 1 + index * 0.1
|
|
405
|
+
: 99 - index * 0.1;
|
|
406
|
+
},
|
|
407
|
+
}),
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const { container }: RenderResult = renderWidget({
|
|
411
|
+
serviceLevelObjectiveId: SLO_ID.toString(),
|
|
412
|
+
sloMetric: sloMetric,
|
|
413
|
+
displayType: SloWidgetDisplayType.Chart,
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
expectPlotted(await findLinePaths(container));
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
test("draws the SLO's target as a reference line on the SLI chart", async () => {
|
|
420
|
+
const { container }: RenderResult = renderWidget({
|
|
421
|
+
serviceLevelObjectiveId: SLO_ID.toString(),
|
|
422
|
+
sloMetric: SloWidgetMetric.Sli,
|
|
423
|
+
displayType: SloWidgetDisplayType.Chart,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
await findLinePaths(container);
|
|
427
|
+
|
|
428
|
+
expect(
|
|
429
|
+
container.querySelectorAll("line.recharts-reference-line-line").length,
|
|
430
|
+
).toBeGreaterThan(0);
|
|
431
|
+
expect(screen.getByText("Target 99.9%")).toBeInTheDocument();
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
test("plots decimal strings, which is how ClickHouse returns the value", async () => {
|
|
435
|
+
publicResponse = {
|
|
436
|
+
data: buildHistory({
|
|
437
|
+
valueAt: (index: number): string => {
|
|
438
|
+
return `${99.9 - index * 0.05}`;
|
|
439
|
+
},
|
|
440
|
+
}),
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const { container }: RenderResult = renderWidget({
|
|
444
|
+
serviceLevelObjectiveId: SLO_ID.toString(),
|
|
445
|
+
displayType: SloWidgetDisplayType.Chart,
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
expectPlotted(await findLinePaths(container));
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
test("shows the empty-history state instead of a blank chart frame", async () => {
|
|
452
|
+
publicResponse = { data: [] };
|
|
453
|
+
|
|
454
|
+
const { container }: RenderResult = renderWidget({
|
|
455
|
+
serviceLevelObjectiveId: SLO_ID.toString(),
|
|
456
|
+
displayType: SloWidgetDisplayType.Chart,
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
expect(
|
|
460
|
+
await screen.findByText(/No history for the selected/i),
|
|
461
|
+
).toBeInTheDocument();
|
|
462
|
+
expect(container.querySelectorAll(LINE_SELECTOR).length).toBe(0);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
test("keeps the tile display working alongside the chart fix", async () => {
|
|
466
|
+
const { container }: RenderResult = renderWidget({
|
|
467
|
+
serviceLevelObjectiveId: SLO_ID.toString(),
|
|
468
|
+
sloMetric: SloWidgetMetric.Sli,
|
|
469
|
+
displayType: SloWidgetDisplayType.Tile,
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
expect(await screen.findByText("99.95%")).toBeInTheDocument();
|
|
473
|
+
expect(screen.getByText("target 99.9%")).toBeInTheDocument();
|
|
474
|
+
// A Tile never asks for history, so it never draws a series.
|
|
475
|
+
expect(container.querySelectorAll(LINE_SELECTOR).length).toBe(0);
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
test("still plots when the widget tile is small enough to clamp its height", async () => {
|
|
479
|
+
const { container }: RenderResult = renderWidget(
|
|
480
|
+
{
|
|
481
|
+
serviceLevelObjectiveId: SLO_ID.toString(),
|
|
482
|
+
displayType: SloWidgetDisplayType.Chart,
|
|
483
|
+
},
|
|
484
|
+
{ dashboardComponentHeightInPx: 100, dashboardComponentWidthInPx: 140 },
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
expectPlotted(await findLinePaths(container));
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
describe("SLO chart widget on an authenticated dashboard", () => {
|
|
492
|
+
test("plots a line through the private analytics route too", async () => {
|
|
493
|
+
const { container }: RenderResult = renderWidget({
|
|
494
|
+
serviceLevelObjectiveId: SLO_ID.toString(),
|
|
495
|
+
sloMetric: SloWidgetMetric.Sli,
|
|
496
|
+
displayType: SloWidgetDisplayType.Chart,
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
expectPlotted(await findLinePaths(container));
|
|
500
|
+
|
|
501
|
+
expect(aggregateMock).toHaveBeenCalledTimes(1);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
test("renders the same geometry as the public dashboard for the same series", async () => {
|
|
505
|
+
const authenticated: RenderResult = renderWidget({
|
|
506
|
+
serviceLevelObjectiveId: SLO_ID.toString(),
|
|
507
|
+
sloMetric: SloWidgetMetric.Sli,
|
|
508
|
+
displayType: SloWidgetDisplayType.Chart,
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
const authenticatedGeometry: string = expectPlotted(
|
|
512
|
+
await findLinePaths(authenticated.container),
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
cleanup();
|
|
516
|
+
|
|
517
|
+
setPublicDashboardContext(PUBLIC_DASHBOARD_CONTEXT);
|
|
518
|
+
getCurrentProjectIdMock.mockReturnValue(null);
|
|
519
|
+
|
|
520
|
+
const anonymous: RenderResult = renderWidget({
|
|
521
|
+
serviceLevelObjectiveId: SLO_ID.toString(),
|
|
522
|
+
sloMetric: SloWidgetMetric.Sli,
|
|
523
|
+
displayType: SloWidgetDisplayType.Chart,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
const anonymousGeometry: string = expectPlotted(
|
|
527
|
+
await findLinePaths(anonymous.container),
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
/*
|
|
531
|
+
* Same SLO, same window, same rows — the two surfaces differ only in
|
|
532
|
+
* which endpoint served them, so the drawn line must be identical.
|
|
533
|
+
*/
|
|
534
|
+
expect(anonymousGeometry).toBe(authenticatedGeometry);
|
|
535
|
+
});
|
|
536
|
+
});
|