@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,447 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { describe, expect, jest, test } from "@jest/globals";
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* The bundler turns an .svg import into a URL string; jest's asset mapper
|
|
8
|
+
* turns it into {}, which UserElement then feeds to Route.fromString and
|
|
9
|
+
* which throws. Hand it the string shape the browser actually gets.
|
|
10
|
+
*/
|
|
11
|
+
const BLANK_PROFILE_PIC: string = "/blank-profile.svg";
|
|
12
|
+
|
|
13
|
+
jest.mock("../../../UI/Images/users/blank-profile.svg", () => {
|
|
14
|
+
return BLANK_PROFILE_PIC;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* UserElement is the one user row the dashboard renders everywhere - tables,
|
|
19
|
+
* logs, on-call surfaces, "archived by" cells - so it is the single place the
|
|
20
|
+
* email had to be added. The email was already on the wire at almost every
|
|
21
|
+
* call site; the component simply dropped it, using it only as a stand-in when
|
|
22
|
+
* the name was missing.
|
|
23
|
+
*
|
|
24
|
+
* What these pin: the email is a line of its own beneath the name, it is never
|
|
25
|
+
* printed twice, and neither the avatar nor the "OneUptime" automation row
|
|
26
|
+
* regressed while adding it.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import UserElement from "../../../../App/FeatureSet/Dashboard/src/Components/User/User";
|
|
30
|
+
import UsersElement from "../../../../App/FeatureSet/Dashboard/src/Components/User/Users";
|
|
31
|
+
import User from "../../../Models/DatabaseModels/User";
|
|
32
|
+
import Email from "../../../Types/Email";
|
|
33
|
+
import Name from "../../../Types/Name";
|
|
34
|
+
|
|
35
|
+
const USER_ID: string = "00000000-0000-4000-8000-000000000001";
|
|
36
|
+
const OTHER_USER_ID: string = "00000000-0000-4000-8000-000000000002";
|
|
37
|
+
|
|
38
|
+
type BuildUserModelFunction = (spec: {
|
|
39
|
+
id?: string | undefined;
|
|
40
|
+
name?: string | undefined;
|
|
41
|
+
email?: string | undefined;
|
|
42
|
+
}) => User;
|
|
43
|
+
|
|
44
|
+
const buildUserModel: BuildUserModelFunction = (spec: {
|
|
45
|
+
id?: string | undefined;
|
|
46
|
+
name?: string | undefined;
|
|
47
|
+
email?: string | undefined;
|
|
48
|
+
}): User => {
|
|
49
|
+
const user: User = new User();
|
|
50
|
+
|
|
51
|
+
if (spec.id) {
|
|
52
|
+
user._id = spec.id;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (spec.name) {
|
|
56
|
+
user.name = new Name(spec.name);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (spec.email) {
|
|
60
|
+
user.email = new Email(spec.email);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return user;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
type AvatarFunction = () => HTMLImageElement;
|
|
67
|
+
|
|
68
|
+
const avatar: AvatarFunction = (): HTMLImageElement => {
|
|
69
|
+
const images: Array<HTMLElement> = screen.getAllByRole("img");
|
|
70
|
+
return images[0] as HTMLImageElement;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
describe("UserElement", () => {
|
|
74
|
+
describe("showing the email", () => {
|
|
75
|
+
/*
|
|
76
|
+
* The whole point of the change. If this fails, every user row in the
|
|
77
|
+
* dashboard is back to a bare name with no way to tell two people with the
|
|
78
|
+
* same name apart.
|
|
79
|
+
*/
|
|
80
|
+
test("shows the email underneath the name", () => {
|
|
81
|
+
render(
|
|
82
|
+
<UserElement
|
|
83
|
+
user={{ _id: USER_ID, name: "Jane Doe", email: "jane@acme.com" }}
|
|
84
|
+
/>,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(screen.getByText("Jane Doe")).toBeInTheDocument();
|
|
88
|
+
expect(screen.getByTestId("user-email")).toHaveTextContent(
|
|
89
|
+
"jane@acme.com",
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("puts the email on a line of its own, not inside the name", () => {
|
|
94
|
+
render(
|
|
95
|
+
<UserElement
|
|
96
|
+
user={{ _id: USER_ID, name: "Jane Doe", email: "jane@acme.com" }}
|
|
97
|
+
/>,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
expect(screen.getByText("Jane Doe")).not.toHaveTextContent(
|
|
101
|
+
"jane@acme.com",
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("reads the email off a User model, not just a plain object", () => {
|
|
106
|
+
render(
|
|
107
|
+
<UserElement
|
|
108
|
+
user={buildUserModel({
|
|
109
|
+
id: USER_ID,
|
|
110
|
+
name: "Jane Doe",
|
|
111
|
+
email: "jane@acme.com",
|
|
112
|
+
})}
|
|
113
|
+
/>,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
expect(screen.getByText("Jane Doe")).toBeInTheDocument();
|
|
117
|
+
expect(screen.getByTestId("user-email")).toHaveTextContent(
|
|
118
|
+
"jane@acme.com",
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
* Callers that hand-build a user out of an API response pass the
|
|
124
|
+
* serialized form of an Email. toString() on that shape yields
|
|
125
|
+
* "[object Object]", which is what the reader would otherwise see.
|
|
126
|
+
*/
|
|
127
|
+
test("unwraps a serialized Email rather than printing [object Object]", () => {
|
|
128
|
+
render(
|
|
129
|
+
<UserElement
|
|
130
|
+
user={{
|
|
131
|
+
_id: USER_ID,
|
|
132
|
+
name: "Jane Doe",
|
|
133
|
+
email: { _type: "Email", value: "jane@acme.com" },
|
|
134
|
+
}}
|
|
135
|
+
/>,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
expect(screen.getByTestId("user-email")).toHaveTextContent(
|
|
139
|
+
"jane@acme.com",
|
|
140
|
+
);
|
|
141
|
+
expect(screen.queryByText(/object Object/)).not.toBeInTheDocument();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("unwraps a serialized Name the same way", () => {
|
|
145
|
+
render(
|
|
146
|
+
<UserElement
|
|
147
|
+
user={{
|
|
148
|
+
_id: USER_ID,
|
|
149
|
+
name: { _type: "Name", value: "Jane Doe" },
|
|
150
|
+
email: "jane@acme.com",
|
|
151
|
+
}}
|
|
152
|
+
/>,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
expect(screen.getByText("Jane Doe")).toBeInTheDocument();
|
|
156
|
+
expect(screen.queryByText(/object Object/)).not.toBeInTheDocument();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe("not showing it twice", () => {
|
|
161
|
+
/*
|
|
162
|
+
* The name line already falls back to the email. Rendering the subline
|
|
163
|
+
* unconditionally would print the address twice on every user who has not
|
|
164
|
+
* set a name - which is most users right after they are invited.
|
|
165
|
+
*/
|
|
166
|
+
test("keeps an email-only user to a single line", () => {
|
|
167
|
+
render(<UserElement user={{ _id: USER_ID, email: "jane@acme.com" }} />);
|
|
168
|
+
|
|
169
|
+
expect(screen.getByText("jane@acme.com")).toBeInTheDocument();
|
|
170
|
+
expect(screen.queryByTestId("user-email")).not.toBeInTheDocument();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("adds no empty line for a user with a name but no email", () => {
|
|
174
|
+
render(<UserElement user={{ _id: USER_ID, name: "Jane Doe" }} />);
|
|
175
|
+
|
|
176
|
+
expect(screen.getByText("Jane Doe")).toBeInTheDocument();
|
|
177
|
+
expect(screen.queryByTestId("user-email")).not.toBeInTheDocument();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("does not repeat an address stored as both the name and the email", () => {
|
|
181
|
+
render(
|
|
182
|
+
<UserElement
|
|
183
|
+
user={{
|
|
184
|
+
_id: USER_ID,
|
|
185
|
+
name: "jane@acme.com",
|
|
186
|
+
email: "jane@acme.com",
|
|
187
|
+
}}
|
|
188
|
+
/>,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
expect(screen.getByText("jane@acme.com")).toBeInTheDocument();
|
|
192
|
+
expect(screen.queryByTestId("user-email")).not.toBeInTheDocument();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("treats an empty-string name as no name", () => {
|
|
196
|
+
render(
|
|
197
|
+
<UserElement
|
|
198
|
+
user={{ _id: USER_ID, name: "", email: "jane@acme.com" }}
|
|
199
|
+
/>,
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
expect(screen.getByText("jane@acme.com")).toBeInTheDocument();
|
|
203
|
+
expect(screen.queryByTestId("user-email")).not.toBeInTheDocument();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("treats an empty-string email as no email", () => {
|
|
207
|
+
render(
|
|
208
|
+
<UserElement user={{ _id: USER_ID, name: "Jane Doe", email: "" }} />,
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
expect(screen.getByText("Jane Doe")).toBeInTheDocument();
|
|
212
|
+
expect(screen.queryByTestId("user-email")).not.toBeInTheDocument();
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("the profile picture", () => {
|
|
217
|
+
test("points the avatar at the user's own picture", () => {
|
|
218
|
+
render(
|
|
219
|
+
<UserElement
|
|
220
|
+
user={{ _id: USER_ID, name: "Jane Doe", email: "jane@acme.com" }}
|
|
221
|
+
/>,
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
expect(avatar()).toHaveAttribute(
|
|
225
|
+
"src",
|
|
226
|
+
`/api/user/profile-picture/${USER_ID}`,
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("builds the avatar from a User model's id", () => {
|
|
231
|
+
render(
|
|
232
|
+
<UserElement
|
|
233
|
+
user={buildUserModel({
|
|
234
|
+
id: OTHER_USER_ID,
|
|
235
|
+
name: "John Roe",
|
|
236
|
+
email: "john@acme.com",
|
|
237
|
+
})}
|
|
238
|
+
/>,
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
expect(avatar()).toHaveAttribute(
|
|
242
|
+
"src",
|
|
243
|
+
`/api/user/profile-picture/${OTHER_USER_ID}`,
|
|
244
|
+
);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("accepts `id` as well as `_id`", () => {
|
|
248
|
+
render(
|
|
249
|
+
<UserElement
|
|
250
|
+
user={{ id: USER_ID, name: "Jane Doe", email: "jane@acme.com" }}
|
|
251
|
+
/>,
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
expect(avatar()).toHaveAttribute(
|
|
255
|
+
"src",
|
|
256
|
+
`/api/user/profile-picture/${USER_ID}`,
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("falls back to the blank picture, and still shows name and email, when there is no id", () => {
|
|
261
|
+
render(
|
|
262
|
+
<UserElement user={{ name: "Jane Doe", email: "jane@acme.com" }} />,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
expect(screen.getByText("Jane Doe")).toBeInTheDocument();
|
|
266
|
+
expect(screen.getByTestId("user-email")).toHaveTextContent(
|
|
267
|
+
"jane@acme.com",
|
|
268
|
+
);
|
|
269
|
+
expect(avatar()).toHaveAttribute("src", BLANK_PROFILE_PIC);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("survives an id that is not a valid ObjectID", () => {
|
|
273
|
+
render(
|
|
274
|
+
<UserElement
|
|
275
|
+
user={{ _id: "not-an-id", name: "Jane Doe", email: "jane@acme.com" }}
|
|
276
|
+
/>,
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
expect(screen.getByText("Jane Doe")).toBeInTheDocument();
|
|
280
|
+
expect(screen.getByTestId("user-email")).toHaveTextContent(
|
|
281
|
+
"jane@acme.com",
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("names the avatar after the user so it is not an unlabelled image", () => {
|
|
286
|
+
render(
|
|
287
|
+
<UserElement
|
|
288
|
+
user={{ _id: USER_ID, name: "Jane Doe", email: "jane@acme.com" }}
|
|
289
|
+
/>,
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
expect(avatar()).toHaveAttribute("alt", "Jane Doe");
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
describe("callers that want the compact row back", () => {
|
|
297
|
+
test("hideEmail drops the second line", () => {
|
|
298
|
+
render(
|
|
299
|
+
<UserElement
|
|
300
|
+
user={{ _id: USER_ID, name: "Jane Doe", email: "jane@acme.com" }}
|
|
301
|
+
hideEmail={true}
|
|
302
|
+
/>,
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
expect(screen.getByText("Jane Doe")).toBeInTheDocument();
|
|
306
|
+
expect(screen.queryByTestId("user-email")).not.toBeInTheDocument();
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
test("emailClassName replaces the default styling", () => {
|
|
310
|
+
render(
|
|
311
|
+
<UserElement
|
|
312
|
+
user={{ _id: USER_ID, name: "Jane Doe", email: "jane@acme.com" }}
|
|
313
|
+
emailClassName="text-sm text-red-500"
|
|
314
|
+
/>,
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
expect(screen.getByTestId("user-email")).toHaveClass("text-sm");
|
|
318
|
+
expect(screen.getByTestId("user-email")).not.toHaveClass("text-xs");
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("keeps the default styling when no override is given", () => {
|
|
322
|
+
render(
|
|
323
|
+
<UserElement
|
|
324
|
+
user={{ _id: USER_ID, name: "Jane Doe", email: "jane@acme.com" }}
|
|
325
|
+
/>,
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
expect(screen.getByTestId("user-email")).toHaveClass("text-gray-500");
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
describe("rows that are not a person", () => {
|
|
333
|
+
/*
|
|
334
|
+
* An empty user object means the action was taken by automation. That row
|
|
335
|
+
* has no email and must not grow one.
|
|
336
|
+
*/
|
|
337
|
+
test("still reads OneUptime for an automated action", () => {
|
|
338
|
+
render(<UserElement user={{}} />);
|
|
339
|
+
|
|
340
|
+
expect(screen.getByText("OneUptime")).toBeInTheDocument();
|
|
341
|
+
expect(screen.queryByTestId("user-email")).not.toBeInTheDocument();
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
/*
|
|
345
|
+
* A missing user reads as automation too - there is nobody to name and, in
|
|
346
|
+
* particular, no email to invent.
|
|
347
|
+
*/
|
|
348
|
+
test("reads OneUptime for a null user", () => {
|
|
349
|
+
render(<UserElement user={null} />);
|
|
350
|
+
|
|
351
|
+
expect(screen.getByText("OneUptime")).toBeInTheDocument();
|
|
352
|
+
expect(screen.queryByTestId("user-email")).not.toBeInTheDocument();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
test("reads OneUptime for an absent user", () => {
|
|
356
|
+
render(<UserElement />);
|
|
357
|
+
|
|
358
|
+
expect(screen.getByText("OneUptime")).toBeInTheDocument();
|
|
359
|
+
expect(screen.queryByTestId("user-email")).not.toBeInTheDocument();
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test("gives the automation avatar an alt so it is not unlabelled", () => {
|
|
363
|
+
render(<UserElement user={{}} />);
|
|
364
|
+
|
|
365
|
+
expect(avatar()).toHaveAttribute("alt", "Automation");
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
describe("prefix and suffix", () => {
|
|
370
|
+
test("keeps the prefix beside the name and the email below it", () => {
|
|
371
|
+
render(
|
|
372
|
+
<UserElement
|
|
373
|
+
user={{ _id: USER_ID, name: "Jane Doe", email: "jane@acme.com" }}
|
|
374
|
+
prefix="Assigned to"
|
|
375
|
+
/>,
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
expect(screen.getByText("Assigned to")).toBeInTheDocument();
|
|
379
|
+
expect(screen.getByTestId("user-email")).toHaveTextContent(
|
|
380
|
+
"jane@acme.com",
|
|
381
|
+
);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test("keeps rendering the suffix alongside the email", () => {
|
|
385
|
+
render(
|
|
386
|
+
<UserElement
|
|
387
|
+
user={{ _id: USER_ID, name: "Jane Doe", email: "jane@acme.com" }}
|
|
388
|
+
suffix="(on call)"
|
|
389
|
+
/>,
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
expect(screen.getByText("(on call)")).toBeInTheDocument();
|
|
393
|
+
expect(screen.getByTestId("user-email")).toBeInTheDocument();
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
describe("UsersElement", () => {
|
|
399
|
+
test("shows the email for every user in the list", () => {
|
|
400
|
+
render(
|
|
401
|
+
<UsersElement
|
|
402
|
+
users={[
|
|
403
|
+
buildUserModel({
|
|
404
|
+
id: USER_ID,
|
|
405
|
+
name: "Jane Doe",
|
|
406
|
+
email: "jane@acme.com",
|
|
407
|
+
}),
|
|
408
|
+
buildUserModel({
|
|
409
|
+
id: OTHER_USER_ID,
|
|
410
|
+
name: "John Roe",
|
|
411
|
+
email: "john@acme.com",
|
|
412
|
+
}),
|
|
413
|
+
]}
|
|
414
|
+
/>,
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
const emails: Array<HTMLElement> = screen.getAllByTestId("user-email");
|
|
418
|
+
|
|
419
|
+
expect(emails).toHaveLength(2);
|
|
420
|
+
expect(emails[0]).toHaveTextContent("jane@acme.com");
|
|
421
|
+
expect(emails[1]).toHaveTextContent("john@acme.com");
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test("forwards hideEmail down to each row", () => {
|
|
425
|
+
render(
|
|
426
|
+
<UsersElement
|
|
427
|
+
users={[
|
|
428
|
+
buildUserModel({
|
|
429
|
+
id: USER_ID,
|
|
430
|
+
name: "Jane Doe",
|
|
431
|
+
email: "jane@acme.com",
|
|
432
|
+
}),
|
|
433
|
+
]}
|
|
434
|
+
hideEmail={true}
|
|
435
|
+
/>,
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
expect(screen.getByText("Jane Doe")).toBeInTheDocument();
|
|
439
|
+
expect(screen.queryByTestId("user-email")).not.toBeInTheDocument();
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
test("still says so when there is nobody to list", () => {
|
|
443
|
+
render(<UsersElement users={[]} />);
|
|
444
|
+
|
|
445
|
+
expect(screen.getByText("No users.")).toBeInTheDocument();
|
|
446
|
+
});
|
|
447
|
+
});
|