Skip to content
Draft
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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Opens [Plannotator](https://github.com/backnotprop/plannotator) plan reviews ins

When Plannotator opens a browser to show a plan review, this extension intercepts the request and opens it in a VS Code panel instead:

1. The extension injects a `PLANNOTATOR_BROWSER` environment variable into integrated terminals
1. The extension injects a `PLANNOTATOR_BROWSER` environment variable into integrated terminals (both existing and new)
2. When Plannotator opens a URL, the bundled router script sends it to the extension via a local HTTP server
3. The extension opens the URL in a custom WebviewPanel with an embedded iframe
4. A local reverse proxy handles cookie persistence (VS Code webview iframes don't support cookies natively) — settings are stored in VS Code's global state and restored transparently
Expand All @@ -46,7 +46,8 @@ When Plannotator opens a browser to show a plan review, this extension intercept

### URL opens in external browser instead of VS Code
- Ensure `plannotatorWebview.injectBrowser` is enabled
- Open a **new** terminal after installing the extension (existing terminals won't have the env var)
- Reload VS Code window after enabling the extension (Cmd/Ctrl+Shift+P → "Developer: Reload Window")
- Check the Output panel (View → Output → Plannotator) for any error messages

### Panel shows a blank page
- Check if Plannotator's server is still running
Expand Down
71 changes: 71 additions & 0 deletions mocks/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,38 @@ export const ViewColumn = {
Three: 3,
};

// Mock Terminal
export interface Terminal {
name: string;
processId: Thenable<number | undefined>;
creationOptions: Readonly<TerminalOptions | ExtensionTerminalOptions>;
exitStatus: { code: number } | undefined;
sendText(text: string, shouldExecute?: boolean): void;
show(preserveFocus?: boolean): void;
hide(): void;
dispose(): void;
}

export interface TerminalOptions {
name?: string;
shellPath?: string;
shellArgs?: string | string[];
cwd?: string | Uri;
env?: { [key: string]: string | null | undefined };
}

export interface ExtensionTerminalOptions {
name: string;
pty: unknown;
}

// Mock terminals storage for testing
const mockTerminals: Terminal[] = [];

export const window = {
get terminals(): Terminal[] {
return [...mockTerminals];
},
registerUriHandler(_handler: unknown) {
return { dispose() {} };
},
Expand Down Expand Up @@ -181,3 +212,43 @@ export function createMockExtensionContext(
workspaceState: { get: () => undefined, update: async () => {} },
};
}

// Helper to create a mock terminal for testing
export function createMockTerminal(
name: string,
shellPath?: string
): Terminal {
const sentTexts: string[] = [];
const creationOptions: TerminalOptions = {
name,
...(shellPath && { shellPath }),
};

const terminal: Terminal = {
name,
processId: Promise.resolve(Math.floor(Math.random() * 10000)),
creationOptions,
exitStatus: undefined,
sendText(text: string, _shouldExecute?: boolean) {
sentTexts.push(text);
},
show(_preserveFocus?: boolean) {},
hide() {},
dispose() {},
};

// Store sent texts for test assertions
(terminal as any).sentTexts = sentTexts;

return terminal;
}

// Helper to add a mock terminal to window.terminals
export function addMockTerminal(terminal: Terminal): void {
mockTerminals.push(terminal);
}

// Helper to clear mock terminals
export function clearMockTerminals(): void {
mockTerminals.length = 0;
}
Loading