@q9labsai/chalk-react-native 4.1.11 → 4.1.13
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 +4 -2
- package/dist/{chunk-MMBE2BVK.js → chunk-4NLA35HE.js} +649 -407
- package/dist/{chunk-LZ67SXGB.js → chunk-MK6OO4V7.js} +6 -0
- package/dist/client.js +1 -1
- package/dist/components/Entrance.d.ts.map +1 -1
- package/dist/components/logo/Logo.d.ts +11 -0
- package/dist/components/logo/Logo.d.ts.map +1 -0
- package/dist/components/native-space-view/SpaceTopBar.android.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/ios-pad.js +4 -2
- package/dist/ios-phone.js +4 -2
- package/dist/ios.js +4 -2
- package/dist/macos.js +4 -2
- package/package.json +4 -4
- package/src/callkit/callkit.test.ts +12 -10
- package/src/components/Entrance.tsx +2 -1
- package/src/components/logo/Logo.tsx +309 -0
- package/src/components/native-space-view/SpaceTopBar.android.tsx +2 -1
- package/src/index.ts +2 -0
- package/src/space-client/create-native-space-client.ts +8 -0
- package/dist/components/LogoElements.d.ts +0 -6
- package/dist/components/LogoElements.d.ts.map +0 -1
- 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 -19
- 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/LogoElements.test.tsx +0 -60
- package/src/components/LogoElements.tsx +0 -119
- 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/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 -43
- 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,11 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
vi.mock("./SpaceView.shared", () => ({ SpaceViewShared: () => null }));
|
|
4
|
-
|
|
5
|
-
describe("SpaceView.ios-phone", () => {
|
|
6
|
-
it("exposes the canonical platform component name", async () => {
|
|
7
|
-
const { SpaceViewIosPhone } = await import("./SpaceView.ios-phone");
|
|
8
|
-
|
|
9
|
-
expect(SpaceViewIosPhone).toBeTypeOf("function");
|
|
10
|
-
});
|
|
11
|
-
});
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
vi.mock("./SpaceView.shared", () => ({ SpaceViewShared: () => null }));
|
|
4
|
-
|
|
5
|
-
describe("SpaceView.macos", () => {
|
|
6
|
-
it("exposes the canonical platform component name", async () => {
|
|
7
|
-
const { SpaceViewMacos } = await import("./SpaceView.macos");
|
|
8
|
-
|
|
9
|
-
expect(SpaceViewMacos).toBeTypeOf("function");
|
|
10
|
-
});
|
|
11
|
-
});
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
|
|
4
|
-
const state = vi.hoisted(() => ({
|
|
5
|
-
controller: {
|
|
6
|
-
actionsOpen: false,
|
|
7
|
-
activeReactions: [],
|
|
8
|
-
canChat: true,
|
|
9
|
-
canParticipants: true,
|
|
10
|
-
derived: { allParticipants: [], gridPages: [], isCompactViewport: false, isStageMode: false, primaryContent: "grid", screenShareTrack: null, screenSharer: null },
|
|
11
|
-
handRaised: false,
|
|
12
|
-
handleLeave: vi.fn(),
|
|
13
|
-
isCameraOff: false,
|
|
14
|
-
isMuted: false,
|
|
15
|
-
layout: { layout: "grid" },
|
|
16
|
-
openPanel: vi.fn(),
|
|
17
|
-
panel: null,
|
|
18
|
-
raisedHandCount: 0,
|
|
19
|
-
reactionPickerOpen: false,
|
|
20
|
-
selfName: "Taylor",
|
|
21
|
-
setActionsOpen: vi.fn(),
|
|
22
|
-
setReactionPickerOpen: vi.fn(),
|
|
23
|
-
simulatorMediaDisabled: false,
|
|
24
|
-
spaceName: "Design space",
|
|
25
|
-
chat: { unreadCount: 0 },
|
|
26
|
-
whiteboard: { canDraw: true, elements: [], isOpen: false, openParticipants: [] },
|
|
27
|
-
},
|
|
28
|
-
useSpaceViewController: vi.fn(),
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
|
-
vi.mock("react-native", () => ({
|
|
32
|
-
Alert: { alert: vi.fn() },
|
|
33
|
-
StyleSheet: { create: <T,>(styles: T) => styles },
|
|
34
|
-
Text: "Text",
|
|
35
|
-
View: "View",
|
|
36
|
-
}));
|
|
37
|
-
vi.mock("../ui/theme", () => ({ Theme: { colors: { darkCanvas: "#0a0a0b", primary: "#1bb6a6", primaryForeground: "#ffffff" } } }));
|
|
38
|
-
vi.mock("../ui/native-theme", () => ({
|
|
39
|
-
useNativeTheme: () => ({ colors: { darkCanvas: "#0a0a0b", primary: "#1bb6a6", primaryForeground: "#ffffff" } }),
|
|
40
|
-
}));
|
|
41
|
-
vi.mock("../utils/ios-simulator", () => ({ getIosSimulatorMediaMessage: () => "Media is unavailable in the simulator." }));
|
|
42
|
-
vi.mock("./ReactionPicker", () => ({ ReactionPicker: "ReactionPicker" }));
|
|
43
|
-
vi.mock("./native-space-view/SpaceActionMenu", () => ({ SpaceActionMenu: "SpaceActionMenu", selectSpaceReaction: vi.fn() }));
|
|
44
|
-
vi.mock("./native-space-view/SettingsSheet", () => ({ SettingsSheet: "SettingsSheet" }));
|
|
45
|
-
vi.mock("./native-space-view/SpacePanelSheet", () => ({ SpacePanelSheet: "SpacePanelSheet" }));
|
|
46
|
-
vi.mock("./native-space-view/SpaceBottomDock.android", () => ({ SpaceBottomDockAndroid: "SpaceBottomDockAndroid" }));
|
|
47
|
-
vi.mock("./native-space-view/SpaceGrid.android", () => ({ SpaceGridAndroid: "SpaceGridAndroid" }));
|
|
48
|
-
vi.mock("./native-space-view/SpaceStage.android", () => ({ SpaceStageAndroid: "SpaceStageAndroid" }));
|
|
49
|
-
vi.mock("./native-space-view/SpaceTopBar.android", () => ({ SpaceTopBarAndroid: "SpaceTopBarAndroid" }));
|
|
50
|
-
vi.mock("./native-space-view/SpaceWhiteboardSurface", () => ({ SpaceWhiteboardSurface: "SpaceWhiteboardSurface" }));
|
|
51
|
-
vi.mock("./native-space-view/useSpaceViewController", () => ({ useSpaceViewController: state.useSpaceViewController }));
|
|
52
|
-
|
|
53
|
-
import { SpaceViewShared } from "./SpaceView.shared";
|
|
54
|
-
|
|
55
|
-
describe("SpaceViewShared", () => {
|
|
56
|
-
const source = readFileSync(new URL("./SpaceView.shared.tsx", import.meta.url), "utf8");
|
|
57
|
-
|
|
58
|
-
beforeEach(() => {
|
|
59
|
-
state.controller.whiteboard.isOpen = false;
|
|
60
|
-
state.useSpaceViewController.mockClear();
|
|
61
|
-
state.useSpaceViewController.mockReturnValue(state.controller);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
afterEach(() => {
|
|
65
|
-
state.controller.whiteboard.isOpen = false;
|
|
66
|
-
state.useSpaceViewController.mockClear();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("passes reconnecting state and capability-gated controls to the native surface", () => {
|
|
70
|
-
const props = { onLeave: vi.fn(), onOpenDiagnostics: vi.fn(), reconnecting: true, logoUrl: "https://cdn.example.test/logo.png", spaceName: "Design space" };
|
|
71
|
-
const tree = SpaceViewShared(props);
|
|
72
|
-
|
|
73
|
-
expect(state.useSpaceViewController).toHaveBeenCalledWith(props);
|
|
74
|
-
expect(findText(tree)).toContain("Reconnecting… controls will resume shortly.");
|
|
75
|
-
expect(findElement(tree, "SpaceTopBarAndroid")).toMatchObject({ props: { logoUrl: props.logoUrl, spaceName: props.spaceName } });
|
|
76
|
-
expect(findElement(tree, "SpaceBottomDockAndroid")).toMatchObject({ props: { onOpenChat: expect.any(Function), unreadChatCount: 0 } });
|
|
77
|
-
expect(findElement(tree, "SpaceActionMenu")).toMatchObject({ props: { onOpenDiagnostics: props.onOpenDiagnostics } });
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("renders the whiteboard surface instead of a stage or grid when it is open", () => {
|
|
81
|
-
state.controller.whiteboard.isOpen = true;
|
|
82
|
-
|
|
83
|
-
const tree = SpaceViewShared({ onLeave: vi.fn() });
|
|
84
|
-
|
|
85
|
-
expect(findElement(tree, "SpaceWhiteboardSurface")).toBeTruthy();
|
|
86
|
-
expect(findElement(tree, "SpaceStageAndroid")).toBeUndefined();
|
|
87
|
-
expect(findElement(tree, "SpaceGridAndroid")).toBeUndefined();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("mounts the canonical panel sheet that owns the attachment composer", () => {
|
|
91
|
-
const panelSource = readFileSync(new URL("./native-space-view/SpacePanelSheet.tsx", import.meta.url), "utf8");
|
|
92
|
-
const chatSource = readFileSync(new URL("./native-space-view/SpaceChatSheet.tsx", import.meta.url), "utf8");
|
|
93
|
-
|
|
94
|
-
expect(source).toContain("<SpacePanelSheet controller={controller} />");
|
|
95
|
-
expect(source).not.toContain("<SpacePanel controller={controller} />");
|
|
96
|
-
expect(source).toContain("<SettingsSheet controller={controller}");
|
|
97
|
-
expect(panelSource).toContain("<SpaceChatSheet");
|
|
98
|
-
expect(chatSource).toContain('accessibilityLabel="Attach file"');
|
|
99
|
-
expect(chatSource).toContain("controller.removeChatAttachment(index)");
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
function findElement(value: unknown, type: string): { readonly props?: Record<string, unknown> } | undefined {
|
|
104
|
-
if (Array.isArray(value)) return value.map((child) => findElement(child, type)).find(Boolean);
|
|
105
|
-
if (!value || typeof value !== "object") return undefined;
|
|
106
|
-
const element = value as { readonly type?: unknown; readonly props?: Record<string, unknown> };
|
|
107
|
-
if (element.type === type) return element;
|
|
108
|
-
return findElement(element.props?.children, type);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function findText(value: unknown): string[] {
|
|
112
|
-
if (typeof value === "string") return [value];
|
|
113
|
-
if (Array.isArray(value)) return value.flatMap(findText);
|
|
114
|
-
if (!value || typeof value !== "object") return [];
|
|
115
|
-
return findText((value as { readonly props?: { readonly children?: unknown } }).props?.children);
|
|
116
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
vi.mock("../platform/platform", () => ({ resolvePlatformVariant: () => "android" }));
|
|
4
|
-
vi.mock("./SpaceView.android", () => ({ SpaceViewAndroid: () => null }));
|
|
5
|
-
vi.mock("./SpaceView.ios-pad", () => ({ SpaceViewIosPad: () => null }));
|
|
6
|
-
vi.mock("./SpaceView.ios-phone", () => ({ SpaceViewIosPhone: () => null }));
|
|
7
|
-
vi.mock("./SpaceView.macos", () => ({ SpaceViewMacos: () => null }));
|
|
8
|
-
|
|
9
|
-
describe("SpaceView", () => {
|
|
10
|
-
it("selects the platform-specific Space", async () => {
|
|
11
|
-
const { SpaceView } = await import("./SpaceView");
|
|
12
|
-
|
|
13
|
-
expect(SpaceView({ onLeave: vi.fn() })).toBeTruthy();
|
|
14
|
-
});
|
|
15
|
-
});
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import type { AndroidConnectionServiceControllerDependencies, AndroidConnectionServiceControllerInput } from "./android-connection-service-controller";
|
|
3
|
-
import { AndroidConnectionServiceController } from "./android-connection-service-controller";
|
|
4
|
-
|
|
5
|
-
async function flushMicrotasks(): Promise<void> {
|
|
6
|
-
await Promise.resolve();
|
|
7
|
-
await Promise.resolve();
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function createInput(overrides: Partial<AndroidConnectionServiceControllerInput> = {}): AndroidConnectionServiceControllerInput {
|
|
11
|
-
return {
|
|
12
|
-
displayName: "Guest",
|
|
13
|
-
enabled: true,
|
|
14
|
-
hasVideo: true,
|
|
15
|
-
joinNonce: 1,
|
|
16
|
-
onDisconnectRequest: vi.fn(),
|
|
17
|
-
phase: "joining",
|
|
18
|
-
spaceId: "space-1",
|
|
19
|
-
spaceName: "Daily Standup",
|
|
20
|
-
...overrides,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function createDependencies() {
|
|
25
|
-
let listener: Parameters<AndroidConnectionServiceControllerDependencies["addListener"]>[0] | undefined;
|
|
26
|
-
const dependencies: AndroidConnectionServiceControllerDependencies = {
|
|
27
|
-
addListener: vi.fn((nextListener) => {
|
|
28
|
-
listener = nextListener;
|
|
29
|
-
return vi.fn();
|
|
30
|
-
}),
|
|
31
|
-
endCall: vi.fn(async () => true),
|
|
32
|
-
ensureRegistered: vi.fn(async () => true),
|
|
33
|
-
setActive: vi.fn(async () => true),
|
|
34
|
-
startCall: vi.fn(async () => true),
|
|
35
|
-
};
|
|
36
|
-
return { dependencies, getListener: () => listener };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
describe("AndroidConnectionServiceController", () => {
|
|
40
|
-
it("starts, activates, and filters disconnect events for the current call", async () => {
|
|
41
|
-
const onDisconnectRequest = vi.fn();
|
|
42
|
-
const { dependencies, getListener } = createDependencies();
|
|
43
|
-
const controller = new AndroidConnectionServiceController(createInput({ onDisconnectRequest }), dependencies);
|
|
44
|
-
const unsubscribe = controller.subscribe(() => {});
|
|
45
|
-
|
|
46
|
-
expect(dependencies.ensureRegistered).toHaveBeenCalledTimes(1);
|
|
47
|
-
expect(dependencies.startCall).toHaveBeenCalledWith({
|
|
48
|
-
callId: "space-1:1",
|
|
49
|
-
displayName: "Guest",
|
|
50
|
-
hasVideo: true,
|
|
51
|
-
spaceId: "space-1",
|
|
52
|
-
spaceName: "Daily Standup",
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
controller.update(createInput({ onDisconnectRequest, phase: "live" }));
|
|
56
|
-
await flushMicrotasks();
|
|
57
|
-
expect(dependencies.setActive).toHaveBeenCalledWith("space-1:1");
|
|
58
|
-
|
|
59
|
-
getListener()?.({ callId: "other:1", reason: "remote", type: "disconnect" });
|
|
60
|
-
getListener()?.({ callId: "space-1:1", reason: "remote", type: "disconnect" });
|
|
61
|
-
expect(onDisconnectRequest).toHaveBeenCalledTimes(1);
|
|
62
|
-
|
|
63
|
-
controller.update(createInput({ onDisconnectRequest, phase: "entrance" }));
|
|
64
|
-
await flushMicrotasks();
|
|
65
|
-
expect(dependencies.endCall).toHaveBeenCalledWith("space-1:1", { reason: "canceled" });
|
|
66
|
-
|
|
67
|
-
unsubscribe();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("ends an active call locally and removes the native listener on unsubscribe", () => {
|
|
71
|
-
const { dependencies } = createDependencies();
|
|
72
|
-
const controller = new AndroidConnectionServiceController(createInput(), dependencies);
|
|
73
|
-
const unsubscribe = controller.subscribe(() => {});
|
|
74
|
-
|
|
75
|
-
unsubscribe();
|
|
76
|
-
|
|
77
|
-
expect(dependencies.endCall).toHaveBeenCalledWith("space-1:1", { reason: "local" });
|
|
78
|
-
expect(dependencies.addListener).toHaveBeenCalledTimes(1);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { observeNativeFrameNotObservable } from "./episode-diagnostic-render-observer";
|
|
3
|
-
|
|
4
|
-
const REGISTRY = Symbol.for("@chalk/private/episode-diagnostic-render-registry/v1");
|
|
5
|
-
|
|
6
|
-
describe("observeNativeFrameNotObservable", () => {
|
|
7
|
-
it("emits an explicit gap because the native RTC view exposes no first-frame callback", () => {
|
|
8
|
-
const track = {};
|
|
9
|
-
const report = vi.fn();
|
|
10
|
-
(globalThis as unknown as Record<symbol, unknown>)[REGISTRY] = new WeakMap([[track, report]]);
|
|
11
|
-
|
|
12
|
-
observeNativeFrameNotObservable(track);
|
|
13
|
-
|
|
14
|
-
expect(report).toHaveBeenCalledWith("not_observable");
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("is inert when diagnostics did not register the track", () => {
|
|
18
|
-
const report = vi.fn();
|
|
19
|
-
(globalThis as unknown as Record<symbol, unknown>)[REGISTRY] = new WeakMap();
|
|
20
|
-
|
|
21
|
-
expect(() => observeNativeFrameNotObservable({})).not.toThrow();
|
|
22
|
-
expect(report).not.toHaveBeenCalled();
|
|
23
|
-
});
|
|
24
|
-
});
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { createAnimationRefController, type StoppableAnimation } from "./native-animation-controller";
|
|
3
|
-
|
|
4
|
-
function createAnimation(): StoppableAnimation {
|
|
5
|
-
return {
|
|
6
|
-
start: vi.fn(),
|
|
7
|
-
stop: vi.fn(),
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
describe("createAnimationRefController", () => {
|
|
12
|
-
it("starts animations once per attachment and stops them on detachment", () => {
|
|
13
|
-
const firstAnimation = createAnimation();
|
|
14
|
-
const secondAnimation = createAnimation();
|
|
15
|
-
const ref = createAnimationRefController(() => [firstAnimation, secondAnimation]);
|
|
16
|
-
|
|
17
|
-
ref({});
|
|
18
|
-
ref({});
|
|
19
|
-
|
|
20
|
-
expect(firstAnimation.start).toHaveBeenCalledOnce();
|
|
21
|
-
expect(secondAnimation.start).toHaveBeenCalledOnce();
|
|
22
|
-
expect(firstAnimation.stop).not.toHaveBeenCalled();
|
|
23
|
-
|
|
24
|
-
ref(null);
|
|
25
|
-
ref(null);
|
|
26
|
-
|
|
27
|
-
expect(firstAnimation.stop).toHaveBeenCalledOnce();
|
|
28
|
-
expect(secondAnimation.stop).toHaveBeenCalledOnce();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("creates a fresh animation set when a node is attached again", () => {
|
|
32
|
-
const firstAnimation = createAnimation();
|
|
33
|
-
const secondAnimation = createAnimation();
|
|
34
|
-
const createAnimations = vi.fn().mockReturnValueOnce([firstAnimation]).mockReturnValueOnce([secondAnimation]);
|
|
35
|
-
const ref = createAnimationRefController(createAnimations);
|
|
36
|
-
|
|
37
|
-
ref({});
|
|
38
|
-
ref(null);
|
|
39
|
-
ref({});
|
|
40
|
-
|
|
41
|
-
expect(createAnimations).toHaveBeenCalledTimes(2);
|
|
42
|
-
expect(firstAnimation.start).toHaveBeenCalledOnce();
|
|
43
|
-
expect(firstAnimation.stop).toHaveBeenCalledOnce();
|
|
44
|
-
expect(secondAnimation.start).toHaveBeenCalledOnce();
|
|
45
|
-
expect(secondAnimation.stop).not.toHaveBeenCalled();
|
|
46
|
-
});
|
|
47
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { formatChatAttachmentSize, isLatestChatMessageVisible } from "./native-chat";
|
|
3
|
-
|
|
4
|
-
describe("native chat viewport", () => {
|
|
5
|
-
it("reports the latest message only when the viewport reaches the content bottom", () => {
|
|
6
|
-
expect(isLatestChatMessageVisible({ contentHeight: 600, viewportHeight: 300, scrollOffset: 120 })).toBe(false);
|
|
7
|
-
expect(isLatestChatMessageVisible({ contentHeight: 600, viewportHeight: 300, scrollOffset: 292 })).toBe(true);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("does not treat unmeasured content as visible", () => {
|
|
11
|
-
expect(isLatestChatMessageVisible({ contentHeight: 0, viewportHeight: 300, scrollOffset: 0 })).toBe(false);
|
|
12
|
-
expect(isLatestChatMessageVisible({ contentHeight: 300, viewportHeight: 0, scrollOffset: 0 })).toBe(false);
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
describe("native chat attachment metadata", () => {
|
|
17
|
-
it("formats byte lengths for compact attachment rows", () => {
|
|
18
|
-
expect(formatChatAttachmentSize(512)).toBe("512 B");
|
|
19
|
-
expect(formatChatAttachmentSize(1_536)).toBe("1.5 KB");
|
|
20
|
-
expect(formatChatAttachmentSize(2_097_152)).toBe("2.0 MB");
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { EntranceControllerStore } from "./entrance-controller-store";
|
|
3
|
-
|
|
4
|
-
function createStore(options: Partial<ConstructorParameters<typeof EntranceControllerStore>[0]> = {}) {
|
|
5
|
-
return new EntranceControllerStore({
|
|
6
|
-
displayName: "Guest",
|
|
7
|
-
initialAudioEnabled: false,
|
|
8
|
-
initialVideoEnabled: false,
|
|
9
|
-
simulatorMediaDisabled: false,
|
|
10
|
-
joinDisabled: false,
|
|
11
|
-
onJoin: vi.fn(),
|
|
12
|
-
...options,
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
describe("EntranceControllerStore", () => {
|
|
17
|
-
it("toggles media, edits the name, and latches join", () => {
|
|
18
|
-
const onJoin = vi.fn();
|
|
19
|
-
const store = createStore({ onJoin });
|
|
20
|
-
const listener = vi.fn();
|
|
21
|
-
const unsubscribe = store.subscribe(listener);
|
|
22
|
-
|
|
23
|
-
store.setDisplayName("Host");
|
|
24
|
-
store.toggleAudio();
|
|
25
|
-
store.toggleVideo();
|
|
26
|
-
store.setInputFocused(true);
|
|
27
|
-
store.handleJoin();
|
|
28
|
-
store.handleJoin();
|
|
29
|
-
|
|
30
|
-
expect(store.getSnapshot()).toMatchObject({
|
|
31
|
-
displayName: "Host",
|
|
32
|
-
audioEnabled: true,
|
|
33
|
-
videoEnabled: true,
|
|
34
|
-
isInputFocused: true,
|
|
35
|
-
isSubmitting: true,
|
|
36
|
-
});
|
|
37
|
-
expect(onJoin).toHaveBeenCalledTimes(1);
|
|
38
|
-
expect(onJoin).toHaveBeenCalledWith({ displayName: "Host", microphoneEnabled: true, cameraEnabled: true });
|
|
39
|
-
expect(listener).toHaveBeenCalled();
|
|
40
|
-
|
|
41
|
-
unsubscribe();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("resets the submit latch when joining is re-enabled", () => {
|
|
45
|
-
const onJoin = vi.fn();
|
|
46
|
-
const store = createStore({ joinDisabled: true, onJoin });
|
|
47
|
-
|
|
48
|
-
store.handleJoin();
|
|
49
|
-
expect(onJoin).not.toHaveBeenCalled();
|
|
50
|
-
|
|
51
|
-
store.update({ simulatorMediaDisabled: false, joinDisabled: false, onJoin });
|
|
52
|
-
store.handleJoin();
|
|
53
|
-
expect(onJoin).toHaveBeenCalledTimes(1);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("does not submit a blank display name", () => {
|
|
57
|
-
const onJoin = vi.fn();
|
|
58
|
-
const store = createStore({ displayName: " ", onJoin });
|
|
59
|
-
|
|
60
|
-
store.handleJoin();
|
|
61
|
-
|
|
62
|
-
expect(onJoin).not.toHaveBeenCalled();
|
|
63
|
-
expect(store.getSnapshot().isSubmitting).toBe(false);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("disables media and ignores toggles on the iOS simulator", () => {
|
|
67
|
-
const store = createStore({ initialAudioEnabled: true, initialVideoEnabled: true });
|
|
68
|
-
|
|
69
|
-
store.update({ simulatorMediaDisabled: true, joinDisabled: false, onJoin: vi.fn() });
|
|
70
|
-
store.toggleAudio();
|
|
71
|
-
store.toggleVideo();
|
|
72
|
-
|
|
73
|
-
expect(store.getSnapshot()).toMatchObject({ audioEnabled: false, videoEnabled: false });
|
|
74
|
-
});
|
|
75
|
-
});
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { Participant } from "../internal/core";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { shouldRenderNativeMediaTrack } from "./native-media-visibility";
|
|
4
|
-
|
|
5
|
-
function participant(overrides: Partial<Participant> = {}): Participant {
|
|
6
|
-
return {
|
|
7
|
-
id: overrides.id ?? "participant-1",
|
|
8
|
-
displayName: overrides.displayName ?? "Participant",
|
|
9
|
-
role: overrides.role ?? "participant",
|
|
10
|
-
isLocal: overrides.isLocal ?? false,
|
|
11
|
-
videoEnabled: overrides.videoEnabled ?? false,
|
|
12
|
-
audioEnabled: overrides.audioEnabled ?? true,
|
|
13
|
-
isScreenSharing: overrides.isScreenSharing ?? false,
|
|
14
|
-
isSpeaking: overrides.isSpeaking ?? false,
|
|
15
|
-
handRaised: overrides.handRaised ?? false,
|
|
16
|
-
connectionQuality: overrides.connectionQuality ?? 100,
|
|
17
|
-
videoTrack: overrides.videoTrack ?? null,
|
|
18
|
-
audioTrack: overrides.audioTrack ?? null,
|
|
19
|
-
screenShareTrack: overrides.screenShareTrack ?? null,
|
|
20
|
-
screenShareAudioTrack: overrides.screenShareAudioTrack ?? null,
|
|
21
|
-
joinedAt: overrides.joinedAt ?? new Date(),
|
|
22
|
-
metadata: overrides.metadata ?? {},
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function track(readyState: "live" | "ended" = "live"): MediaStreamTrack {
|
|
27
|
-
return { readyState } as MediaStreamTrack;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
describe("shouldRenderNativeMediaTrack", () => {
|
|
31
|
-
it("hides video when the participant has turned their camera off even if a stale track object remains", () => {
|
|
32
|
-
expect(
|
|
33
|
-
shouldRenderNativeMediaTrack({
|
|
34
|
-
participant: participant({
|
|
35
|
-
videoEnabled: false,
|
|
36
|
-
videoTrack: track(),
|
|
37
|
-
}),
|
|
38
|
-
track: track(),
|
|
39
|
-
}),
|
|
40
|
-
).toBe(false);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("hides video when the track has already ended", () => {
|
|
44
|
-
expect(
|
|
45
|
-
shouldRenderNativeMediaTrack({
|
|
46
|
-
participant: participant({
|
|
47
|
-
videoEnabled: true,
|
|
48
|
-
videoTrack: track("ended"),
|
|
49
|
-
}),
|
|
50
|
-
track: track("ended"),
|
|
51
|
-
}),
|
|
52
|
-
).toBe(false);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("still renders live non-participant media such as stage screen share tracks", () => {
|
|
56
|
-
expect(
|
|
57
|
-
shouldRenderNativeMediaTrack({
|
|
58
|
-
participant: null,
|
|
59
|
-
track: track(),
|
|
60
|
-
}),
|
|
61
|
-
).toBe(true);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("renders a live screen-share track even when the sharer camera is off", () => {
|
|
65
|
-
expect(
|
|
66
|
-
shouldRenderNativeMediaTrack({
|
|
67
|
-
participant: participant({
|
|
68
|
-
videoEnabled: false,
|
|
69
|
-
isScreenSharing: true,
|
|
70
|
-
screenShareTrack: track(),
|
|
71
|
-
}),
|
|
72
|
-
track: track(),
|
|
73
|
-
mediaKind: "screen-share",
|
|
74
|
-
}),
|
|
75
|
-
).toBe(true);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { SpaceCallKitController, type SpaceCallKitPort, type SpaceCallKitSyncInput } from "./native-space-callkit-controller";
|
|
3
|
-
|
|
4
|
-
vi.mock("react-native", () => ({
|
|
5
|
-
NativeModules: {},
|
|
6
|
-
Platform: { OS: "ios" },
|
|
7
|
-
}));
|
|
8
|
-
|
|
9
|
-
function createInput(overrides: Partial<SpaceCallKitSyncInput> = {}): SpaceCallKitSyncInput {
|
|
10
|
-
return {
|
|
11
|
-
callKit: true,
|
|
12
|
-
hasVideo: true,
|
|
13
|
-
isAudioEnabled: true,
|
|
14
|
-
joinNonce: 1,
|
|
15
|
-
onEndCall: vi.fn(),
|
|
16
|
-
onToggleAudio: vi.fn(async () => false),
|
|
17
|
-
phase: "joining",
|
|
18
|
-
spaceId: "space-1",
|
|
19
|
-
spaceName: "Daily Standup",
|
|
20
|
-
...overrides,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
describe("SpaceCallKitController", () => {
|
|
25
|
-
it("configures, starts, reports, handles mute actions, and ends the native call", async () => {
|
|
26
|
-
let eventListener: Parameters<SpaceCallKitPort["addListener"]>[0] | undefined;
|
|
27
|
-
const removeListener = vi.fn();
|
|
28
|
-
const port: SpaceCallKitPort = {
|
|
29
|
-
isSupported: true,
|
|
30
|
-
addListener: vi.fn((listener) => {
|
|
31
|
-
eventListener = listener;
|
|
32
|
-
return { remove: removeListener };
|
|
33
|
-
}),
|
|
34
|
-
configure: vi.fn(async () => ({ isSupported: true })),
|
|
35
|
-
endCall: vi.fn(async () => {}),
|
|
36
|
-
reportConnected: vi.fn(async () => {}),
|
|
37
|
-
startCall: vi.fn(async () => ({ callUUID: "call-1" })),
|
|
38
|
-
};
|
|
39
|
-
const controller = new SpaceCallKitController(port);
|
|
40
|
-
const input = createInput();
|
|
41
|
-
|
|
42
|
-
controller.start();
|
|
43
|
-
controller.sync(input);
|
|
44
|
-
await Promise.resolve();
|
|
45
|
-
expect(port.configure).toHaveBeenCalledOnce();
|
|
46
|
-
expect(port.startCall).toHaveBeenCalledOnce();
|
|
47
|
-
|
|
48
|
-
controller.sync({ ...input, phase: "live" });
|
|
49
|
-
await Promise.resolve();
|
|
50
|
-
expect(port.reportConnected).toHaveBeenCalledWith({ callUUID: "call-1" });
|
|
51
|
-
|
|
52
|
-
eventListener?.({ callUUID: "call-1", muted: true, type: "setMutedCallAction" });
|
|
53
|
-
expect(input.onToggleAudio).toHaveBeenCalledOnce();
|
|
54
|
-
|
|
55
|
-
controller.sync({ ...input, phase: "entrance" });
|
|
56
|
-
await Promise.resolve();
|
|
57
|
-
expect(port.endCall).toHaveBeenCalledWith({ callUUID: "call-1" });
|
|
58
|
-
|
|
59
|
-
controller.stop();
|
|
60
|
-
expect(removeListener).toHaveBeenCalledOnce();
|
|
61
|
-
});
|
|
62
|
-
});
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
3
|
-
|
|
4
|
-
const appearanceState = vi.hoisted(() => ({
|
|
5
|
-
theme: { colors: { ink: "#light-ink", ink2: "#light-muted", ink3: "#light-subtle", line: "#light-line", primary: "#light-primary", surfaceMuted: "#light-surface", success: "#light-success" } },
|
|
6
|
-
appearance: { palette: "light", texture: "none", tokens: { stage: "#light-stage", textMuted: "#light-muted" } },
|
|
7
|
-
setPalette: vi.fn(),
|
|
8
|
-
setTexture: vi.fn(),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
vi.mock("react-native", () => ({ Pressable: "Pressable", StyleSheet: { create: <T,>(styles: T) => styles }, Text: "Text", View: "View" }));
|
|
12
|
-
vi.mock("@hugeicons/react-native", () => ({ HugeiconsIcon: "HugeiconsIcon" }));
|
|
13
|
-
vi.mock("@hugeicons/core-free-icons/dist/esm/CheckmarkCircle01Icon", () => ({ default: "CheckmarkCircle01Icon" }), { virtual: true });
|
|
14
|
-
vi.mock("../../ui/native-appearance-context", () => ({ useNativeAppearance: () => appearanceState }));
|
|
15
|
-
vi.mock("../../ui/native-theme", () => ({ useNativeTheme: () => appearanceState.theme }));
|
|
16
|
-
|
|
17
|
-
import { AppearanceSettings } from "./AppearanceSettings";
|
|
18
|
-
|
|
19
|
-
const source = readFileSync(new URL("./AppearanceSettings.tsx", import.meta.url), "utf8");
|
|
20
|
-
|
|
21
|
-
describe("AppearanceSettings", () => {
|
|
22
|
-
it("exposes every palette family and texture as an accessible selection", () => {
|
|
23
|
-
expect(source).toContain('(["light", "dark"] as const)');
|
|
24
|
-
expect(source).toContain("THEME_PALETTES.filter");
|
|
25
|
-
expect(source).toContain("THEME_TEXTURES.map");
|
|
26
|
-
expect(source).toContain("accessibilityState={{ selected }}");
|
|
27
|
-
expect(source).toContain("setPalette");
|
|
28
|
-
expect(source).toContain("setTexture");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("renders palette controls with the active light and dark NativeTheme", () => {
|
|
32
|
-
appearanceState.theme.colors = { ...appearanceState.theme.colors, ink2: "#light-muted", line: "#light-line", surfaceMuted: "#light-surface" };
|
|
33
|
-
const lightTree = AppearanceSettings();
|
|
34
|
-
expect(resolveStyle(findByAccessibilityLabel(lightTree, "Use Chalk Light palette")?.style)).toEqual(expect.arrayContaining([expect.objectContaining({ backgroundColor: "#light-surface", borderColor: "#light-line" })]));
|
|
35
|
-
|
|
36
|
-
appearanceState.theme.colors = { ...appearanceState.theme.colors, ink2: "#dark-muted", line: "#dark-line", surfaceMuted: "#dark-surface" };
|
|
37
|
-
const darkTree = AppearanceSettings();
|
|
38
|
-
expect(resolveStyle(findByAccessibilityLabel(darkTree, "Use Chalk Light palette")?.style)).toEqual(expect.arrayContaining([expect.objectContaining({ backgroundColor: "#dark-surface", borderColor: "#dark-line" })]));
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
function resolveStyle(style: unknown): unknown {
|
|
43
|
-
return typeof style === "function" ? (style as (state: { readonly pressed: boolean }) => unknown)({ pressed: false }) : style;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function findByAccessibilityLabel(value: unknown, label: string): { readonly style?: unknown } | undefined {
|
|
47
|
-
if (Array.isArray(value)) return value.map((child) => findByAccessibilityLabel(child, label)).find(Boolean);
|
|
48
|
-
if (!value || typeof value !== "object") return undefined;
|
|
49
|
-
const element = value as { readonly props?: { readonly accessibilityLabel?: unknown; readonly children?: unknown; readonly style?: unknown } };
|
|
50
|
-
if (typeof (element as { readonly type?: unknown }).type === "function") {
|
|
51
|
-
const component = (element as { readonly type: (props: Record<string, unknown>) => unknown }).type;
|
|
52
|
-
return findByAccessibilityLabel(component((element.props ?? {}) as Record<string, unknown>), label);
|
|
53
|
-
}
|
|
54
|
-
if (element.props?.accessibilityLabel === label) return element.props;
|
|
55
|
-
return findByAccessibilityLabel(element.props?.children, label);
|
|
56
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
|
|
4
|
-
const source = readFileSync(new URL("./ParticipantActionSheet.tsx", import.meta.url), "utf8");
|
|
5
|
-
|
|
6
|
-
describe("ParticipantActionSheet", () => {
|
|
7
|
-
it("keeps capability-derived actions and explicit destructive styling", () => {
|
|
8
|
-
expect(source).toContain("buildParticipantActionDescriptors({");
|
|
9
|
-
expect(source).toContain("descriptor.destructive");
|
|
10
|
-
expect(source).toContain('accessibilityLabel="Close Participant actions"');
|
|
11
|
-
expect(source).toContain("StyleSheet.absoluteFillObject");
|
|
12
|
-
expect(source).not.toContain("setParticipantRole");
|
|
13
|
-
expect(source).not.toContain("AssignableParticipantRole");
|
|
14
|
-
expect(source).not.toContain("role:");
|
|
15
|
-
expect(source).not.toContain("paddingBottom: 112");
|
|
16
|
-
expect(source).not.toContain("bottom: 94");
|
|
17
|
-
});
|
|
18
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
|
|
4
|
-
const source = readFileSync(new URL("./SettingsSheet.tsx", import.meta.url), "utf8");
|
|
5
|
-
|
|
6
|
-
describe("SettingsSheet", () => {
|
|
7
|
-
it("exposes accessible tabs and real device toggles", () => {
|
|
8
|
-
expect(source).toContain('accessibilityRole="tablist"');
|
|
9
|
-
expect(source).toContain('["devices", "appearance", "space"]');
|
|
10
|
-
expect(source).toContain("controller.toggleAudio");
|
|
11
|
-
expect(source).toContain("controller.toggleVideo");
|
|
12
|
-
expect(source).toContain("<AppearanceSettings />");
|
|
13
|
-
expect(source).toContain("StyleSheet.absoluteFillObject");
|
|
14
|
-
expect(source).not.toContain("marginBottom: 94");
|
|
15
|
-
expect(source).not.toContain("bottom: 94");
|
|
16
|
-
});
|
|
17
|
-
});
|