feat: initial LexAI Chrome extension scaffold
Some checks failed
CI — Test & Build / Unit Tests (push) Failing after 54s
CI — Test & Build / Build Extension (push) Has been skipped

- WXT + React 18 + TypeScript setup
- Background service worker (LLM API proxy)
- Content script (floating toolbar + text selection)
- Options page (provider/model/API key config)
- Popup UI
- Gitea Actions workflows (CI, preview, release)
- Vitest unit tests + Playwright E2E setup
- Supports: OpenAI, Anthropic, Groq, OpenRouter
This commit is contained in:
Nemo
2026-03-06 09:34:15 +08:00
commit b04076081c
20 changed files with 6254 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
import { test, expect } from '@playwright/test';
test('LexAI popup opens correctly', async ({ page }) => {
// Navigate to extension popup
await page.goto('chrome-extension://[EXTENSION_ID]/popup/index.html');
// Check LexAI branding is present
await expect(page.locator('text=LexAI')).toBeVisible();
// Check settings button is present
await expect(page.locator('button:has-text("Open Settings")')).toBeVisible();
});
test('Options page saves API key', async ({ page }) => {
await page.goto('chrome-extension://[EXTENSION_ID]/options/index.html');
// Fill in API key
await page.fill('input[type="password"]', 'sk-test-key');
// Select provider
await page.selectOption('select:first-of-type', 'openai');
// Save
await page.click('button:has-text("Save Settings")');
// Confirm saved
await expect(page.locator('text=Saved!')).toBeVisible();
});

View File

@@ -0,0 +1,28 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
describe('Background Service Worker', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should return error when no API key is configured', async () => {
(chrome.storage.local.get as any).mockImplementation((keys: any, cb: any) => cb({}));
// Simulate the handleAnalyzeText function behavior
const config = await new Promise<any>((resolve) =>
chrome.storage.local.get(['provider', 'apiKey', 'model'], resolve)
);
expect(config.apiKey).toBeUndefined();
});
it('should store API key correctly', async () => {
const testConfig = { provider: 'openai', apiKey: 'sk-test123', model: 'gpt-4o-mini' };
await new Promise<void>((resolve) =>
chrome.storage.local.set(testConfig, resolve)
);
expect(chrome.storage.local.set).toHaveBeenCalledWith(testConfig, expect.any(Function));
});
});

20
tests/unit/setup.ts Normal file
View File

@@ -0,0 +1,20 @@
// Mock Chrome extension APIs
global.chrome = {
storage: {
local: {
get: vi.fn((keys, cb) => cb({})),
set: vi.fn((data, cb) => cb && cb()),
},
sync: {
get: vi.fn((keys, cb) => cb({})),
set: vi.fn((data, cb) => cb && cb()),
},
},
runtime: {
sendMessage: vi.fn(),
onMessage: {
addListener: vi.fn(),
},
openOptionsPage: vi.fn(),
},
} as any;