Add 260+ Claude Code skills from skills.sh
Complete collection of AI agent skills including: - Frontend Development (Vue, React, Next.js, Three.js) - Backend Development (NestJS, FastAPI, Node.js) - Mobile Development (React Native, Expo) - Testing (E2E, frontend, webapp) - DevOps (GitHub Actions, CI/CD) - Marketing (SEO, copywriting, analytics) - Security (binary analysis, vulnerability scanning) - And many more... Synchronized from: https://skills.sh/ Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
211
dev-browser/extension/__tests__/CDPRouter.test.ts
Normal file
211
dev-browser/extension/__tests__/CDPRouter.test.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { fakeBrowser } from "wxt/testing";
|
||||
import { CDPRouter } from "../services/CDPRouter";
|
||||
import { TabManager } from "../services/TabManager";
|
||||
import type { Logger } from "../utils/logger";
|
||||
import type { ExtensionCommandMessage } from "../utils/types";
|
||||
|
||||
// Mock chrome.debugger since fakeBrowser doesn't include it
|
||||
const mockDebuggerSendCommand = vi.fn();
|
||||
|
||||
vi.stubGlobal("chrome", {
|
||||
...fakeBrowser,
|
||||
debugger: {
|
||||
sendCommand: mockDebuggerSendCommand,
|
||||
attach: vi.fn(),
|
||||
detach: vi.fn(),
|
||||
onEvent: { addListener: vi.fn(), hasListener: vi.fn() },
|
||||
onDetach: { addListener: vi.fn(), hasListener: vi.fn() },
|
||||
getTargets: vi.fn().mockResolvedValue([]),
|
||||
},
|
||||
});
|
||||
|
||||
describe("CDPRouter", () => {
|
||||
let cdpRouter: CDPRouter;
|
||||
let tabManager: TabManager;
|
||||
let mockLogger: Logger;
|
||||
let mockSendMessage: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeBrowser.reset();
|
||||
mockDebuggerSendCommand.mockReset();
|
||||
|
||||
mockLogger = {
|
||||
log: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
mockSendMessage = vi.fn();
|
||||
|
||||
tabManager = new TabManager({
|
||||
logger: mockLogger,
|
||||
sendMessage: mockSendMessage,
|
||||
});
|
||||
|
||||
cdpRouter = new CDPRouter({
|
||||
logger: mockLogger,
|
||||
tabManager,
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleCommand", () => {
|
||||
it("should return early for non-forwardCDPCommand methods", async () => {
|
||||
const msg = {
|
||||
id: 1,
|
||||
method: "someOtherMethod" as const,
|
||||
params: { method: "Test.method" },
|
||||
};
|
||||
|
||||
// @ts-expect-error - testing invalid method
|
||||
const result = await cdpRouter.handleCommand(msg);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should throw error when no tab found for command", async () => {
|
||||
const msg: ExtensionCommandMessage = {
|
||||
id: 1,
|
||||
method: "forwardCDPCommand",
|
||||
params: {
|
||||
method: "Page.navigate",
|
||||
sessionId: "unknown-session",
|
||||
},
|
||||
};
|
||||
|
||||
await expect(cdpRouter.handleCommand(msg)).rejects.toThrow(
|
||||
"No tab found for method Page.navigate"
|
||||
);
|
||||
});
|
||||
|
||||
it("should find tab by sessionId", async () => {
|
||||
tabManager.set(123, {
|
||||
sessionId: "session-1",
|
||||
targetId: "target-1",
|
||||
state: "connected",
|
||||
});
|
||||
|
||||
mockDebuggerSendCommand.mockResolvedValue({ result: "ok" });
|
||||
|
||||
const msg: ExtensionCommandMessage = {
|
||||
id: 1,
|
||||
method: "forwardCDPCommand",
|
||||
params: {
|
||||
method: "Page.navigate",
|
||||
sessionId: "session-1",
|
||||
params: { url: "https://example.com" },
|
||||
},
|
||||
};
|
||||
|
||||
await cdpRouter.handleCommand(msg);
|
||||
|
||||
expect(mockDebuggerSendCommand).toHaveBeenCalledWith(
|
||||
{ tabId: 123, sessionId: undefined },
|
||||
"Page.navigate",
|
||||
{ url: "https://example.com" }
|
||||
);
|
||||
});
|
||||
|
||||
it("should find tab via child session", async () => {
|
||||
tabManager.set(123, {
|
||||
sessionId: "parent-session",
|
||||
targetId: "target-1",
|
||||
state: "connected",
|
||||
});
|
||||
tabManager.trackChildSession("child-session", 123);
|
||||
|
||||
mockDebuggerSendCommand.mockResolvedValue({});
|
||||
|
||||
const msg: ExtensionCommandMessage = {
|
||||
id: 1,
|
||||
method: "forwardCDPCommand",
|
||||
params: {
|
||||
method: "Runtime.evaluate",
|
||||
sessionId: "child-session",
|
||||
},
|
||||
};
|
||||
|
||||
await cdpRouter.handleCommand(msg);
|
||||
|
||||
expect(mockDebuggerSendCommand).toHaveBeenCalledWith(
|
||||
{ tabId: 123, sessionId: "child-session" },
|
||||
"Runtime.evaluate",
|
||||
undefined
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleDebuggerEvent", () => {
|
||||
it("should forward CDP events to relay", () => {
|
||||
tabManager.set(123, {
|
||||
sessionId: "session-1",
|
||||
targetId: "target-1",
|
||||
state: "connected",
|
||||
});
|
||||
|
||||
const sendMessage = vi.fn();
|
||||
|
||||
cdpRouter.handleDebuggerEvent(
|
||||
{ tabId: 123 },
|
||||
"Page.loadEventFired",
|
||||
{ timestamp: 12345 },
|
||||
sendMessage
|
||||
);
|
||||
|
||||
expect(sendMessage).toHaveBeenCalledWith({
|
||||
method: "forwardCDPEvent",
|
||||
params: {
|
||||
sessionId: "session-1",
|
||||
method: "Page.loadEventFired",
|
||||
params: { timestamp: 12345 },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should track child sessions on Target.attachedToTarget", () => {
|
||||
tabManager.set(123, {
|
||||
sessionId: "session-1",
|
||||
targetId: "target-1",
|
||||
state: "connected",
|
||||
});
|
||||
|
||||
const sendMessage = vi.fn();
|
||||
|
||||
cdpRouter.handleDebuggerEvent(
|
||||
{ tabId: 123 },
|
||||
"Target.attachedToTarget",
|
||||
{ sessionId: "new-child-session", targetInfo: {} },
|
||||
sendMessage
|
||||
);
|
||||
|
||||
expect(tabManager.getParentTabId("new-child-session")).toBe(123);
|
||||
});
|
||||
|
||||
it("should untrack child sessions on Target.detachedFromTarget", () => {
|
||||
tabManager.set(123, {
|
||||
sessionId: "session-1",
|
||||
targetId: "target-1",
|
||||
state: "connected",
|
||||
});
|
||||
tabManager.trackChildSession("child-session", 123);
|
||||
|
||||
const sendMessage = vi.fn();
|
||||
|
||||
cdpRouter.handleDebuggerEvent(
|
||||
{ tabId: 123 },
|
||||
"Target.detachedFromTarget",
|
||||
{ sessionId: "child-session" },
|
||||
sendMessage
|
||||
);
|
||||
|
||||
expect(tabManager.getParentTabId("child-session")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should ignore events for unknown tabs", () => {
|
||||
const sendMessage = vi.fn();
|
||||
|
||||
cdpRouter.handleDebuggerEvent({ tabId: 999 }, "Page.loadEventFired", {}, sendMessage);
|
||||
|
||||
expect(sendMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
45
dev-browser/extension/__tests__/StateManager.test.ts
Normal file
45
dev-browser/extension/__tests__/StateManager.test.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { describe, it, expect, beforeEach } from "vitest";
|
||||
import { fakeBrowser } from "wxt/testing";
|
||||
import { StateManager } from "../services/StateManager";
|
||||
|
||||
describe("StateManager", () => {
|
||||
let stateManager: StateManager;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeBrowser.reset();
|
||||
stateManager = new StateManager();
|
||||
});
|
||||
|
||||
describe("getState", () => {
|
||||
it("should return default inactive state when no stored state", async () => {
|
||||
const state = await stateManager.getState();
|
||||
expect(state).toEqual({ isActive: false });
|
||||
});
|
||||
|
||||
it("should return stored state when available", async () => {
|
||||
await fakeBrowser.storage.local.set({
|
||||
devBrowserActiveState: { isActive: true },
|
||||
});
|
||||
|
||||
const state = await stateManager.getState();
|
||||
expect(state).toEqual({ isActive: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe("setState", () => {
|
||||
it("should persist state to storage", async () => {
|
||||
await stateManager.setState({ isActive: true });
|
||||
|
||||
const stored = await fakeBrowser.storage.local.get("devBrowserActiveState");
|
||||
expect(stored.devBrowserActiveState).toEqual({ isActive: true });
|
||||
});
|
||||
|
||||
it("should update state from active to inactive", async () => {
|
||||
await stateManager.setState({ isActive: true });
|
||||
await stateManager.setState({ isActive: false });
|
||||
|
||||
const state = await stateManager.getState();
|
||||
expect(state).toEqual({ isActive: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
170
dev-browser/extension/__tests__/TabManager.test.ts
Normal file
170
dev-browser/extension/__tests__/TabManager.test.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { fakeBrowser } from "wxt/testing";
|
||||
import { TabManager } from "../services/TabManager";
|
||||
import type { Logger } from "../utils/logger";
|
||||
|
||||
describe("TabManager", () => {
|
||||
let tabManager: TabManager;
|
||||
let mockLogger: Logger;
|
||||
let mockSendMessage: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeBrowser.reset();
|
||||
|
||||
mockLogger = {
|
||||
log: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
mockSendMessage = vi.fn();
|
||||
|
||||
tabManager = new TabManager({
|
||||
logger: mockLogger,
|
||||
sendMessage: mockSendMessage,
|
||||
});
|
||||
});
|
||||
|
||||
describe("getBySessionId", () => {
|
||||
it("should return undefined when no tabs exist", () => {
|
||||
const result = tabManager.getBySessionId("session-1");
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should find tab by session ID", () => {
|
||||
tabManager.set(123, {
|
||||
sessionId: "session-1",
|
||||
targetId: "target-1",
|
||||
state: "connected",
|
||||
});
|
||||
|
||||
const result = tabManager.getBySessionId("session-1");
|
||||
expect(result).toEqual({
|
||||
tabId: 123,
|
||||
tab: {
|
||||
sessionId: "session-1",
|
||||
targetId: "target-1",
|
||||
state: "connected",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("getByTargetId", () => {
|
||||
it("should return undefined when no tabs exist", () => {
|
||||
const result = tabManager.getByTargetId("target-1");
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should find tab by target ID", () => {
|
||||
tabManager.set(456, {
|
||||
sessionId: "session-2",
|
||||
targetId: "target-2",
|
||||
state: "connected",
|
||||
});
|
||||
|
||||
const result = tabManager.getByTargetId("target-2");
|
||||
expect(result).toEqual({
|
||||
tabId: 456,
|
||||
tab: {
|
||||
sessionId: "session-2",
|
||||
targetId: "target-2",
|
||||
state: "connected",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("child sessions", () => {
|
||||
it("should track child sessions", () => {
|
||||
tabManager.trackChildSession("child-session-1", 123);
|
||||
expect(tabManager.getParentTabId("child-session-1")).toBe(123);
|
||||
});
|
||||
|
||||
it("should untrack child sessions", () => {
|
||||
tabManager.trackChildSession("child-session-1", 123);
|
||||
tabManager.untrackChildSession("child-session-1");
|
||||
expect(tabManager.getParentTabId("child-session-1")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("set/get/has", () => {
|
||||
it("should set and get tab info", () => {
|
||||
tabManager.set(789, { state: "connecting" });
|
||||
expect(tabManager.get(789)).toEqual({ state: "connecting" });
|
||||
expect(tabManager.has(789)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return undefined for unknown tabs", () => {
|
||||
expect(tabManager.get(999)).toBeUndefined();
|
||||
expect(tabManager.has(999)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("detach", () => {
|
||||
it("should send detached event and remove tab", () => {
|
||||
tabManager.set(123, {
|
||||
sessionId: "session-1",
|
||||
targetId: "target-1",
|
||||
state: "connected",
|
||||
});
|
||||
|
||||
tabManager.detach(123, false);
|
||||
|
||||
expect(mockSendMessage).toHaveBeenCalledWith({
|
||||
method: "forwardCDPEvent",
|
||||
params: {
|
||||
method: "Target.detachedFromTarget",
|
||||
params: { sessionId: "session-1", targetId: "target-1" },
|
||||
},
|
||||
});
|
||||
|
||||
expect(tabManager.has(123)).toBe(false);
|
||||
});
|
||||
|
||||
it("should clean up child sessions when detaching", () => {
|
||||
tabManager.set(123, {
|
||||
sessionId: "session-1",
|
||||
targetId: "target-1",
|
||||
state: "connected",
|
||||
});
|
||||
tabManager.trackChildSession("child-1", 123);
|
||||
tabManager.trackChildSession("child-2", 123);
|
||||
|
||||
tabManager.detach(123, false);
|
||||
|
||||
expect(tabManager.getParentTabId("child-1")).toBeUndefined();
|
||||
expect(tabManager.getParentTabId("child-2")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should do nothing for unknown tabs", () => {
|
||||
tabManager.detach(999, false);
|
||||
expect(mockSendMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("clear", () => {
|
||||
it("should clear all tabs and child sessions", () => {
|
||||
tabManager.set(1, { state: "connected" });
|
||||
tabManager.set(2, { state: "connected" });
|
||||
tabManager.trackChildSession("child-1", 1);
|
||||
|
||||
tabManager.clear();
|
||||
|
||||
expect(tabManager.has(1)).toBe(false);
|
||||
expect(tabManager.has(2)).toBe(false);
|
||||
expect(tabManager.getParentTabId("child-1")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAllTabIds", () => {
|
||||
it("should return all tab IDs", () => {
|
||||
tabManager.set(1, { state: "connected" });
|
||||
tabManager.set(2, { state: "connecting" });
|
||||
tabManager.set(3, { state: "error" });
|
||||
|
||||
const ids = tabManager.getAllTabIds();
|
||||
expect(ids).toEqual([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
});
|
||||
119
dev-browser/extension/__tests__/logger.test.ts
Normal file
119
dev-browser/extension/__tests__/logger.test.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { createLogger } from "../utils/logger";
|
||||
|
||||
describe("createLogger", () => {
|
||||
let mockSendMessage: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockSendMessage = vi.fn();
|
||||
vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
vi.spyOn(console, "debug").mockImplementation(() => {});
|
||||
vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
});
|
||||
|
||||
describe("log", () => {
|
||||
it("should log to console and send message", () => {
|
||||
const logger = createLogger(mockSendMessage);
|
||||
logger.log("test message", 123);
|
||||
|
||||
expect(console.log).toHaveBeenCalledWith("[dev-browser]", "test message", 123);
|
||||
expect(mockSendMessage).toHaveBeenCalledWith({
|
||||
method: "log",
|
||||
params: {
|
||||
level: "log",
|
||||
args: ["test message", "123"],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("debug", () => {
|
||||
it("should debug to console and send message", () => {
|
||||
const logger = createLogger(mockSendMessage);
|
||||
logger.debug("debug info");
|
||||
|
||||
expect(console.debug).toHaveBeenCalledWith("[dev-browser]", "debug info");
|
||||
expect(mockSendMessage).toHaveBeenCalledWith({
|
||||
method: "log",
|
||||
params: {
|
||||
level: "debug",
|
||||
args: ["debug info"],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("error", () => {
|
||||
it("should error to console and send message", () => {
|
||||
const logger = createLogger(mockSendMessage);
|
||||
logger.error("error occurred");
|
||||
|
||||
expect(console.error).toHaveBeenCalledWith("[dev-browser]", "error occurred");
|
||||
expect(mockSendMessage).toHaveBeenCalledWith({
|
||||
method: "log",
|
||||
params: {
|
||||
level: "error",
|
||||
args: ["error occurred"],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("argument formatting", () => {
|
||||
it("should format undefined as string", () => {
|
||||
const logger = createLogger(mockSendMessage);
|
||||
logger.log(undefined);
|
||||
|
||||
expect(mockSendMessage).toHaveBeenCalledWith({
|
||||
method: "log",
|
||||
params: {
|
||||
level: "log",
|
||||
args: ["undefined"],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should format null as string", () => {
|
||||
const logger = createLogger(mockSendMessage);
|
||||
logger.log(null);
|
||||
|
||||
expect(mockSendMessage).toHaveBeenCalledWith({
|
||||
method: "log",
|
||||
params: {
|
||||
level: "log",
|
||||
args: ["null"],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should JSON stringify objects", () => {
|
||||
const logger = createLogger(mockSendMessage);
|
||||
logger.log({ key: "value" });
|
||||
|
||||
expect(mockSendMessage).toHaveBeenCalledWith({
|
||||
method: "log",
|
||||
params: {
|
||||
level: "log",
|
||||
args: ['{"key":"value"}'],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle circular objects gracefully", () => {
|
||||
const logger = createLogger(mockSendMessage);
|
||||
const circular: Record<string, unknown> = { a: 1 };
|
||||
circular.self = circular;
|
||||
|
||||
logger.log(circular);
|
||||
|
||||
// Should fall back to String() when JSON.stringify fails
|
||||
expect(mockSendMessage).toHaveBeenCalledWith({
|
||||
method: "log",
|
||||
params: {
|
||||
level: "log",
|
||||
args: ["[object Object]"],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user