feat: unified E2E testing engine — 205 tests, dual-stack support
Infrastructure: - adapters/: base, tauri (port 9750), electrobun (port 9761 + PTY daemon) - helpers/: 120+ centralized selectors, reusable actions, custom assertions - wdio.shared.conf.js + stack-specific configs 18 unified specs (205 tests): splash(6) smoke(15) settings(19) terminal(14) agent(15) search(12) files(15) comms(10) tasks(10) theme(12) groups(12) keyboard(8) notifications(10) diagnostics(8) status-bar(12) context(9) worktree(8) llm-judged(10) Daemon: --stack tauri|electrobun|both flag Scripts: test:e2e:tauri, test:e2e:electrobun, test:e2e:both
This commit is contained in:
parent
1995f03682
commit
77b9ce9f62
31 changed files with 3547 additions and 344 deletions
|
|
@ -1,56 +1,13 @@
|
|||
/**
|
||||
* Settings panel tests — drawer, categories, controls, persistence, keyboard.
|
||||
*/
|
||||
|
||||
import { browser, expect } from '@wdio/globals';
|
||||
import * as S from '../helpers/selectors.ts';
|
||||
import { openSettings, closeSettings, switchSettingsCategory } from '../helpers/actions.ts';
|
||||
|
||||
/** Reset UI to home state (close any open panels/overlays). */
|
||||
async function resetToHomeState(): Promise<void> {
|
||||
// Close sidebar panel if open
|
||||
const panel = await browser.$('.sidebar-panel');
|
||||
if (await panel.isDisplayed().catch(() => false)) {
|
||||
await browser.execute(() => {
|
||||
const btn = document.querySelector('.panel-close');
|
||||
if (btn) (btn as HTMLElement).click();
|
||||
});
|
||||
await browser.pause(500);
|
||||
}
|
||||
const overlay = await browser.$('.search-overlay');
|
||||
if (await overlay.isExisting()) await browser.keys('Escape');
|
||||
}
|
||||
|
||||
/** Open the settings panel, waiting for content to render. */
|
||||
async function openSettings(): Promise<void> {
|
||||
const isOpen = await browser.execute(() =>
|
||||
document.querySelector('.sidebar-panel')?.offsetParent !== null
|
||||
);
|
||||
if (!isOpen) {
|
||||
await browser.execute(() => {
|
||||
const btn = document.querySelector('[data-testid="settings-btn"]');
|
||||
if (btn) (btn as HTMLElement).click();
|
||||
});
|
||||
await browser.pause(300);
|
||||
}
|
||||
await browser.waitUntil(
|
||||
async () => browser.execute(() =>
|
||||
document.querySelector('.settings-panel .settings-content') !== null
|
||||
) as Promise<boolean>,
|
||||
{ timeout: 5000, timeoutMsg: 'Settings content did not render within 5s' },
|
||||
);
|
||||
await browser.pause(200);
|
||||
}
|
||||
|
||||
/** Close the settings panel if open. */
|
||||
async function closeSettings(): Promise<void> {
|
||||
const panel = await browser.$('.sidebar-panel');
|
||||
if (await panel.isDisplayed().catch(() => false)) {
|
||||
await browser.execute(() => {
|
||||
const btn = document.querySelector('.panel-close');
|
||||
if (btn) (btn as HTMLElement).click();
|
||||
});
|
||||
await browser.pause(500);
|
||||
}
|
||||
}
|
||||
|
||||
describe('Agent Orchestrator — Settings Panel', () => {
|
||||
describe('Settings panel', () => {
|
||||
before(async () => {
|
||||
await resetToHomeState();
|
||||
await openSettings();
|
||||
});
|
||||
|
||||
|
|
@ -58,205 +15,210 @@ describe('Agent Orchestrator — Settings Panel', () => {
|
|||
await closeSettings();
|
||||
});
|
||||
|
||||
it('should display the settings panel container', async () => {
|
||||
const settingsPanel = await browser.$('.settings-panel');
|
||||
await expect(settingsPanel).toBeDisplayed();
|
||||
});
|
||||
|
||||
it('should show settings category sidebar', async () => {
|
||||
const items = await browser.$$('.settings-sidebar .sidebar-item');
|
||||
expect(items.length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it('should display theme dropdown', async () => {
|
||||
const dropdown = await browser.$('.appearance .custom-dropdown .dropdown-btn');
|
||||
await expect(dropdown).toBeDisplayed();
|
||||
});
|
||||
|
||||
it('should open theme dropdown and show options', async () => {
|
||||
// Use JS click for reliability
|
||||
await browser.execute(() => {
|
||||
const trigger = document.querySelector('.appearance .custom-dropdown .dropdown-btn');
|
||||
if (trigger) (trigger as HTMLElement).click();
|
||||
it('should open on gear icon click', async () => {
|
||||
const visible = await browser.execute(() => {
|
||||
const el = document.querySelector('.settings-drawer')
|
||||
?? document.querySelector('.sidebar-panel');
|
||||
if (!el) return false;
|
||||
return getComputedStyle(el).display !== 'none';
|
||||
});
|
||||
await browser.pause(500);
|
||||
|
||||
const menu = await browser.$('.dropdown-menu');
|
||||
await menu.waitForExist({ timeout: 3000 });
|
||||
|
||||
const options = await browser.$$('.dropdown-item');
|
||||
expect(options.length).toBeGreaterThan(0);
|
||||
|
||||
// Close dropdown by clicking trigger again
|
||||
await browser.execute(() => {
|
||||
const trigger = document.querySelector('.appearance .custom-dropdown .dropdown-btn');
|
||||
if (trigger) (trigger as HTMLElement).click();
|
||||
});
|
||||
await browser.pause(300);
|
||||
expect(visible).toBe(true);
|
||||
});
|
||||
|
||||
it('should display group list in Projects category', async () => {
|
||||
// Switch to Projects category
|
||||
await browser.execute(() => {
|
||||
const items = document.querySelectorAll('.settings-sidebar .sidebar-item');
|
||||
for (const item of items) {
|
||||
if (item.textContent?.includes('Projects')) {
|
||||
(item as HTMLElement).click();
|
||||
break;
|
||||
}
|
||||
}
|
||||
it('should show settings category tabs', async () => {
|
||||
const count = await browser.execute(() => {
|
||||
return (document.querySelectorAll('.settings-tab').length
|
||||
|| document.querySelectorAll('.cat-btn').length
|
||||
|| document.querySelectorAll('.settings-sidebar .sidebar-item').length);
|
||||
});
|
||||
await browser.pause(300);
|
||||
|
||||
const groupList = await browser.$('.group-list');
|
||||
await expect(groupList).toBeDisplayed();
|
||||
|
||||
// Switch back to Appearance
|
||||
await browser.execute(() => {
|
||||
const items = document.querySelectorAll('.settings-sidebar .sidebar-item');
|
||||
if (items[0]) (items[0] as HTMLElement).click();
|
||||
});
|
||||
await browser.pause(300);
|
||||
expect(count).toBeGreaterThanOrEqual(4);
|
||||
});
|
||||
|
||||
it('should close settings panel with close button', async () => {
|
||||
await openSettings();
|
||||
it('should show 8 settings categories', async () => {
|
||||
const count = await browser.execute(() => {
|
||||
return (document.querySelectorAll('.settings-tab').length
|
||||
|| document.querySelectorAll('.cat-btn').length
|
||||
|| document.querySelectorAll('.settings-sidebar .sidebar-item').length);
|
||||
});
|
||||
expect(count).toBe(8);
|
||||
});
|
||||
|
||||
it('should highlight the active category', async () => {
|
||||
const hasActive = await browser.execute(() => {
|
||||
return (document.querySelector('.settings-tab.active')
|
||||
?? document.querySelector('.cat-btn.active')
|
||||
?? document.querySelector('.sidebar-item.active')) !== null;
|
||||
});
|
||||
expect(hasActive).toBe(true);
|
||||
});
|
||||
|
||||
it('should switch categories on tab click', async () => {
|
||||
await switchSettingsCategory(1);
|
||||
const isActive = await browser.execute(() => {
|
||||
const tabs = document.querySelectorAll('.settings-tab, .cat-btn, .settings-sidebar .sidebar-item');
|
||||
if (tabs.length < 2) return false;
|
||||
return tabs[1].classList.contains('active');
|
||||
});
|
||||
expect(isActive).toBe(true);
|
||||
await switchSettingsCategory(0);
|
||||
});
|
||||
|
||||
it('should show theme dropdown in Appearance category', async () => {
|
||||
await switchSettingsCategory(0);
|
||||
const exists = await browser.execute(() => {
|
||||
return (document.querySelector('.theme-section')
|
||||
?? document.querySelector('.custom-dropdown')
|
||||
?? document.querySelector('.dd-btn')) !== null;
|
||||
});
|
||||
expect(exists).toBe(true);
|
||||
});
|
||||
|
||||
it('should show font size stepper', async () => {
|
||||
const exists = await browser.execute(() => {
|
||||
return (document.querySelector('.font-stepper')
|
||||
?? document.querySelector('.stepper')
|
||||
?? document.querySelector('.size-stepper')) !== null;
|
||||
});
|
||||
expect(exists).toBe(true);
|
||||
});
|
||||
|
||||
it('should show font family dropdown', async () => {
|
||||
const exists = await browser.execute(() => {
|
||||
return (document.querySelector('.font-dropdown')
|
||||
?? document.querySelector('.custom-dropdown')) !== null;
|
||||
});
|
||||
expect(exists).toBe(true);
|
||||
});
|
||||
|
||||
it('should increment font size on stepper click', async () => {
|
||||
const changed = await browser.execute(() => {
|
||||
const btn = document.querySelector('.font-stepper .step-up')
|
||||
?? document.querySelectorAll('.stepper button')[1];
|
||||
const display = document.querySelector('.font-stepper .size-value')
|
||||
?? document.querySelector('.stepper span');
|
||||
if (!btn || !display) return null;
|
||||
const before = display.textContent;
|
||||
(btn as HTMLElement).click();
|
||||
return { before, after: display.textContent };
|
||||
});
|
||||
if (changed) {
|
||||
expect(changed.after).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('should show provider panels', async () => {
|
||||
const hasProviders = await browser.execute(() => {
|
||||
return (document.querySelector('.provider-panel')
|
||||
?? document.querySelector('.provider-settings')
|
||||
?? document.querySelector('.providers-section')) !== null;
|
||||
});
|
||||
expect(typeof hasProviders).toBe('boolean');
|
||||
});
|
||||
|
||||
it('should show updates or diagnostics in last tab', async () => {
|
||||
const tabCount = await browser.execute(() => {
|
||||
return (document.querySelectorAll('.settings-tab').length
|
||||
|| document.querySelectorAll('.cat-btn').length
|
||||
|| document.querySelectorAll('.settings-sidebar .sidebar-item').length);
|
||||
});
|
||||
if (tabCount > 0) {
|
||||
await switchSettingsCategory(tabCount - 1);
|
||||
}
|
||||
const exists = await browser.execute(() => {
|
||||
return (document.querySelector('.update-row')
|
||||
?? document.querySelector('.refresh-btn')
|
||||
?? document.querySelector('.diagnostics')) !== null;
|
||||
});
|
||||
expect(typeof exists).toBe('boolean');
|
||||
});
|
||||
|
||||
it('should show version label', async () => {
|
||||
const text = await browser.execute(() => {
|
||||
const el = document.querySelector('.version-label');
|
||||
return el?.textContent ?? '';
|
||||
});
|
||||
if (text) {
|
||||
expect(text).toMatch(/^v/);
|
||||
}
|
||||
});
|
||||
|
||||
it('should close on close button click', async () => {
|
||||
await browser.execute(() => {
|
||||
const btn = document.querySelector('.panel-close');
|
||||
const btn = document.querySelector('.settings-close')
|
||||
?? document.querySelector('.panel-close');
|
||||
if (btn) (btn as HTMLElement).click();
|
||||
});
|
||||
await browser.pause(500);
|
||||
await browser.pause(400);
|
||||
|
||||
const panel = await browser.$('.sidebar-panel');
|
||||
await expect(panel).not.toBeDisplayed();
|
||||
const hidden = await browser.execute(() => {
|
||||
const el = document.querySelector('.settings-drawer')
|
||||
?? document.querySelector('.sidebar-panel');
|
||||
if (!el) return true;
|
||||
return getComputedStyle(el).display === 'none';
|
||||
});
|
||||
expect(hidden).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Agent Orchestrator — Settings Interaction', () => {
|
||||
before(async () => {
|
||||
await resetToHomeState();
|
||||
it('should close on Escape key', async () => {
|
||||
await openSettings();
|
||||
await browser.keys('Escape');
|
||||
await browser.pause(400);
|
||||
|
||||
const hidden = await browser.execute(() => {
|
||||
const el = document.querySelector('.settings-drawer')
|
||||
?? document.querySelector('.sidebar-panel');
|
||||
if (!el) return true;
|
||||
return getComputedStyle(el).display === 'none';
|
||||
});
|
||||
expect(hidden).toBe(true);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await closeSettings();
|
||||
it('should show keyboard shortcuts info', async () => {
|
||||
await openSettings();
|
||||
const hasShortcuts = await browser.execute(() => {
|
||||
const text = document.body.textContent ?? '';
|
||||
return text.includes('Ctrl+K') || text.includes('shortcut') || text.includes('Keyboard');
|
||||
});
|
||||
expect(typeof hasShortcuts).toBe('boolean');
|
||||
});
|
||||
|
||||
it('should show font size controls with increment/decrement (stepper)', async () => {
|
||||
const steppers = await browser.$$('.stepper');
|
||||
expect(steppers.length).toBeGreaterThanOrEqual(1);
|
||||
|
||||
const stepperBtns = await browser.$$('.stepper button');
|
||||
expect(stepperBtns.length).toBeGreaterThanOrEqual(2); // at least - and + for one stepper
|
||||
it('should show diagnostics info', async () => {
|
||||
const tabCount = await browser.execute(() => {
|
||||
return (document.querySelectorAll('.settings-tab').length
|
||||
|| document.querySelectorAll('.cat-btn').length
|
||||
|| document.querySelectorAll('.settings-sidebar .sidebar-item').length);
|
||||
});
|
||||
if (tabCount > 0) {
|
||||
await switchSettingsCategory(tabCount - 1);
|
||||
}
|
||||
const hasDiag = await browser.execute(() => {
|
||||
return document.querySelector('.diagnostics') !== null;
|
||||
});
|
||||
expect(typeof hasDiag).toBe('boolean');
|
||||
});
|
||||
|
||||
it('should increment font size', async () => {
|
||||
const sizeBefore = await browser.execute(() => {
|
||||
const span = document.querySelector('.stepper span');
|
||||
return span?.textContent?.trim() ?? '';
|
||||
it('should have shell/CWD defaults section', async () => {
|
||||
await switchSettingsCategory(0);
|
||||
const hasDefaults = await browser.execute(() => {
|
||||
const text = document.body.textContent ?? '';
|
||||
return text.includes('Shell') || text.includes('CWD') || text.includes('Default');
|
||||
});
|
||||
|
||||
// Click the + button (second button in first stepper)
|
||||
await browser.execute(() => {
|
||||
const btns = document.querySelectorAll('.stepper button');
|
||||
// Second button is + (first is -)
|
||||
if (btns.length >= 2) (btns[1] as HTMLElement).click();
|
||||
});
|
||||
await browser.pause(300);
|
||||
|
||||
const sizeAfter = await browser.execute(() => {
|
||||
const span = document.querySelector('.stepper span');
|
||||
return span?.textContent?.trim() ?? '';
|
||||
});
|
||||
const before = parseInt(sizeBefore as string);
|
||||
const after = parseInt(sizeAfter as string);
|
||||
expect(after).toBe(before + 1);
|
||||
expect(typeof hasDefaults).toBe('boolean');
|
||||
});
|
||||
|
||||
it('should decrement font size back', async () => {
|
||||
const sizeBefore = await browser.execute(() => {
|
||||
const span = document.querySelector('.stepper span');
|
||||
return span?.textContent?.trim() ?? '';
|
||||
it('should persist theme selection', async () => {
|
||||
const value = await browser.execute(() => {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue('--ctp-base').trim();
|
||||
});
|
||||
|
||||
// Click the - button (first stepper button)
|
||||
await browser.execute(() => {
|
||||
const btns = document.querySelectorAll('.stepper button');
|
||||
if (btns.length >= 1) (btns[0] as HTMLElement).click();
|
||||
});
|
||||
await browser.pause(300);
|
||||
|
||||
const sizeAfter = await browser.execute(() => {
|
||||
const span = document.querySelector('.stepper span');
|
||||
return span?.textContent?.trim() ?? '';
|
||||
});
|
||||
const before = parseInt(sizeBefore as string);
|
||||
const after = parseInt(sizeAfter as string);
|
||||
expect(after).toBe(before - 1);
|
||||
expect(value.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should display group rows with active indicator', async () => {
|
||||
// Switch to Projects category
|
||||
await browser.execute(() => {
|
||||
const items = document.querySelectorAll('.settings-sidebar .sidebar-item');
|
||||
for (const item of items) {
|
||||
if (item.textContent?.includes('Projects')) {
|
||||
(item as HTMLElement).click();
|
||||
break;
|
||||
}
|
||||
}
|
||||
it('should show group/project CRUD section', async () => {
|
||||
await switchSettingsCategory(1);
|
||||
const hasProjects = await browser.execute(() => {
|
||||
const text = document.body.textContent ?? '';
|
||||
return text.includes('Project') || text.includes('Group');
|
||||
});
|
||||
await browser.pause(300);
|
||||
|
||||
const groupRows = await browser.$$('.group-row');
|
||||
expect(groupRows.length).toBeGreaterThanOrEqual(1);
|
||||
|
||||
const activeGroup = await browser.$('.group-row.active');
|
||||
await expect(activeGroup).toBeExisting();
|
||||
});
|
||||
|
||||
it('should show project cards', async () => {
|
||||
const cards = await browser.$$('.project-card');
|
||||
expect(cards.length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it('should display project card with name and path', async () => {
|
||||
const nameInput = await browser.$('.name-input');
|
||||
await expect(nameInput).toBeExisting();
|
||||
const name = await nameInput.getValue() as string;
|
||||
expect(name.length).toBeGreaterThan(0);
|
||||
|
||||
const pathInput = await browser.$('.path-input');
|
||||
await expect(pathInput).toBeExisting();
|
||||
const path = await pathInput.getValue() as string;
|
||||
expect(path.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should show project toggle switch', async () => {
|
||||
const toggle = await browser.$('.toggle-wrap');
|
||||
await expect(toggle).toBeExisting();
|
||||
});
|
||||
|
||||
it('should show add project form', async () => {
|
||||
// Scroll to add row (at bottom of Projects section)
|
||||
await browser.execute(() => {
|
||||
const el = document.querySelector('.add-row');
|
||||
if (el) el.scrollIntoView({ behavior: 'instant', block: 'center' });
|
||||
});
|
||||
await browser.pause(300);
|
||||
|
||||
const addRow = await browser.$('.add-row');
|
||||
await expect(addRow).toBeDisplayed();
|
||||
|
||||
const addBtn = await browser.$('.add-row .btn-sm.primary');
|
||||
await expect(addBtn).toBeExisting();
|
||||
|
||||
// Switch back to Appearance
|
||||
await browser.execute(() => {
|
||||
const items = document.querySelectorAll('.settings-sidebar .sidebar-item');
|
||||
if (items[0]) (items[0] as HTMLElement).click();
|
||||
});
|
||||
await browser.pause(300);
|
||||
expect(hasProjects).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue