Files
2026-07-13 13:20:22 +08:00

211 lines
9.1 KiB
TypeScript

/**
* Reusable selectors for E2E tests.
*
* Because the app currently has **no** `data-testid` attributes, we rely on
* CSS class names, Arco-Design component classes, and text-content matching.
*
* When the app adds `data-testid` later, update these selectors in one place.
*/
// ── Generic ──────────────────────────────────────────────────────────────────
/** Chat text input (textarea / contenteditable / textbox). */
export const CHAT_INPUT = 'textarea, [contenteditable="true"], [role="textbox"]';
// ── Settings sidebar (route-based page) ──────────────────────────────────────
export const SETTINGS_SIDER = '.settings-sider';
export const SETTINGS_SIDER_ITEM = '.settings-sider__item';
export const SETTINGS_SIDER_ITEM_LABEL = '.settings-sider__item-label';
/** Match a settings sider item by logical tab ID (builtin/extension global id). */
export function settingsSiderItemById(id: string): string {
return `${SETTINGS_SIDER_ITEM}[data-settings-id="${id}"]`;
}
// ── Settings modal ───────────────────────────────────────────────────────────
export const SETTINGS_MODAL = '.settings-modal';
// ── Arco Design components ───────────────────────────────────────────────────
export const ARCO_SWITCH = '.arco-switch';
export const ARCO_SWITCH_CHECKED = '.arco-switch-checked';
export const ARCO_COLLAPSE_ITEM = '.arco-collapse-item';
export const ARCO_COLLAPSE_HEADER = '.arco-collapse-item-header';
export const ARCO_TABS_HEADER_TITLE = '.arco-tabs-header-title';
export const ARCO_MESSAGE_SUCCESS = '.arco-message-success';
// ── Guid page ───────────────────────────────────────────────────────────────
/** Guid page chat input textarea. */
export const GUID_INPUT = '.guid-input-card-shell textarea';
// ── Mode selector ──────────────────────────────────────────────────────────
/** Mode selector pill (AgentModeSelector compact mode). */
export const MODE_SELECTOR = '[data-testid="mode-selector"]';
/** Match mode dropdown menu item by mode value. */
export function modeMenuItemByValue(value: string): string {
return `[data-mode-value="${value}"]`;
}
// ── Conversation page ───────────────────────────────────────────────────────
/** Agent status message badge (connecting / session_active / error). */
export const AGENT_STATUS_MESSAGE = '.agent-status-message';
/** AI (left-aligned) text message container. */
export const AI_TEXT_MESSAGE = '[data-testid="message-text-left"]';
/** User (right-aligned) text message container. */
export const USER_TEXT_MESSAGE = '[data-testid="message-text-right"]';
/** Text content element inside a message (works for both user/AI). */
export const MESSAGE_TEXT_CONTENT = '[data-testid="message-text-content"]';
// ── Sidebar ─────────────────────────────────────────────────────────────────
/** New chat trigger button in sidebar (CSS module hash varies). */
export const NEW_CHAT_TRIGGER = 'div[class*="newChatTrigger"]';
// ── Agent pill bar ───────────────────────────────────────────────────────────
/** Match an agent logo by its alt text (e.g. "claude logo"). */
export function agentLogoByBackend(backend: string): string {
return `img[alt="${backend} logo"]`;
}
/** Stable selector for all agent pills on guid page. */
export const AGENT_PILL = '[data-agent-pill="true"]';
/**
* Match an agent pill by backend (claude/gemini/...).
*
* The guid pill bar used to expose `data-agent-backend`, but after the ACP
* unification it now consistently exposes `data-testid="agent-pill-${backend}"`
* and `data-agent-key`. Keep the old selector path as a fallback so older
* packaged builds remain testable.
*/
export function agentPillByBackend(backend: string): string {
return [
`[data-testid="agent-pill-${backend}"]`,
`${AGENT_PILL}[data-agent-key="${backend}"]`,
`${AGENT_PILL}[data-agent-backend="${backend}"]`,
].join(', ');
}
/** Match currently selected agent pill. */
export const AGENT_PILL_SELECTED = `${AGENT_PILL}[data-agent-selected="true"]`;
/** Model selector button on the guid page. */
export const MODEL_SELECTOR_BTN = 'button.sendbox-model-btn.guid-config-btn';
// ── Channel list ─────────────────────────────────────────────────────────────
export const CHANNEL_IDS = ['telegram', 'lark', 'dingtalk', 'slack', 'discord'] as const;
export type ChannelId = (typeof CHANNEL_IDS)[number];
/** Match a channel row by channel id. */
export function channelItemById(id: string): string {
return `[data-channel-id="${id}"]`;
}
/** Match a channel switch by channel id. */
export function channelSwitchById(id: string): string {
return `[data-channel-switch-for="${id}"]`;
}
/** Match WebUI page tabs by key (`webui` / `channels`). */
export function webuiTabByKey(key: 'webui' | 'channels'): string {
return `[data-webui-tab="${key}"]`;
}
// ── Assistant Settings ──────────────────────────────────────────────────────
/** Assistant card by ID. */
export function assistantCardById(id: string): string {
return `[data-testid="assistant-card-${id}"]`;
}
/** Assistant enabled switch by ID. */
export function assistantSwitchById(id: string): string {
return `[data-testid="switch-enabled-${id}"]`;
}
/** Preset assistant pill by ID on guid page. */
export function presetPillById(id: string): string {
return `[data-testid="preset-pill-${id}"]`;
}
/** Overflow assistant pill by ID on guid page. */
export function assistantOverflowPillById(id: string): string {
return `[data-testid="assistant-overflow-${id}"]`;
}
/** Stable selector for all assistant pills on guid page. */
export const ASSISTANT_PILL = '[data-testid^="preset-pill-"], [data-testid^="assistant-overflow-"]';
/** Match currently selected assistant pill. */
export const ASSISTANT_PILL_SELECTED =
'[data-testid^="preset-pill-"][data-assistant-selected="true"], [data-testid^="assistant-overflow-"][data-assistant-selected="true"]';
/** Assistant editor surface: full-page editor or legacy drawer wrapper. */
export const ASSISTANT_EDITOR_SURFACE = '[data-testid="assistant-editor-page"], [data-testid="assistant-edit-drawer"]';
/** Create assistant button. */
export const BTN_CREATE_ASSISTANT = '[data-testid="btn-create-assistant"]';
/** Save assistant button. */
export const BTN_SAVE_ASSISTANT = '[data-testid="btn-save-assistant"]';
/** Delete assistant button. */
export const BTN_DELETE_ASSISTANT = '[data-testid="btn-delete-assistant"]';
/** Skills section in the assistant editor. */
export const SKILLS_SECTION = '[data-testid="skills-section"]';
/** Skills indicator on conversation page. */
export const SKILLS_INDICATOR = '[data-testid="skills-indicator"]';
/** Skills indicator count. */
export const SKILLS_INDICATOR_COUNT = '[data-testid="skills-indicator-count"]';
/** Agent badge on conversation page. */
export const AGENT_BADGE = '[data-testid="agent-badge"]';
/** Search toggle button. */
export const BTN_SEARCH_ASSISTANT = '[data-testid="btn-search-toggle"]';
/** Search input field. */
export const INPUT_SEARCH_ASSISTANT = '[data-testid="input-search-assistant"]';
/** Match the duplicate button for an assistant. */
export function assistantDuplicateById(id: string): string {
return `[data-testid="btn-duplicate-${id}"]`;
}
/** Match the edit button for an assistant. */
export function assistantEditById(id: string): string {
return `[data-testid="btn-edit-${id}"]`;
}
/** Name input in the assistant editor. */
export const INPUT_ASSISTANT_NAME = '[data-testid="input-assistant-name"]';
/** Description input in the assistant editor. */
export const INPUT_ASSISTANT_DESC = '[data-testid="input-assistant-desc"]';
/** Main Agent select in the assistant editor. */
export const SELECT_ASSISTANT_AGENT = '[data-testid="select-assistant-agent"]';
/** Add Skills button in the assistant editor. */
export const BTN_ADD_SKILLS = '[data-testid="btn-add-skills"]';
/** Skills collapse container. */
export const SKILLS_COLLAPSE = '[data-testid="skills-collapse"]';
/** Confirm delete button inside modal. */
export const BTN_CONFIRM_DELETE = '.delete-assistant-modal .arco-btn-status-danger';