@q9labsai/chalk-react-native 4.1.12 → 4.1.14
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/android.js +2 -2
- package/dist/{chunk-FVHH65FR.js → chunk-4NLA35HE.js} +1 -1
- package/dist/{chunk-LZ67SXGB.js → chunk-MK6OO4V7.js} +6 -0
- package/dist/client.js +1 -1
- package/dist/index.js +2 -2
- package/dist/ios-pad.js +2 -2
- package/dist/ios-phone.js +2 -2
- package/dist/ios.js +2 -2
- package/dist/macos.js +2 -2
- package/package.json +4 -4
- package/src/callkit/callkit.test.ts +12 -10
- package/src/space-client/create-native-space-client.ts +8 -0
- package/dist/test-support/test-renderer.d.ts +0 -20
- package/dist/test-support/test-renderer.d.ts.map +0 -1
- package/src/android/connection-service.test.ts +0 -114
- package/src/callkit/resolve-space-callkit-options.test.ts +0 -68
- package/src/client.test.ts +0 -12
- package/src/clipboard.test.ts +0 -79
- package/src/components/Chalk.test.tsx +0 -223
- package/src/components/EmbeddedWhiteboard.test.tsx +0 -92
- package/src/components/Entrance.test.tsx +0 -26
- package/src/components/EntranceView.android.test.tsx +0 -100
- package/src/components/EntranceView.ios-pad.test.tsx +0 -160
- package/src/components/EntranceView.ios-phone.test.tsx +0 -137
- package/src/components/EntranceView.test.tsx +0 -15
- package/src/components/ReactionPicker.test.tsx +0 -19
- package/src/components/RtcVideoView.test.tsx +0 -12
- package/src/components/SpaceView.android.test.tsx +0 -11
- package/src/components/SpaceView.ios-pad.test.tsx +0 -11
- package/src/components/SpaceView.ios-phone.test.tsx +0 -11
- package/src/components/SpaceView.macos.test.tsx +0 -11
- package/src/components/SpaceView.shared.test.tsx +0 -116
- package/src/components/SpaceView.test.tsx +0 -15
- package/src/components/android-connection-service-controller.test.ts +0 -80
- package/src/components/episode-diagnostic-render-observer.test.ts +0 -24
- package/src/components/logo/Logo.test.tsx +0 -144
- package/src/components/native-animation-controller.test.ts +0 -47
- package/src/components/native-chat.test.ts +0 -22
- package/src/components/native-entrance/entrance-controller-store.test.ts +0 -75
- package/src/components/native-media-visibility.test.ts +0 -77
- package/src/components/native-space-callkit-controller.test.ts +0 -62
- package/src/components/native-space-view/AppearanceSettings.test.tsx +0 -56
- package/src/components/native-space-view/ParticipantActionSheet.test.tsx +0 -18
- package/src/components/native-space-view/SettingsSheet.test.tsx +0 -17
- package/src/components/native-space-view/SpaceActionMenu.test.tsx +0 -126
- package/src/components/native-space-view/SpaceBottomDock.android.test.tsx +0 -61
- package/src/components/native-space-view/SpaceChatSheet.test.tsx +0 -18
- package/src/components/native-space-view/SpaceMoreSheet.test.tsx +0 -15
- package/src/components/native-space-view/SpacePanelSheet.test.tsx +0 -15
- package/src/components/native-space-view/SpacePeopleSheet.test.tsx +0 -13
- package/src/components/native-space-view/SpaceSurfacePrimitives.test.tsx +0 -12
- package/src/components/native-space-view/SpaceTopBar.android.test.tsx +0 -50
- package/src/components/native-space-view/SpaceWhiteboardSurface.test.tsx +0 -85
- package/src/components/native-space-view/diagnostics.test.ts +0 -31
- package/src/components/native-space-view/format-space-duration.test.ts +0 -13
- package/src/components/native-space-view/native-space-grid-keys.test.ts +0 -11
- package/src/components/native-space-view/native-space-page-store.test.ts +0 -23
- package/src/components/native-space-view/space-chat-attachments.test.ts +0 -33
- package/src/components/native-space-view/space-progressive-surface-helpers.test.ts +0 -24
- package/src/components/native-space-view/useSpaceViewController.contract.test.ts +0 -10
- package/src/components/native-space-view/useSpaceViewController.test.ts +0 -106
- package/src/components/native-space-view/useSpaceViewDerived.test.ts +0 -58
- package/src/components/native-space-view/useSpaceViewPanels.controlled.test.ts +0 -40
- package/src/components/native-space-view/useSpaceViewPanels.test.ts +0 -73
- package/src/components/space-lifecycle.test.ts +0 -19
- package/src/context/space-client-context.test.tsx +0 -10
- package/src/diagnostics.test.ts +0 -29
- package/src/hooks/entrance-preview-store.test.ts +0 -79
- package/src/hooks/space-hooks.test.ts +0 -76
- package/src/hooks/useLayout.test.ts +0 -16
- package/src/invites.test.ts +0 -51
- package/src/public-surface.test.ts +0 -26
- package/src/runtime.test.ts +0 -83
- package/src/space-client/cloudflare-rtk-native.test.ts +0 -118
- package/src/space-client/create-native-space-client.test.ts +0 -300
- package/src/telemetry.test.ts +0 -143
- package/src/test-support/test-renderer.test.ts +0 -9
- package/src/test-support/test-renderer.ts +0 -28
- package/src/ui/appearance.test.ts +0 -61
- package/src/ui/native-appearance-context.controlled.test.tsx +0 -41
- package/src/ui/native-appearance-context.test.tsx +0 -14
- package/src/ui/native-theme.test.ts +0 -34
- package/src/ui/native-theme.test.tsx +0 -14
- package/src/ui/participant-avatar.test.ts +0 -29
- package/src/ui/theme-tokens.test.ts +0 -14
- package/src/ui/theme.test.ts +0 -36
- package/src/utils/build-chalk-invite-link.test.ts +0 -12
- package/src/utils/ios-simulator.test.ts +0 -96
- package/src/utils/native-space-layout.test.ts +0 -170
- package/src/whiteboard/embedded-whiteboard-assets.test.ts +0 -31
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
const nativeTheme = vi.hoisted(() => ({ colorScheme: "light" as "light" | "dark" }));
|
|
4
|
-
|
|
5
|
-
vi.mock("react", () => ({
|
|
6
|
-
useCallback: <T,>(callback: T) => callback,
|
|
7
|
-
}));
|
|
8
|
-
vi.mock("react-native", () => ({
|
|
9
|
-
StyleSheet: { create: <Styles,>(styles: Styles) => styles },
|
|
10
|
-
}));
|
|
11
|
-
vi.mock("../../ui/native-theme", () => ({
|
|
12
|
-
useNativeTheme: () => nativeTheme,
|
|
13
|
-
}));
|
|
14
|
-
vi.mock("../EmbeddedWhiteboard", () => ({
|
|
15
|
-
EmbeddedWhiteboard: "EmbeddedWhiteboard",
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
import type { SpaceWhiteboardState } from "./useSpaceViewPanels";
|
|
19
|
-
import { forwardNativeSpaceWhiteboardFailure, forwardNativeSpaceWhiteboardMetric, shouldRenderNativeSpaceWhiteboard, SpaceWhiteboardSurface } from "./SpaceWhiteboardSurface";
|
|
20
|
-
|
|
21
|
-
describe("SpaceWhiteboardSurface", () => {
|
|
22
|
-
it("renders only for an open canonical whiteboard transport", () => {
|
|
23
|
-
expect(shouldRenderNativeSpaceWhiteboard(controller({ isOpen: false, transport: null }))).toBe(false);
|
|
24
|
-
expect(shouldRenderNativeSpaceWhiteboard(controller({ isOpen: true, transport: null }))).toBe(false);
|
|
25
|
-
expect(
|
|
26
|
-
shouldRenderNativeSpaceWhiteboard(
|
|
27
|
-
controller({
|
|
28
|
-
isOpen: true,
|
|
29
|
-
transport: {} as NonNullable<SpaceWhiteboardState["transport"]>,
|
|
30
|
-
}),
|
|
31
|
-
),
|
|
32
|
-
).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("forwards renderer metrics in production runtimes", () => {
|
|
36
|
-
const onMetric = vi.fn();
|
|
37
|
-
const metric = { name: "whiteboard.frame.delay_ms", value: 17 };
|
|
38
|
-
|
|
39
|
-
forwardNativeSpaceWhiteboardMetric(metric, onMetric);
|
|
40
|
-
|
|
41
|
-
expect(onMetric).toHaveBeenCalledWith(metric);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("forwards renderer failures without relying on development logging", () => {
|
|
45
|
-
const onMetric = vi.fn();
|
|
46
|
-
forwardNativeSpaceWhiteboardFailure({ code: "renderer_load_failed", recoverable: true }, onMetric);
|
|
47
|
-
expect(onMetric).toHaveBeenCalledWith({
|
|
48
|
-
name: "whiteboard.renderer.failure",
|
|
49
|
-
value: 1,
|
|
50
|
-
attributes: { code: "renderer_load_failed", recoverable: true },
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const element = SpaceWhiteboardSurface({ whiteboard: controller({ isOpen: true, transport: {} as NonNullable<SpaceWhiteboardState["transport"]>, onMetric }) });
|
|
54
|
-
element?.props.onError({ code: "renderer_stopped", message: "renderer stopped", recoverable: true });
|
|
55
|
-
expect(onMetric).toHaveBeenCalledTimes(2);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("passes the active NativeTheme color scheme to the embedded renderer", () => {
|
|
59
|
-
nativeTheme.colorScheme = "light";
|
|
60
|
-
const lightElement = SpaceWhiteboardSurface({ whiteboard: controller({ isOpen: true, transport: {} as NonNullable<SpaceWhiteboardState["transport"]> }) });
|
|
61
|
-
expect(lightElement).toMatchObject({ props: { theme: "light" } });
|
|
62
|
-
|
|
63
|
-
nativeTheme.colorScheme = "dark";
|
|
64
|
-
const darkElement = SpaceWhiteboardSurface({ whiteboard: controller({ isOpen: true, transport: {} as NonNullable<SpaceWhiteboardState["transport"]> }) });
|
|
65
|
-
expect(darkElement).toMatchObject({ props: { theme: "dark" } });
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
function controller(overrides: Partial<SpaceWhiteboardState>): SpaceWhiteboardState {
|
|
70
|
-
return {
|
|
71
|
-
isOpen: false,
|
|
72
|
-
canDraw: false,
|
|
73
|
-
canClear: false,
|
|
74
|
-
elements: [],
|
|
75
|
-
openParticipants: [],
|
|
76
|
-
transport: null,
|
|
77
|
-
journeyId: "journey-test",
|
|
78
|
-
open: vi.fn(),
|
|
79
|
-
close: vi.fn(),
|
|
80
|
-
toggle: vi.fn(),
|
|
81
|
-
requestSync: vi.fn(),
|
|
82
|
-
clear: vi.fn(),
|
|
83
|
-
...overrides,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { buildSpaceViewDiagnosticsSnapshot } from "./diagnostics";
|
|
4
|
-
|
|
5
|
-
describe("buildSpaceViewDiagnosticsSnapshot", () => {
|
|
6
|
-
it("surfaces canonical feature and moderation availability", () => {
|
|
7
|
-
const snapshot = buildSpaceViewDiagnosticsSnapshot({
|
|
8
|
-
featureFlags: {
|
|
9
|
-
chat: true,
|
|
10
|
-
participants: true,
|
|
11
|
-
screenShare: false,
|
|
12
|
-
reactions: true,
|
|
13
|
-
handRaise: true,
|
|
14
|
-
whiteboard: true,
|
|
15
|
-
},
|
|
16
|
-
canEndEpisode: false,
|
|
17
|
-
participantCount: 2,
|
|
18
|
-
raisedHandCount: 0,
|
|
19
|
-
unreadChatCount: 0,
|
|
20
|
-
isScreenShareActive: false,
|
|
21
|
-
isLocalScreenSharing: false,
|
|
22
|
-
screenShareSharerParticipantId: null,
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
expect(snapshot.actionAvailability.screenShare).toMatchObject({
|
|
26
|
-
enabled: false,
|
|
27
|
-
reason: "feature-disabled",
|
|
28
|
-
});
|
|
29
|
-
expect(snapshot.actionAvailability.moderation.reason).toBe("capability-unavailable");
|
|
30
|
-
});
|
|
31
|
-
});
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { formatSpaceDuration } from "./format-space-duration";
|
|
4
|
-
|
|
5
|
-
describe("formatSpaceDuration", () => {
|
|
6
|
-
it.each([
|
|
7
|
-
[0, "0:00"],
|
|
8
|
-
[59, "0:59"],
|
|
9
|
-
[125, "2:05"],
|
|
10
|
-
])("formats %s elapsed seconds", (secondsElapsed, expected) => {
|
|
11
|
-
expect(formatSpaceDuration(secondsElapsed)).toBe(expected);
|
|
12
|
-
});
|
|
13
|
-
});
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { nativeSpaceGridKey } from "./native-space-grid-keys";
|
|
4
|
-
|
|
5
|
-
describe("nativeSpaceGridKey", () => {
|
|
6
|
-
it("changes when the ordered participant ids change and identifies an empty page", () => {
|
|
7
|
-
expect(nativeSpaceGridKey("page", [{ id: "participant-a" }, { id: "participant-b" }] as never)).toBe("page-participant-a|participant-b");
|
|
8
|
-
expect(nativeSpaceGridKey("page", [{ id: "participant-b" }, { id: "participant-a" }] as never)).toBe("page-participant-b|participant-a");
|
|
9
|
-
expect(nativeSpaceGridKey("page", [])).toBe("page-empty");
|
|
10
|
-
});
|
|
11
|
-
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { createSpacePageStore } from "./native-space-page-store";
|
|
3
|
-
|
|
4
|
-
describe("createSpacePageStore", () => {
|
|
5
|
-
it("keeps a non-negative page and notifies subscribers when it changes", () => {
|
|
6
|
-
const store = createSpacePageStore();
|
|
7
|
-
const listener = vi.fn();
|
|
8
|
-
const unsubscribe = store.subscribe(listener);
|
|
9
|
-
|
|
10
|
-
store.setPage(2);
|
|
11
|
-
store.setPage(2);
|
|
12
|
-
store.clampToPageCount(1);
|
|
13
|
-
|
|
14
|
-
expect(store.getSnapshot()).toBe(0);
|
|
15
|
-
expect(listener).toHaveBeenCalledTimes(2);
|
|
16
|
-
|
|
17
|
-
unsubscribe();
|
|
18
|
-
store.setPage(3);
|
|
19
|
-
|
|
20
|
-
expect(store.getSnapshot()).toBe(3);
|
|
21
|
-
expect(listener).toHaveBeenCalledTimes(2);
|
|
22
|
-
});
|
|
23
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { CHALK_CHAT_ATTACHMENT_LIMITS } from "@q9labsai/chalk-client";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
3
|
-
|
|
4
|
-
import { normalizeChatFileDrafts, uploadAndSendNativeChatAttachments } from "./space-chat-attachments";
|
|
5
|
-
|
|
6
|
-
describe("native Space chat attachments", () => {
|
|
7
|
-
it("accepts lazy file readers and enforces the client bounds", () => {
|
|
8
|
-
const result = normalizeChatFileDrafts([
|
|
9
|
-
{ name: "note.txt", type: "text/plain", size: 5, arrayBuffer: async () => new TextEncoder().encode("hello").buffer },
|
|
10
|
-
{ fileName: "image.png", mimeType: "image/png", bytes: new Uint8Array([1, 2]).buffer },
|
|
11
|
-
]);
|
|
12
|
-
|
|
13
|
-
expect(result.error).toBeNull();
|
|
14
|
-
expect(result.files.map((file) => file.fileName)).toEqual(["note.txt", "image.png"]);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("rejects unsupported types and excess staged files before upload", () => {
|
|
18
|
-
expect(normalizeChatFileDrafts([{ name: "script.js", type: "application/javascript", size: 1, arrayBuffer: async () => new ArrayBuffer(1) }])).toMatchObject({ files: [], error: "This file type is not supported in Space chat." });
|
|
19
|
-
const files = Array.from({ length: CHALK_CHAT_ATTACHMENT_LIMITS.maximumPerMessage + 1 }, (_, index) => ({ name: `note-${index}.txt`, type: "text/plain", size: 1, arrayBuffer: async () => new ArrayBuffer(1) }));
|
|
20
|
-
expect(normalizeChatFileDrafts(files)).toMatchObject({ files: [], error: `You can attach up to ${CHALK_CHAT_ATTACHMENT_LIMITS.maximumPerMessage} files per message.` });
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("uploads every staged file before sending an attachment-only message", async () => {
|
|
24
|
-
const files = normalizeChatFileDrafts([{ name: "note.txt", type: "text/plain", size: 1, arrayBuffer: async () => new ArrayBuffer(1) }]).files;
|
|
25
|
-
const upload = vi.fn(async () => ({ attachmentId: "note.txt", fileName: "note.txt", mimeType: "text/plain" as const, byteLength: 1 }));
|
|
26
|
-
const send = vi.fn(async () => undefined);
|
|
27
|
-
|
|
28
|
-
await uploadAndSendNativeChatAttachments(files, "", upload, send);
|
|
29
|
-
|
|
30
|
-
expect(upload).toHaveBeenCalledOnce();
|
|
31
|
-
expect(send).toHaveBeenCalledWith({ text: "", attachments: [{ attachmentId: "note.txt", fileName: "note.txt", mimeType: "text/plain", byteLength: 1 }] });
|
|
32
|
-
});
|
|
33
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { buildParticipantActionDescriptors, formatChatTimestamp } from "./space-progressive-surface-helpers";
|
|
4
|
-
|
|
5
|
-
describe("space progressive surface helpers", () => {
|
|
6
|
-
it("only exposes capability-backed Participant actions", () => {
|
|
7
|
-
const actions = buildParticipantActionDescriptors({
|
|
8
|
-
canMuteParticipants: true,
|
|
9
|
-
canRequestMedia: false,
|
|
10
|
-
canStopParticipantCamera: false,
|
|
11
|
-
canStopParticipantScreenShare: false,
|
|
12
|
-
canRemoveParticipants: true,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
expect(actions.map((action) => action.label)).toEqual(["Mute microphone", "Remove Participant"]);
|
|
16
|
-
expect(actions.at(-1)?.destructive).toBe(true);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it("formats valid chat timestamps and ignores empty metadata", () => {
|
|
20
|
-
expect(formatChatTimestamp(0)).toBe("");
|
|
21
|
-
expect(formatChatTimestamp(Number.NaN)).toBe("");
|
|
22
|
-
expect(formatChatTimestamp(Date.UTC(2026, 7, 4, 12, 34))).toMatch(/\d{1,2}:34/);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import type { SpaceViewControllerContractCheck } from "./useSpaceViewController.contract";
|
|
4
|
-
|
|
5
|
-
describe("useSpaceViewController contract", () => {
|
|
6
|
-
it("keeps the bidirectional return-shape assertion compiled", () => {
|
|
7
|
-
const contractCheck: SpaceViewControllerContractCheck = true;
|
|
8
|
-
expect(contractCheck).toBe(true);
|
|
9
|
-
});
|
|
10
|
-
});
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
const directClient = vi.hoisted(() => ({
|
|
4
|
-
chat: { files: { url: vi.fn(() => "https://example.test/file") }, loadOlder: vi.fn(async () => ({ status: "loaded" })), markRead: vi.fn(async () => null), send: vi.fn(async () => ({})) },
|
|
5
|
-
media: {
|
|
6
|
-
acceptRequest: vi.fn(async () => undefined),
|
|
7
|
-
declineRequest: vi.fn(async () => undefined),
|
|
8
|
-
setCameraEnabled: vi.fn(async () => undefined),
|
|
9
|
-
setMicrophoneEnabled: vi.fn(async () => undefined),
|
|
10
|
-
setScreenShareEnabled: vi.fn(async () => undefined),
|
|
11
|
-
},
|
|
12
|
-
participants: {
|
|
13
|
-
admit: vi.fn(async () => undefined),
|
|
14
|
-
assignRole: vi.fn(async () => undefined),
|
|
15
|
-
deny: vi.fn(async () => undefined),
|
|
16
|
-
lowerHand: vi.fn(async () => undefined),
|
|
17
|
-
mute: vi.fn(async () => undefined),
|
|
18
|
-
raiseHand: vi.fn(async () => undefined),
|
|
19
|
-
remove: vi.fn(async () => undefined),
|
|
20
|
-
renameSelf: vi.fn(async () => undefined),
|
|
21
|
-
requestMedia: vi.fn(async () => ({ status: "delivered" })),
|
|
22
|
-
stopScreenShare: vi.fn(async () => undefined),
|
|
23
|
-
stopVideo: vi.fn(async () => undefined),
|
|
24
|
-
},
|
|
25
|
-
reactions: { send: vi.fn(async () => ({})) },
|
|
26
|
-
whiteboard: { transport: vi.fn(() => null) },
|
|
27
|
-
}));
|
|
28
|
-
|
|
29
|
-
vi.mock("react", () => ({
|
|
30
|
-
useCallback: <T>(callback: T) => callback,
|
|
31
|
-
useEffect: () => undefined,
|
|
32
|
-
useMemo: <T>(factory: () => T) => factory(),
|
|
33
|
-
useState: <T>(initial: T): readonly [T, (next: T) => void] => [initial, vi.fn()],
|
|
34
|
-
}));
|
|
35
|
-
vi.mock("react-native", () => ({ Alert: { alert: vi.fn() }, Linking: { openURL: vi.fn(async () => undefined) } }));
|
|
36
|
-
vi.mock("../../context/space-client-context", () => ({ useSpaceClient: () => directClient }));
|
|
37
|
-
vi.mock("../../hooks/space-hooks", () => ({
|
|
38
|
-
useSelf: () => ({ participantId: "self", displayName: "Taylor", handRaised: false, can: (capability: string) => capability !== "endEpisode" }),
|
|
39
|
-
useParticipants: () => ({
|
|
40
|
-
roster: [
|
|
41
|
-
{
|
|
42
|
-
participantId: "self",
|
|
43
|
-
displayName: "Taylor",
|
|
44
|
-
role: "collaborator",
|
|
45
|
-
eligibleRoles: [],
|
|
46
|
-
capabilities: [],
|
|
47
|
-
handRaised: false,
|
|
48
|
-
media: { microphone: "active", camera: "active", screenShare: "inactive" },
|
|
49
|
-
presence: { state: "connected", speaking: false, activeSpeaker: false },
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
admissionQueue: [],
|
|
53
|
-
}),
|
|
54
|
-
useMedia: () => ({
|
|
55
|
-
local: {
|
|
56
|
-
microphone: { state: "enabled", track: null },
|
|
57
|
-
camera: { state: "enabled", track: null },
|
|
58
|
-
screen: { state: "disabled", track: null },
|
|
59
|
-
},
|
|
60
|
-
screenShare: { state: "disabled", track: null },
|
|
61
|
-
remote: [],
|
|
62
|
-
incomingRequests: [],
|
|
63
|
-
}),
|
|
64
|
-
useChat: () => ({ status: "ready", messages: [], pendingSends: [], readReceipts: [], unreadCount: 0, pagination: { cursor: null, hasOlder: false, historyTruncated: false } }),
|
|
65
|
-
useReactions: () => ({ active: [] }),
|
|
66
|
-
useWhiteboard: () => ({ engine: { status: "ready", sceneId: null, revision: null, error: null } }),
|
|
67
|
-
}));
|
|
68
|
-
vi.mock("../../utils/ios-simulator", () => ({ isIosSimulator: () => false }));
|
|
69
|
-
vi.mock("./useSpaceViewPanels", () => ({
|
|
70
|
-
useSpaceViewPanels: () => ({
|
|
71
|
-
layout: { layout: "grid" },
|
|
72
|
-
panel: null,
|
|
73
|
-
secondsElapsed: 5,
|
|
74
|
-
formattedDuration: "0:05",
|
|
75
|
-
actionsOpen: false,
|
|
76
|
-
reactionPickerOpen: false,
|
|
77
|
-
whiteboard: { isOpen: false, canDraw: true, canClear: false, elements: [], openParticipants: [], transport: null },
|
|
78
|
-
handleLeave: vi.fn(),
|
|
79
|
-
openPanel: vi.fn(),
|
|
80
|
-
closePanel: vi.fn(),
|
|
81
|
-
handleInviteParticipants: vi.fn(),
|
|
82
|
-
}),
|
|
83
|
-
}));
|
|
84
|
-
vi.mock("./useSpaceViewDerived", () => ({ useSpaceViewDerived: () => ({ isStageMode: false, gridPages: [], allParticipants: [] }) }));
|
|
85
|
-
|
|
86
|
-
import { useSpaceViewController } from "./useSpaceViewController";
|
|
87
|
-
|
|
88
|
-
describe("useSpaceViewController", () => {
|
|
89
|
-
it("binds native Space actions directly to the canonical SpaceClient", async () => {
|
|
90
|
-
const controller = useSpaceViewController({ spaceName: "Design review", onLeave: vi.fn() });
|
|
91
|
-
|
|
92
|
-
expect(controller.participants.localParticipant?.displayName).toBe("Taylor");
|
|
93
|
-
expect(controller.chat.isEnabled).toBe(true);
|
|
94
|
-
expect(controller.canSettings).toBe(true);
|
|
95
|
-
expect(controller.canInvite).toBe(false);
|
|
96
|
-
controller.toggleAudio();
|
|
97
|
-
controller.sendReaction("🎉");
|
|
98
|
-
controller.requestStartParticipantCamera("participant-2");
|
|
99
|
-
|
|
100
|
-
await vi.waitFor(() => {
|
|
101
|
-
expect(directClient.media.setMicrophoneEnabled).toHaveBeenCalledWith(false);
|
|
102
|
-
expect(directClient.reactions.send).toHaveBeenCalledWith("🎉");
|
|
103
|
-
expect(directClient.participants.requestMedia).toHaveBeenCalledWith("participant-2", "camera");
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
});
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
const viewport = vi.hoisted(() => ({ width: 500 }));
|
|
4
|
-
const defaultViewportWidth = 500;
|
|
5
|
-
|
|
6
|
-
vi.mock("react", () => ({
|
|
7
|
-
useMemo: <T>(factory: () => T) => factory(),
|
|
8
|
-
}));
|
|
9
|
-
vi.mock("react-native", () => ({
|
|
10
|
-
useWindowDimensions: () => ({ width: viewport.width, height: 800 }),
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
import { useSpaceViewDerived } from "./useSpaceViewDerived";
|
|
14
|
-
|
|
15
|
-
describe("useSpaceViewDerived", () => {
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
viewport.width = defaultViewportWidth;
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
afterEach(() => {
|
|
21
|
-
viewport.width = defaultViewportWidth;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("marks compact whiteboard layouts and keeps the local participant first", () => {
|
|
25
|
-
const local = { id: "local", screenShareTrack: null };
|
|
26
|
-
const remote = { id: "remote", screenShareTrack: null };
|
|
27
|
-
|
|
28
|
-
const derived = useSpaceViewDerived({
|
|
29
|
-
participants: [remote] as never,
|
|
30
|
-
localParticipant: local as never,
|
|
31
|
-
screenShare: { isActive: false, isLocalSharing: false, sharerParticipantId: null, videoTrack: null },
|
|
32
|
-
isWhiteboardOpen: true,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
expect(derived.isCompactViewport).toBe(true);
|
|
36
|
-
expect(derived.primaryContent).toBe("whiteboard");
|
|
37
|
-
expect(derived.allParticipants.map((participant) => participant.id)).toEqual(["local", "remote"]);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("uses the wide viewport to expose an active remote screen share", () => {
|
|
41
|
-
viewport.width = 1024;
|
|
42
|
-
const local = { id: "local", screenShareTrack: null };
|
|
43
|
-
const remote = { id: "remote", screenShareTrack: null };
|
|
44
|
-
const track = { readyState: "live" } as never;
|
|
45
|
-
|
|
46
|
-
const derived = useSpaceViewDerived({
|
|
47
|
-
participants: [remote] as never,
|
|
48
|
-
localParticipant: local as never,
|
|
49
|
-
screenShare: { isActive: true, isLocalSharing: false, sharerParticipantId: "remote", videoTrack: track },
|
|
50
|
-
isWhiteboardOpen: false,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
expect(derived.isCompactViewport).toBe(false);
|
|
54
|
-
expect(derived.primaryContent).toBe("screen-share");
|
|
55
|
-
expect(derived.screenSharer).toBe(remote);
|
|
56
|
-
expect(derived.screenShareTrack).toBe(track);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// @vitest-environment happy-dom
|
|
2
|
-
|
|
3
|
-
import { describe, expect, it, vi } from "vitest";
|
|
4
|
-
|
|
5
|
-
import { act, renderHook } from "../../test-support/test-renderer";
|
|
6
|
-
|
|
7
|
-
const alert = vi.hoisted(() => ({ alert: vi.fn() }));
|
|
8
|
-
|
|
9
|
-
vi.mock("react-native", () => ({ Alert: alert, Share: { share: vi.fn(async () => undefined) } }));
|
|
10
|
-
|
|
11
|
-
import { useSpaceViewPanels } from "./useSpaceViewPanels";
|
|
12
|
-
|
|
13
|
-
describe("controlled Space view fixture state", () => {
|
|
14
|
-
it("updates production panel and whiteboard state without remounting the hook", () => {
|
|
15
|
-
const base = {
|
|
16
|
-
canWhiteboard: true,
|
|
17
|
-
canDraw: true,
|
|
18
|
-
canClear: false,
|
|
19
|
-
canEndEpisode: false,
|
|
20
|
-
transport: null,
|
|
21
|
-
onLeave: vi.fn(),
|
|
22
|
-
onEndEpisode: undefined,
|
|
23
|
-
run: vi.fn(async (action: () => void | Promise<void>) => {
|
|
24
|
-
await action();
|
|
25
|
-
}),
|
|
26
|
-
};
|
|
27
|
-
const { result, rerender } = renderHook(({ state }) => useSpaceViewPanels({ ...base, controlledState: state }), { initialProps: { state: { layout: "grid" as const, panel: null, settingsOpen: false, whiteboardOpen: false } } });
|
|
28
|
-
|
|
29
|
-
expect(result.current.layout.layout).toBe("grid");
|
|
30
|
-
expect(result.current.panel).toBeNull();
|
|
31
|
-
expect(result.current.whiteboard.isOpen).toBe(false);
|
|
32
|
-
|
|
33
|
-
act(() => rerender({ state: { layout: "presentation", panel: "participants", settingsOpen: true, whiteboardOpen: true } }));
|
|
34
|
-
|
|
35
|
-
expect(result.current.layout.layout).toBe("presentation");
|
|
36
|
-
expect(result.current.panel).toBe("participants");
|
|
37
|
-
expect(result.current.settingsOpen).toBe(true);
|
|
38
|
-
expect(result.current.whiteboard.isOpen).toBe(true);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
const alert = vi.hoisted(() => ({ alert: vi.fn() }));
|
|
4
|
-
|
|
5
|
-
vi.mock("react", () => ({
|
|
6
|
-
useCallback: <T>(callback: T) => callback,
|
|
7
|
-
useEffect: () => undefined,
|
|
8
|
-
useMemo: <T>(factory: () => T) => factory(),
|
|
9
|
-
useRef: <T>(current: T) => ({ current }),
|
|
10
|
-
useState: <T>(initial: T): readonly [T, (next: T) => void] => [initial, vi.fn()],
|
|
11
|
-
}));
|
|
12
|
-
vi.mock("react-native", () => ({ Alert: alert, Share: { share: vi.fn(async () => undefined) } }));
|
|
13
|
-
vi.mock("../../hooks/useLayout", () => ({
|
|
14
|
-
useLayout: () => ({ layout: "grid", isMobileView: true, isFullscreen: false, setLayout: vi.fn(), toggleLayout: vi.fn(), toggleFullscreen: vi.fn(async () => undefined) }),
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
|
-
import { useSpaceViewPanels } from "./useSpaceViewPanels";
|
|
18
|
-
|
|
19
|
-
describe("useSpaceViewPanels", () => {
|
|
20
|
-
it("composes layout, panel state, whiteboard state, and leave actions", () => {
|
|
21
|
-
const panels = useSpaceViewPanels({
|
|
22
|
-
spaceName: "Space",
|
|
23
|
-
inviteLink: "https://example.test/space",
|
|
24
|
-
canWhiteboard: true,
|
|
25
|
-
canDraw: true,
|
|
26
|
-
canClear: false,
|
|
27
|
-
canEndEpisode: true,
|
|
28
|
-
transport: null,
|
|
29
|
-
onLeave: vi.fn(),
|
|
30
|
-
onEndEpisode: vi.fn(),
|
|
31
|
-
run: vi.fn(async (action) => {
|
|
32
|
-
await action();
|
|
33
|
-
}),
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
expect(panels).toMatchObject({ layout: { layout: "grid" }, panel: null, formattedDuration: "0:00", whiteboard: { isOpen: false, canDraw: true } });
|
|
37
|
-
expect(panels.whiteboard.journeyId).toMatch(/^native-whiteboard-|^[0-9a-f-]{36}$/u);
|
|
38
|
-
expect(panels.whiteboard.onMetric).toBeUndefined();
|
|
39
|
-
panels.openPanel("participants");
|
|
40
|
-
expect(panels.openPanel).toBeTypeOf("function");
|
|
41
|
-
panels.handleLeave();
|
|
42
|
-
expect(alert.alert).toHaveBeenCalledWith("Leave Space?", "Choose how you want to leave.", expect.any(Array));
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("forwards registered W3C journey context to the whiteboard surface", () => {
|
|
46
|
-
const onMetric = vi.fn();
|
|
47
|
-
const panels = useSpaceViewPanels({
|
|
48
|
-
canWhiteboard: true,
|
|
49
|
-
canDraw: true,
|
|
50
|
-
canClear: false,
|
|
51
|
-
canEndEpisode: false,
|
|
52
|
-
transport: null,
|
|
53
|
-
onLeave: vi.fn(),
|
|
54
|
-
onEndEpisode: undefined,
|
|
55
|
-
run: vi.fn(async (action) => {
|
|
56
|
-
await action();
|
|
57
|
-
}),
|
|
58
|
-
journeyContext: {
|
|
59
|
-
journeyId: "00000000-0000-4000-8000-000000000001",
|
|
60
|
-
traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
|
|
61
|
-
tracestate: "chalk=mobile",
|
|
62
|
-
recordWhiteboardMetric: onMetric,
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
expect(panels.whiteboard).toMatchObject({
|
|
67
|
-
journeyId: "00000000-0000-4000-8000-000000000001",
|
|
68
|
-
traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
|
|
69
|
-
tracestate: "chalk=mobile",
|
|
70
|
-
onMetric,
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
import ts from "typescript";
|
|
4
|
-
import { describe, expect, it } from "vitest";
|
|
5
|
-
|
|
6
|
-
describe("SpaceLifecyclePhase", () => {
|
|
7
|
-
it("keeps the exported source contract on the canonical native phases", () => {
|
|
8
|
-
const sourcePath = fileURLToPath(new URL("./space-lifecycle.ts", import.meta.url));
|
|
9
|
-
const sourceFile = ts.createSourceFile("space-lifecycle.ts", readFileSync(sourcePath, "utf8"), ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
10
|
-
const declaration = sourceFile.statements.find((statement): statement is ts.TypeAliasDeclaration => ts.isTypeAliasDeclaration(statement) && statement.name.text === "SpaceLifecyclePhase");
|
|
11
|
-
|
|
12
|
-
expect(declaration).toBeDefined();
|
|
13
|
-
if (!declaration || !ts.isUnionTypeNode(declaration.type)) throw new Error("SpaceLifecyclePhase must remain an exported string union.");
|
|
14
|
-
|
|
15
|
-
expect(declaration.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)).toBe(true);
|
|
16
|
-
const phases = declaration.type.types.map((member) => (ts.isLiteralTypeNode(member) && ts.isStringLiteral(member.literal) ? member.literal.text : "<invalid>"));
|
|
17
|
-
expect(phases).toEqual(["entrance", "joining", "live", "left"]);
|
|
18
|
-
});
|
|
19
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { ChalkProvider, useSpaceClient } from "./space-client-context";
|
|
4
|
-
|
|
5
|
-
describe("ChalkProvider", () => {
|
|
6
|
-
it("provides the canonical SpaceClient context entry", () => {
|
|
7
|
-
expect(ChalkProvider).toBeTypeOf("function");
|
|
8
|
-
expect(useSpaceClient).toBeTypeOf("function");
|
|
9
|
-
});
|
|
10
|
-
});
|
package/src/diagnostics.test.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { buildDevDiagnosticsCopyText, classifyTarget, getDevDiagnosticsState, recordDiagnosticsFailure, resetDevDiagnosticsState, setDevDiagnosticsEnvironment } from "./diagnostics";
|
|
4
|
-
|
|
5
|
-
describe("native diagnostics", () => {
|
|
6
|
-
beforeEach(resetDevDiagnosticsState);
|
|
7
|
-
|
|
8
|
-
it("does not retain caller credentials", () => {
|
|
9
|
-
expect(buildDevDiagnosticsCopyText()).not.toContain("inviteToken");
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it("uses the public-invite API as the single environment boundary", () => {
|
|
13
|
-
setDevDiagnosticsEnvironment({
|
|
14
|
-
apiBaseURL: "http://localhost:8787",
|
|
15
|
-
});
|
|
16
|
-
expect(getDevDiagnosticsState().environment.target).toBe("local");
|
|
17
|
-
expect(classifyTarget("https://api.chalkmeet.com")).toBe("production");
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("records failures without legacy auth or transport state", () => {
|
|
21
|
-
recordDiagnosticsFailure("connection-join", "Sync unavailable");
|
|
22
|
-
expect(getDevDiagnosticsState().lastFailure).toMatchObject({
|
|
23
|
-
source: "connection-join",
|
|
24
|
-
message: "Sync unavailable",
|
|
25
|
-
});
|
|
26
|
-
expect(buildDevDiagnosticsCopyText()).not.toContain("apiKey");
|
|
27
|
-
expect(buildDevDiagnosticsCopyText()).not.toContain("RealtimeKit");
|
|
28
|
-
});
|
|
29
|
-
});
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import type { NativeMediaStream, NativeMediaStreamTrack } from "../media/native-webrtc";
|
|
3
|
-
import { createEntrancePreviewStore } from "./entrance-preview-store";
|
|
4
|
-
|
|
5
|
-
function createStream(withVideoTrack: boolean): { stream: NativeMediaStream; wasStopped: () => boolean } {
|
|
6
|
-
let stopped = false;
|
|
7
|
-
const track: NativeMediaStreamTrack = {
|
|
8
|
-
id: "camera-track",
|
|
9
|
-
kind: "video",
|
|
10
|
-
enabled: true,
|
|
11
|
-
muted: false,
|
|
12
|
-
readyState: "live",
|
|
13
|
-
stop: () => {
|
|
14
|
-
stopped = true;
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
|
-
const stream: NativeMediaStream = {
|
|
18
|
-
toURL: () => "camera-stream",
|
|
19
|
-
getTracks: () => (withVideoTrack ? [track] : []),
|
|
20
|
-
getVideoTracks: () => (withVideoTrack ? [track] : []),
|
|
21
|
-
getAudioTracks: () => [],
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
return { stream, wasStopped: () => stopped };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
describe("createEntrancePreviewStore", () => {
|
|
28
|
-
it("starts the camera on subscription and stops it on cleanup", async () => {
|
|
29
|
-
const camera = createStream(true);
|
|
30
|
-
const getUserMedia = vi.fn(async () => camera.stream);
|
|
31
|
-
const store = createEntrancePreviewStore({
|
|
32
|
-
enabled: true,
|
|
33
|
-
simulatorVideoDisabled: false,
|
|
34
|
-
simulatorVideoMessage: "simulator",
|
|
35
|
-
getUserMedia,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const unsubscribe = store.subscribe(() => {});
|
|
39
|
-
await Promise.resolve();
|
|
40
|
-
|
|
41
|
-
expect(getUserMedia).toHaveBeenCalledWith({ audio: false, video: { facingMode: "user" } });
|
|
42
|
-
expect(store.getSnapshot()).toEqual({ previewStream: camera.stream, previewError: null });
|
|
43
|
-
|
|
44
|
-
unsubscribe();
|
|
45
|
-
expect(camera.wasStopped()).toBe(true);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("reports simulator video as unavailable without opening the camera", () => {
|
|
49
|
-
const getUserMedia = vi.fn(async () => createStream(true).stream);
|
|
50
|
-
const store = createEntrancePreviewStore({
|
|
51
|
-
enabled: true,
|
|
52
|
-
simulatorVideoDisabled: true,
|
|
53
|
-
simulatorVideoMessage: "Video is unavailable in the iOS simulator",
|
|
54
|
-
getUserMedia,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const unsubscribe = store.subscribe(() => {});
|
|
58
|
-
|
|
59
|
-
expect(store.getSnapshot()).toEqual({ previewStream: null, previewError: "Video is unavailable in the iOS simulator" });
|
|
60
|
-
expect(getUserMedia).not.toHaveBeenCalled();
|
|
61
|
-
unsubscribe();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("keeps the preview disabled when Entrance disables preview", () => {
|
|
65
|
-
const getUserMedia = vi.fn(async () => createStream(true).stream);
|
|
66
|
-
const store = createEntrancePreviewStore({
|
|
67
|
-
enabled: false,
|
|
68
|
-
simulatorVideoDisabled: false,
|
|
69
|
-
simulatorVideoMessage: "simulator",
|
|
70
|
-
getUserMedia,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const unsubscribe = store.subscribe(() => {});
|
|
74
|
-
|
|
75
|
-
expect(store.getSnapshot()).toEqual({ previewStream: null, previewError: null });
|
|
76
|
-
expect(getUserMedia).not.toHaveBeenCalled();
|
|
77
|
-
unsubscribe();
|
|
78
|
-
});
|
|
79
|
-
});
|