@peers-app/peers-ui 0.27.3 → 0.27.5
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/dist/screens/network-viewer/group-details-modal.d.ts +4 -0
- package/dist/screens/network-viewer/group-details-modal.js +6 -46
- package/dist/screens/network-viewer/network-viewer-ipc.d.ts +3 -2
- package/dist/screens/network-viewer/network-viewer-ipc.js +6 -0
- package/dist/screens/network-viewer/sync-recovery.js +103 -1
- package/dist/screens/variables/variable-details.js +2 -1
- package/dist/tabs-layout/network-status-indicator.js +26 -12
- package/dist/tabs-layout/use-network-status.d.ts +6 -1
- package/dist/tabs-layout/use-network-status.js +27 -4
- package/package.json +9 -3
- package/.claude/settings.local.json +0 -34
- package/.github/README.md +0 -52
- package/.github/workflows/delete-all-artifacts.yml +0 -49
- package/.github/workflows/publish.yml +0 -51
- package/babel.config.js +0 -7
- package/biome.json +0 -198
- package/dist/app-updates/app-updates-consumer.test.d.ts +0 -1
- package/dist/app-updates/app-updates-consumer.test.js +0 -56
- package/dist/app-updates/app-updates-settings.test.d.ts +0 -1
- package/dist/app-updates/app-updates-settings.test.js +0 -93
- package/dist/app-updates/remote-app-update-panel.test.d.ts +0 -1
- package/dist/app-updates/remote-app-update-panel.test.js +0 -165
- package/dist/components/access-level-field.test.d.ts +0 -1
- package/dist/components/access-level-field.test.js +0 -28
- package/dist/components/message-logs/tool-access-approval-card.test.d.ts +0 -1
- package/dist/components/message-logs/tool-access-approval-card.test.js +0 -102
- package/dist/components/responsive-section-nav.test.d.ts +0 -1
- package/dist/components/responsive-section-nav.test.js +0 -45
- package/dist/globals-hash.test.d.ts +0 -1
- package/dist/globals-hash.test.js +0 -16
- package/dist/hooks/use-observable-state.test.d.ts +0 -1
- package/dist/hooks/use-observable-state.test.js +0 -64
- package/dist/hooks/use-observable.test.d.ts +0 -1
- package/dist/hooks/use-observable.test.js +0 -191
- package/dist/hooks/use-rows.test.d.ts +0 -1
- package/dist/hooks/use-rows.test.js +0 -175
- package/dist/hooks/use-tool.test.d.ts +0 -1
- package/dist/hooks/use-tool.test.js +0 -190
- package/dist/host-shell/host-shell-consumer.test.d.ts +0 -1
- package/dist/host-shell/host-shell-consumer.test.js +0 -28
- package/dist/root-layout/root-layout-host.test.d.ts +0 -1
- package/dist/root-layout/root-layout-host.test.js +0 -86
- package/dist/screens/account/account-screen.test.d.ts +0 -1
- package/dist/screens/account/account-screen.test.js +0 -51
- package/dist/screens/assistants/assistant-config.test.d.ts +0 -1
- package/dist/screens/assistants/assistant-config.test.js +0 -57
- package/dist/screens/contacts/contact-list.test.d.ts +0 -1
- package/dist/screens/contacts/contact-list.test.js +0 -48
- package/dist/screens/device-pairing/device-pairing.test.d.ts +0 -1
- package/dist/screens/device-pairing/device-pairing.test.js +0 -119
- package/dist/screens/devices/devices-list.test.d.ts +0 -1
- package/dist/screens/devices/devices-list.test.js +0 -53
- package/dist/screens/groups/group-list.test.d.ts +0 -1
- package/dist/screens/groups/group-list.test.js +0 -38
- package/dist/screens/identity/identity-routes.test.d.ts +0 -1
- package/dist/screens/identity/identity-routes.test.js +0 -51
- package/dist/screens/identity/identity-screen.test.d.ts +0 -1
- package/dist/screens/identity/identity-screen.test.js +0 -103
- package/dist/screens/invites/accept-invite.test.d.ts +0 -1
- package/dist/screens/invites/accept-invite.test.js +0 -103
- package/dist/screens/invites/activity-view.test.d.ts +0 -1
- package/dist/screens/invites/activity-view.test.js +0 -108
- package/dist/screens/invites/invite-classify.test.d.ts +0 -1
- package/dist/screens/invites/invite-classify.test.js +0 -115
- package/dist/screens/network-viewer/network-viewer-ipc.test.d.ts +0 -1
- package/dist/screens/network-viewer/network-viewer-ipc.test.js +0 -61
- package/dist/screens/network-viewer/network-viewer.section.test.d.ts +0 -1
- package/dist/screens/network-viewer/network-viewer.section.test.js +0 -63
- package/dist/screens/network-viewer/sync-recovery.test.d.ts +0 -1
- package/dist/screens/network-viewer/sync-recovery.test.js +0 -104
- package/dist/screens/packages/package-helpers.test.d.ts +0 -1
- package/dist/screens/packages/package-helpers.test.js +0 -53
- package/dist/screens/packages/package-runtime-status.test.d.ts +0 -1
- package/dist/screens/packages/package-runtime-status.test.js +0 -50
- package/dist/screens/packages/packages-ipc.test.d.ts +0 -1
- package/dist/screens/packages/packages-ipc.test.js +0 -43
- package/dist/screens/settings/settings-page.test.d.ts +0 -1
- package/dist/screens/settings/settings-page.test.js +0 -19
- package/dist/screens/tools/tool-access-ipc.test.d.ts +0 -1
- package/dist/screens/tools/tool-access-ipc.test.js +0 -40
- package/dist/screens/tools/tool-catalog-ipc.test.d.ts +0 -1
- package/dist/screens/tools/tool-catalog-ipc.test.js +0 -52
- package/dist/tabs-layout/network-status-indicator.test.d.ts +0 -1
- package/dist/tabs-layout/network-status-indicator.test.js +0 -665
- package/dist/tabs-layout/tabs-state.test.d.ts +0 -1
- package/dist/tabs-layout/tabs-state.test.js +0 -110
- package/dist/tabs-layout/use-network-status.test.d.ts +0 -1
- package/dist/tabs-layout/use-network-status.test.js +0 -45
- package/dist/ui-router/ui-loader.test.d.ts +0 -1
- package/dist/ui-router/ui-loader.test.js +0 -91
- package/jest.config.js +0 -25
- package/release-guard.js +0 -10
- package/src/app-updates/app-update-controls.tsx +0 -73
- package/src/app-updates/app-updates-consumer.test.ts +0 -83
- package/src/app-updates/app-updates-consumer.ts +0 -121
- package/src/app-updates/app-updates-settings.test.tsx +0 -109
- package/src/app-updates/app-updates-settings.tsx +0 -136
- package/src/app-updates/remote-app-update-panel.test.tsx +0 -205
- package/src/app-updates/remote-app-update-panel.tsx +0 -210
- package/src/app-updates/use-app-update-status.ts +0 -16
- package/src/command-palette/command-palette-ui.tsx +0 -564
- package/src/command-palette/command-palette.ts +0 -242
- package/src/components/access-level-field.test.tsx +0 -32
- package/src/components/access-level-field.tsx +0 -68
- package/src/components/checkbox.tsx +0 -25
- package/src/components/group-switcher.tsx +0 -526
- package/src/components/input-date.tsx +0 -29
- package/src/components/input-number.tsx +0 -83
- package/src/components/input.tsx +0 -25
- package/src/components/inverse-lazy-list.tsx +0 -107
- package/src/components/io-schema-values.tsx +0 -103
- package/src/components/io-schema.tsx +0 -259
- package/src/components/lazy-list.tsx +0 -115
- package/src/components/list-screen.tsx +0 -126
- package/src/components/loading-indicator.tsx +0 -7
- package/src/components/markdown-editor/autolink-plugin.tsx +0 -29
- package/src/components/markdown-editor/editor-inline.tsx +0 -9
- package/src/components/markdown-editor/editor.tsx +0 -158
- package/src/components/markdown-editor/markdown-plugin.tsx +0 -202
- package/src/components/markdown-editor/mention-node.ts +0 -204
- package/src/components/markdown-editor/mentions-plugin.tsx +0 -354
- package/src/components/markdown-editor/move-line-plugin.tsx +0 -186
- package/src/components/markdown-editor/select-line-boundary-plugin.tsx +0 -95
- package/src/components/markdown-editor/theme.ts +0 -46
- package/src/components/markdown-editor/toolbar.tsx +0 -269
- package/src/components/markdown-with-mentions.tsx +0 -198
- package/src/components/message-logs/message-logs.tsx +0 -523
- package/src/components/message-logs/tool-access-approval-card.test.tsx +0 -127
- package/src/components/message-logs/tool-access-approval-card.tsx +0 -178
- package/src/components/messages/avatar.tsx +0 -171
- package/src/components/messages/channel-message-list.tsx +0 -189
- package/src/components/messages/channel-view.tsx +0 -92
- package/src/components/messages/message-compose.tsx +0 -192
- package/src/components/messages/message-display.tsx +0 -231
- package/src/components/messages/thread-message-list.tsx +0 -135
- package/src/components/qr-scanner.tsx +0 -180
- package/src/components/responsive-section-nav.test.tsx +0 -88
- package/src/components/responsive-section-nav.tsx +0 -92
- package/src/components/router.tsx +0 -175
- package/src/components/save-button.tsx +0 -134
- package/src/components/sortable-list.tsx +0 -130
- package/src/components/tabs.tsx +0 -76
- package/src/components/tooltip.tsx +0 -43
- package/src/components/trust-level-badge.tsx +0 -44
- package/src/components/trust-level-dropdown.tsx +0 -136
- package/src/components/typeahead.tsx +0 -357
- package/src/globals-hash.test.ts +0 -19
- package/src/globals.tsx +0 -226
- package/src/hooks/index.ts +0 -8
- package/src/hooks/use-host-contract.ts +0 -75
- package/src/hooks/use-observable-state.test.tsx +0 -101
- package/src/hooks/use-observable-state.ts +0 -29
- package/src/hooks/use-observable.test.tsx +0 -258
- package/src/hooks/use-observable.ts +0 -67
- package/src/hooks/use-on-screen.ts +0 -24
- package/src/hooks/use-promise.ts +0 -34
- package/src/hooks/use-rows.test.tsx +0 -196
- package/src/hooks/use-rows.ts +0 -120
- package/src/hooks/use-subscription.ts +0 -24
- package/src/hooks/use-tool.test.tsx +0 -225
- package/src/hooks/use-tool.ts +0 -116
- package/src/host-shell/host-shell-consumer.test.ts +0 -32
- package/src/host-shell/host-shell-consumer.ts +0 -53
- package/src/index.tsx +0 -42
- package/src/mention-configs.ts +0 -185
- package/src/operator-console/operator-console-state.ts +0 -119
- package/src/operator-console/operator-console.tsx +0 -233
- package/src/peers-services-registration.ts +0 -6
- package/src/root-layout/global-overlays.ts +0 -50
- package/src/root-layout/layout-picker.tsx +0 -47
- package/src/root-layout/layouts/full-screen-layout.tsx +0 -144
- package/src/root-layout/layouts/tabs-with-console-layout.tsx +0 -260
- package/src/root-layout/root-layout-host.test.tsx +0 -108
- package/src/root-layout/root-layout-host.tsx +0 -147
- package/src/root-layout/root-layout-registry.ts +0 -51
- package/src/root-layout/root-layout-state.ts +0 -55
- package/src/root-layout/root-layout.ts +0 -32
- package/src/screens/account/account-screen.test.tsx +0 -65
- package/src/screens/account/account-screen.tsx +0 -942
- package/src/screens/assistants/assistant-config.test.tsx +0 -54
- package/src/screens/assistants/assistant-config.tsx +0 -121
- package/src/screens/assistants/assistant-details.tsx +0 -89
- package/src/screens/assistants/assistant-info.tsx +0 -66
- package/src/screens/assistants/assistant-list.tsx +0 -145
- package/src/screens/assistants/assistant-tools.tsx +0 -82
- package/src/screens/console-logs/console-logs-list.tsx +0 -374
- package/src/screens/console-logs/log-display.tsx +0 -137
- package/src/screens/console-logs/log-filters.tsx +0 -64
- package/src/screens/console-logs/mobile-log-card.tsx +0 -161
- package/src/screens/console-logs/resizable-table-header.tsx +0 -110
- package/src/screens/contacts/contact-details.tsx +0 -123
- package/src/screens/contacts/contact-list.test.tsx +0 -59
- package/src/screens/contacts/contact-list.tsx +0 -286
- package/src/screens/contacts/index.ts +0 -2
- package/src/screens/data-explorer/data-explorer.tsx +0 -633
- package/src/screens/data-explorer/index.ts +0 -6
- package/src/screens/data-explorer/query-executor.tsx +0 -520
- package/src/screens/device-pairing/device-pairing.test.tsx +0 -143
- package/src/screens/device-pairing/device-pairing.tsx +0 -461
- package/src/screens/device-pairing/identity-ipc.ts +0 -25
- package/src/screens/device-pairing/index.ts +0 -2
- package/src/screens/device-pairing/use-device-pairing-state.ts +0 -15
- package/src/screens/devices/devices-list.test.tsx +0 -68
- package/src/screens/devices/devices-list.tsx +0 -365
- package/src/screens/devices/index.ts +0 -1
- package/src/screens/groups/group-details.tsx +0 -160
- package/src/screens/groups/group-invite-panel.tsx +0 -311
- package/src/screens/groups/group-list.test.tsx +0 -44
- package/src/screens/groups/group-list.tsx +0 -317
- package/src/screens/groups/group-members.tsx +0 -343
- package/src/screens/groups/index.ts +0 -3
- package/src/screens/identity/identity-routes.test.ts +0 -70
- package/src/screens/identity/identity-routes.ts +0 -301
- package/src/screens/identity/identity-screen.test.tsx +0 -138
- package/src/screens/identity/identity-screen.tsx +0 -282
- package/src/screens/identity/index.ts +0 -2
- package/src/screens/identity/invite-contact-panel.tsx +0 -45
- package/src/screens/identity/profile-invite.tsx +0 -93
- package/src/screens/invites/accept-invite.test.tsx +0 -115
- package/src/screens/invites/accept-invite.tsx +0 -318
- package/src/screens/invites/activity-view.test.tsx +0 -127
- package/src/screens/invites/activity-view.tsx +0 -162
- package/src/screens/invites/index.ts +0 -6
- package/src/screens/invites/invite-capture.ts +0 -104
- package/src/screens/invites/invite-classify.test.ts +0 -129
- package/src/screens/invites/invite-share.tsx +0 -104
- package/src/screens/invites/invites-ipc.ts +0 -25
- package/src/screens/invites/pending-invites-list.tsx +0 -256
- package/src/screens/network-viewer/connection-troubleshooter.tsx +0 -395
- package/src/screens/network-viewer/cpu-usage-graph.tsx +0 -294
- package/src/screens/network-viewer/device-details-modal.tsx +0 -339
- package/src/screens/network-viewer/group-details-modal.tsx +0 -421
- package/src/screens/network-viewer/index.ts +0 -8
- package/src/screens/network-viewer/network-viewer-ipc.test.ts +0 -72
- package/src/screens/network-viewer/network-viewer-ipc.ts +0 -39
- package/src/screens/network-viewer/network-viewer-section.ts +0 -12
- package/src/screens/network-viewer/network-viewer.section.test.tsx +0 -68
- package/src/screens/network-viewer/network-viewer.tsx +0 -708
- package/src/screens/network-viewer/remote-logs-panel.tsx +0 -190
- package/src/screens/network-viewer/sync-recovery.test.tsx +0 -114
- package/src/screens/network-viewer/sync-recovery.tsx +0 -461
- package/src/screens/network-viewer/usage-graph.tsx +0 -455
- package/src/screens/packages/local-folder-grants-panel.tsx +0 -290
- package/src/screens/packages/local-folders-ipc.ts +0 -28
- package/src/screens/packages/package-details.tsx +0 -166
- package/src/screens/packages/package-helpers.test.ts +0 -57
- package/src/screens/packages/package-helpers.ts +0 -137
- package/src/screens/packages/package-info.tsx +0 -354
- package/src/screens/packages/package-list.tsx +0 -519
- package/src/screens/packages/package-network-panel.tsx +0 -82
- package/src/screens/packages/package-new-local.tsx +0 -100
- package/src/screens/packages/package-runtime-status.test.tsx +0 -58
- package/src/screens/packages/package-runtime-status.tsx +0 -68
- package/src/screens/packages/package-versions.tsx +0 -298
- package/src/screens/packages/packages-ipc.test.ts +0 -47
- package/src/screens/packages/packages-ipc.ts +0 -125
- package/src/screens/peer-types/peer-type-details.tsx +0 -172
- package/src/screens/peer-types/peer-type-list.tsx +0 -64
- package/src/screens/search/global-search.tsx +0 -406
- package/src/screens/settings/color-mode-dropdown.tsx +0 -82
- package/src/screens/settings/settings-page.test.tsx +0 -24
- package/src/screens/settings/settings-page.tsx +0 -426
- package/src/screens/setup-user.tsx +0 -355
- package/src/screens/tools/tool-access-ipc.test.ts +0 -55
- package/src/screens/tools/tool-access-ipc.ts +0 -27
- package/src/screens/tools/tool-catalog-ipc.test.ts +0 -52
- package/src/screens/tools/tool-catalog-ipc.ts +0 -51
- package/src/screens/tools/tool-details.tsx +0 -52
- package/src/screens/tools/tool-info.tsx +0 -45
- package/src/screens/tools/tool-list.tsx +0 -106
- package/src/screens/tools/tool-schema.tsx +0 -44
- package/src/screens/tools/tool-test-details.tsx +0 -89
- package/src/screens/tools/tool-test-list.tsx +0 -73
- package/src/screens/variables/variable-details.tsx +0 -289
- package/src/screens/variables/variable-list.tsx +0 -110
- package/src/screens/welcome-modal.tsx +0 -294
- package/src/screens/workflows/workflow-details.tsx +0 -115
- package/src/screens/workflows/workflow-info.tsx +0 -22
- package/src/screens/workflows/workflow-instructions.tsx +0 -134
- package/src/screens/workflows/workflow-list.tsx +0 -117
- package/src/setupTests.ts +0 -38
- package/src/system-apps/assistants.app.ts +0 -7
- package/src/system-apps/console-logs.app.ts +0 -7
- package/src/system-apps/data-explorer.app.ts +0 -8
- package/src/system-apps/identity.app.ts +0 -9
- package/src/system-apps/index.ts +0 -77
- package/src/system-apps/mobile-settings.app.ts +0 -7
- package/src/system-apps/network-viewer.app.ts +0 -8
- package/src/system-apps/packages.app.ts +0 -7
- package/src/system-apps/search.app.ts +0 -8
- package/src/system-apps/settings.app.ts +0 -7
- package/src/system-apps/threads.app.ts +0 -7
- package/src/system-apps/tools.app.ts +0 -7
- package/src/system-apps/types.app.ts +0 -7
- package/src/system-apps/variables.app.ts +0 -7
- package/src/system-apps/workflows.app.ts +0 -7
- package/src/tabs-layout/network-status-indicator.test.tsx +0 -799
- package/src/tabs-layout/network-status-indicator.tsx +0 -756
- package/src/tabs-layout/tabs-layout.tsx +0 -857
- package/src/tabs-layout/tabs-state.test.ts +0 -152
- package/src/tabs-layout/tabs-state.ts +0 -587
- package/src/tabs-layout/use-network-status.test.tsx +0 -58
- package/src/tabs-layout/use-network-status.ts +0 -68
- package/src/ui-defaults/index.ts +0 -2
- package/src/ui-defaults/list-screen.tsx +0 -94
- package/src/ui-defaults/markdown-field.tsx +0 -87
- package/src/ui-router/routes-loader.ts +0 -193
- package/src/ui-router/ui-loader.test.tsx +0 -108
- package/src/ui-router/ui-loader.tsx +0 -606
- package/src/utils.ts +0 -95
- package/tsconfig.json +0 -18
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
-
const react_1 = require("@testing-library/react");
|
|
5
|
-
const remote_app_update_panel_1 = require("./remote-app-update-panel");
|
|
6
|
-
const getDeviceUpdateStatus = jest.fn();
|
|
7
|
-
const installUpdateAndRestartOnDevice = jest.fn();
|
|
8
|
-
jest.mock("./app-updates-consumer", () => ({
|
|
9
|
-
...jest.requireActual("./app-updates-consumer"),
|
|
10
|
-
getAppUpdatesConsumer: () => Promise.resolve({
|
|
11
|
-
tools: {
|
|
12
|
-
getDeviceUpdateStatus: (...args) => getDeviceUpdateStatus(...args),
|
|
13
|
-
installUpdateAndRestartOnDevice: (...args) => installUpdateAndRestartOnDevice(...args),
|
|
14
|
-
},
|
|
15
|
-
}),
|
|
16
|
-
}));
|
|
17
|
-
function remoteStatus(overrides = {}) {
|
|
18
|
-
return {
|
|
19
|
-
supported: true,
|
|
20
|
-
canRestart: true,
|
|
21
|
-
platform: "win32",
|
|
22
|
-
currentVersion: "1.0.0",
|
|
23
|
-
latestVersion: "1.1.0",
|
|
24
|
-
downloadedVersion: "1.1.0",
|
|
25
|
-
state: "downloaded",
|
|
26
|
-
installOnRestart: true,
|
|
27
|
-
installOnQuitArmed: true,
|
|
28
|
-
remoteRestartAllowed: true,
|
|
29
|
-
...overrides,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
describe("RemoteAppUpdatePanel", () => {
|
|
33
|
-
beforeEach(() => {
|
|
34
|
-
getDeviceUpdateStatus.mockReset();
|
|
35
|
-
installUpdateAndRestartOnDevice.mockReset();
|
|
36
|
-
installUpdateAndRestartOnDevice.mockResolvedValue({ action: "installing" });
|
|
37
|
-
jest.spyOn(window, "confirm").mockReturnValue(true);
|
|
38
|
-
});
|
|
39
|
-
afterEach(() => {
|
|
40
|
-
jest.restoreAllMocks();
|
|
41
|
-
});
|
|
42
|
-
it("updates the other device after confirmation", async () => {
|
|
43
|
-
getDeviceUpdateStatus.mockResolvedValue(remoteStatus());
|
|
44
|
-
const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(remote_app_update_panel_1.RemoteAppUpdatePanel, { deviceId: "desktop", deviceName: "Desktop" }));
|
|
45
|
-
const button = await view.findByRole("button", { name: /Update & restart/ });
|
|
46
|
-
expect(view.getByTestId("remote-app-update")).toHaveTextContent("Version 1.0.0 · Version 1.1.0 is ready to install");
|
|
47
|
-
expect(getDeviceUpdateStatus).toHaveBeenCalledWith({ deviceId: "desktop" });
|
|
48
|
-
react_1.fireEvent.click(button);
|
|
49
|
-
expect(window.confirm).toHaveBeenCalledWith("Update Desktop to 1.1.0 and restart it?");
|
|
50
|
-
await (0, react_1.waitFor)(() => expect(installUpdateAndRestartOnDevice).toHaveBeenCalledWith({ deviceId: "desktop" }));
|
|
51
|
-
await view.findByText("Request sent");
|
|
52
|
-
});
|
|
53
|
-
it("offers a plain restart when the device is up to date", async () => {
|
|
54
|
-
getDeviceUpdateStatus.mockResolvedValue(remoteStatus({ state: "up-to-date" }));
|
|
55
|
-
const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(remote_app_update_panel_1.RemoteAppUpdatePanel, { deviceId: "desktop" }));
|
|
56
|
-
react_1.fireEvent.click(await view.findByRole("button", { name: /^Restart$/ }));
|
|
57
|
-
expect(window.confirm).toHaveBeenCalledWith("Restart desktop? It will check for an update first.");
|
|
58
|
-
await (0, react_1.waitFor)(() => expect(installUpdateAndRestartOnDevice).toHaveBeenCalled());
|
|
59
|
-
});
|
|
60
|
-
it("does nothing when the confirmation is declined", async () => {
|
|
61
|
-
window.confirm.mockReturnValue(false);
|
|
62
|
-
getDeviceUpdateStatus.mockResolvedValue(remoteStatus());
|
|
63
|
-
const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(remote_app_update_panel_1.RemoteAppUpdatePanel, { deviceId: "desktop" }));
|
|
64
|
-
react_1.fireEvent.click(await view.findByRole("button", { name: /Update & restart/ }));
|
|
65
|
-
expect(installUpdateAndRestartOnDevice).not.toHaveBeenCalled();
|
|
66
|
-
});
|
|
67
|
-
it("explains why the action is disabled", async () => {
|
|
68
|
-
getDeviceUpdateStatus.mockResolvedValue(remoteStatus({ remoteRestartAllowed: false }));
|
|
69
|
-
const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(remote_app_update_panel_1.RemoteAppUpdatePanel, { deviceId: "desktop" }));
|
|
70
|
-
expect(await view.findByRole("button", { name: /Update & restart/ })).toBeDisabled();
|
|
71
|
-
expect(view.getByTestId("remote-app-update")).toHaveTextContent("does not allow remote restarts");
|
|
72
|
-
getDeviceUpdateStatus.mockResolvedValue(remoteStatus({ installNeedsElevation: true }));
|
|
73
|
-
const elevated = (0, react_1.render)((0, jsx_runtime_1.jsx)(remote_app_update_panel_1.RemoteAppUpdatePanel, { deviceId: "linux-box" }));
|
|
74
|
-
await (0, react_1.waitFor)(() => expect(elevated.getAllByRole("button", { name: /Update & restart/ })[1]).toBeDisabled());
|
|
75
|
-
expect(elevated.getAllByTestId("remote-app-update")[1]).toHaveTextContent("administrator password");
|
|
76
|
-
});
|
|
77
|
-
it.each([
|
|
78
|
-
[
|
|
79
|
-
"a device that cannot restart itself",
|
|
80
|
-
() => Promise.resolve(remoteStatus({ canRestart: false })),
|
|
81
|
-
],
|
|
82
|
-
[
|
|
83
|
-
"a device that does not trust this user",
|
|
84
|
-
() => Promise.reject(new Error("Permission denied: tool 'getUpdateStatus'.run on contract x")),
|
|
85
|
-
],
|
|
86
|
-
[
|
|
87
|
-
"a build without App Updates",
|
|
88
|
-
() => Promise.reject(new Error("No contract provider registered for x v1")),
|
|
89
|
-
],
|
|
90
|
-
])("renders nothing for %s", async (_label, result) => {
|
|
91
|
-
getDeviceUpdateStatus.mockImplementation(result);
|
|
92
|
-
const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(remote_app_update_panel_1.RemoteAppUpdatePanel, { deviceId: "desktop" }));
|
|
93
|
-
await (0, react_1.waitFor)(() => expect(view.queryByTestId("remote-app-update")).toBeNull());
|
|
94
|
-
});
|
|
95
|
-
it("reports an unreachable device", async () => {
|
|
96
|
-
getDeviceUpdateStatus.mockRejectedValue(new Error("Device desktop is not connected"));
|
|
97
|
-
const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(remote_app_update_panel_1.RemoteAppUpdatePanel, { deviceId: "desktop" }));
|
|
98
|
-
await view.findByText("Not reachable: Device desktop is not connected");
|
|
99
|
-
getDeviceUpdateStatus.mockResolvedValue(remoteStatus());
|
|
100
|
-
react_1.fireEvent.click(view.getByRole("button", { name: "Retry" }));
|
|
101
|
-
await view.findByRole("button", { name: /Update & restart/ });
|
|
102
|
-
});
|
|
103
|
-
it("clears the previous device's action while a new target loads", async () => {
|
|
104
|
-
getDeviceUpdateStatus.mockResolvedValueOnce(remoteStatus());
|
|
105
|
-
const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(remote_app_update_panel_1.RemoteAppUpdatePanel, { deviceId: "desktop" }));
|
|
106
|
-
await view.findByRole("button", { name: /Update & restart/ });
|
|
107
|
-
let finish;
|
|
108
|
-
getDeviceUpdateStatus.mockImplementationOnce(() => new Promise((resolve) => {
|
|
109
|
-
finish = resolve;
|
|
110
|
-
}));
|
|
111
|
-
view.rerender((0, jsx_runtime_1.jsx)(remote_app_update_panel_1.RemoteAppUpdatePanel, { deviceId: "another-desktop" }));
|
|
112
|
-
expect(view.queryByRole("button", { name: /Update & restart/ })).toBeNull();
|
|
113
|
-
expect(view.getByText("Checking this device…")).toBeTruthy();
|
|
114
|
-
await (0, react_1.act)(async () => {
|
|
115
|
-
await Promise.resolve();
|
|
116
|
-
finish(remoteStatus({ remoteRestartAllowed: false }));
|
|
117
|
-
});
|
|
118
|
-
expect(view.getByRole("button", { name: /Update & restart/ })).toBeDisabled();
|
|
119
|
-
});
|
|
120
|
-
it("accepts slow poll responses without overlapping requests and cleans up timers", async () => {
|
|
121
|
-
jest.useFakeTimers();
|
|
122
|
-
try {
|
|
123
|
-
getDeviceUpdateStatus.mockResolvedValue(remoteStatus());
|
|
124
|
-
const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(remote_app_update_panel_1.RemoteAppUpdatePanel, { deviceId: "desktop" }));
|
|
125
|
-
await (0, react_1.act)(async () => { });
|
|
126
|
-
let finish;
|
|
127
|
-
getDeviceUpdateStatus.mockImplementationOnce(() => new Promise((resolve) => {
|
|
128
|
-
finish = resolve;
|
|
129
|
-
}));
|
|
130
|
-
react_1.fireEvent.click(view.getByRole("button", { name: /Update & restart/ }));
|
|
131
|
-
await (0, react_1.act)(async () => { });
|
|
132
|
-
await (0, react_1.act)(async () => {
|
|
133
|
-
jest.advanceTimersByTime(remote_app_update_panel_1.REMOTE_UPDATE_POLL_MS * 3);
|
|
134
|
-
});
|
|
135
|
-
expect(getDeviceUpdateStatus).toHaveBeenCalledTimes(2);
|
|
136
|
-
await (0, react_1.act)(async () => {
|
|
137
|
-
finish(remoteStatus({ currentVersion: "1.1.0", state: "up-to-date" }));
|
|
138
|
-
});
|
|
139
|
-
expect(view.getByText("Version 1.1.0 · Up to date")).toBeTruthy();
|
|
140
|
-
await (0, react_1.act)(async () => {
|
|
141
|
-
jest.advanceTimersByTime(remote_app_update_panel_1.REMOTE_UPDATE_POLL_MS);
|
|
142
|
-
});
|
|
143
|
-
expect(getDeviceUpdateStatus).toHaveBeenCalledTimes(3);
|
|
144
|
-
view.unmount();
|
|
145
|
-
expect(jest.getTimerCount()).toBe(0);
|
|
146
|
-
}
|
|
147
|
-
finally {
|
|
148
|
-
jest.useRealTimers();
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
it("confirms the newer downloading version instead of the older cached version", async () => {
|
|
152
|
-
getDeviceUpdateStatus.mockResolvedValue(remoteStatus({ state: "downloading", latestVersion: "1.2.0", downloadedVersion: "1.1.0" }));
|
|
153
|
-
const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(remote_app_update_panel_1.RemoteAppUpdatePanel, { deviceId: "desktop", deviceName: "Desktop" }));
|
|
154
|
-
react_1.fireEvent.click(await view.findByRole("button", { name: /Update & restart/ }));
|
|
155
|
-
expect(window.confirm).toHaveBeenCalledWith("Update Desktop to 1.2.0 and restart it?");
|
|
156
|
-
await view.findByText("Request sent");
|
|
157
|
-
});
|
|
158
|
-
it("shows a failed request", async () => {
|
|
159
|
-
getDeviceUpdateStatus.mockResolvedValue(remoteStatus());
|
|
160
|
-
installUpdateAndRestartOnDevice.mockRejectedValue(new Error("Permission denied: this device does not allow other devices to update"));
|
|
161
|
-
const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(remote_app_update_panel_1.RemoteAppUpdatePanel, { deviceId: "desktop" }));
|
|
162
|
-
react_1.fireEvent.click(await view.findByRole("button", { name: /Update & restart/ }));
|
|
163
|
-
await view.findByText(/does not allow other devices to update/);
|
|
164
|
-
});
|
|
165
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
-
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
5
|
-
const react_1 = require("@testing-library/react");
|
|
6
|
-
const access_level_field_1 = require("./access-level-field");
|
|
7
|
-
describe("AccessLevelField", () => {
|
|
8
|
-
it("shows the named preset and current label", () => {
|
|
9
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(access_level_field_1.AccessLevelField, { value: (0, peers_sdk_1.observable)(40) }));
|
|
10
|
-
expect(react_1.screen.getByRole("combobox")).toHaveValue("40");
|
|
11
|
-
expect(react_1.screen.getByText(/Unclassified records default to Self/)).toHaveTextContent("Writer (40)");
|
|
12
|
-
});
|
|
13
|
-
it("writes a named preset onto the observable", () => {
|
|
14
|
-
const value = (0, peers_sdk_1.observable)(40);
|
|
15
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(access_level_field_1.AccessLevelField, { value: value }));
|
|
16
|
-
react_1.fireEvent.change(react_1.screen.getByRole("combobox"), { target: { value: "60" } });
|
|
17
|
-
expect(value()).toBe(60);
|
|
18
|
-
expect(react_1.screen.getByText(/Unclassified records default to Self/)).toHaveTextContent("Admin (60)");
|
|
19
|
-
});
|
|
20
|
-
it("edits a custom integer between named tiers", () => {
|
|
21
|
-
const value = (0, peers_sdk_1.observable)(45);
|
|
22
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(access_level_field_1.AccessLevelField, { value: value }));
|
|
23
|
-
expect(react_1.screen.getByRole("combobox")).toHaveValue("custom");
|
|
24
|
-
react_1.fireEvent.change(react_1.screen.getByRole("spinbutton"), { target: { value: "47" } });
|
|
25
|
-
expect(value()).toBe(47);
|
|
26
|
-
expect(react_1.screen.getByText(/Writer\+ \(47\)/)).toBeInTheDocument();
|
|
27
|
-
});
|
|
28
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
-
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
5
|
-
const react_1 = require("@testing-library/react");
|
|
6
|
-
const tool_access_approval_card_1 = require("./tool-access-approval-card");
|
|
7
|
-
const approve = jest.fn(async () => ({ result: { status: "executed" } }));
|
|
8
|
-
const reject = jest.fn(async () => ({ result: { status: "rejected" } }));
|
|
9
|
-
const getDataContext = jest.fn((dataContextId) => ({ dataContextId }));
|
|
10
|
-
const getToolAccessControlConsumer = jest.fn(async (_dataContextId) => ({
|
|
11
|
-
tools: { approve, reject },
|
|
12
|
-
}));
|
|
13
|
-
jest.mock("../../screens/tools/tool-access-ipc", () => ({
|
|
14
|
-
getToolAccessControlConsumer: (dataContextId) => getToolAccessControlConsumer(dataContextId),
|
|
15
|
-
}));
|
|
16
|
-
jest.mock("@peers-app/peers-sdk", () => {
|
|
17
|
-
const actual = jest.requireActual("@peers-app/peers-sdk");
|
|
18
|
-
return {
|
|
19
|
-
...actual,
|
|
20
|
-
getUserContext: async () => ({
|
|
21
|
-
getDataContext,
|
|
22
|
-
}),
|
|
23
|
-
getCurrentUserAccessLevel: async () => actual.AccessLevel.Self,
|
|
24
|
-
};
|
|
25
|
-
});
|
|
26
|
-
const pending = {
|
|
27
|
-
toolAccessApprovalId: "00testapproval0000000001",
|
|
28
|
-
userId: "00testuser000000000000001",
|
|
29
|
-
assistantId: "00testassistant000000001",
|
|
30
|
-
toolId: "00testtooladmin00000000",
|
|
31
|
-
toolName: "queryDatabase",
|
|
32
|
-
dataContextId: "00testdatacontext00000001",
|
|
33
|
-
argsHash: "hash",
|
|
34
|
-
argsPreview: '{"sql":"select 1"}',
|
|
35
|
-
frozenArgs: { sql: "select 1" },
|
|
36
|
-
requiredAccessLevel: peers_sdk_1.AccessLevel.Owner,
|
|
37
|
-
scope: peers_sdk_1.ToolAccessApprovalScope.once,
|
|
38
|
-
status: peers_sdk_1.ToolAccessApprovalStatus.pending,
|
|
39
|
-
nonce: "nonce-from-table",
|
|
40
|
-
expiresAt: new Date(Date.now() + 60_000),
|
|
41
|
-
createdAt: new Date(),
|
|
42
|
-
};
|
|
43
|
-
describe("ToolAccessApprovalCard", () => {
|
|
44
|
-
beforeEach(() => {
|
|
45
|
-
approve.mockClear();
|
|
46
|
-
reject.mockClear();
|
|
47
|
-
getDataContext.mockClear();
|
|
48
|
-
getToolAccessControlConsumer.mockClear();
|
|
49
|
-
});
|
|
50
|
-
it("renders a pending card with actions", () => {
|
|
51
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(tool_access_approval_card_1.ToolAccessApprovalCard, { approval: pending }));
|
|
52
|
-
expect(react_1.screen.getByText("Approval required")).toBeInTheDocument();
|
|
53
|
-
expect(react_1.screen.getByText("queryDatabase")).toBeInTheDocument();
|
|
54
|
-
expect(react_1.screen.getByText(/Owner \(80\)/)).toBeInTheDocument();
|
|
55
|
-
expect(react_1.screen.getByRole("button", { name: "Allow once" })).toBeInTheDocument();
|
|
56
|
-
expect(react_1.screen.getByRole("button", { name: "Allow in thread (15 min)" })).toBeInTheDocument();
|
|
57
|
-
expect(react_1.screen.getByRole("button", { name: "Allow in this context (15 min)" })).toBeInTheDocument();
|
|
58
|
-
expect(react_1.screen.getByRole("button", { name: "Deny" })).toBeInTheDocument();
|
|
59
|
-
});
|
|
60
|
-
it("evaluates the user role in the approval context", async () => {
|
|
61
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(tool_access_approval_card_1.ToolAccessApprovalCard, { approval: pending }));
|
|
62
|
-
await (0, react_1.waitFor)(() => {
|
|
63
|
-
expect(getDataContext).toHaveBeenCalledWith(pending.dataContextId);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
it("approves once using the table nonce", async () => {
|
|
67
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(tool_access_approval_card_1.ToolAccessApprovalCard, { approval: pending }));
|
|
68
|
-
react_1.fireEvent.click(react_1.screen.getByRole("button", { name: "Allow once" }));
|
|
69
|
-
await (0, react_1.waitFor)(() => {
|
|
70
|
-
expect(getToolAccessControlConsumer).toHaveBeenCalledWith(pending.dataContextId);
|
|
71
|
-
expect(approve).toHaveBeenCalledWith({
|
|
72
|
-
approvalId: pending.toolAccessApprovalId,
|
|
73
|
-
nonce: "nonce-from-table",
|
|
74
|
-
scope: "once",
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
it("denies using the table nonce", async () => {
|
|
79
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(tool_access_approval_card_1.ToolAccessApprovalCard, { approval: pending }));
|
|
80
|
-
react_1.fireEvent.click(react_1.screen.getByRole("button", { name: "Deny" }));
|
|
81
|
-
await (0, react_1.waitFor)(() => {
|
|
82
|
-
expect(reject).toHaveBeenCalledWith({
|
|
83
|
-
approvalId: pending.toolAccessApprovalId,
|
|
84
|
-
nonce: "nonce-from-table",
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
it("hides empty argument previews", () => {
|
|
89
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(tool_access_approval_card_1.ToolAccessApprovalCard, { approval: { ...pending, argsPreview: "{}", frozenArgs: {} } }));
|
|
90
|
-
expect(react_1.screen.queryByText("{}")).not.toBeInTheDocument();
|
|
91
|
-
});
|
|
92
|
-
it("shows a decided card without actions", () => {
|
|
93
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(tool_access_approval_card_1.ToolAccessApprovalCard, { approval: {
|
|
94
|
-
...pending,
|
|
95
|
-
status: peers_sdk_1.ToolAccessApprovalStatus.rejected,
|
|
96
|
-
resultSummary: "Rejected by user",
|
|
97
|
-
} }));
|
|
98
|
-
expect(react_1.screen.getByText("Access rejected")).toBeInTheDocument();
|
|
99
|
-
expect(react_1.screen.getByText("Rejected by user")).toBeInTheDocument();
|
|
100
|
-
expect(react_1.screen.queryByRole("button", { name: "Allow once" })).not.toBeInTheDocument();
|
|
101
|
-
});
|
|
102
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
-
const react_1 = require("@testing-library/react");
|
|
8
|
-
const user_event_1 = __importDefault(require("@testing-library/user-event"));
|
|
9
|
-
const responsive_section_nav_1 = require("./responsive-section-nav");
|
|
10
|
-
jest.mock("../globals", () => {
|
|
11
|
-
const { observable: createObservable } = jest.requireActual("@peers-app/peers-sdk");
|
|
12
|
-
return { isDesktop: createObservable(true) };
|
|
13
|
-
});
|
|
14
|
-
const globals_1 = require("../globals");
|
|
15
|
-
const sections = [
|
|
16
|
-
{ id: "activity", label: "Activity", iconClassName: "bi-inbox", badge: 2 },
|
|
17
|
-
{ id: "people", label: "People", iconClassName: "bi-person" },
|
|
18
|
-
{ id: "groups", label: "Groups" },
|
|
19
|
-
{ id: "devices", label: "Devices" },
|
|
20
|
-
];
|
|
21
|
-
describe("ResponsiveSectionNav", () => {
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
(0, globals_1.isDesktop)(true);
|
|
24
|
-
});
|
|
25
|
-
it("uses a vertical sidebar on desktop and marks the selected section", () => {
|
|
26
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(responsive_section_nav_1.ResponsiveSectionNav, { label: "Identity", sections: sections, activeSectionId: "people", onSelect: () => undefined, toolbar: (0, jsx_runtime_1.jsx)("div", { children: "Quick actions" }), children: (0, jsx_runtime_1.jsx)("div", { children: "People body" }) }));
|
|
27
|
-
expect(react_1.screen.getByRole("navigation", { name: "Identity" }).querySelector("ul")).toHaveClass("nav-pills", "flex-column");
|
|
28
|
-
expect(react_1.screen.getByRole("button", { name: /People/ })).toHaveAttribute("aria-current", "page");
|
|
29
|
-
expect(react_1.screen.getByText("People body")).toBeInTheDocument();
|
|
30
|
-
expect(react_1.screen.getByText("Quick actions")).toBeInTheDocument();
|
|
31
|
-
expect(react_1.screen.getByText("Quick actions").closest("[data-section-scroll-container]")).toBeNull();
|
|
32
|
-
expect(react_1.screen.getByText("People body").closest("[data-section-scroll-container]")).toHaveClass("overflow-auto");
|
|
33
|
-
});
|
|
34
|
-
it("uses a non-wrapping horizontal strip on a narrow screen", () => {
|
|
35
|
-
(0, globals_1.isDesktop)(false);
|
|
36
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(responsive_section_nav_1.ResponsiveSectionNav, { label: "Identity", sections: sections, activeSectionId: "activity", onSelect: () => undefined, children: (0, jsx_runtime_1.jsx)("div", { children: "Activity body" }) }));
|
|
37
|
-
expect(react_1.screen.getByRole("navigation", { name: "Identity" }).querySelector("ul")).toHaveClass("nav-tabs", "flex-nowrap", "overflow-x-auto", "overflow-y-hidden");
|
|
38
|
-
});
|
|
39
|
-
it("reports the section chosen from the keyboard-focusable button", async () => {
|
|
40
|
-
const onSelect = jest.fn();
|
|
41
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(responsive_section_nav_1.ResponsiveSectionNav, { label: "Identity", sections: sections, activeSectionId: "activity", onSelect: onSelect, children: (0, jsx_runtime_1.jsx)("div", { children: "Activity body" }) }));
|
|
42
|
-
await user_event_1.default.click(react_1.screen.getByRole("button", { name: /Groups/ }));
|
|
43
|
-
expect(onSelect).toHaveBeenCalledWith("groups");
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
jest.mock("./tabs-layout/tabs-state", () => ({
|
|
4
|
-
handleMainPathChanged: (_oldPath, newPath, storage) => storage(newPath),
|
|
5
|
-
openNewTab: jest.fn(),
|
|
6
|
-
}));
|
|
7
|
-
const globals_1 = require("./globals");
|
|
8
|
-
describe("initial hash navigation", () => {
|
|
9
|
-
it("lets an incoming legacy deep link win and canonicalizes it", () => {
|
|
10
|
-
(0, globals_1.mainContentPath)("identity/activity");
|
|
11
|
-
window.history.replaceState(null, "", "/#contacts/abcdefghijklmnopqrstuvwxy?from=link");
|
|
12
|
-
(0, globals_1.initializeHashNavigation)();
|
|
13
|
-
expect((0, globals_1.mainContentPath)()).toBe("identity/people/abcdefghijklmnopqrstuvwxy?from=link");
|
|
14
|
-
expect(window.location.hash).toBe("#identity/people/abcdefghijklmnopqrstuvwxy?from=link");
|
|
15
|
-
});
|
|
16
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
-
const react_1 = require("@testing-library/react");
|
|
5
|
-
const use_observable_1 = require("./use-observable");
|
|
6
|
-
const use_observable_state_1 = require("./use-observable-state");
|
|
7
|
-
// Helper component to test useObservableState
|
|
8
|
-
function ObservableStateTestComponent({ initialValue }) {
|
|
9
|
-
const obs = (0, use_observable_state_1.useObservableState)(initialValue);
|
|
10
|
-
const [value] = (0, use_observable_1.useObservable)(obs);
|
|
11
|
-
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { "data-testid": "state-value", children: value }), (0, jsx_runtime_1.jsx)("button", { type: "button", "data-testid": "increment", onClick: () => obs(obs() + 1), children: "Increment" })] }));
|
|
12
|
-
}
|
|
13
|
-
describe("useObservableState", () => {
|
|
14
|
-
it("should create an observable with the initial value", () => {
|
|
15
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(ObservableStateTestComponent, { initialValue: 42 }));
|
|
16
|
-
expect(react_1.screen.getByTestId("state-value")).toHaveTextContent("42");
|
|
17
|
-
});
|
|
18
|
-
it("should re-render when the observable is updated", async () => {
|
|
19
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(ObservableStateTestComponent, { initialValue: 0 }));
|
|
20
|
-
expect(react_1.screen.getByTestId("state-value")).toHaveTextContent("0");
|
|
21
|
-
(0, react_1.act)(() => {
|
|
22
|
-
react_1.screen.getByTestId("increment").click();
|
|
23
|
-
});
|
|
24
|
-
await (0, react_1.waitFor)(() => {
|
|
25
|
-
expect(react_1.screen.getByTestId("state-value")).toHaveTextContent("1");
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
it("should maintain the same observable instance across re-renders", async () => {
|
|
29
|
-
let capturedObs = null;
|
|
30
|
-
function CaptureObsComponent() {
|
|
31
|
-
const obs = (0, use_observable_state_1.useObservableState)(0);
|
|
32
|
-
if (!capturedObs) {
|
|
33
|
-
capturedObs = obs;
|
|
34
|
-
}
|
|
35
|
-
const [value] = (0, use_observable_1.useObservable)(obs);
|
|
36
|
-
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { "data-testid": "value", children: value }), (0, jsx_runtime_1.jsx)("span", { "data-testid": "same-obs", children: obs === capturedObs ? "same" : "different" }), (0, jsx_runtime_1.jsx)("button", { type: "button", "data-testid": "update", onClick: () => obs(obs() + 1), children: "Update" })] }));
|
|
37
|
-
}
|
|
38
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(CaptureObsComponent, {}));
|
|
39
|
-
expect(react_1.screen.getByTestId("same-obs")).toHaveTextContent("same");
|
|
40
|
-
// Trigger a re-render by updating the observable
|
|
41
|
-
(0, react_1.act)(() => {
|
|
42
|
-
react_1.screen.getByTestId("update").click();
|
|
43
|
-
});
|
|
44
|
-
await (0, react_1.waitFor)(() => {
|
|
45
|
-
expect(react_1.screen.getByTestId("value")).toHaveTextContent("1");
|
|
46
|
-
expect(react_1.screen.getByTestId("same-obs")).toHaveTextContent("same");
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
it("should not subscribe when doNotSubscribe is true", () => {
|
|
50
|
-
function NoSubscribeComponent() {
|
|
51
|
-
const obs = (0, use_observable_state_1.useObservableState)(0, true); // doNotSubscribe = true
|
|
52
|
-
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { "data-testid": "value", children: obs() }), (0, jsx_runtime_1.jsx)("button", { type: "button", "data-testid": "update", onClick: () => obs(obs() + 1), children: "Update" })] }));
|
|
53
|
-
}
|
|
54
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(NoSubscribeComponent, {}));
|
|
55
|
-
expect(react_1.screen.getByTestId("value")).toHaveTextContent("0");
|
|
56
|
-
// Update won't cause re-render since we're not subscribed
|
|
57
|
-
(0, react_1.act)(() => {
|
|
58
|
-
react_1.screen.getByTestId("update").click();
|
|
59
|
-
});
|
|
60
|
-
// Value in DOM should still show 0 since component didn't re-render
|
|
61
|
-
// But the observable itself has been updated
|
|
62
|
-
expect(react_1.screen.getByTestId("value")).toHaveTextContent("0");
|
|
63
|
-
});
|
|
64
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
-
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
5
|
-
const react_1 = require("@testing-library/react");
|
|
6
|
-
const use_observable_1 = require("./use-observable");
|
|
7
|
-
// Helper component to test useObservable
|
|
8
|
-
function TestComponent({ obs, testId = "value" }) {
|
|
9
|
-
const [value, setValue] = (0, use_observable_1.useObservable)(obs);
|
|
10
|
-
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { "data-testid": testId, children: JSON.stringify(value) }), (0, jsx_runtime_1.jsx)("button", { type: "button", "data-testid": "setter", onClick: () => setValue("new-value"), children: "Set" })] }));
|
|
11
|
-
}
|
|
12
|
-
// Helper component to test array behavior
|
|
13
|
-
function ArrayTestComponent({ obs }) {
|
|
14
|
-
const [value] = (0, use_observable_1.useObservable)(obs);
|
|
15
|
-
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { "data-testid": "array-value", children: JSON.stringify(value) }), (0, jsx_runtime_1.jsx)("span", { "data-testid": "array-length", children: value.length })] }));
|
|
16
|
-
}
|
|
17
|
-
describe("useObservable", () => {
|
|
18
|
-
it("reads and follows a read-only observable without allowing typed writes", () => {
|
|
19
|
-
const source = (0, peers_sdk_1.observable)(1);
|
|
20
|
-
const view = source;
|
|
21
|
-
const { result } = (0, react_1.renderHook)(() => (0, use_observable_1.useObservable)(view));
|
|
22
|
-
const checkReadonly = () => {
|
|
23
|
-
// @ts-expect-error the read-only hook's setter accepts no values
|
|
24
|
-
result.current[1](2);
|
|
25
|
-
if ((0, peers_sdk_1.isSubscribable)(view)) {
|
|
26
|
-
// @ts-expect-error testing subscribability must not make a read-only view writable
|
|
27
|
-
view(2);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
void checkReadonly;
|
|
31
|
-
const initial = result.current[0];
|
|
32
|
-
expect(initial).toBe(1);
|
|
33
|
-
(0, react_1.act)(() => source(3));
|
|
34
|
-
expect(result.current[0]).toBe(3);
|
|
35
|
-
});
|
|
36
|
-
describe("basic functionality", () => {
|
|
37
|
-
it("should return the current value of an observable", () => {
|
|
38
|
-
const obs = (0, peers_sdk_1.observable)("hello");
|
|
39
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(TestComponent, { obs: obs }));
|
|
40
|
-
expect(react_1.screen.getByTestId("value")).toHaveTextContent('"hello"');
|
|
41
|
-
});
|
|
42
|
-
it("should return the value when passed a plain value (non-observable)", () => {
|
|
43
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(TestComponent, { obs: "plain-value" }));
|
|
44
|
-
expect(react_1.screen.getByTestId("value")).toHaveTextContent('"plain-value"');
|
|
45
|
-
});
|
|
46
|
-
it("should re-render when observable value changes", async () => {
|
|
47
|
-
const obs = (0, peers_sdk_1.observable)("initial");
|
|
48
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(TestComponent, { obs: obs }));
|
|
49
|
-
expect(react_1.screen.getByTestId("value")).toHaveTextContent('"initial"');
|
|
50
|
-
(0, react_1.act)(() => {
|
|
51
|
-
obs("updated");
|
|
52
|
-
});
|
|
53
|
-
await (0, react_1.waitFor)(() => {
|
|
54
|
-
expect(react_1.screen.getByTestId("value")).toHaveTextContent('"updated"');
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
it("should update the observable when setter is called", async () => {
|
|
58
|
-
const obs = (0, peers_sdk_1.observable)("initial");
|
|
59
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(TestComponent, { obs: obs }));
|
|
60
|
-
(0, react_1.act)(() => {
|
|
61
|
-
react_1.screen.getByTestId("setter").click();
|
|
62
|
-
});
|
|
63
|
-
await (0, react_1.waitFor)(() => {
|
|
64
|
-
expect(obs()).toBe("new-value");
|
|
65
|
-
expect(react_1.screen.getByTestId("value")).toHaveTextContent('"new-value"');
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
it("should handle undefined values", () => {
|
|
69
|
-
const obs = (0, peers_sdk_1.observable)(undefined);
|
|
70
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(TestComponent, { obs: obs }));
|
|
71
|
-
// undefined serializes to empty string in JSON.stringify, but actually shows as nothing
|
|
72
|
-
expect(react_1.screen.getByTestId("value")).toHaveTextContent("");
|
|
73
|
-
});
|
|
74
|
-
it("should handle object values", async () => {
|
|
75
|
-
const obs = (0, peers_sdk_1.observable)({ name: "test", count: 5 });
|
|
76
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(TestComponent, { obs: obs }));
|
|
77
|
-
expect(react_1.screen.getByTestId("value")).toHaveTextContent('{"name":"test","count":5}');
|
|
78
|
-
(0, react_1.act)(() => {
|
|
79
|
-
obs({ name: "updated", count: 10 });
|
|
80
|
-
});
|
|
81
|
-
await (0, react_1.waitFor)(() => {
|
|
82
|
-
expect(react_1.screen.getByTestId("value")).toHaveTextContent('{"name":"updated","count":10}');
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
describe("array handling", () => {
|
|
87
|
-
it("should handle array values", () => {
|
|
88
|
-
const obs = (0, peers_sdk_1.observable)(["a", "b", "c"]);
|
|
89
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(ArrayTestComponent, { obs: obs }));
|
|
90
|
-
expect(react_1.screen.getByTestId("array-value")).toHaveTextContent('["a","b","c"]');
|
|
91
|
-
expect(react_1.screen.getByTestId("array-length")).toHaveTextContent("3");
|
|
92
|
-
});
|
|
93
|
-
it("should re-render when array is replaced with new reference", async () => {
|
|
94
|
-
const obs = (0, peers_sdk_1.observable)(["a", "b"]);
|
|
95
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(ArrayTestComponent, { obs: obs }));
|
|
96
|
-
expect(react_1.screen.getByTestId("array-length")).toHaveTextContent("2");
|
|
97
|
-
(0, react_1.act)(() => {
|
|
98
|
-
obs(["a", "b", "c"]); // New array reference
|
|
99
|
-
});
|
|
100
|
-
await (0, react_1.waitFor)(() => {
|
|
101
|
-
expect(react_1.screen.getByTestId("array-length")).toHaveTextContent("3");
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
it("should NOT re-render when array is mutated in place (requires immutable updates)", async () => {
|
|
105
|
-
// This tests the case where an array is mutated in place and notifySubscribers is called.
|
|
106
|
-
//
|
|
107
|
-
// IMPORTANT BEHAVIORAL NOTE:
|
|
108
|
-
// The OLD implementation (useState + useEffect) had special handling that would
|
|
109
|
-
// spread the array when it detected same-reference mutation:
|
|
110
|
-
// if (isArray(newData) && newData === data) setData([...newData])
|
|
111
|
-
//
|
|
112
|
-
// The NEW implementation (useSyncExternalStore) does NOT do this.
|
|
113
|
-
// useSyncExternalStore uses Object.is() comparison on getSnapshot results.
|
|
114
|
-
// If the reference is the same, React won't re-render.
|
|
115
|
-
//
|
|
116
|
-
// This is actually the CORRECT behavior - it encourages immutable data patterns
|
|
117
|
-
// which are required for React 18 concurrent rendering to work correctly.
|
|
118
|
-
// Mutating data in place can cause tearing in concurrent mode.
|
|
119
|
-
const obs = (0, peers_sdk_1.observable)(["a", "b"]);
|
|
120
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(ArrayTestComponent, { obs: obs }));
|
|
121
|
-
expect(react_1.screen.getByTestId("array-length")).toHaveTextContent("2");
|
|
122
|
-
(0, react_1.act)(() => {
|
|
123
|
-
// Mutate the array in place (BAD PRACTICE - don't do this!)
|
|
124
|
-
const arr = obs();
|
|
125
|
-
arr.push("c");
|
|
126
|
-
obs.notifySubscribers();
|
|
127
|
-
});
|
|
128
|
-
// The observable itself has the mutated value
|
|
129
|
-
expect(obs()).toEqual(["a", "b", "c"]);
|
|
130
|
-
// But the component did NOT re-render because the reference didn't change
|
|
131
|
-
// This is expected with useSyncExternalStore - it requires immutable updates
|
|
132
|
-
expect(react_1.screen.getByTestId("array-length")).toHaveTextContent("2");
|
|
133
|
-
});
|
|
134
|
-
it("should re-render when array is properly updated with spread", async () => {
|
|
135
|
-
// This is the recommended pattern for updating arrays
|
|
136
|
-
const obs = (0, peers_sdk_1.observable)(["a", "b"]);
|
|
137
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(ArrayTestComponent, { obs: obs }));
|
|
138
|
-
expect(react_1.screen.getByTestId("array-length")).toHaveTextContent("2");
|
|
139
|
-
(0, react_1.act)(() => {
|
|
140
|
-
obs([...obs(), "c"]); // Proper immutable update
|
|
141
|
-
});
|
|
142
|
-
await (0, react_1.waitFor)(() => {
|
|
143
|
-
expect(react_1.screen.getByTestId("array-length")).toHaveTextContent("3");
|
|
144
|
-
expect(react_1.screen.getByTestId("array-value")).toHaveTextContent('["a","b","c"]');
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
describe("subscription management", () => {
|
|
149
|
-
it("should unsubscribe when component unmounts", () => {
|
|
150
|
-
const obs = (0, peers_sdk_1.observable)("test");
|
|
151
|
-
const initialSubscriberCount = obs.subscriberCount();
|
|
152
|
-
const { unmount } = (0, react_1.render)((0, jsx_runtime_1.jsx)(TestComponent, { obs: obs }));
|
|
153
|
-
// Should have subscribed
|
|
154
|
-
expect(obs.subscriberCount()).toBeGreaterThan(initialSubscriberCount);
|
|
155
|
-
unmount();
|
|
156
|
-
// Should have unsubscribed
|
|
157
|
-
expect(obs.subscriberCount()).toBe(initialSubscriberCount);
|
|
158
|
-
});
|
|
159
|
-
it("should handle multiple components subscribing to same observable", async () => {
|
|
160
|
-
const obs = (0, peers_sdk_1.observable)("shared");
|
|
161
|
-
(0, react_1.render)((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(TestComponent, { obs: obs, testId: "value1" }), (0, jsx_runtime_1.jsx)(TestComponent, { obs: obs, testId: "value2" })] }));
|
|
162
|
-
expect(react_1.screen.getByTestId("value1")).toHaveTextContent('"shared"');
|
|
163
|
-
expect(react_1.screen.getByTestId("value2")).toHaveTextContent('"shared"');
|
|
164
|
-
(0, react_1.act)(() => {
|
|
165
|
-
obs("updated");
|
|
166
|
-
});
|
|
167
|
-
await (0, react_1.waitFor)(() => {
|
|
168
|
-
expect(react_1.screen.getByTestId("value1")).toHaveTextContent('"updated"');
|
|
169
|
-
expect(react_1.screen.getByTestId("value2")).toHaveTextContent('"updated"');
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
describe("rapid updates", () => {
|
|
174
|
-
it("should handle rapid sequential updates correctly", async () => {
|
|
175
|
-
const obs = (0, peers_sdk_1.observable)(0);
|
|
176
|
-
function CounterComponent() {
|
|
177
|
-
const [value] = (0, use_observable_1.useObservable)(obs);
|
|
178
|
-
return (0, jsx_runtime_1.jsx)("span", { "data-testid": "counter", children: value });
|
|
179
|
-
}
|
|
180
|
-
(0, react_1.render)((0, jsx_runtime_1.jsx)(CounterComponent, {}));
|
|
181
|
-
(0, react_1.act)(() => {
|
|
182
|
-
for (let i = 1; i <= 10; i++) {
|
|
183
|
-
obs(i);
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
await (0, react_1.waitFor)(() => {
|
|
187
|
-
expect(react_1.screen.getByTestId("counter")).toHaveTextContent("10");
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|