mirror of
https://github.com/software-mansion/argent.git
synced 2026-09-14 19:27:14 +08:00
73b619bfed
* Reapply "feat: enhance React profiler session handling with device ID support" This reverts commit2bf25cb950. * Reapply "refactor: remove device-related fields from React profiler session" This reverts commitbadf127411. * Reapply "refactor: unify device ID handling across debugger and profiler tools, make device_id required, not optional" This reverts commitc6f1fd7578. * format: run format * refactor: enforce device ID requirement in JsRuntimeDebugger and ReactProfilerSession blueprints * feat: add appName and deviceName to React profiler session and related tools * fix: update React profiler session output to reflect accurate field names for runtime and project details * skills: enhance skills for Metro debugger and React profiler tools to clarify device_id usage and connection diagnostics * format: run format * test: update NetworkInspector integration tests to include mock-device in service resolutions
237 lines
8.3 KiB
TypeScript
237 lines
8.3 KiB
TypeScript
import { describe, it, expect, vi } from "vitest";
|
|
import { Registry, ServiceState } from "@argent/registry";
|
|
import {
|
|
networkInspectorBlueprint,
|
|
NETWORK_INSPECTOR_NAMESPACE,
|
|
} from "../../src/blueprints/network-inspector";
|
|
|
|
describe("NetworkInspector blueprint", () => {
|
|
it("has namespace 'NetworkInspector'", () => {
|
|
expect(networkInspectorBlueprint.namespace).toBe("NetworkInspector");
|
|
expect(NETWORK_INSPECTOR_NAMESPACE).toBe("NetworkInspector");
|
|
});
|
|
|
|
it("getURN returns correct format", () => {
|
|
expect(networkInspectorBlueprint.getURN("8081:SIM-UDID-123")).toBe(
|
|
"NetworkInspector:8081:SIM-UDID-123"
|
|
);
|
|
expect(networkInspectorBlueprint.getURN("3000:DEV-B")).toBe("NetworkInspector:3000:DEV-B");
|
|
});
|
|
|
|
it("declares JsRuntimeDebugger as a dependency via getDependencies", () => {
|
|
expect(networkInspectorBlueprint.getDependencies).toBeDefined();
|
|
const deps = networkInspectorBlueprint.getDependencies!("8081:SIM-UDID-123");
|
|
expect(deps).toEqual({ debugger: "JsRuntimeDebugger:8081:SIM-UDID-123" });
|
|
});
|
|
|
|
it("getDependencies uses the composite port:deviceId payload for the JsRuntimeDebugger URN", () => {
|
|
const deps3000 = networkInspectorBlueprint.getDependencies!("3000:DEV-A");
|
|
expect(deps3000).toEqual({ debugger: "JsRuntimeDebugger:3000:DEV-A" });
|
|
|
|
const deps9090 = networkInspectorBlueprint.getDependencies!("9090:DEV-B");
|
|
expect(deps9090).toEqual({ debugger: "JsRuntimeDebugger:9090:DEV-B" });
|
|
});
|
|
|
|
it("factory reuses the CDP client from the debugger dependency (does NOT create its own)", async () => {
|
|
const mockEvaluate = vi.fn().mockResolvedValue(JSON.stringify({ installed: true }));
|
|
const mockCdp = {
|
|
evaluate: mockEvaluate,
|
|
events: { on: vi.fn() },
|
|
};
|
|
const mockDebuggerApi = {
|
|
port: 8081,
|
|
cdp: mockCdp,
|
|
};
|
|
|
|
const instance = await networkInspectorBlueprint.factory({ debugger: mockDebuggerApi }, "8081");
|
|
|
|
// The API's cdp should be the SAME object as the debugger's cdp
|
|
expect(instance.api.cdp).toBe(mockCdp);
|
|
expect(instance.api.port).toBe(8081);
|
|
});
|
|
|
|
it("factory injects the network interceptor script via cdp.evaluate", async () => {
|
|
const mockEvaluate = vi.fn().mockResolvedValue(JSON.stringify({ installed: true }));
|
|
const mockCdp = {
|
|
evaluate: mockEvaluate,
|
|
events: { on: vi.fn() },
|
|
};
|
|
const mockDebuggerApi = {
|
|
port: 8081,
|
|
cdp: mockCdp,
|
|
};
|
|
|
|
await networkInspectorBlueprint.factory({ debugger: mockDebuggerApi }, "8081");
|
|
|
|
expect(mockEvaluate).toHaveBeenCalledTimes(1);
|
|
// Verify it called evaluate with a string containing the interceptor guard
|
|
const script = mockEvaluate.mock.calls[0][0] as string;
|
|
expect(script).toContain("__argent_network_installed");
|
|
expect(script).toContain("globalThis.fetch");
|
|
});
|
|
|
|
it("factory does not fail if interceptor injection throws (catches error)", async () => {
|
|
const mockEvaluate = vi.fn().mockRejectedValue(new Error("eval failed"));
|
|
const mockCdp = {
|
|
evaluate: mockEvaluate,
|
|
events: { on: vi.fn() },
|
|
};
|
|
const mockDebuggerApi = {
|
|
port: 8081,
|
|
cdp: mockCdp,
|
|
};
|
|
|
|
// Should not throw
|
|
const instance = await networkInspectorBlueprint.factory({ debugger: mockDebuggerApi }, "8081");
|
|
expect(instance.api).toBeDefined();
|
|
expect(instance.api.port).toBe(8081);
|
|
});
|
|
|
|
it("dispose does NOT disconnect the CDP client (owned by JsRuntimeDebugger)", async () => {
|
|
const mockDisconnect = vi.fn();
|
|
const mockCdp = {
|
|
evaluate: vi.fn().mockResolvedValue("{}"),
|
|
disconnect: mockDisconnect,
|
|
events: { on: vi.fn() },
|
|
};
|
|
const mockDebuggerApi = {
|
|
port: 8081,
|
|
cdp: mockCdp,
|
|
};
|
|
|
|
const instance = await networkInspectorBlueprint.factory({ debugger: mockDebuggerApi }, "8081");
|
|
|
|
await instance.dispose();
|
|
expect(mockDisconnect).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("emits 'terminated' when the CDP connection disconnects", async () => {
|
|
let disconnectHandler: ((error?: Error) => void) | null = null;
|
|
const mockCdp = {
|
|
evaluate: vi.fn().mockResolvedValue("{}"),
|
|
events: {
|
|
on: vi.fn((event: string, handler: (error?: Error) => void) => {
|
|
if (event === "disconnected") {
|
|
disconnectHandler = handler;
|
|
}
|
|
}),
|
|
},
|
|
};
|
|
const mockDebuggerApi = {
|
|
port: 8081,
|
|
cdp: mockCdp,
|
|
};
|
|
|
|
const instance = await networkInspectorBlueprint.factory({ debugger: mockDebuggerApi }, "8081");
|
|
|
|
const terminatedHandler = vi.fn();
|
|
instance.events.on("terminated", terminatedHandler);
|
|
|
|
// Simulate CDP disconnect
|
|
expect(disconnectHandler).not.toBeNull();
|
|
disconnectHandler!(new Error("connection lost"));
|
|
|
|
expect(terminatedHandler).toHaveBeenCalledOnce();
|
|
expect(terminatedHandler.mock.calls[0][0]).toBeInstanceOf(Error);
|
|
expect(terminatedHandler.mock.calls[0][0].message).toBe("connection lost");
|
|
});
|
|
|
|
it("registers in a Registry and resolves correctly with JsRuntimeDebugger dependency", async () => {
|
|
// Use the real Registry to verify wiring is correct at the registry level.
|
|
// We create a mock JsRuntimeDebugger blueprint that returns a fake API.
|
|
const registry = new Registry();
|
|
|
|
const mockCdp = {
|
|
evaluate: vi.fn().mockResolvedValue(JSON.stringify({ installed: true })),
|
|
events: { on: vi.fn() },
|
|
};
|
|
|
|
// Register a fake JsRuntimeDebugger blueprint
|
|
registry.registerBlueprint({
|
|
namespace: "JsRuntimeDebugger",
|
|
getURN(payload: string) {
|
|
return `JsRuntimeDebugger:${payload}`;
|
|
},
|
|
async factory() {
|
|
const { TypedEventEmitter } = await import("@argent/registry");
|
|
const events = new TypedEventEmitter();
|
|
return {
|
|
api: { port: 8081, cdp: mockCdp, projectRoot: "/test" },
|
|
dispose: async () => {},
|
|
events,
|
|
};
|
|
},
|
|
});
|
|
|
|
registry.registerBlueprint(networkInspectorBlueprint);
|
|
|
|
const api = (await registry.resolveService("NetworkInspector:8081:SIM-UDID-123")) as {
|
|
port: number;
|
|
cdp: unknown;
|
|
};
|
|
|
|
expect(api.port).toBe(8081);
|
|
expect(api.cdp).toBe(mockCdp);
|
|
|
|
// Both services should be RUNNING
|
|
expect(registry.getServiceState("JsRuntimeDebugger:8081:SIM-UDID-123")).toBe(
|
|
ServiceState.RUNNING
|
|
);
|
|
expect(registry.getServiceState("NetworkInspector:8081:SIM-UDID-123")).toBe(
|
|
ServiceState.RUNNING
|
|
);
|
|
|
|
await registry.dispose();
|
|
});
|
|
|
|
it("emits 'terminated' with fallback Error when disconnectHandler called with undefined", async () => {
|
|
let disconnectHandler: ((error?: Error) => void) | null = null;
|
|
const mockCdp = {
|
|
evaluate: vi.fn().mockResolvedValue("{}"),
|
|
events: {
|
|
on: vi.fn((event: string, handler: (error?: Error) => void) => {
|
|
if (event === "disconnected") {
|
|
disconnectHandler = handler;
|
|
}
|
|
}),
|
|
},
|
|
};
|
|
const mockDebuggerApi = {
|
|
port: 8081,
|
|
cdp: mockCdp,
|
|
};
|
|
|
|
const instance = await networkInspectorBlueprint.factory({ debugger: mockDebuggerApi }, "8081");
|
|
|
|
const terminatedHandler = vi.fn();
|
|
instance.events.on("terminated", terminatedHandler);
|
|
|
|
// Simulate CDP disconnect with no error (undefined)
|
|
disconnectHandler!(undefined as unknown as Error);
|
|
|
|
expect(terminatedHandler).toHaveBeenCalledOnce();
|
|
const emittedError = terminatedHandler.mock.calls[0][0];
|
|
expect(emittedError).toBeInstanceOf(Error);
|
|
expect(emittedError.message).toBe("CDP disconnected");
|
|
});
|
|
|
|
it("does not import discovery, target-selection, or construct CDPClient", async () => {
|
|
// Read the source file to verify no dangerous imports exist.
|
|
// This is a static analysis check to catch regressions.
|
|
const fs = await import("node:fs/promises");
|
|
const path = await import("node:path");
|
|
const source = await fs.readFile(
|
|
path.resolve(__dirname, "../../src/blueprints/network-inspector.ts"),
|
|
"utf-8"
|
|
);
|
|
|
|
// Must NOT import from discovery or target-selection (that would mean it creates its own connection)
|
|
expect(source).not.toContain('from "../utils/debugger/discovery"');
|
|
expect(source).not.toContain('from "../utils/debugger/target-selection"');
|
|
// Must NOT construct new CDPClient (only imports the type)
|
|
expect(source).not.toContain("new CDPClient");
|
|
// Should use type-only import for CDPClient
|
|
expect(source).toContain("import type");
|
|
});
|
|
});
|