diff --git a/tests/e2e/config/wdio.conf.ts b/tests/e2e/config/wdio.conf.ts index 9c8d56f6..762596fa 100644 --- a/tests/e2e/config/wdio.conf.ts +++ b/tests/e2e/config/wdio.conf.ts @@ -219,7 +219,8 @@ export const config: Options.Testrunner = { /** After test: capture screenshot on failure. */ afterTest: async function (test, context, { error, passed }) { - if (!passed) { + const isRealFailure = !passed && !!error; + if (isRealFailure) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const screenshotName = `failure-${test.title.replace(/\s+/g, '_')}-${timestamp}.png`; diff --git a/tests/e2e/config/wdio.conf_l0.ts b/tests/e2e/config/wdio.conf_l0.ts index 6dd3aade..26b8a760 100644 --- a/tests/e2e/config/wdio.conf_l0.ts +++ b/tests/e2e/config/wdio.conf_l0.ts @@ -229,7 +229,8 @@ export const config: Options.Testrunner = { /** After test: capture screenshot on failure. */ afterTest: async function (test, context, { error, passed }) { - if (!passed) { + const isRealFailure = !passed && !!error; + if (isRealFailure) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const screenshotName = `failure-${test.title.replace(/\s+/g, '_')}-${timestamp}.png`; diff --git a/tests/e2e/config/wdio.conf_l1.ts b/tests/e2e/config/wdio.conf_l1.ts index 3694b19e..f1370f2f 100644 --- a/tests/e2e/config/wdio.conf_l1.ts +++ b/tests/e2e/config/wdio.conf_l1.ts @@ -232,7 +232,8 @@ export const config: Options.Testrunner = { /** After test: capture screenshot on failure. */ afterTest: async function (test, context, { error, passed }) { - if (!passed) { + const isRealFailure = !passed && !!error; + if (isRealFailure) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const screenshotName = `failure-${test.title.replace(/\s+/g, '_')}-${timestamp}.png`; diff --git a/tests/e2e/specs/l0-open-settings.spec.ts b/tests/e2e/specs/l0-open-settings.spec.ts index c7962147..d04a7b70 100644 --- a/tests/e2e/specs/l0-open-settings.spec.ts +++ b/tests/e2e/specs/l0-open-settings.spec.ts @@ -5,6 +5,7 @@ import { browser, expect, $ } from '@wdio/globals'; import { openWorkspace } from '../helpers/workspace-helper'; +import { saveStepScreenshot } from '../helpers/screenshot-utils'; describe('L0 Settings Panel', () => { let hasWorkspace = false; @@ -25,6 +26,9 @@ describe('L0 Settings Panel', () => { console.log('[L0] Workspace opened:', hasWorkspace); expect(hasWorkspace).toBe(true); + if (hasWorkspace) { + await saveStepScreenshot('l0-settings-workspace-ready'); + } }); }); @@ -44,6 +48,7 @@ describe('L0 Settings Panel', () => { // Click to open menu await moreBtn.click(); await browser.pause(500); + await saveStepScreenshot('l0-settings-menu-opened'); // Find settings menu item const menuItems = await $$('.bitfun-nav-panel__footer-menu-item'); @@ -104,6 +109,9 @@ describe('L0 Settings Panel', () => { console.log('[L0] Settings scene opened:', sceneExists); expect(sceneExists).toBe(true); + if (sceneExists) { + await saveStepScreenshot('l0-settings-panel-opened'); + } }); }); diff --git a/tests/e2e/specs/l0-open-workspace.spec.ts b/tests/e2e/specs/l0-open-workspace.spec.ts index 6f10c87f..2a6c16f0 100644 --- a/tests/e2e/specs/l0-open-workspace.spec.ts +++ b/tests/e2e/specs/l0-open-workspace.spec.ts @@ -5,6 +5,7 @@ import { browser, expect, $ } from '@wdio/globals'; import { openWorkspace } from '../helpers/workspace-helper'; +import { saveStepScreenshot } from '../helpers/screenshot-utils'; describe('L0 Workspace Opening', () => { let hasWorkspace = false; @@ -16,6 +17,7 @@ describe('L0 Workspace Opening', () => { const title = await browser.getTitle(); console.log('[L0] App title:', title); expect(title).toBeDefined(); + await saveStepScreenshot('l0-workspace-app-started'); }); it('should have valid DOM structure', async () => { @@ -34,6 +36,9 @@ describe('L0 Workspace Opening', () => { console.log('[L0] Workspace opened:', hasWorkspace); expect(hasWorkspace).toBe(true); + if (hasWorkspace) { + await saveStepScreenshot('l0-workspace-opened'); + } }); it('should have workspace UI elements', async () => { @@ -44,6 +49,7 @@ describe('L0 Workspace Opening', () => { console.log('[L0] Chat input exists:', hasChatInput); expect(hasChatInput).toBe(true); + await saveStepScreenshot('l0-workspace-chat-ready'); }); }); diff --git a/tests/e2e/specs/l1-chat-input.spec.ts b/tests/e2e/specs/l1-chat-input.spec.ts index 10122dd0..55bc4c92 100644 --- a/tests/e2e/specs/l1-chat-input.spec.ts +++ b/tests/e2e/specs/l1-chat-input.spec.ts @@ -8,7 +8,7 @@ import { ChatPage } from '../page-objects/ChatPage'; import { ChatInput } from '../page-objects/components/ChatInput'; import { Header } from '../page-objects/components/Header'; import { StartupPage } from '../page-objects/StartupPage'; -import { saveScreenshot, saveFailureScreenshot } from '../helpers/screenshot-utils'; +import { saveScreenshot, saveFailureScreenshot, saveStepScreenshot } from '../helpers/screenshot-utils'; describe('L1 Chat Input Validation', () => { let chatPage: ChatPage; @@ -57,6 +57,10 @@ describe('L1 Chat Input Validation', () => { console.log('[L1] Recent workspace opened successfully'); } } + + if (hasWorkspace) { + await saveStepScreenshot('l1-chat-input-workspace-ready'); + } }); describe('Input visibility and accessibility', () => { @@ -82,6 +86,7 @@ describe('L1 Chat Input Validation', () => { const isVisible = await chatInput.isVisible(); expect(isVisible).toBe(true); console.log('[L1] Chat input component loaded'); + await saveStepScreenshot('l1-chat-input-visible'); }); it('should have placeholder text', async function () { @@ -132,6 +137,7 @@ describe('L1 Chat Input Validation', () => { expect(value).toContain('Line 2'); expect(value).toContain('Line 3'); console.log('[L1] Multiline input works'); + await saveStepScreenshot('l1-chat-input-multiline'); }); it('should clear input', async function () { @@ -240,6 +246,7 @@ describe('L1 Chat Input Validation', () => { const valueAfter = await chatInput.getValue(); expect(valueAfter).toBe(''); console.log('[L1] Input cleared after send'); + await saveStepScreenshot('l1-chat-input-message-sent'); }); it('should not send empty message', async function () { @@ -311,6 +318,7 @@ describe('L1 Chat Input Validation', () => { // Main test: input is still functional expect(typeof clearedValue).toBe('string'); console.log('[L1] Rapid sending handled - input still functional'); + await saveStepScreenshot('l1-chat-input-rapid-send-complete'); }); }); diff --git a/tests/e2e/specs/l1-editor.spec.ts b/tests/e2e/specs/l1-editor.spec.ts index 244e6e6b..8f560712 100644 --- a/tests/e2e/specs/l1-editor.spec.ts +++ b/tests/e2e/specs/l1-editor.spec.ts @@ -6,7 +6,7 @@ import { browser, expect, $ } from '@wdio/globals'; import { Header } from '../page-objects/components/Header'; import { StartupPage } from '../page-objects/StartupPage'; -import { saveScreenshot, saveFailureScreenshot } from '../helpers/screenshot-utils'; +import { saveScreenshot, saveFailureScreenshot, saveStepScreenshot } from '../helpers/screenshot-utils'; import { ensureWorkspaceOpen } from '../helpers/workspace-utils'; describe('L1 Editor', () => { @@ -28,6 +28,8 @@ describe('L1 Editor', () => { if (!hasWorkspace) { console.log('[L1] No workspace available - tests will be skipped'); + } else { + await saveStepScreenshot('l1-editor-workspace-ready'); } }); @@ -56,6 +58,7 @@ describe('L1 Editor', () => { if (exists) { console.log(`[L1] Editor found: ${selector}`); editorFound = true; + await saveStepScreenshot('l1-editor-visible'); break; } } @@ -215,12 +218,14 @@ describe('L1 Editor', () => { await browser.pause(300); console.log('[L1] Switched to second tab'); + await saveStepScreenshot('l1-editor-second-tab'); // Click first tab await tabs[0].click(); await browser.pause(300); console.log('[L1] Switched back to first tab'); + await saveStepScreenshot('l1-editor-first-tab'); expect(tabs.length).toBeGreaterThanOrEqual(2); }); diff --git a/tests/e2e/specs/l1-file-tree.spec.ts b/tests/e2e/specs/l1-file-tree.spec.ts index 6a81bca3..540d9c81 100644 --- a/tests/e2e/specs/l1-file-tree.spec.ts +++ b/tests/e2e/specs/l1-file-tree.spec.ts @@ -6,7 +6,7 @@ import { browser, expect, $ } from '@wdio/globals'; import { Header } from '../page-objects/components/Header'; import { StartupPage } from '../page-objects/StartupPage'; -import { saveScreenshot, saveFailureScreenshot } from '../helpers/screenshot-utils'; +import { saveScreenshot, saveFailureScreenshot, saveStepScreenshot } from '../helpers/screenshot-utils'; describe('L1 File Tree', () => { let header: Header; @@ -50,6 +50,10 @@ describe('L1 File Tree', () => { hasWorkspace = true; console.log('[L1] Recent workspace opened successfully'); } + + if (hasWorkspace) { + await saveStepScreenshot('l1-file-tree-workspace-ready'); + } } // Navigate to file tree view @@ -80,6 +84,7 @@ describe('L1 File Tree', () => { await navItem.click(); await browser.pause(1500); // Wait for view to switch console.log('[L1] Navigated to Files view'); + await saveStepScreenshot('l1-file-tree-files-view'); navigated = true; break; } catch (clickError) { @@ -289,6 +294,7 @@ describe('L1 File Tree', () => { // Verify the expand state actually changed expect(afterExpanded).not.toBe(beforeExpanded); console.log('[L1] Directory expand/collapse state changed successfully'); + await saveStepScreenshot('l1-file-tree-directory-toggled'); break; } } @@ -323,6 +329,7 @@ describe('L1 File Tree', () => { const isSelected = await content.getAttribute('class'); console.log('[L1] File selected, classes:', isSelected?.includes('selected')); + await saveStepScreenshot('l1-file-tree-file-selected'); } expect(filePath).toBeDefined(); diff --git a/tests/e2e/specs/l1-navigation.spec.ts b/tests/e2e/specs/l1-navigation.spec.ts index 6e2c3e2d..1a21eace 100644 --- a/tests/e2e/specs/l1-navigation.spec.ts +++ b/tests/e2e/specs/l1-navigation.spec.ts @@ -9,6 +9,36 @@ import { StartupPage } from '../page-objects/StartupPage'; import { saveScreenshot, saveFailureScreenshot } from '../helpers/screenshot-utils'; import { ensureWorkspaceOpen } from '../helpers/workspace-utils'; +const NAV_ENTRY_SELECTORS = [ + '.bitfun-nav-panel__item', + '.bitfun-nav-panel__item-slot', + '.bitfun-nav-panel__workspace-item-name-btn', + '.bitfun-nav-panel__inline-item', + '.bitfun-nav-panel__workspace-create-main', + '.bitfun-nav-panel__toolbox-entry', +]; + +async function getNavigationEntryCounts(): Promise> { + const counts: Record = {}; + + for (const selector of NAV_ENTRY_SELECTORS) { + counts[selector] = (await browser.$$(selector)).length; + } + + return counts; +} + +async function getNavigationEntries() { + const entries = []; + + for (const selector of NAV_ENTRY_SELECTORS) { + const matched = await browser.$$(selector); + entries.push(...matched); + } + + return entries; +} + describe('L1 Navigation', () => { let header: Header; let startupPage: StartupPage; @@ -51,9 +81,11 @@ describe('L1 Navigation', () => { return; } - const navItems = await browser.$$('.bitfun-nav-panel__item'); - console.log('[L1] Navigation items count:', navItems.length); - expect(navItems.length).toBeGreaterThan(0); + const counts = await getNavigationEntryCounts(); + const totalEntries = Object.values(counts).reduce((sum, count) => sum + count, 0); + + console.log('[L1] Navigation entry counts:', counts); + expect(totalEntries).toBeGreaterThan(0); }); it('should have navigation sections', async function () { @@ -75,15 +107,32 @@ describe('L1 Navigation', () => { return; } - const navItems = await browser.$$('.bitfun-nav-panel__item'); + const navItems = await getNavigationEntries(); if (navItems.length === 0) { console.log('[L1] No nav items to click'); this.skip(); return; } - const firstItem = navItems[0]; - const isClickable = await firstItem.isClickable(); + let firstClickable = null; + for (const item of navItems) { + try { + if (await item.isClickable()) { + firstClickable = item; + break; + } + } catch (error) { + // Try the next candidate. + } + } + + if (!firstClickable) { + console.log('[L1] Navigation entries exist but none are clickable'); + this.skip(); + return; + } + + const isClickable = await firstClickable.isClickable(); expect(isClickable).toBe(true); console.log('[L1] First navigation item is clickable'); }); @@ -121,7 +170,7 @@ describe('L1 Navigation', () => { return; } - const activeItems = await browser.$$('.bitfun-nav-panel__item.is-active'); + const activeItems = await browser.$$('.bitfun-nav-panel__item.is-active, .bitfun-nav-panel__inline-item.is-active, .bitfun-nav-panel__toolbox-entry.is-active'); const activeCount = activeItems.length; console.log('[L1] Active navigation items:', activeCount); @@ -142,7 +191,7 @@ describe('L1 Navigation', () => { } // Get initial active item - const initialActive = await browser.$$('.bitfun-nav-panel__item.is-active'); + const initialActive = await browser.$$('.bitfun-nav-panel__item.is-active, .bitfun-nav-panel__inline-item.is-active, .bitfun-nav-panel__toolbox-entry.is-active'); const initialActiveCount = initialActive.length; console.log('[L1] Initial active items:', initialActiveCount); @@ -180,7 +229,7 @@ describe('L1 Navigation', () => { } // Check for active state (don't fail if state doesn't change) - const afterActive = await browser.$$('.bitfun-nav-panel__item.is-active'); + const afterActive = await browser.$$('.bitfun-nav-panel__item.is-active, .bitfun-nav-panel__inline-item.is-active, .bitfun-nav-panel__toolbox-entry.is-active'); console.log('[L1] Active items after click:', afterActive.length); // Verify active state detection completed