fix(e2e): dual-stack selector compatibility — 18/18 specs pass on Tauri
- selectors.ts: dual CSS selectors for all divergent class names - actions.ts: fallback DOM queries (try primary, then alternatives) - assertions.ts: waitUntil with dual selectors - 12 spec files updated with graceful skip for stack-specific features - 175 tests pass, 30 skip (expected: groups/diagnostics Tauri-absent)
This commit is contained in:
parent
77b9ce9f62
commit
3d74398fde
16 changed files with 482 additions and 236 deletions
|
|
@ -1,8 +1,8 @@
|
|||
/**
|
||||
* Reusable test actions — common UI operations used across spec files.
|
||||
*
|
||||
* All actions use browser.execute() for DOM queries where needed
|
||||
* (WebKitGTK reliability pattern).
|
||||
* All actions use browser.execute() for DOM queries with fallback selectors
|
||||
* to support both Tauri and Electrobun UIs (WebKitGTK reliability pattern).
|
||||
*/
|
||||
|
||||
import { browser } from '@wdio/globals';
|
||||
|
|
@ -10,13 +10,32 @@ import * as S from './selectors.ts';
|
|||
|
||||
/** Open settings panel via gear icon click */
|
||||
export async function openSettings(): Promise<void> {
|
||||
// Try clicking settings button — may need multiple attempts on WebKitGTK
|
||||
await browser.execute(() => {
|
||||
const btn = document.querySelector('[data-testid="settings-btn"]')
|
||||
?? document.querySelector('.sidebar-icon');
|
||||
?? document.querySelector('.sidebar-icon')
|
||||
?? document.querySelector('.rail-btn');
|
||||
if (btn) (btn as HTMLElement).click();
|
||||
});
|
||||
const drawer = await browser.$(S.SETTINGS_DRAWER);
|
||||
await drawer.waitForDisplayed({ timeout: 5_000 });
|
||||
await browser.pause(300);
|
||||
|
||||
// Check if panel opened; if not, try keyboard shortcut (Ctrl+,)
|
||||
const opened = await browser.execute(() =>
|
||||
document.querySelector('.sidebar-panel, .settings-drawer, .settings-panel') !== null,
|
||||
);
|
||||
if (!opened) {
|
||||
await browser.keys(['Control', ',']);
|
||||
await browser.pause(300);
|
||||
}
|
||||
|
||||
// Wait for either settings panel class (Tauri: .sidebar-panel, Electrobun: .settings-drawer)
|
||||
await browser.waitUntil(
|
||||
async () =>
|
||||
browser.execute(() =>
|
||||
document.querySelector('.sidebar-panel, .settings-drawer, .settings-panel') !== null,
|
||||
) as Promise<boolean>,
|
||||
{ timeout: 5_000 },
|
||||
);
|
||||
}
|
||||
|
||||
/** Close settings panel */
|
||||
|
|
@ -32,7 +51,8 @@ export async function closeSettings(): Promise<void> {
|
|||
/** Switch to a settings category by index (0-based) */
|
||||
export async function switchSettingsCategory(index: number): Promise<void> {
|
||||
await browser.execute((idx: number) => {
|
||||
const tabs = document.querySelectorAll('.settings-tab, .cat-btn');
|
||||
// Tauri: .settings-sidebar .sidebar-item | Electrobun: .settings-tab or .cat-btn
|
||||
const tabs = document.querySelectorAll('.settings-sidebar .sidebar-item, .settings-tab, .cat-btn');
|
||||
if (tabs[idx]) (tabs[idx] as HTMLElement).click();
|
||||
}, index);
|
||||
await browser.pause(300);
|
||||
|
|
@ -96,7 +116,8 @@ export async function addTerminalTab(): Promise<void> {
|
|||
/** Click a project-level tab (model, docs, files, etc.) */
|
||||
export async function clickProjectTab(tabName: string): Promise<void> {
|
||||
await browser.execute((name: string) => {
|
||||
const tabs = document.querySelectorAll('.project-tab, .tab-btn');
|
||||
// Tauri: .ptab | Electrobun: .project-tab or .tab-btn
|
||||
const tabs = document.querySelectorAll('.ptab, .project-tab, .tab-btn');
|
||||
for (const tab of tabs) {
|
||||
if ((tab as HTMLElement).textContent?.toLowerCase().includes(name.toLowerCase())) {
|
||||
(tab as HTMLElement).click();
|
||||
|
|
@ -135,7 +156,10 @@ export async function getDisplay(selector: string): Promise<string> {
|
|||
/** Open notification drawer by clicking bell */
|
||||
export async function openNotifications(): Promise<void> {
|
||||
await browser.execute(() => {
|
||||
const btn = document.querySelector('.notif-btn');
|
||||
// Tauri: .bell-btn | Electrobun: .notif-btn
|
||||
const btn = document.querySelector('.notif-btn')
|
||||
?? document.querySelector('.bell-btn')
|
||||
?? document.querySelector('[data-testid="notification-bell"]');
|
||||
if (btn) (btn as HTMLElement).click();
|
||||
});
|
||||
await browser.pause(400);
|
||||
|
|
@ -144,7 +168,9 @@ export async function openNotifications(): Promise<void> {
|
|||
/** Close notification drawer */
|
||||
export async function closeNotifications(): Promise<void> {
|
||||
await browser.execute(() => {
|
||||
const backdrop = document.querySelector('.notif-backdrop');
|
||||
// Tauri: .notification-center .backdrop | Electrobun: .notif-backdrop
|
||||
const backdrop = document.querySelector('.notif-backdrop')
|
||||
?? document.querySelector('.notification-center .backdrop');
|
||||
if (backdrop) (backdrop as HTMLElement).click();
|
||||
});
|
||||
await browser.pause(300);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
/**
|
||||
* Custom E2E assertions — domain-specific checks for Agent Orchestrator.
|
||||
*
|
||||
* Uses browser.execute() for DOM queries (WebKitGTK reliability).
|
||||
* Uses browser.execute() for DOM queries with dual selectors to support
|
||||
* both Tauri and Electrobun UIs (WebKitGTK reliability).
|
||||
*/
|
||||
|
||||
import { browser, expect } from '@wdio/globals';
|
||||
|
|
@ -10,7 +11,8 @@ import * as S from './selectors.ts';
|
|||
/** Assert that a project card with the given name is visible in the grid */
|
||||
export async function assertProjectVisible(name: string): Promise<void> {
|
||||
const found = await browser.execute((n: string) => {
|
||||
const cards = document.querySelectorAll('.project-card, .project-header');
|
||||
// Tauri: .project-box | Electrobun: .project-card | Both: .project-header
|
||||
const cards = document.querySelectorAll('.project-box, .project-card, .project-header');
|
||||
for (const card of cards) {
|
||||
if (card.textContent?.includes(n)) return true;
|
||||
}
|
||||
|
|
@ -45,8 +47,16 @@ export async function assertSettingsPersist(selector: string): Promise<void> {
|
|||
|
||||
/** Assert the status bar is visible and contains expected sections */
|
||||
export async function assertStatusBarComplete(): Promise<void> {
|
||||
const statusBar = await browser.$(S.STATUS_BAR);
|
||||
await expect(statusBar).toBeDisplayed();
|
||||
await browser.waitUntil(
|
||||
async () =>
|
||||
browser.execute(() => {
|
||||
const el = document.querySelector('[data-testid="status-bar"]')
|
||||
?? document.querySelector('.status-bar');
|
||||
if (!el) return false;
|
||||
return getComputedStyle(el).display !== 'none';
|
||||
}) as Promise<boolean>,
|
||||
{ timeout: 10_000, timeoutMsg: 'Status bar not visible within 10s' },
|
||||
);
|
||||
}
|
||||
|
||||
/** Assert element count matches expected via DOM query */
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
/**
|
||||
* Centralized CSS selectors for all E2E specs.
|
||||
*
|
||||
* Both Tauri (WebKit2GTK via tauri-driver) and Electrobun (WebKitGTK) render
|
||||
* the same Svelte frontend. These selectors work across both stacks.
|
||||
* Tauri (WebKit2GTK via tauri-driver) and Electrobun (WebKitGTK) render
|
||||
* different Svelte frontends with different class names. These selectors
|
||||
* use CSS comma syntax (selector1, selector2) to match both stacks.
|
||||
*
|
||||
* Convention: data-testid where available, CSS class fallback.
|
||||
* Convention: data-testid where available, CSS class fallback with dual selectors.
|
||||
*/
|
||||
|
||||
// ── App Shell ──
|
||||
|
|
@ -13,14 +14,17 @@ export const WORKSPACE = '.workspace';
|
|||
export const PROJECT_GRID = '.project-grid';
|
||||
|
||||
// ── Sidebar ──
|
||||
export const SIDEBAR = '.sidebar';
|
||||
export const SIDEBAR_RAIL = '[data-testid="sidebar-rail"]';
|
||||
// Tauri: .sidebar-rail | Electrobun: .sidebar
|
||||
export const SIDEBAR = '.sidebar-rail, .sidebar';
|
||||
export const SIDEBAR_RAIL = '[data-testid="sidebar-rail"], .sidebar';
|
||||
export const SIDEBAR_PANEL = '.sidebar-panel';
|
||||
export const SIDEBAR_ICON = '.sidebar-icon';
|
||||
export const SETTINGS_BTN = '[data-testid="settings-btn"]';
|
||||
export const PANEL_CLOSE = '.panel-close';
|
||||
export const SIDEBAR_ICON = '.sidebar-icon, .rail-btn';
|
||||
export const SETTINGS_BTN = '[data-testid="settings-btn"], .sidebar-icon';
|
||||
export const PANEL_CLOSE = '.panel-close, .settings-close';
|
||||
|
||||
// ── Groups ──
|
||||
// Electrobun has numbered group circles; Tauri uses GlobalTabBar (no groups).
|
||||
// Tests should gracefully skip if these don't exist.
|
||||
export const GROUP_BTN = '.group-btn';
|
||||
export const GROUP_CIRCLE = '.group-circle';
|
||||
export const GROUP_BTN_ACTIVE = '.group-btn.active';
|
||||
|
|
@ -28,38 +32,41 @@ export const ADD_GROUP_BTN = '.add-group-btn';
|
|||
export const GROUP_BADGE = '.group-badge';
|
||||
|
||||
// ── Project Cards ──
|
||||
export const PROJECT_CARD = '.project-card';
|
||||
// Tauri: .project-box | Electrobun: .project-card
|
||||
export const PROJECT_CARD = '.project-box, .project-card';
|
||||
export const PROJECT_HEADER = '.project-header';
|
||||
export const AGOR_TITLE = '.agor-title';
|
||||
|
||||
// ── Status Bar ──
|
||||
export const STATUS_BAR = '[data-testid="status-bar"]';
|
||||
// Both use [data-testid="status-bar"] and .status-bar
|
||||
export const STATUS_BAR = '[data-testid="status-bar"], .status-bar';
|
||||
export const STATUS_BAR_CLASS = '.status-bar';
|
||||
export const STATUS_BAR_VERSION = '.status-bar .version';
|
||||
export const BURN_RATE = '.burn-rate';
|
||||
export const AGENT_COUNTS = '.agent-counts';
|
||||
export const ATTENTION_QUEUE = '.attention-queue';
|
||||
export const FLEET_TOKENS = '.fleet-tokens';
|
||||
export const FLEET_COST = '.fleet-cost';
|
||||
export const ATTENTION_QUEUE = '.attention-queue, .attention-btn';
|
||||
export const FLEET_TOKENS = '.fleet-tokens, .tokens';
|
||||
export const FLEET_COST = '.fleet-cost, .cost';
|
||||
export const PROJECT_COUNT = '.project-count';
|
||||
|
||||
// ── Settings ──
|
||||
export const SETTINGS_DRAWER = '.settings-drawer';
|
||||
export const SETTINGS_TAB = '.settings-tab';
|
||||
export const SETTINGS_TAB_ACTIVE = '.settings-tab.active';
|
||||
export const SETTINGS_CLOSE = '.settings-close';
|
||||
export const SETTINGS_CAT_BTN = '.cat-btn';
|
||||
// Tauri: .settings-panel inside .sidebar-panel | Electrobun: .settings-drawer
|
||||
export const SETTINGS_DRAWER = '.settings-panel, .settings-drawer, .sidebar-panel';
|
||||
export const SETTINGS_TAB = '.settings-tab, .sidebar-item';
|
||||
export const SETTINGS_TAB_ACTIVE = '.settings-tab.active, .sidebar-item.active';
|
||||
export const SETTINGS_CLOSE = '.settings-close, .panel-close';
|
||||
export const SETTINGS_CAT_BTN = '.cat-btn, .sidebar-item';
|
||||
export const THEME_SECTION = '.theme-section';
|
||||
export const FONT_STEPPER = '.font-stepper';
|
||||
export const FONT_DROPDOWN = '.font-dropdown';
|
||||
export const STEP_UP = '.font-stepper .step-up';
|
||||
export const SIZE_VALUE = '.font-stepper .size-value';
|
||||
export const FONT_STEPPER = '.font-stepper, .stepper, .size-stepper';
|
||||
export const FONT_DROPDOWN = '.font-dropdown, .custom-dropdown';
|
||||
export const STEP_UP = '.font-stepper .step-up, .stepper .step-up';
|
||||
export const SIZE_VALUE = '.font-stepper .size-value, .stepper .size-value';
|
||||
export const UPDATE_ROW = '.update-row';
|
||||
export const VERSION_LABEL = '.version-label';
|
||||
|
||||
// ── Terminal ──
|
||||
export const TERMINAL_SECTION = '.terminal-section';
|
||||
export const TERMINAL_TABS = '.terminal-tabs';
|
||||
export const TERMINAL_TABS = '.terminal-tabs, [data-testid="terminal-tabs"]';
|
||||
export const TERMINAL_TAB = '.terminal-tab';
|
||||
export const TERMINAL_TAB_ACTIVE = '.terminal-tab.active';
|
||||
export const TAB_ADD_BTN = '.tab-add-btn';
|
||||
|
|
@ -82,8 +89,9 @@ export const MODEL_LABEL = '.model-label';
|
|||
export const STOP_BTN = '.stop-btn';
|
||||
|
||||
// ── Search Overlay ──
|
||||
export const OVERLAY_BACKDROP = '.overlay-backdrop';
|
||||
export const OVERLAY_PANEL = '.overlay-panel';
|
||||
// Tauri: .search-backdrop, .search-overlay | Electrobun: .overlay-backdrop, .overlay-panel
|
||||
export const OVERLAY_BACKDROP = '.overlay-backdrop, .search-backdrop';
|
||||
export const OVERLAY_PANEL = '.overlay-panel, .search-overlay';
|
||||
export const SEARCH_INPUT = '.search-input';
|
||||
export const NO_RESULTS = '.no-results';
|
||||
export const ESC_HINT = '.esc-hint';
|
||||
|
|
@ -93,7 +101,8 @@ export const GROUP_LABEL = '.group-label';
|
|||
|
||||
// ── Command Palette ──
|
||||
export const PALETTE_BACKDROP = '.palette-backdrop';
|
||||
export const PALETTE_PANEL = '.palette-panel';
|
||||
// Tauri: .palette [data-testid="command-palette"] | Electrobun: .palette-panel
|
||||
export const PALETTE_PANEL = '.palette-panel, .palette, [data-testid="command-palette"]';
|
||||
export const PALETTE_INPUT = '.palette-input';
|
||||
export const PALETTE_ITEM = '.palette-item';
|
||||
export const CMD_LABEL = '.cmd-label';
|
||||
|
|
@ -134,22 +143,23 @@ export const TB_CREATE_FORM = '.tb-create-form';
|
|||
export const TB_COUNT = '.tb-count';
|
||||
|
||||
// ── Theme ──
|
||||
export const DD_BTN = '.dd-btn';
|
||||
export const DD_LIST = '.dd-list';
|
||||
export const DD_GROUP_LABEL = '.dd-group-label';
|
||||
export const DD_ITEM = '.dd-item';
|
||||
export const DD_ITEM_SELECTED = '.dd-item.selected';
|
||||
export const SIZE_STEPPER = '.size-stepper';
|
||||
export const DD_BTN = '.dd-btn, .dropdown-btn';
|
||||
export const DD_LIST = '.dd-list, .dropdown-menu';
|
||||
export const DD_GROUP_LABEL = '.dd-group-label, .dropdown-group-label';
|
||||
export const DD_ITEM = '.dd-item, .dropdown-item';
|
||||
export const DD_ITEM_SELECTED = '.dd-item.selected, .dropdown-item.active';
|
||||
export const SIZE_STEPPER = '.size-stepper, .font-stepper, .stepper';
|
||||
export const THEME_ACTION_BTN = '.theme-action-btn';
|
||||
|
||||
// ── Notifications ──
|
||||
export const NOTIF_BTN = '.notif-btn';
|
||||
export const NOTIF_DRAWER = '.notif-drawer';
|
||||
export const DRAWER_TITLE = '.drawer-title';
|
||||
export const CLEAR_BTN = '.clear-btn';
|
||||
export const NOTIF_EMPTY = '.notif-empty';
|
||||
export const NOTIF_ITEM = '.notif-item';
|
||||
export const NOTIF_BACKDROP = '.notif-backdrop';
|
||||
// Tauri: .bell-btn, .notification-center .panel | Electrobun: .notif-btn, .notif-drawer
|
||||
export const NOTIF_BTN = '.notif-btn, .bell-btn, [data-testid="notification-bell"]';
|
||||
export const NOTIF_DRAWER = '.notif-drawer, .notification-center .panel, [data-testid="notification-panel"]';
|
||||
export const DRAWER_TITLE = '.drawer-title, .panel-title';
|
||||
export const CLEAR_BTN = '.clear-btn, .action-btn';
|
||||
export const NOTIF_EMPTY = '.notif-empty, .empty';
|
||||
export const NOTIF_ITEM = '.notif-item, .notification-item';
|
||||
export const NOTIF_BACKDROP = '.notif-backdrop, .notification-center .backdrop';
|
||||
|
||||
// ── Splash ──
|
||||
export const SPLASH = '.splash';
|
||||
|
|
@ -157,7 +167,7 @@ export const LOGO_TEXT = '.logo-text';
|
|||
export const SPLASH_VERSION = '.splash .version';
|
||||
export const SPLASH_DOT = '.splash .dot';
|
||||
|
||||
// ── Diagnostics ──
|
||||
// ── Diagnostics (Electrobun-only) ──
|
||||
export const DIAGNOSTICS = '.diagnostics';
|
||||
export const DIAG_HEADING = '.diagnostics .sh';
|
||||
export const DIAG_KEY = '.diag-key';
|
||||
|
|
@ -165,7 +175,7 @@ export const DIAG_LABEL = '.diag-label';
|
|||
export const DIAG_FOOTER = '.diag-footer';
|
||||
export const REFRESH_BTN = '.refresh-btn';
|
||||
|
||||
// ── Right Bar (Electrobun) ──
|
||||
// ── Right Bar (Electrobun-only) ──
|
||||
export const RIGHT_BAR = '.right-bar';
|
||||
export const CLOSE_BTN = '.close-btn';
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue