Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions apps/web/src/components/device/DeviceSetup.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type { DeviceServiceState } from "@t3tools/contracts";
import { describe, expect, it } from "vite-plus/test";

import { deviceHubReadiness, platformSetupStatus } from "./DeviceSetup";

const deviceState = (overrides: Partial<DeviceServiceState> = {}): DeviceServiceState => ({
hosts: [
{
id: "local",
kind: "local",
label: "This machine",
hubInstalled: false,
agentDeviceInstalled: false,
platforms: [
{ platform: "ios", available: true },
{ platform: "android", available: true },
],
},
],
hostStatus: "ready",
hostStatuses: {},
devices: [],
sessions: [],
onboardingCompleted: false,
agentAccessEnabled: false,
hubBasePath: "/api/device-hub",
revision: 0,
...overrides,
});

describe("device setup guidance", () => {
it("directs users to install an iOS runtime and create an Android virtual device", () => {
expect(platformSetupStatus(deviceState(), "ios").message).toContain("Xcode Settings");
expect(platformSetupStatus(deviceState(), "android").message).toContain("Device Manager");
});

it("preserves a specific missing-tool explanation from the server", () => {
const state = deviceState({
hosts: [
{
...deviceState().hosts[0]!,
platforms: [
{ platform: "ios", available: true },
{ platform: "android", available: false, reason: "Android Emulator is missing." },
],
},
],
});
expect(platformSetupStatus(state, "android").message).toBe("Android Emulator is missing.");
});

it("counts a remote device host when this machine has no simulator tooling", () => {
const state = deviceState({
hostStatus: "idle",
hostStatuses: { macmini: { status: "ready" } },
hosts: [
{
...deviceState().hosts[0]!,
platforms: [
{ platform: "ios", available: false, reason: "Xcode was not found." },
{ platform: "android", available: false, reason: "Android SDK was not found." },
],
},
{
id: "macmini",
kind: "ssh",
label: "Mac Mini",
hubInstalled: true,
agentDeviceInstalled: true,
platforms: [
{ platform: "ios", available: true },
{ platform: "android", available: false, reason: "Android SDK was not found." },
],
},
],
devices: [
{
hostId: "macmini",
id: "iphone",
platform: "ios",
name: "iPhone 16 Pro",
version: "iOS 27.0",
booted: true,
physical: false,
},
],
});
expect(deviceHubReadiness(state).ready).toBe(true);
expect(platformSetupStatus(state, "ios")).toEqual({
ready: true,
message: "Xcode and iOS Simulator are available on Mac Mini.",
});
expect(platformSetupStatus(state, "android").ready).toBe(false);
});
});
102 changes: 102 additions & 0 deletions apps/web/src/components/settings/IntegrationsSettings.browser.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { DEFAULT_CLIENT_SETTINGS, DEFAULT_UNIFIED_SETTINGS } from "@t3tools/contracts";
import {
createMemoryHistory,
createRootRoute,
createRouter,
RouterProvider,
} from "@tanstack/react-router";
import { act, StrictMode } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test";

const { listBrowserImportSources } = vi.hoisted(() => ({
listBrowserImportSources: vi.fn().mockResolvedValue([]),
}));

vi.mock(import("../preview/previewBridge"), async (original) => {
const actual = await original();
return {
...actual,
previewBridge: { ...actual.previewBridge, listBrowserImportSources } as never,
};
});
vi.mock(import("../../env"), async (original) => ({ ...(await original()), isElectron: true }));
vi.mock(import("../../state/environments"), async (original) => ({
...(await original()),
useEnvironments: (() => ({ environments: [], isReady: true })) as never,
usePrimaryEnvironment: () => null,
usePrimaryEnvironmentId: () => null,
}));
vi.mock(import("../../hooks/useSettings"), async (original) => ({
...(await original()),
useClientSettings: ((selector?: (settings: typeof DEFAULT_CLIENT_SETTINGS) => unknown) =>
selector ? selector(DEFAULT_CLIENT_SETTINGS) : DEFAULT_CLIENT_SETTINGS) as never,
useClientSettingsHydrated: () => true,
usePrimarySettingsAvailable: () => true,
usePrimarySettings: (() => DEFAULT_UNIFIED_SETTINGS) as never,
useUpdatePrimarySettings: (() => vi.fn()) as never,
}));
vi.mock(import("./ProjectDefaultsSettings"), async (original) => ({
...(await original()),
ProjectDefaultsSettings: (() => null) as never,
}));
vi.mock(import("./SettingsScopeContext"), async (original) => ({
...(await original()),
useSettingsScope: (() => ({
scope: { kind: "all", environmentIds: [] },
search: {},
environment: null,
environments: [],
target: null,
connectedEnvironments: [],
targets: [],
})) as never,
useOptionalSettingsScope: () => null,
}));

import { IntegrationsSettingsPanel } from "./IntegrationsSettings";

let root: Root;
let container: HTMLDivElement;

beforeEach(() => {
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
listBrowserImportSources.mockClear();
container = document.createElement("div");
document.body.append(container);
root = createRoot(container);
});

afterEach(async () => {
await act(async () => root.unmount());
container.remove();
vi.unstubAllGlobals();
});

async function openSettings() {
const router = createRouter({
routeTree: createRootRoute({ component: IntegrationsSettingsPanel }),
history: createMemoryHistory(),
});
await router.load();
await act(async () =>
root.render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>,
),
);
await vi.waitFor(() => expect(container.querySelector("section#browser")).not.toBeNull());
}

describe("Integrations browser discovery", () => {
it("does not scan browser files when entering or revisiting settings", async () => {
await openSettings();
expect(listBrowserImportSources).not.toHaveBeenCalled();

await act(async () => root.unmount());
root = createRoot(container);
await openSettings();
expect(listBrowserImportSources).not.toHaveBeenCalled();
});
});
Loading
Loading