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
33 changes: 12 additions & 21 deletions src/__tests__/integration/connection-flow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import {
ConnectionContext,
ConnectionContextValue,
} from "@/hooks/useConnection";
import {
seedActiveDevice,
seedDeviceRegistry,
} from "@/test-utils/deviceRegistry";
import { ReactNode } from "react";

// Test wrapper that provides connection context
Expand All @@ -33,14 +37,13 @@ function ConnectionWrapper({
}

describe("Connection Flow Integration", () => {
beforeEach(() => {
beforeEach(async () => {
// Reset stores to initial state
useStatusStore.setState({
...useStatusStore.getState(),
connected: false,
connectionState: ConnectionState.IDLE,
connectionError: "",
targetDeviceAddress: "",
// Seed encryptionState as plaintext so connected-state assertions don't
// hit the verifying UI gate (encryptionState === "unknown" -> connecting).
encryptionState: "plaintext",
Expand All @@ -51,13 +54,13 @@ describe("Connection Flow Integration", () => {
_hasHydrated: true,
});

// Set a default device address for most tests
localStorage.setItem("deviceAddress", "192.168.1.100");
// Most tests just need a device to be selected; the address is whatever
// its active endpoint resolves to.
await seedActiveDevice({ address: "192.168.1.100" });
});

afterEach(() => {
vi.restoreAllMocks();
localStorage.clear();
});

describe("connection state transitions", () => {
Expand Down Expand Up @@ -207,13 +210,8 @@ describe("Connection Flow Integration", () => {
});

describe("device address handling", () => {
it("should show placeholder text when no address is set on native platform", async () => {
// Clear localStorage to simulate no address
localStorage.removeItem("deviceAddress");

// Mock native platform so getDeviceAddress returns empty (on web it falls back to hostname)
const { Capacitor } = await import("@capacitor/core");
vi.mocked(Capacitor.isNativePlatform).mockReturnValue(true);
it("should show placeholder text when no device has been saved", async () => {
await seedDeviceRegistry([]);

const connectionValue: ConnectionContextValue = {
activeConnection: null,
Expand All @@ -234,14 +232,10 @@ describe("Connection Flow Integration", () => {
expect(
screen.getByText("settings.enterDeviceAddress"),
).toBeInTheDocument();

// Reset mock
vi.mocked(Capacitor.isNativePlatform).mockReturnValue(false);
});

it("should show device address in subtitle when connecting", () => {
// Set actual localStorage value for the test
localStorage.setItem("deviceAddress", "10.0.0.50");
it("should show device address in subtitle when connecting", async () => {
await seedActiveDevice({ address: "10.0.0.50" });

const connectionValue: ConnectionContextValue = {
activeConnection: null,
Expand All @@ -259,9 +253,6 @@ describe("Connection Flow Integration", () => {
);

expect(screen.getByText("10.0.0.50")).toBeInTheDocument();

// Clean up
localStorage.removeItem("deviceAddress");
});
});

Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/integration/home-page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
ConnectionContext,
ConnectionContextValue,
} from "@/hooks/useConnection";
import { seedActiveDevice } from "@/test-utils/deviceRegistry";
import { ReactNode } from "react";

function expectVisibleEmptyValues(regionName: string, count: number) {
Expand Down Expand Up @@ -59,7 +60,7 @@ const connectedContext: ConnectionContextValue = {
};

describe("Home Page Integration", () => {
beforeEach(() => {
beforeEach(async () => {
// Seed a deterministic baseline for every store field these tests touch
// so prior-test mutations cannot leak in. encryptionState: "plaintext"
// keeps connected-state assertions out of the verifying UI gate
Expand All @@ -86,12 +87,11 @@ describe("Home Page Integration", () => {
showFilenames: false,
});

localStorage.setItem("deviceAddress", "192.168.1.100");
await seedActiveDevice({ address: "192.168.1.100" });
});

afterEach(() => {
vi.restoreAllMocks();
localStorage.clear();
});

describe("Last Scanned Info", () => {
Expand Down
7 changes: 3 additions & 4 deletions src/__tests__/integration/index-route.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
ConnectionContext,
ConnectionContextValue,
} from "@/hooks/useConnection";
import { seedActiveDevice } from "@/test-utils/deviceRegistry";

function expectVisibleEmptyValues(regionName: string, count: number) {
const region = screen.getByRole("region", { name: regionName });
Expand Down Expand Up @@ -194,7 +195,6 @@ vi.mock("@/lib/coreApi", () => ({
run: vi.fn().mockResolvedValue(undefined),
mediaControl: vi.fn().mockResolvedValue(undefined),
},
getDeviceAddress: vi.fn(() => "192.168.1.100"),
}));

vi.mock("@/lib/toastUtils", () => ({
Expand Down Expand Up @@ -318,7 +318,7 @@ function seedPrimaryPlaylist({
}

describe("Index Route Integration", () => {
beforeEach(() => {
beforeEach(async () => {
vi.clearAllMocks();
// Drop the setWriteOpen callback captured from a prior Index render
mockScanOperationsProps.current = null;
Expand Down Expand Up @@ -392,12 +392,11 @@ describe("Index Route Integration", () => {
// Reset announcer mock
mockAnnounce.mockClear();

localStorage.setItem("deviceAddress", "192.168.1.100");
await seedActiveDevice({ address: "192.168.1.100" });
});

afterEach(() => {
vi.restoreAllMocks();
localStorage.clear();
});

describe("Page Structure", () => {
Expand Down
29 changes: 15 additions & 14 deletions src/__tests__/integration/network-scan-modal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { render, screen, waitFor, act } from "../../test-utils";
import userEvent from "@testing-library/user-event";
import { NetworkScanModal } from "@/components/NetworkScanModal";
import { Capacitor } from "@capacitor/core";
import { credentialStore } from "@/lib/crypto/credentials";
import {
__simulateDeviceDiscovered,
type ZeroConfService,
Expand Down Expand Up @@ -344,13 +343,10 @@ describe("NetworkScanModal", () => {
});

describe("device selection", () => {
it("should select normalized hostname and register its IP credential fallback", async () => {
it("should hand over the hostname and the IP it resolved to", async () => {
// Arrange
const user = userEvent.setup();
const onSelectDevice = vi.fn();
const registerFallback = vi
.spyOn(credentialStore, "registerFallback")
.mockImplementation(() => undefined);

render(
<NetworkScanModal
Expand Down Expand Up @@ -384,17 +380,19 @@ describe("NetworkScanModal", () => {
// Act - Click the device card
await user.click(screen.getByText("MiSTer"));

// Assert
// Assert — the registry needs both: the hostname is what the record is
// built around, the IP is what the socket can actually reach today.
expect(onSelectDevice).toHaveBeenCalledWith(
expect.objectContaining({ address: "mister.local", name: "MiSTer" }),
);
expect(registerFallback).toHaveBeenCalledWith(
"mister.local",
"192.168.1.100",
expect.objectContaining({
hostname: "mister.local",
addresses: ["192.168.1.100"],
port: 7497,
name: "MiSTer",
}),
);
});

it("should include port in selection when not default", async () => {
it("should carry the announced port through to the selection", async () => {
// Arrange
const user = userEvent.setup();
const onSelectDevice = vi.fn();
Expand Down Expand Up @@ -433,7 +431,8 @@ describe("NetworkScanModal", () => {
// Assert
expect(onSelectDevice).toHaveBeenCalledWith(
expect.objectContaining({
address: "test-device.local:9000",
hostname: "test-device.local",
port: 9000,
name: "Custom Device",
}),
);
Expand Down Expand Up @@ -470,9 +469,11 @@ describe("NetworkScanModal", () => {

await user.click(await screen.findByText("Fallback Device"));

// With no hostname announced there is nothing but the IP to build on.
expect(onSelectDevice).toHaveBeenCalledWith(
expect.objectContaining({
address: "192.168.1.100",
hostname: undefined,
addresses: ["192.168.1.100"],
name: "Fallback Device",
}),
);
Expand Down
1 change: 0 additions & 1 deletion src/__tests__/unit/App.firebase-auth.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@ vi.mock("@/hooks/useDataCache", () => ({
}));

vi.mock("@/lib/coreApi", () => ({
getDeviceAddress: vi.fn(() => "192.168.1.100"),
coreApi: { addListener: vi.fn(() => ({ remove: vi.fn() })) },
}));

Expand Down
1 change: 0 additions & 1 deletion src/__tests__/unit/App.integration.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ vi.mock("@/lib/store", () => {
});

vi.mock("@/lib/coreApi", () => ({
getDeviceAddress: vi.fn(() => "192.168.1.100"),
coreApi: {
addListener: vi.fn(() => ({ remove: vi.fn() })),
},
Expand Down
Loading