import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron'; test.describe('Makelore module navigation without setup flow', () => { test('keeps the four module entries in a proportional vertical stack while resizing the window', async ({ launchElectronApp }) => { const app = await launchElectronApp({ skipSetup: true }); try { const page = await getStableWindow(app); await expect(page.getByTestId('ai-module-selection-page')).toBeVisible(); const moduleCards = page.locator('[data-testid^="ai-module-option-"]'); const readCardMetrics = async () => await moduleCards.evaluateAll((elements) => elements.map((element) => { const bounds = element.getBoundingClientRect(); const image = element.querySelector('.module-option-card-image')?.getBoundingClientRect(); return { left: bounds.left, top: bounds.top, width: bounds.width, height: bounds.height, imageWidth: image?.width ?? 0, imageHeight: image?.height ?? 0, }; })); const initialMetrics = await readCardMetrics(); expect(initialMetrics).toHaveLength(4); await expect(moduleCards.first().locator('.module-option-card-arrow')).toHaveCount(0); await expect(moduleCards.first().locator('.module-option-artwork')).toHaveCount(0); await expect.poll(async () => await moduleCards.first().evaluate((element) => { const surface = element.querySelector('.module-option-card-surface'); const frame = element.querySelector('.module-option-card-frame'); if (!surface || !frame) return false; const surfaceStyle = getComputedStyle(surface); const frameStyle = getComputedStyle(frame); const surfaceInsets = [surfaceStyle.top, surfaceStyle.right, surfaceStyle.bottom, surfaceStyle.left] .map((value) => Number.parseFloat(value)); return frameStyle.display === 'none' && surfaceInsets.every((value) => value === 0) && surfaceStyle.transform === 'none'; })).toBe(true); const initialImageStyles = await moduleCards.first().evaluate((element) => { const image = element.querySelector('.module-option-card-image'); const cardHeight = element.getBoundingClientRect().height; if (!image) return null; const style = getComputedStyle(image); return { height: Number.parseFloat(style.height), cardHeight, marginTop: Number.parseFloat(style.marginTop), marginBottom: Number.parseFloat(style.marginBottom), marginLeft: Number.parseFloat(style.marginLeft), }; }); expect(initialImageStyles).not.toBeNull(); expect(initialImageStyles!.height).toBeLessThan(initialImageStyles!.cardHeight); expect(initialImageStyles!.marginTop).toBe(1); expect(initialImageStyles!.marginBottom).toBe(1); expect(initialImageStyles!.marginTop).toBe(initialImageStyles!.marginBottom); expect(initialImageStyles!.marginLeft).toBeGreaterThan(0); expect(initialImageStyles!.marginLeft).toBe(initialImageStyles!.marginTop); expect(Math.max(...initialMetrics.map((card) => card.left)) - Math.min(...initialMetrics.map((card) => card.left))).toBeLessThan(1); expect(initialMetrics.map((card) => card.top)).toEqual( [...initialMetrics].sort((top, bottom) => top.top - bottom.top).map((card) => card.top), ); const initialWidth = initialMetrics[0]?.width ?? 0; expect(initialMetrics[0]?.imageWidth ?? 0).toBeGreaterThan(initialWidth * 0.24); expect(initialMetrics[0]?.imageWidth ?? 0).toBeLessThan(initialWidth / 2); expect(initialMetrics[0]?.imageHeight ?? 0).toBeGreaterThan((initialMetrics[0]?.height ?? 0) * 0.75); await app.evaluate(({ BrowserWindow }) => { BrowserWindow.getAllWindows()[0]?.setSize(1100, 700); }); await expect.poll(async () => (await readCardMetrics())[0]?.width ?? 0).toBeLessThan(initialWidth - 1); const resizedMetrics = await readCardMetrics(); expect(resizedMetrics).toHaveLength(4); expect(Math.max(...resizedMetrics.map((card) => card.left)) - Math.min(...resizedMetrics.map((card) => card.left))).toBeLessThan(1); expect(resizedMetrics.map((card) => card.top)).toEqual( [...resizedMetrics].sort((top, bottom) => top.top - bottom.top).map((card) => card.top), ); expect(resizedMetrics[0]!.imageWidth).toBeGreaterThan(resizedMetrics[0]!.width * 0.24); expect(resizedMetrics[0]!.imageWidth).toBeLessThan(resizedMetrics[0]!.width / 2); expect(resizedMetrics[0]!.imageHeight).toBeGreaterThan(resizedMetrics[0]!.height * 0.75); await page.mouse.move(20, 20); const restingTransforms = await moduleCards.first().evaluate((element) => { const surface = element.querySelector('.module-option-card-surface'); const image = element.querySelector('.module-option-card-image'); const imageMedia = element.querySelector('.module-option-card-image-media'); const content = element.querySelector('.module-option-card-content'); const title = element.querySelector('.module-option-card-title'); return { surfaceShadow: surface ? getComputedStyle(surface).boxShadow : 'none', image: image ? getComputedStyle(image).transform : 'none', imageMedia: imageMedia ? getComputedStyle(imageMedia).transform : 'none', content: content ? getComputedStyle(content).transform : 'none', title: title ? getComputedStyle(title).transform : 'none', }; }); await moduleCards.first().hover(); await moduleCards.first().focus(); await expect.poll(async () => await moduleCards.first().evaluate((element, rest) => { const surface = element.querySelector('.module-option-card-surface'); const image = element.querySelector('.module-option-card-image'); const imageMedia = element.querySelector('.module-option-card-image-media'); const content = element.querySelector('.module-option-card-content'); const title = element.querySelector('.module-option-card-title'); const description = element.querySelector('.module-option-card-description'); const getTranslateZ = (transform: string) => { if (!transform.startsWith('matrix3d(')) return 0; const values = transform.slice(9, -1).split(',').map(Number); return values[14] ?? 0; }; const titleTransform = title ? getComputedStyle(title).transform : 'none'; const descriptionTransform = description ? getComputedStyle(description).transform : 'none'; return Boolean(surface) && Boolean(image) && Boolean(imageMedia) && Boolean(content) && Boolean(title) && Boolean(description) && getComputedStyle(surface).transform !== 'none' && getComputedStyle(surface).boxShadow !== rest.surfaceShadow && getComputedStyle(surface).borderTopColor === 'rgb(255, 255, 255)' && getComputedStyle(image).transform !== 'none' && getComputedStyle(imageMedia).transform !== 'none' && getComputedStyle(content).transform !== 'none' && titleTransform !== 'none' && descriptionTransform !== 'none' && getTranslateZ(titleTransform) > getTranslateZ(descriptionTransform) + 20; }, restingTransforms)).toBe(true); await expect.poll(async () => await moduleCards.first().evaluate((element, rest) => { const image = element.querySelector('.module-option-card-image'); const imageMedia = element.querySelector('.module-option-card-image-media'); const content = element.querySelector('.module-option-card-content'); const title = element.querySelector('.module-option-card-title'); return Boolean(image && imageMedia && content && title) && getComputedStyle(image!).transform !== rest.image && getComputedStyle(imageMedia!).transform !== rest.imageMedia && getComputedStyle(content!).transform !== rest.content && getComputedStyle(title!).transform !== rest.title; }, restingTransforms)).toBe(true); } finally { await closeElectronApp(app); } }); test('returns to the module chooser from the sidebar entry', async ({ launchElectronApp }) => { const app = await launchElectronApp({ skipSetup: true }); try { const page = await getStableWindow(app); const minimumSize = await app.evaluate(({ BrowserWindow }) => BrowserWindow.getAllWindows()[0]?.getMinimumSize()); expect(minimumSize).toEqual([1024, 700]); await expect(page.getByTestId('ai-module-selection-page')).toBeVisible(); await page.getByTestId('ai-module-option-programming').click(); await expect(page.getByTestId('main-layout')).toBeVisible(); await expect(page.getByText('请先新建项目')).toHaveCount(0); await expect(page.getByText('一念成光,万物可创。')).toBeVisible(); await expect(page.getByRole('img', { name: 'Makelore logo' })).toBeVisible(); await expect(page.getByTestId('sidebar-module-switcher-trigger')).toContainText('编程 Code'); await expect(page.getByTestId('sidebar-create-project')).toBeVisible(); await expect(page.getByTestId('sidebar-nav-publish')).toHaveCount(0); await expect(page.getByTestId('sidebar-nav-project-config')).toHaveCount(0); await expect(page.getByTestId('sidebar-nav-agent-chat')).toHaveCount(0); await expect(page.getByRole('button', { name: '添加已有项目' })).toHaveCount(0); await expect(page.getByText('成长成就在这里')).toHaveCount(0); await expect(page.getByText('已创建 Phaser 2D 小游戏起步工程')).toHaveCount(0); await expect(page.getByTestId('game-asset-browser')).toHaveCount(0); await expect(page.getByTestId('game-asset-review-summary')).toHaveCount(0); await expect(page.getByTestId('sidebar-module-switcher')).toBeVisible(); const moduleHomeTrigger = page.getByTestId('sidebar-module-switcher-trigger'); await expect(moduleHomeTrigger).toHaveAttribute('aria-label', '返回首页'); await expect(moduleHomeTrigger).toHaveCSS('padding-top', '8px'); await expect(moduleHomeTrigger).toHaveCSS('padding-right', '8px'); await expect(moduleHomeTrigger).toHaveCSS('padding-bottom', '8px'); await expect(moduleHomeTrigger).toHaveCSS('padding-left', '8px'); await expect(page.getByTestId('sidebar-module-switcher-menu')).toHaveCount(0); await moduleHomeTrigger.click(); await expect(page).toHaveURL(/\/module-select$/); await expect(page.getByTestId('ai-module-selection-page')).toBeVisible(); await page.getByTestId('ai-module-option-robot').click(); await expect(page).toHaveURL(/\/ai-hardware$/); await expect(page.getByTestId('ai-hardware-page')).toBeVisible(); await expect(page.getByTestId('sidebar-robot-navigation')).toBeVisible(); await expect(page.getByTestId('sidebar-module-switcher-trigger')).toContainText('Robot 机器'); await page.getByTestId('sidebar-module-switcher-trigger').click(); await expect(page).toHaveURL(/\/module-select$/); await expect(page.getByTestId('ai-module-selection-page')).toBeVisible(); } finally { await closeElectronApp(app); } }); });