import { test, expect, chromium } from '@playwright/test';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const projectRoot = path.resolve(__dirname, '../..');
const extensionPath = path.join(projectRoot, 'browser-extension');
const demoPagePath = path.join(projectRoot, 'examples', 'devtools_demo.html');
test.describe('DevTools Extension E2E Tests', () => {
let browser;
let context;
let httpServer;
const HTTP_PORT = 8765;
test.beforeAll(async () => {
const { spawn } = await import('child_process');
httpServer = spawn('python3', ['-m', 'http.server', HTTP_PORT.toString()], {
cwd: projectRoot,
stdio: 'ignore'
});
await new Promise(resolve => setTimeout(resolve, 2000));
const userDataDir = path.join(projectRoot, '.playwright-chrome-profile');
context = await chromium.launchPersistentContext(userDataDir, {
headless: false, args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
'--no-sandbox',
],
});
});
test.afterAll(async () => {
await context?.close();
httpServer?.kill();
});
test('extension loads without errors', async () => {
const page = await context.newPage();
const errors = [];
page.on('pageerror', error => errors.push(error.message));
await page.goto(`http://localhost:${HTTP_PORT}/examples/devtools_demo.html`);
await page.waitForSelector('h1', { timeout: 5000 });
const criticalErrors = errors.filter(err =>
!err.includes('Extension context invalidated') );
expect(criticalErrors).toHaveLength(0);
await page.close();
});
test('demo page loads and shows DevTools info', async () => {
const page = await context.newPage();
await page.goto(`http://localhost:${HTTP_PORT}/examples/devtools_demo.html`);
await page.waitForSelector('#devtoolsStatus', { timeout: 5000 });
const heading = await page.locator('h1').textContent();
expect(heading).toContain('AbsurderSQL');
await page.close();
});
test('initialize database button works', async () => {
const page = await context.newPage();
await page.goto(`http://localhost:${HTTP_PORT}/examples/devtools_demo.html`);
await page.waitForSelector('#initBtn', { timeout: 5000 });
await page.click('#initBtn');
await page.waitForSelector('.log-success', { timeout: 5000 });
const logContent = await page.locator('.log-success').first().textContent();
expect(logContent).toContain('initialized');
await page.close();
});
test('run queries button generates telemetry', async () => {
const page = await context.newPage();
await page.goto(`http://localhost:${HTTP_PORT}/examples/devtools_demo.html`);
await page.click('#initBtn');
await page.waitForTimeout(200);
await page.click('#queryBtn');
await page.waitForTimeout(500);
const spanCount = await page.locator('#totalSpans').textContent();
expect(parseInt(spanCount)).toBeGreaterThan(0);
await page.close();
});
test('flush functionality works via page API', async () => {
const page = await context.newPage();
await page.goto(`http://localhost:${HTTP_PORT}/examples/devtools_demo.html`);
await page.click('#initBtn');
await page.waitForTimeout(200);
await page.click('#queryBtn');
await page.waitForTimeout(500);
const flushResult = await page.evaluate(() => {
if (typeof window.handleDevToolsMessage === 'function') {
return window.handleDevToolsMessage({ type: 'flush_spans' });
}
return null;
});
expect(flushResult).not.toBeNull();
expect(flushResult.success).toBe(true);
await page.close();
});
test('configuration update works via page API', async () => {
const page = await context.newPage();
await page.goto(`http://localhost:${HTTP_PORT}/examples/devtools_demo.html`);
const configResult = await page.evaluate(() => {
if (typeof window.handleDevToolsMessage === 'function') {
return window.handleDevToolsMessage({
type: 'config_update',
config: {
endpoint: 'http://localhost:9999/v1/traces',
batchSize: 50
}
});
}
return null;
});
expect(configResult).not.toBeNull();
expect(configResult.success).toBe(true);
await page.close();
});
test('buffer operations work via page API', async () => {
const page = await context.newPage();
await page.goto(`http://localhost:${HTTP_PORT}/examples/devtools_demo.html`);
await page.click('#initBtn');
await page.waitForTimeout(200);
const bufferResult = await page.evaluate(() => {
if (typeof window.handleDevToolsMessage === 'function') {
return window.handleDevToolsMessage({ type: 'get_buffer' });
}
return null;
});
expect(bufferResult).not.toBeNull();
expect(bufferResult.buffer).toBeDefined();
const clearResult = await page.evaluate(() => {
if (typeof window.handleDevToolsMessage === 'function') {
return window.handleDevToolsMessage({ type: 'clear_buffer' });
}
return null;
});
expect(clearResult).not.toBeNull();
expect(clearResult.success).toBe(true);
await page.close();
});
test('chrome.runtime.sendMessage is available for extension communication', async () => {
const page = await context.newPage();
await page.goto(`http://localhost:${HTTP_PORT}/examples/devtools_demo.html`);
const hasChromeRuntime = await page.evaluate(() => {
return typeof chrome !== 'undefined' &&
typeof chrome.runtime !== 'undefined' &&
typeof chrome.runtime.sendMessage === 'function';
});
expect(hasChromeRuntime).toBe(true);
await page.close();
});
test('sendToDevTools function exists and is callable', async () => {
const page = await context.newPage();
await page.goto(`http://localhost:${HTTP_PORT}/examples/devtools_demo.html`);
await page.click('#initBtn');
await page.waitForTimeout(200);
const sendResult = await page.evaluate(() => {
try {
window.sendToDevTools('test_event', { test: 'data' });
return { success: true };
} catch (e) {
return { success: false, error: e.message };
}
});
expect(sendResult.success).toBe(true);
await page.close();
});
test('activity log shows all operations', async () => {
const page = await context.newPage();
await page.goto(`http://localhost:${HTTP_PORT}/examples/devtools_demo.html`);
await page.click('#initBtn');
await page.waitForTimeout(200);
await page.click('#queryBtn');
await page.waitForTimeout(500);
const logEntries = await page.locator('.log-entry').count();
expect(logEntries).toBeGreaterThan(5);
await page.close();
});
test('error generation works', async () => {
const page = await context.newPage();
await page.goto(`http://localhost:${HTTP_PORT}/examples/devtools_demo.html`);
await page.click('#initBtn');
await page.waitForTimeout(200);
await page.click('#errorBtn');
await page.waitForTimeout(200);
const hasError = await page.locator('.log-error').count();
expect(hasError).toBeGreaterThan(0);
await page.close();
});
test('load generation works', async () => {
const page = await context.newPage();
await page.goto(`http://localhost:${HTTP_PORT}/examples/devtools_demo.html`);
await page.click('#initBtn');
await page.waitForTimeout(200);
await page.click('#loadBtn');
await page.waitForTimeout(1000);
const spanCount = await page.locator('#totalSpans').textContent();
expect(parseInt(spanCount)).toBeGreaterThan(10);
await page.close();
});
});