/** * Puppeteer test for OpenAI API key configuration through the UI. * * This test verifies: * 1. User can navigate to settings * 2. User can enter OpenAI API key * 3. Settings are saved correctly * 4. API key is used for research */ const puppeteer = require('puppeteer'); const { expect } = require('chai'); const { getLaunchOptions } = require('./helpers'); // Test configuration const BASE_URL = process.env.TEST_URL || 'http://localhost:5000'; const TEST_USERNAME = 'test_openai_user'; const TEST_PASSWORD = 'Test_password_123'; const TEST_OPENAI_KEY = 'sk-test-1234567890abcdef'; describe('OpenAI API Key Configuration UI Test', function() { this.timeout(60000); // 60 second timeout for UI tests let browser; let page; before(async () => { // Shared options include the seeded profile that disables Chrome's // password leak detection (see helpers/index.js and #4430). browser = await puppeteer.launch(getLaunchOptions()); page = await browser.newPage(); // Set viewport for consistent testing await page.setViewport({ width: 1280, height: 720 }); // Enable console logging for debugging page.on('console', msg => console.log('Browser console:', msg.text())); }); after(async () => { if (browser) { await browser.close(); } }); beforeEach(async () => { // Navigate to home page before each test await page.goto(BASE_URL, { waitUntil: 'domcontentloaded' }); }); describe('User Registration and Login', () => { it('should register a new user', async () => { // Click on Register link await page.waitForSelector('a[href="/register"]'); await page.click('a[href="/register"]'); // Wait for registration form await page.waitForSelector('form#registerForm'); // Fill registration form await page.type('input[name="username"]', TEST_USERNAME); await page.type('input[name="password"]', TEST_PASSWORD); await page.type('input[name="confirm_password"]', TEST_PASSWORD); // Submit form await page.click('button[type="submit"]'); // Wait for redirect to login or dashboard await page.waitForNavigation({ waitUntil: 'domcontentloaded' }); // Check if registration was successful const url = page.url(); expect(url).to.include('/login').or.to.include('/'); }); it('should login with credentials', async () => { // Navigate to login if not already there const currentUrl = page.url(); if (!currentUrl.includes('/login')) { await page.goto(`${BASE_URL}/login`, { waitUntil: 'domcontentloaded' }); } // Wait for login form await page.waitForSelector('form'); // Fill login form await page.type('input[name="username"]', TEST_USERNAME); await page.type('input[name="password"]', TEST_PASSWORD); // Submit form await page.click('button[type="submit"]'); // Wait for redirect to dashboard await page.waitForNavigation({ waitUntil: 'domcontentloaded' }); // Verify we're logged in const dashboardElement = await page.$('.dashboard, #dashboard, [data-page="home"]'); expect(dashboardElement).to.not.be.null; }); }); describe('OpenAI API Key Configuration', () => { beforeEach(async () => { // Ensure we're logged in await loginUser(page, TEST_USERNAME, TEST_PASSWORD); }); it('should navigate to settings page', async () => { // Click on Settings link/button const settingsSelector = 'a[href="/settings"], button:contains("Settings"), .settings-link'; await page.waitForSelector(settingsSelector); await page.click(settingsSelector); // Wait for settings page to load await page.waitForSelector('.settings-page, #settings, [data-page="settings"]'); // Verify we're on settings page const url = page.url(); expect(url).to.include('/settings'); }); it('should configure OpenAI provider and API key', async () => { // Navigate to settings await page.goto(`${BASE_URL}/settings`, { waitUntil: 'domcontentloaded' }); // Select OpenAI as provider const providerSelect = await page.$('select[name="llm.provider"], #llm-provider'); if (providerSelect) { await page.select('select[name="llm.provider"], #llm-provider', 'openai'); } else { // Alternative: click on OpenAI option if using custom dropdown await page.click('[data-provider="openai"], .provider-option[data-value="openai"]'); } // Wait for OpenAI settings to appear await page.waitForSelector('input[name="llm.openai.api_key"], #openai-api-key', { visible: true }); // Clear and enter API key const apiKeyInput = await page.$('input[name="llm.openai.api_key"], #openai-api-key'); await apiKeyInput.click({ clickCount: 3 }); // Select all await apiKeyInput.type(TEST_OPENAI_KEY); // Select model (optional) const modelSelect = await page.$('select[name="llm.model"], #llm-model'); if (modelSelect) { await page.select('select[name="llm.model"], #llm-model', 'gpt-3.5-turbo'); } // Save settings await page.click('button[type="submit"], .save-settings, #save-settings'); // Wait for save confirmation await page.waitForSelector('.success-message, .toast-success, [data-status="success"]', { timeout: 5000 }); // Verify settings were saved const successMessage = await page.$eval( '.success-message, .toast-success, [data-status="success"]', el => el.textContent ); expect(successMessage).to.include('saved').or.to.include('success'); }); it('should mask API key after saving', async () => { // Navigate to settings await page.goto(`${BASE_URL}/settings`, { waitUntil: 'domcontentloaded' }); // Wait for settings to load await page.waitForSelector('input[name="llm.openai.api_key"], #openai-api-key'); // Check if API key is masked const apiKeyInput = await page.$('input[name="llm.openai.api_key"], #openai-api-key'); const inputType = await apiKeyInput.evaluate(el => el.type); const inputValue = await apiKeyInput.evaluate(el => el.value); // Should be password type or show masked value expect(inputType).to.equal('password').or.expect(inputValue).to.match(/\*{4,}/); }); }); describe('Research with OpenAI', () => { beforeEach(async () => { // Ensure we're logged in and have OpenAI configured await loginUser(page, TEST_USERNAME, TEST_PASSWORD); }); it('should perform research using configured OpenAI API', async () => { // Navigate to home/research page await page.goto(BASE_URL, { waitUntil: 'domcontentloaded' }); // Find and fill research query input await page.waitForSelector('input[name="query"], #query, .search-input'); await page.type('input[name="query"], #query, .search-input', 'What is machine learning?'); // Click search/research button await page.click('button[type="submit"], .search-button, #start-research'); // Wait for research to start await page.waitForSelector('.research-status, .loading, [data-status="running"]', { timeout: 10000 }); // Wait for research to complete (with longer timeout) await page.waitForSelector('.research-complete, .research-summary, [data-status="completed"]', { timeout: 30000 }); // Verify results contain expected content const summaryText = await page.$eval( '.research-summary, .summary-content, [data-content="summary"]', el => el.textContent ); expect(summaryText).to.include('machine learning').or.to.include('Machine Learning'); expect(summaryText.length).to.be.greaterThan(100); // Should have substantial content }); it('should show error if API key is invalid', async () => { // Configure with invalid API key await page.goto(`${BASE_URL}/settings`, { waitUntil: 'domcontentloaded' }); // Set invalid API key const apiKeyInput = await page.$('input[name="llm.openai.api_key"], #openai-api-key'); await apiKeyInput.click({ clickCount: 3 }); await apiKeyInput.type('sk-invalid-key'); // Save settings await page.click('button[type="submit"], .save-settings, #save-settings'); await page.waitForTimeout(1000); // Wait for save // Try to perform research await page.goto(BASE_URL, { waitUntil: 'domcontentloaded' }); await page.type('input[name="query"], #query, .search-input', 'Test query'); await page.click('button[type="submit"], .search-button, #start-research'); // Wait for error message await page.waitForSelector('.error-message, .toast-error, [data-status="error"]', { timeout: 15000 }); // Verify error message mentions API key or authentication const errorText = await page.$eval( '.error-message, .toast-error, [data-status="error"]', el => el.textContent ); expect(errorText.toLowerCase()).to.include('api').or.include('auth').or.include('key'); }); }); describe('Settings Persistence', () => { it('should persist OpenAI settings across sessions', async () => { // Login await loginUser(page, TEST_USERNAME, TEST_PASSWORD); // Configure OpenAI await page.goto(`${BASE_URL}/settings`, { waitUntil: 'domcontentloaded' }); await page.select('select[name="llm.provider"], #llm-provider', 'openai'); await page.type('input[name="llm.openai.api_key"], #openai-api-key', TEST_OPENAI_KEY); await page.click('button[type="submit"], .save-settings, #save-settings'); // Wait for save await page.waitForTimeout(2000); // Logout await page.click('a[href="/logout"], .logout-button, #logout'); await page.waitForNavigation({ waitUntil: 'domcontentloaded' }); // Login again await loginUser(page, TEST_USERNAME, TEST_PASSWORD); // Go to settings await page.goto(`${BASE_URL}/settings`, { waitUntil: 'domcontentloaded' }); // Verify provider is still OpenAI const selectedProvider = await page.$eval( 'select[name="llm.provider"], #llm-provider', el => el.value ); expect(selectedProvider).to.equal('openai'); // Verify API key field has value (even if masked) const apiKeyValue = await page.$eval( 'input[name="llm.openai.api_key"], #openai-api-key', el => el.value ); expect(apiKeyValue).to.not.be.empty; }); }); }); // Helper function to login async function loginUser(page, username, password) { const currentUrl = page.url(); // Check if already logged in if (currentUrl.includes('/settings') || currentUrl.includes('/research')) { return; } // Navigate to login await page.goto(`${BASE_URL}/login`, { waitUntil: 'domcontentloaded' }); // Fill and submit login form await page.type('input[name="username"]', username); await page.type('input[name="password"]', password); await page.click('button[type="submit"]'); // Wait for redirect await page.waitForNavigation({ waitUntil: 'domcontentloaded' }); }