Accepted native Client lifecycle and Windows capture aspect phase. All Web/Server and three-platform Client checks passed.
117 lines
3.7 KiB
TypeScript
117 lines
3.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { createElement } from "react";
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
|
|
import { CaptureSourcePicker } from "../src/client/components/living/CaptureSourcePicker";
|
|
|
|
import {
|
|
defaultNativeCapturePath,
|
|
nativeCaptureTargetKey,
|
|
} from "../src/client/native/capture-selection";
|
|
import type {
|
|
NativeAdapter,
|
|
NativeCaptureTarget,
|
|
} from "../src/client/native/wire";
|
|
|
|
function target(
|
|
title: string,
|
|
sourceId: string,
|
|
): Extract<NativeCaptureTarget, { kind: "window" }> {
|
|
return {
|
|
kind: "window",
|
|
title,
|
|
sourceId,
|
|
pid: 10,
|
|
creationTime: "123456",
|
|
};
|
|
}
|
|
|
|
describe("native capture source selection", () => {
|
|
const game = target("My Game", "1");
|
|
|
|
it("uses the complete window identity as the UI key", () => {
|
|
expect(nativeCaptureTargetKey(game)).toBe("window:1:10:123456");
|
|
expect(
|
|
nativeCaptureTargetKey({ ...game, creationTime: "654321" }),
|
|
).not.toBe(nativeCaptureTargetKey(game));
|
|
expect(
|
|
nativeCaptureTargetKey({
|
|
kind: "display",
|
|
sourceId: "2",
|
|
title: "Display 1",
|
|
}),
|
|
).toBe("display:2");
|
|
});
|
|
|
|
it("separates windows from displays and locks Browser switching during native sharing", () => {
|
|
const html = renderToStaticMarkup(createElement(CaptureSourcePicker, {
|
|
nativeSources: {
|
|
kind: "ready",
|
|
sources: [game, { kind: "display", sourceId: "2", title: "Display 1" }],
|
|
processAudio: false,
|
|
systemAudio: true,
|
|
},
|
|
browserAvailable: false,
|
|
audioLocked: true,
|
|
onBrowser: () => {},
|
|
onNative: () => {},
|
|
onPreview: async () => null,
|
|
onRefresh: () => {},
|
|
onCancel: () => {},
|
|
}));
|
|
expect(html).toContain('data-source-tab="browser"');
|
|
expect(html).toMatch(/data-source-tab="browser"[^>]*disabled=""/);
|
|
expect(html).toMatch(/data-native-source="window:1:10:123456"[^>]*disabled=""/);
|
|
expect(html).not.toContain('data-native-source="display:2"');
|
|
expect(html.match(/role="tab"/g)).toHaveLength(3);
|
|
});
|
|
|
|
it("retains the platform-owned combined source picker", () => {
|
|
const html = renderToStaticMarkup(createElement(CaptureSourcePicker, {
|
|
nativeSources: {
|
|
kind: "ready",
|
|
sources: [{ kind: "picker", sourceId: "1", title: "System picker" }],
|
|
processAudio: false,
|
|
systemAudio: true,
|
|
},
|
|
onBrowser: () => {},
|
|
onNative: () => {},
|
|
onPreview: async () => null,
|
|
onRefresh: () => {},
|
|
onCancel: () => {},
|
|
}));
|
|
expect(html).toContain('data-native-source="picker:1"');
|
|
expect(html).toContain('aria-checked="true"');
|
|
});
|
|
});
|
|
|
|
describe("native adapter selection", () => {
|
|
const adapters: NativeAdapter[] = [
|
|
{ index: 0, name: "GPU A", identity: "a", hardwareH264: [] },
|
|
{
|
|
index: 1,
|
|
name: "GPU B",
|
|
identity: "b",
|
|
hardwareH264: [{ index: 0, name: "H264", identity: "encoder" }],
|
|
},
|
|
];
|
|
|
|
it("selects the first complete hardware path for UI capture", () => {
|
|
expect(defaultNativeCapturePath(adapters)).toEqual({
|
|
adapterIndex: 1,
|
|
encoderIndex: 0,
|
|
});
|
|
expect(defaultNativeCapturePath([adapters[0]!])).toBeNull();
|
|
});
|
|
|
|
it("uses software VP8 only when that capture capability exists", () => {
|
|
expect(defaultNativeCapturePath(adapters, "vp8", true)).toEqual({
|
|
adapterIndex: 0, encoderIndex: 0,
|
|
});
|
|
expect(defaultNativeCapturePath(adapters, "vp8", false)).toBeNull();
|
|
expect(defaultNativeCapturePath(adapters, "auto", true)?.adapterIndex).toBe(1);
|
|
expect(defaultNativeCapturePath([adapters[0]!], "auto", true)?.adapterIndex).toBe(0);
|
|
expect(defaultNativeCapturePath([adapters[0]!], "h264", true)).toBeNull();
|
|
});
|
|
});
|