From 1abc98be1b05dcf168462764aefe9287c7794646 Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Thu, 12 Feb 2026 19:05:14 +1100 Subject: [PATCH] added isolated config.yaml --- ui/desktop/tests/e2e/app.spec.ts | 72 +++++++------------------------- ui/desktop/tests/e2e/fixtures.ts | 26 ++++++++++-- 2 files changed, 36 insertions(+), 62 deletions(-) diff --git a/ui/desktop/tests/e2e/app.spec.ts b/ui/desktop/tests/e2e/app.spec.ts index d9f5071a7a..cb9ae325e1 100644 --- a/ui/desktop/tests/e2e/app.spec.ts +++ b/ui/desktop/tests/e2e/app.spec.ts @@ -49,63 +49,16 @@ test.afterEach(async () => { async function selectProvider(mainWindow: any, provider: Provider) { console.log(`Selecting provider: ${provider.name}`); - // If we're already in the chat interface, we need to reset providers - const chatTextarea = await mainWindow.waitForSelector('[data-testid="chat-input"]', { - timeout: 2000 - }).catch(() => null); - - if (chatTextarea) { - // Navigate to Settings via sidebar to reset providers - console.log('Opening settings to reset providers...'); - const settingsButton = await mainWindow.waitForSelector('[data-testid="sidebar-settings-button"]', { - timeout: 5000, - state: 'visible' - }); - await settingsButton.click(); - - // Wait for settings page to load and navigate to Models tab - await mainWindow.waitForSelector('[data-testid="settings-models-tab"]', { - timeout: 5000, - state: 'visible' - }); - - const modelsTab = await mainWindow.waitForSelector('[data-testid="settings-models-tab"]'); - await modelsTab.click(); - - // Wait for models section to load - await mainWindow.waitForTimeout(1000); - - // Click Reset Provider and Model button - console.log('Clicking Reset provider and model...'); - const resetButton = await mainWindow.waitForSelector('button:has-text("Reset provider and model")', { - timeout: 5000, - state: 'visible' - }); - await resetButton.click(); - - // Wait for the reset to complete - await mainWindow.waitForTimeout(1000); - } - - // Wait for React app to be ready and animations to complete - await mainWindow.waitForFunction(() => { - const root = document.getElementById('root'); - return root && root.children.length > 0; - }); - await mainWindow.waitForTimeout(10000); - - // Take a screenshot before proceeding - await mainWindow.screenshot({ path: `test-results/before-provider-${provider.name.toLowerCase()}-check.png` }); - - // Check if we're already at the chat interface (provider already configured) - const chatInputAfterReset = await mainWindow.waitForSelector('[data-testid="chat-input"]', { - timeout: 2000, + // Each test gets a fresh app with an isolated config (via GOOSE_PATH_ROOT in fixtures). + // The config is seeded with GOOSE_PROVIDER, so the chat interface should be available. + const chatInput = await mainWindow.waitForSelector('[data-testid="chat-input"]', { + timeout: 10000, state: 'visible' }).catch(() => null); - if (chatInputAfterReset) { + if (chatInput) { console.log('Provider already configured, chat interface is available'); - return; // Provider is already selected, no need to do anything + return; } // Check if we're on the welcome screen with "Other Providers" section @@ -501,19 +454,22 @@ test.describe('Goose App', () => { console.log('Clicked Add Extension button'); // Wait for the Running Quotes extension to appear in the list + // ExtensionItem renders console.log('Waiting for Running Quotes extension to appear...'); try { - const extensionCard = await mainWindow.waitForSelector( - 'div.flex:has-text("Running Quotes")', + await mainWindow.waitForSelector( + '#extension-running-quotes', { timeout: 30000, state: 'visible' } ); - // Verify the extension is enabled - await mainWindow.waitForTimeout(1000); - const toggleButton = await extensionCard.$('button[role="switch"][data-state="checked"]'); + // Wait for the extension toggle to become checked (MCP server may take time to start) + const toggleButton = await mainWindow.waitForSelector( + '#extension-running-quotes button[role="switch"][data-state="checked"]', + { timeout: 10000, state: 'visible' } + ).catch(() => null); const isEnabled = !!toggleButton; console.log('Extension enabled:', isEnabled); diff --git a/ui/desktop/tests/e2e/fixtures.ts b/ui/desktop/tests/e2e/fixtures.ts index 1db875f4df..d3184dfdbc 100644 --- a/ui/desktop/tests/e2e/fixtures.ts +++ b/ui/desktop/tests/e2e/fixtures.ts @@ -2,6 +2,8 @@ import { test as base, Page, Browser, chromium } from '@playwright/test'; import { spawn, ChildProcess } from 'child_process'; import { join } from 'path'; import { promisify } from 'util'; +import * as fs from 'fs'; +import * as os from 'os'; const execAsync = promisify(require('child_process').exec); @@ -12,12 +14,10 @@ type GooseTestFixtures = { /** * Test-scoped fixture that launches a fresh Electron app for EACH test. * - * Isolation: ⚠️ Partial - each test gets a fresh app instance, but uses ambient user config + * Isolation: ✅ Full - each test gets a fresh app instance with an isolated config directory + * via GOOSE_PATH_ROOT, so tests never touch ~/.config/goose/ * Speed: ⚠️ Slow - ~3s startup overhead per test * - * This ensures each test starts with a fresh app instance, but the app uses the - * user's existing Goose configuration (providers, models, etc.). - * * Usage: * import { test, expect } from './fixtures'; * @@ -34,6 +34,15 @@ export const test = base.extend({ let appProcess: ChildProcess | null = null; let browser: Browser | null = null; + // Create an isolated config directory so tests never touch ~/.config/goose/ + const tempDir = fs.mkdtempSync(join(os.tmpdir(), 'goose-test-')); + const configDir = join(tempDir, 'config'); + fs.mkdirSync(configDir, { recursive: true }); + fs.mkdirSync(join(tempDir, 'data'), { recursive: true }); + fs.mkdirSync(join(tempDir, 'state'), { recursive: true }); + fs.writeFileSync(join(configDir, 'config.yaml'), 'GOOSE_PROVIDER: databricks\nGOOSE_MODEL: databricks-claude-haiku-4-5\nGOOSE_TELEMETRY_ENABLED: false\nDATABRICKS_HOST: https://block-lakehouse-production.cloud.databricks.com/\n'); + console.log(`Using isolated config directory: ${tempDir}`); + try { // Assign a unique debug port for this test to enable parallel execution // Base port 9222, offset by worker index * 100 + parallel slot @@ -54,6 +63,7 @@ export const test = base.extend({ ENABLE_PLAYWRIGHT: 'true', PLAYWRIGHT_DEBUG_PORT: debugPort.toString(), // Unique port per test for parallel execution RUST_LOG: 'info', // Enable info-level logging for goosed backend + GOOSE_PATH_ROOT: tempDir, } }); @@ -163,6 +173,14 @@ export const test = base.extend({ } } } + + // Clean up the isolated config directory + try { + fs.rmSync(tempDir, { recursive: true, force: true }); + console.log('Cleaned up isolated config directory'); + } catch (error) { + console.error('Error cleaning up temp directory:', error); + } } }, });