mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Merge pull request #757 from RooVetGit/cte/hmr-dx
Fall back to js bundle when local webview app not running in development
This commit is contained in:
commit
bf7ec3180d
5 changed files with 69 additions and 40 deletions
|
|
@ -34,6 +34,7 @@ module.exports = {
|
|||
transformIgnorePatterns: [
|
||||
"node_modules/(?!(@modelcontextprotocol|delay|p-wait-for|globby|serialize-error|strip-ansi|default-shell|os-name)/)",
|
||||
],
|
||||
roots: ["<rootDir>/src", "<rootDir>/webview-ui/src"],
|
||||
modulePathIgnorePatterns: [".vscode-test"],
|
||||
reporters: [["jest-simple-dot-reporter", {}]],
|
||||
setupFiles: [],
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ const openClineInNewTab = async ({ context, outputChannel }: Omit<RegisterComman
|
|||
dark: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "rocket.png"),
|
||||
}
|
||||
|
||||
tabProvider.resolveWebviewView(panel)
|
||||
await tabProvider.resolveWebviewView(panel)
|
||||
|
||||
// Lock the editor group so clicking on files doesn't open them over the panel
|
||||
await delay(100)
|
||||
|
|
|
|||
|
|
@ -256,11 +256,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
await visibleProvider.initClineWithTask(prompt)
|
||||
}
|
||||
|
||||
resolveWebviewView(
|
||||
webviewView: vscode.WebviewView | vscode.WebviewPanel,
|
||||
//context: vscode.WebviewViewResolveContext<unknown>, used to recreate a deallocated webview, but we don't need this since we use retainContextWhenHidden
|
||||
//token: vscode.CancellationToken
|
||||
): void | Thenable<void> {
|
||||
async resolveWebviewView(webviewView: vscode.WebviewView | vscode.WebviewPanel) {
|
||||
this.outputChannel.appendLine("Resolving webview view")
|
||||
this.view = webviewView
|
||||
|
||||
|
|
@ -277,7 +273,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
|
||||
webviewView.webview.html =
|
||||
this.context.extensionMode === vscode.ExtensionMode.Development
|
||||
? this.getHMRHtmlContent(webviewView.webview)
|
||||
? await this.getHMRHtmlContent(webviewView.webview)
|
||||
: this.getHtmlContent(webviewView.webview)
|
||||
|
||||
// Sets up an event listener to listen for messages passed from the webview view context
|
||||
|
|
@ -402,9 +398,22 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
await this.view?.webview.postMessage(message)
|
||||
}
|
||||
|
||||
private getHMRHtmlContent(webview: vscode.Webview): string {
|
||||
const nonce = getNonce()
|
||||
private async getHMRHtmlContent(webview: vscode.Webview): Promise<string> {
|
||||
const localPort = "5173"
|
||||
const localServerUrl = `localhost:${localPort}`
|
||||
|
||||
// Check if local dev server is running.
|
||||
try {
|
||||
await axios.get(`http://${localServerUrl}`)
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(
|
||||
"Local development server is not running, HMR will not work. Please run 'npm run dev' before launching the extension to enable HMR.",
|
||||
)
|
||||
|
||||
return this.getHtmlContent(webview)
|
||||
}
|
||||
|
||||
const nonce = getNonce()
|
||||
const stylesUri = getUri(webview, this.context.extensionUri, ["webview-ui", "build", "assets", "index.css"])
|
||||
const codiconsUri = getUri(webview, this.context.extensionUri, [
|
||||
"node_modules",
|
||||
|
|
@ -415,8 +424,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
])
|
||||
|
||||
const file = "src/index.tsx"
|
||||
const localPort = "5173"
|
||||
const localServerUrl = `localhost:${localPort}`
|
||||
const scriptUri = `http://${localServerUrl}/${file}`
|
||||
|
||||
const reactRefresh = /*html*/ `
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
import { ClineProvider } from "../ClineProvider"
|
||||
// npx jest src/core/webview/__tests__/ClineProvider.test.ts
|
||||
|
||||
import * as vscode from "vscode"
|
||||
import axios from "axios"
|
||||
|
||||
import { ClineProvider } from "../ClineProvider"
|
||||
import { ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessage"
|
||||
import { setSoundEnabled } from "../../../utils/sound"
|
||||
import { defaultModeSlug, modes } from "../../../shared/modes"
|
||||
import { addCustomInstructions } from "../../prompts/sections/custom-instructions"
|
||||
import { experimentDefault, experiments } from "../../../shared/experiments"
|
||||
import { defaultModeSlug } from "../../../shared/modes"
|
||||
import { experimentDefault } from "../../../shared/experiments"
|
||||
|
||||
// Mock custom-instructions module
|
||||
const mockAddCustomInstructions = jest.fn()
|
||||
|
||||
jest.mock("../../prompts/sections/custom-instructions", () => ({
|
||||
addCustomInstructions: mockAddCustomInstructions,
|
||||
}))
|
||||
|
|
@ -202,7 +206,6 @@ describe("ClineProvider", () => {
|
|||
let mockOutputChannel: vscode.OutputChannel
|
||||
let mockWebviewView: vscode.WebviewView
|
||||
let mockPostMessage: jest.Mock
|
||||
let visibilityChangeCallback: (e?: unknown) => void
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset mocks
|
||||
|
|
@ -270,13 +273,13 @@ describe("ClineProvider", () => {
|
|||
return { dispose: jest.fn() }
|
||||
}),
|
||||
onDidChangeVisibility: jest.fn().mockImplementation((callback) => {
|
||||
visibilityChangeCallback = callback
|
||||
return { dispose: jest.fn() }
|
||||
}),
|
||||
} as unknown as vscode.WebviewView
|
||||
|
||||
provider = new ClineProvider(mockContext, mockOutputChannel)
|
||||
// @ts-ignore - accessing private property for testing
|
||||
|
||||
// @ts-ignore - Accessing private property for testing.
|
||||
provider.customModesManager = mockCustomModesManager
|
||||
})
|
||||
|
||||
|
|
@ -288,18 +291,36 @@ describe("ClineProvider", () => {
|
|||
expect(ClineProvider.getVisibleInstance()).toBe(provider)
|
||||
})
|
||||
|
||||
test("resolveWebviewView sets up webview correctly", () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
test("resolveWebviewView sets up webview correctly", async () => {
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
|
||||
expect(mockWebviewView.webview.options).toEqual({
|
||||
enableScripts: true,
|
||||
localResourceRoots: [mockContext.extensionUri],
|
||||
})
|
||||
|
||||
expect(mockWebviewView.webview.html).toContain("<!DOCTYPE html>")
|
||||
})
|
||||
|
||||
test("resolveWebviewView sets up webview correctly in development mode even if local server is not running", async () => {
|
||||
provider = new ClineProvider(
|
||||
{ ...mockContext, extensionMode: vscode.ExtensionMode.Development },
|
||||
mockOutputChannel,
|
||||
)
|
||||
;(axios.get as jest.Mock).mockRejectedValueOnce(new Error("Network error"))
|
||||
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
|
||||
expect(mockWebviewView.webview.options).toEqual({
|
||||
enableScripts: true,
|
||||
localResourceRoots: [mockContext.extensionUri],
|
||||
})
|
||||
|
||||
expect(mockWebviewView.webview.html).toContain("<!DOCTYPE html>")
|
||||
})
|
||||
|
||||
test("postMessageToWebview sends message to webview", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
|
||||
const mockState: ExtensionState = {
|
||||
version: "1.0.0",
|
||||
|
|
@ -341,7 +362,7 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
test("handles webviewDidLaunch message", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
|
||||
// Get the message handler from onDidReceiveMessage
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
|
@ -420,7 +441,7 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
test("handles writeDelayMs message", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
await messageHandler({ type: "writeDelayMs", value: 2000 })
|
||||
|
|
@ -430,7 +451,7 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
test("updates sound utility when sound setting changes", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
|
||||
// Get the message handler from onDidReceiveMessage
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
|
@ -470,7 +491,7 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
test("loads saved API config when switching modes", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
// Mock ConfigManager methods
|
||||
|
|
@ -491,7 +512,7 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
test("saves current config when switching to mode without config", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
// Mock ConfigManager methods
|
||||
|
|
@ -519,7 +540,7 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
test("saves config as default for current mode when loading config", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
provider.configManager = {
|
||||
|
|
@ -540,7 +561,7 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
test("handles request delay settings messages", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
// Test alwaysApproveResubmit
|
||||
|
|
@ -555,7 +576,7 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
test("handles updatePrompt message correctly", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
// Mock existing prompts
|
||||
|
|
@ -650,7 +671,7 @@ describe("ClineProvider", () => {
|
|||
)
|
||||
})
|
||||
test("handles mode-specific custom instructions updates", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
// Mock existing prompts
|
||||
|
|
@ -707,7 +728,7 @@ describe("ClineProvider", () => {
|
|||
|
||||
// Create new provider with updated mock context
|
||||
provider = new ClineProvider(mockContext, mockOutputChannel)
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
provider.configManager = {
|
||||
|
|
@ -732,10 +753,10 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
describe("deleteMessage", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
// Mock window.showInformationMessage
|
||||
;(vscode.window.showInformationMessage as jest.Mock) = jest.fn()
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
})
|
||||
|
||||
test('handles "Just this message" deletion correctly', async () => {
|
||||
|
|
@ -861,9 +882,9 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
describe("getSystemPrompt", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
mockPostMessage.mockClear()
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
// Reset and setup mock
|
||||
mockAddCustomInstructions.mockClear()
|
||||
mockAddCustomInstructions.mockImplementation(
|
||||
|
|
@ -1111,7 +1132,7 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
// Resolve webview and trigger getSystemPrompt
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const architectHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
await architectHandler({ type: "getSystemPrompt" })
|
||||
|
||||
|
|
@ -1125,9 +1146,9 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
describe("handleModeSwitch", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
// Set up webview for each test
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
})
|
||||
|
||||
test("loads saved API config when switching modes", async () => {
|
||||
|
|
@ -1188,7 +1209,7 @@ describe("ClineProvider", () => {
|
|||
|
||||
describe("updateCustomMode", () => {
|
||||
test("updates both file and state when updating custom mode", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
// Mock CustomModesManager methods
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ suite("Roo Code Task", () => {
|
|||
|
||||
try {
|
||||
// Initialize provider with panel.
|
||||
provider.resolveWebviewView(panel)
|
||||
await provider.resolveWebviewView(panel)
|
||||
|
||||
// Wait for webview to launch.
|
||||
let startTime = Date.now()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue