/** * Terminal tests — tab bar, tab CRUD, PTY I/O, collapse/expand, resize. */ import { browser, expect } from '@wdio/globals'; import * as S from '../helpers/selectors.ts'; import { addTerminalTab } from '../helpers/actions.ts'; describe('Terminal section', () => { it('should show the terminal tab bar', async () => { const exists = await browser.execute((sel: string) => { return document.querySelector(sel) !== null; }, S.TERMINAL_TABS); if (exists) { const el = await browser.$(S.TERMINAL_TABS); await expect(el).toBeDisplayed(); } }); it('should have an add-tab button', async () => { const addBtn = await browser.$(S.TAB_ADD_BTN); if (await addBtn.isExisting()) { expect(await addBtn.isClickable()).toBe(true); } }); it('should create a new terminal tab on add click', async function () { // Terminal may be collapsed by default — expand first const hasAddBtn = await browser.execute(() => { const btn = document.querySelector('.tab-add-btn'); if (!btn) return false; return getComputedStyle(btn).display !== 'none'; }); if (!hasAddBtn) { this.skip(); return; } const countBefore = await browser.execute((sel: string) => { return document.querySelectorAll(sel).length; }, S.TERMINAL_TAB); await addTerminalTab(); const countAfter = await browser.execute((sel: string) => { return document.querySelectorAll(sel).length; }, S.TERMINAL_TAB); expect(countAfter).toBeGreaterThanOrEqual(countBefore + 1); }); it('should show an xterm container', async () => { const xterm = await browser.$(S.XTERM); if (await xterm.isExisting()) { await expect(xterm).toBeDisplayed(); } }); it('should accept keyboard input in terminal', async () => { const textarea = await browser.$(S.XTERM_TEXTAREA); if (await textarea.isExisting()) { await textarea.click(); await browser.keys('echo hello'); // Verify no crash — actual output requires PTY daemon } }); it('should highlight active tab', async () => { const count = await browser.execute((sel: string) => { return document.querySelectorAll(sel).length; }, S.TERMINAL_TAB_ACTIVE); if (count > 0) { expect(count).toBe(1); } }); it('should switch tabs on click', async () => { const tabCount = await browser.execute((sel: string) => { return document.querySelectorAll(sel).length; }, S.TERMINAL_TAB); if (tabCount >= 2) { await browser.execute((sel: string) => { const tabs = document.querySelectorAll(sel); if (tabs[1]) (tabs[1] as HTMLElement).click(); }, S.TERMINAL_TAB); await browser.pause(300); const isActive = await browser.execute((sel: string) => { const tabs = document.querySelectorAll(sel); return tabs[1]?.classList.contains('active') ?? false; }, S.TERMINAL_TAB); expect(isActive).toBe(true); } }); it('should show close button on tab hover', async () => { const tabs = await browser.$$(S.TERMINAL_TAB); if (tabs.length === 0) return; await tabs[0].moveTo(); await browser.pause(200); const closeBtn = await tabs[0].$(S.TAB_CLOSE); if (await closeBtn.isExisting()) { await expect(closeBtn).toBeDisplayed(); } }); it('should close a tab on close button click', async () => { const countBefore = await browser.execute((sel: string) => { return document.querySelectorAll(sel).length; }, S.TERMINAL_TAB); if (countBefore < 2) return; const tabs = await browser.$$(S.TERMINAL_TAB); const lastTab = tabs[tabs.length - 1]; await lastTab.moveTo(); await browser.pause(200); const closeBtn = await lastTab.$(S.TAB_CLOSE); if (await closeBtn.isExisting()) { await closeBtn.click(); await browser.pause(300); const countAfter = await browser.execute((sel: string) => { return document.querySelectorAll(sel).length; }, S.TERMINAL_TAB); expect(countAfter).toBeLessThan(countBefore); } }); it('should support collapse/expand toggle', async () => { const collapseBtn = await browser.$(S.TERMINAL_COLLAPSE_BTN); if (!(await collapseBtn.isExisting())) return; await collapseBtn.click(); await browser.pause(300); const h1 = await browser.execute((sel: string) => { const el = document.querySelector(sel); return el ? getComputedStyle(el).height : ''; }, S.TERMINAL_SECTION); await collapseBtn.click(); await browser.pause(300); const h2 = await browser.execute((sel: string) => { const el = document.querySelector(sel); return el ? getComputedStyle(el).height : ''; }, S.TERMINAL_SECTION); expect(h1).not.toBe(h2); }); it('should handle multiple terminal tabs', async function () { // Terminal may be collapsed by default — skip if add button not visible const hasAddBtn = await browser.execute(() => { const btn = document.querySelector('.tab-add-btn'); if (!btn) return false; return getComputedStyle(btn).display !== 'none'; }); if (!hasAddBtn) { this.skip(); return; } // Add two tabs await addTerminalTab(); await addTerminalTab(); const count = await browser.execute((sel: string) => { return document.querySelectorAll(sel).length; }, S.TERMINAL_TAB); expect(count).toBeGreaterThanOrEqual(2); }); it('should handle PTY output display', async () => { // Verify xterm rows container exists (for PTY output rendering) const hasRows = await browser.execute(() => { return document.querySelector('.xterm-rows') !== null; }); if (hasRows) { expect(hasRows).toBe(true); } }); it('should have terminal container with correct dimensions', async () => { const dims = await browser.execute((sel: string) => { const el = document.querySelector(sel); if (!el) return null; const rect = el.getBoundingClientRect(); return { width: rect.width, height: rect.height }; }, S.TERMINAL_SECTION); if (dims) { expect(dims.width).toBeGreaterThan(0); expect(dims.height).toBeGreaterThan(0); } }); it('should have resize handle', async () => { const hasHandle = await browser.execute(() => { return document.querySelector('.resize-handle') ?? document.querySelector('.terminal-resize') !== null; }); expect(typeof hasHandle).toBe('boolean'); }); });