diff --git a/src/pages/Chat/CodingStartHeading.tsx b/src/pages/Chat/CodingStartHeading.tsx
index ab2cfacd..13f1636d 100644
--- a/src/pages/Chat/CodingStartHeading.tsx
+++ b/src/pages/Chat/CodingStartHeading.tsx
@@ -1,8 +1,10 @@
-import { CommunityLink } from '@/components/community/CommunityLink';
+import { Sparkles, Star } from 'lucide-react';
+import mark from '@/assets/consultations/mark.svg';
+import '@/components/community/community.css';
export function CodingStartHeading() {
return
-
+
今天,你想创造什么?
一个游戏、一段故事,或是一个天马行空的想法。
;
diff --git a/src/pages/ModuleSelection/index.tsx b/src/pages/ModuleSelection/index.tsx
index 6ac9fd46..ef54d709 100644
--- a/src/pages/ModuleSelection/index.tsx
+++ b/src/pages/ModuleSelection/index.tsx
@@ -7,6 +7,7 @@ import moduleRobotImage from '@/assets/module-robot.webp';
import logoWordmarkSource from '@/assets/makelore-wordmark-source.png';
import { UserProfileDialog } from '@/components/profile/UserProfileDialog';
import { HomeAccountMenu } from '@/components/account/HomeAccountMenu';
+import { CommunityLink } from '@/components/community/CommunityLink';
import { useCurrentUserProfile } from '@/hooks/use-current-user-profile';
import { aiModules, isAiModuleAllowed, type AiModuleId } from '@/lib/ai-modules';
import { getAuthUserDisplayName } from '@/lib/auth-user-display';
@@ -65,9 +66,10 @@ export function ModuleSelection({ authRequired = true }: { authRequired?: boolea
- {userDisplayName ? `${userDisplayName},欢迎回来!` : '欢迎回来!'}
+ {userDisplayName ? `${userDisplayName}欢迎回来!` : '欢迎回来!'}
今天,想创作什么?
+ 还是去「」找找灵感?
document.documentElement.scrollWidth)).toBeLessThanOrEqual(width);
- await expect(page.getByTestId('community-welcome-link')).toBeVisible();
+ await expect(page.locator('.coding-start-mark')).toBeVisible();
await expect(page.getByTestId('community-invitation')).toHaveCount(0);
while (await teachers.count()) await teachers.getByRole('button', { name: /^收起全部老师介绍/ }).first().click();
await heading.scrollIntoViewIfNeeded();
@@ -96,8 +96,8 @@ test('a short window scrolls the welcome while keeping the composer at the botto
expect(600 - after.y - after.height).toBeLessThanOrEqual(40);
const teachers = page.getByTestId('teacher-welcomes');
while (await teachers.count()) await teachers.getByRole('button', { name: /^收起全部老师介绍/ }).first().click();
- await page.getByTestId('community-welcome-link').scrollIntoViewIfNeeded();
- await expect(page.getByTestId('community-welcome-link')).toBeInViewport({ ratio: 1 });
+ await page.locator('.coding-start-mark').scrollIntoViewIfNeeded();
+ await expect(page.locator('.coding-start-mark')).toBeInViewport({ ratio: 1 });
await expect(page.getByTestId('community-invitation')).toHaveCount(0);
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(375);
});
@@ -146,41 +146,47 @@ for (const width of [1280, 1470]) {
});
}
-test('the welcome entry opens only on click and leaves the draft mounted', async ({ page }) => {
+test('the home gallery opens only on click and is larger than the surrounding text', async ({ page }) => {
await page.setViewportSize({ width: 1280, height: 850 });
const communityRequests: string[] = [];
page.on('request', request => { if (request.url().includes('square.nianxx.cn')) communityRequests.push(request.url()); });
- await page.goto(baseUrl);
- const composer = page.getByRole('textbox');
- await composer.fill('我想做一个自己的小镇');
- await composer.evaluate(node => node.setAttribute('data-mounted', 'same'));
- const calls = () => page.evaluate(() => (window as typeof window & { __communityCalls: unknown[][] }).__communityCalls);
- expect(await calls()).toEqual([]);
- await expect(page.getByTestId('community-invitation')).toHaveCount(0);
- await page.getByTestId('community-welcome-link').click();
+ await page.goto(baseUrl + '?home');
+ await expect(page.getByText('Alice欢迎回来!', { exact: true })).toBeVisible();
+ await expect(page.getByRole('heading', { name: '今天,想创作什么?' })).toBeVisible();
+ const entry = page.getByTestId('community-home-link');
+ await expect(entry).toHaveText('作品广场');
+ await expect(page.locator('.module-selection-inspiration')).toHaveText('还是去「作品广场」找找灵感?');
+ const fontRatio = await entry.evaluate(el => parseFloat(getComputedStyle(el).fontSize) / parseFloat(getComputedStyle(el.closest('p')!).fontSize));
+ expect(fontRatio).toBeCloseTo(1.12, 2);
+ const typography = await entry.evaluate(el => {
+ const own = getComputedStyle(el), surrounding = getComputedStyle(el.closest('p')!);
+ return { font: own.fontFamily, surroundingFont: surrounding.fontFamily, weight: own.fontWeight, surroundingWeight: surrounding.fontWeight, color: own.color, surroundingColor: surrounding.color };
+ });
+ expect(typography.font).toBe(typography.surroundingFont);
+ expect(typography.weight).toBe(typography.surroundingWeight);
+ expect(typography.color).toBe(typography.surroundingColor);
+ await expect(entry.locator('svg')).toHaveCount(0);
+ expect(await page.evaluate(() => (window as typeof window & { __communityCalls: unknown[][] }).__communityCalls)).toEqual([]);
+ await page.screenshot({ path: test.info().outputPath('homepage-gallery-text.png') });
+ await entry.click();
expect(await page.evaluate(() => (window as typeof window & { __communityCalls: unknown[][] }).__communityCalls))
.toEqual([['shell:openExternal', 'https://square.nianxx.cn/#explore']]);
expect(communityRequests).toEqual([]);
await expect(page.locator('iframe')).toHaveCount(0);
- await expect(composer).toHaveValue('我想做一个自己的小镇');
- await expect(composer).toHaveAttribute('data-mounted', 'same');
- await expect(page.getByTestId('coding-start-heading')).toBeVisible();
- await expect(page.getByText('第一条消息已发送(测试)')).toHaveCount(0);
- await page.getByRole('button', { name: '发送', exact: true }).click();
- await expect(page.getByTestId('community-welcome-link')).toHaveCount(0);
- await expect(composer).toHaveAttribute('data-mounted', 'same');
+ await expect(page.getByRole('heading', { name: '今天,想创作什么?' })).toBeVisible();
});
-test('the welcome entry supports keyboard activation and retry while preserving the draft', async ({ page }) => {
+test('the home text entry supports keyboard retry without overflow on small screens', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 600 });
- await page.goto(baseUrl);
- const composer = page.getByRole('textbox');
- await composer.fill('保留我的草稿');
+ await page.emulateMedia({ reducedMotion: 'reduce' });
+ await page.goto(baseUrl + '?home');
await page.evaluate(() => { (window as typeof window & { __communityFailures: number }).__communityFailures = 1; });
- const entry = page.getByTestId('community-welcome-link');
+ const entry = page.getByTestId('community-home-link');
await entry.focus();
await expect(entry).toBeFocused();
await expect(entry).toHaveCSS('outline-style', 'solid');
+ await expect(entry).toHaveCSS('transition-duration', '0s');
+ expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(375);
await entry.press('Enter');
await expect(page.getByRole('alert')).toHaveText('没能打开作品广场,请再点一次试试。');
await expect(entry).toBeEnabled();
@@ -191,7 +197,6 @@ test('the welcome entry supports keyboard activation and retry while preserving
['shell:openExternal', 'https://square.nianxx.cn/#explore'],
['shell:openExternal', 'https://square.nianxx.cn/#explore'],
]);
- await expect(composer).toHaveValue('保留我的草稿');
- await expect(page.getByTestId('coding-start-heading')).toBeVisible();
- await expect(page.getByText('第一条消息已发送(测试)')).toHaveCount(0);
+ await expect(page.getByRole('heading', { name: '今天,想创作什么?' })).toBeVisible();
+ await page.screenshot({ path: test.info().outputPath('homepage-gallery-text-mobile.png') });
});
diff --git a/tests/e2e/fixtures/community-welcome.tsx b/tests/e2e/fixtures/community-welcome.tsx
index 49715672..f9b08df2 100644
--- a/tests/e2e/fixtures/community-welcome.tsx
+++ b/tests/e2e/fixtures/community-welcome.tsx
@@ -1,5 +1,9 @@
import { useState, type ComponentProps } from 'react';
import { createRoot } from 'react-dom/client';
+import { MemoryRouter } from 'react-router-dom';
+import { ModuleSelection } from '../../../src/pages/ModuleSelection';
+import { useAuthStore } from '../../../src/stores/auth';
+import { EMPTY_AGENT_USER_PROFILE, useUserProfileStore } from '../../../src/stores/user-profile';
import { CodingChatBody } from '../../../src/pages/Chat/CodingChatBody';
import { CodingStartHeading } from '../../../src/pages/Chat/CodingStartHeading';
import { CodingComposer } from '../../../src/pages/Chat/CodingComposer';
@@ -46,4 +50,10 @@ export function Layout() {
;
}
-createRoot(document.getElementById('root')!).render(
);
+const home = new URLSearchParams(location.search).has('home');
+if (home) {
+ useAuthStore.setState({ initialized: true, loading: false, accessToken: null,
+ user: { username: 'Alice', userId: 'home-fixture', tenantId: null, deptId: null, authorities: [] } });
+ useUserProfileStore.setState({ profilesByUserId: { 'home-fixture': { ...EMPTY_AGENT_USER_PROFILE, displayName: 'Alice' } }, syncStatesByUserId: {} });
+}
+createRoot(document.getElementById('root')!).render(home ?
:
);
diff --git a/tests/e2e/image-workspace-v2.spec.ts b/tests/e2e/image-workspace-v2.spec.ts
index 64b5e2a3..990990c8 100644
--- a/tests/e2e/image-workspace-v2.spec.ts
+++ b/tests/e2e/image-workspace-v2.spec.ts
@@ -479,8 +479,9 @@ test.describe('AI Design V2 workspace', () => {
const project = sidebar.getByTestId('sidebar-image-project-workspace-1');
const projectsHeading = sidebar.locator('.classroom-projects-heading');
const moduleReturn = sidebar.getByTestId('sidebar-module-switcher-trigger');
- const community = sidebar.getByRole('button', { name: '作品广场', exact: true });
const account = sidebar.getByTestId('sidebar-member-menu-trigger');
+ await expect(sidebar.getByRole('button', { name: '作品广场', exact: true })).toHaveCount(0);
+ await expect(page.getByTestId('community-welcome-link')).toHaveCount(0);
await expect(sidebar).toHaveAttribute('aria-hidden', 'false');
await expect(sidebar).toHaveCSS('background-color', 'rgb(245, 242, 250)');
await expect(createProject).toHaveText('新建项目');
@@ -502,29 +503,20 @@ test.describe('AI Design V2 workspace', () => {
const actions = sidebar.getByRole('button', { name: '项目操作:咖啡机新品主视觉', exact: true });
await expect(actions).toHaveCSS('width', '32px');
await expect(actions).toHaveCSS('height', '32px');
- const [sidebarBox, createBox, headingBox, projectBox, returnBox, communityBox, accountBox] = await Promise.all(
- [sidebar, createProject, projectsHeading, project, moduleReturn, community, account].map((locator) => locator.boundingBox()),
+ const [sidebarBox, createBox, headingBox, projectBox, returnBox, accountBox] = await Promise.all(
+ [sidebar, createProject, projectsHeading, project, moduleReturn, account].map((locator) => locator.boundingBox()),
);
expect(sidebarBox!.width).toBe(256);
expect(createBox!.height).toBe(48);
expect(projectBox!.height).toBe(48);
expect(createBox!.y - sidebarBox!.y).toBe(40);
+ expect(createBox!.x - sidebarBox!.x).toBe(12);
expect(headingBox!.y - sidebarBox!.y).toBe(96);
expect(projectBox!.y - sidebarBox!.y).toBe(136);
expect(returnBox!.y - sidebarBox!.y).toBe(4);
- expect(communityBox!.x).toBe(createBox!.x);
- expect(communityBox!.height).toBe(createBox!.height);
- expect(communityBox!.y).toBeGreaterThan(projectBox!.y + projectBox!.height);
- expect(accountBox!.y - communityBox!.y - communityBox!.height).toBeCloseTo(0, 0);
+ expect(accountBox!.y).toBeGreaterThan(projectBox!.y + projectBox!.height);
+ expect(sidebarBox!.y + sidebarBox!.height - accountBox!.y - accountBox!.height).toBeGreaterThanOrEqual(0);
expect(sidebarBox!.y + sidebarBox!.height - accountBox!.y - accountBox!.height).toBeLessThanOrEqual(14);
- const navigationStyles = await sidebar.evaluate((element) => {
- const recipe = (selector: string) => {
- const style = getComputedStyle(element.querySelector(selector)!);
- return [style.borderRadius, style.borderWidth, style.boxShadow, style.padding, style.fontSize, style.fontWeight, style.lineHeight];
- };
- return { create: recipe('[data-testid="sidebar-create-project"]'), community: recipe('.community-link') };
- });
- expect(navigationStyles.community).toEqual(navigationStyles.create);
const splitBounds = (await split.boundingBox())!;
const conversationBounds = (await conversationPane.boundingBox())!;
diff --git a/tests/e2e/main-navigation.spec.ts b/tests/e2e/main-navigation.spec.ts
index 7362e13f..904aabd7 100644
--- a/tests/e2e/main-navigation.spec.ts
+++ b/tests/e2e/main-navigation.spec.ts
@@ -406,6 +406,7 @@ test.describe('Makelore module navigation without setup flow', () => {
await expect(moduleReturn).toHaveAttribute('aria-label', '返回首页');
await expect(moduleReturn).toHaveAttribute('title', '返回首页');
await expect(page.getByTestId('sidebar-module-return-icon')).toBeVisible();
+ await expect(page.getByTestId('sidebar-module-return-icon')).toHaveClass(/lucide-house/);
await expect(sidebar.locator('.classroom-brand')).toHaveCount(0);
await expect(sidebar).not.toContainText('把脑洞变成作品');
const returnBox = await moduleReturn.boundingBox();
@@ -421,27 +422,17 @@ test.describe('Makelore module navigation without setup flow', () => {
expect(returnBox!.x + returnBox!.width).toBeLessThanOrEqual(collapseBox!.x);
expect(sidebarBox!.x + sidebarBox!.width - collapseBox!.x - collapseBox!.width).toBeLessThanOrEqual(14);
await expect(page.getByTestId('titlebar-sidebar-toggle')).toHaveCount(1);
- const community = sidebar.getByRole('button', { name: '作品广场', exact: true });
- await expect(community).toHaveCount(1);
- const communityBox = await community.boundingBox();
+ await expect(sidebar.getByRole('button', { name: '作品广场', exact: true })).toHaveCount(0);
+ await expect(page.getByTestId('community-welcome-link')).toHaveCount(0);
const createBox = await sidebar.getByTestId('sidebar-create-project').boundingBox();
const headingBox = await sidebar.locator('.classroom-projects-heading').boundingBox();
const accountBox = await sidebar.getByTestId('sidebar-member-menu-trigger').boundingBox();
expect(createBox!.y).toBe(sidebarBox!.y);
+ expect(createBox!.x - sidebarBox!.x).toBe(12);
expect(headingBox!.y - sidebarBox!.y).toBe(56);
- expect(communityBox!.x).toBe(createBox!.x);
- expect(communityBox!.y).toBeGreaterThan(headingBox!.y + headingBox!.height);
- expect(accountBox!.y - communityBox!.y - communityBox!.height).toBeCloseTo(0, 0);
- expect(communityBox!.height).toBe(createBox!.height);
- await expect(community).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
- const buttonStyles = await page.evaluate(() => {
- const styles = (selector: string) => {
- const s = getComputedStyle(document.querySelector(selector)!);
- return [s.borderRadius, s.borderWidth, s.borderColor, s.boxShadow, s.padding, s.fontSize, s.fontWeight];
- };
- return { community: styles('[data-testid=sidebar] .community-link'), create: styles('[data-testid=sidebar-create-project]') };
- });
- expect(buttonStyles.community).toEqual(buttonStyles.create);
+ expect(accountBox!.y).toBeGreaterThan(headingBox!.y + headingBox!.height);
+ expect(sidebarBox!.y + sidebarBox!.height - accountBox!.y - accountBox!.height).toBeGreaterThanOrEqual(0);
+ expect(sidebarBox!.y + sidebarBox!.height - accountBox!.y - accountBox!.height).toBeLessThanOrEqual(14);
expect(headingBox!.y).toBeGreaterThanOrEqual(createBox!.y + createBox!.height);
expect(headingBox!.y - createBox!.y - createBox!.height).toBeLessThanOrEqual(14);
await page.screenshot({ path: test.info().outputPath('chat-code-return-entry.png') });
@@ -471,14 +462,14 @@ test.describe('Makelore module navigation without setup flow', () => {
const b = el.getBoundingClientRect();
return el.contains(document.elementFromPoint(b.x + b.width / 2, b.y + b.height / 2));
})).toBe(true);
- await expect(community).toBeVisible();
- expect(await community.evaluate(element => element.scrollWidth <= element.clientWidth)).toBe(true);
+ await expect(sidebar.getByRole('button', { name: '作品广场', exact: true })).toHaveCount(0);
await page.screenshot({ path: test.info().outputPath('chat-narrow-navigation-row.png') });
await sidebarResizer.dblclick();
await expect(sidebarResizer).toHaveAttribute('aria-valuenow', '171');
const readCodeChrome = () => page.evaluate(() => {
if (document.querySelector('[data-testid="sidebar"] .classroom-brand')) throw new Error('Removed Code brand reappeared');
+ if (document.querySelector('[data-testid="sidebar"] .community-link')) throw new Error('Relocated gallery reappeared in Code sidebar');
const selectors = {
sidebar: '[data-testid="sidebar"]',
titlebar: '[data-testid="titlebar-sidebar-surface"]',
@@ -487,8 +478,6 @@ test.describe('Makelore module navigation without setup flow', () => {
toggle: '[data-testid="titlebar-sidebar-toggle"]',
create: '[data-testid="sidebar-create-project"]',
heading: '[data-testid="sidebar"] .classroom-projects-heading',
- community: '[data-testid="sidebar"] .community-link[data-variant="sidebar"]',
- communityIcon: '[data-testid="sidebar"] .community-link-icon',
account: '[data-testid="sidebar-member-menu-trigger"]',
};
return Object.fromEntries(Object.entries(selectors).map(([name, selector]) => {
@@ -528,6 +517,8 @@ test.describe('Makelore module navigation without setup flow', () => {
await expect(moduleReturn).toBeVisible();
const projectBack = page.getByTestId('project-configuration-back-button');
await expect(projectBack).toBeVisible();
+ await expect(projectBack).toHaveAttribute('title', '返回上一级');
+ await expect(projectBack.locator('svg')).toHaveClass(/lucide-arrow-left/);
await page.mouse.move(1000, 500);
await expect.poll(readCodeChrome).toEqual(chatChrome);
await expect(sidebar).toHaveCSS('background-color', 'rgb(243, 247, 251)');
@@ -592,13 +583,32 @@ test.describe('Makelore module navigation without setup flow', () => {
}
});
- test('aligns the desktop home title with the middle of three stacked horizontal module cards', async ({ launchElectronApp }) => {
+ test('aligns the desktop home introduction with the middle of three stacked horizontal module cards', async ({ launchElectronApp }) => {
const app = await launchElectronApp({ skipSetup: true });
try {
const page = await getStableWindow(app);
await expect(page.getByTestId('ai-module-selection-page')).toBeVisible();
+ const homeGallery = page.getByRole('button', { name: '作品广场', exact: true });
+ const inspiration = page.locator('.module-selection-inspiration');
+ await expect(homeGallery).toHaveText('作品广场');
+ await expect(homeGallery.locator('svg')).toHaveCount(0);
+ await expect(inspiration).toHaveText('还是去「作品广场」找找灵感?');
+ await app.evaluate(({ shell }) => {
+ const scope = globalThis as typeof globalThis & { __homeCommunityOpened?: string[] };
+ scope.__homeCommunityOpened = [];
+ shell.openExternal = async url => { scope.__homeCommunityOpened!.push(url); };
+ });
+ const homeUrl = page.url();
+ await homeGallery.click();
+ await expect.poll(() => app.evaluate(() => (
+ (globalThis as typeof globalThis & { __homeCommunityOpened?: string[] }).__homeCommunityOpened
+ ))).toEqual(['https://square.nianxx.cn/#explore']);
+ await expect(page).toHaveURL(homeUrl);
+ await expect(homeGallery).toBeEnabled();
+ await expect(page.locator('iframe')).toHaveCount(0);
+
const moduleCards = page.locator('[data-testid^="ai-module-option-"]');
await expect(moduleCards.locator('.module-option-card-title')).toHaveText([
'Agents 智能体', 'Code 编程', 'Canvas 设计',
@@ -634,6 +644,13 @@ test.describe('Makelore module navigation without setup flow', () => {
for (const width of [1440, 1024, 768, 375]) {
await app.evaluate(({ BrowserWindow }, windowWidth) => BrowserWindow.getAllWindows()[0]?.setSize(windowWidth, windowWidth === 375 ? 800 : 900), width);
await expect.poll(() => page.evaluate(() => innerWidth)).toBe(width);
+ const galleryType = await homeGallery.evaluate(element => ({
+ label: Number.parseFloat(getComputedStyle(element).fontSize),
+ surrounding: Number.parseFloat(getComputedStyle(element.closest('p')!).fontSize),
+ }));
+ expect(galleryType.surrounding).toBe(width <= 600 ? 15 : 18);
+ expect(galleryType.label).toBeCloseTo(width <= 600 ? 16.8 : 20.16, 2);
+ expect(galleryType.label / galleryType.surrounding).toBeCloseTo(1.12, 2);
const cards = await readCardMetrics();
expect(cards).toHaveLength(3);
expect(Math.max(...cards.map(card => card.left)) - Math.min(...cards.map(card => card.left))).toBeLessThan(1);
@@ -669,7 +686,8 @@ test.describe('Makelore module navigation without setup flow', () => {
});
expect(shift).toBeCloseTo(1.5 * 96 / 2.54, 1);
expect(heading!.x + heading!.width).toBeLessThanOrEqual(cards[0]!.left);
- expect(Math.abs(heading!.y + heading!.height / 2 - cards[1]!.top - cards[1]!.height / 2)).toBeLessThan(1);
+ const intro = await page.locator('.module-selection-intro').boundingBox();
+ expect(Math.abs(intro!.y + intro!.height / 2 - cards[1]!.top - cards[1]!.height / 2)).toBeLessThan(1);
expect(await moduleCards.evaluateAll(elements => elements.every(element => {
const bounds = element.getBoundingClientRect();
return [[2, 2], [bounds.width - 2, bounds.height - 2], [bounds.width / 2, bounds.height / 2]].every(([x, y]) =>
@@ -678,6 +696,8 @@ test.describe('Makelore module navigation without setup flow', () => {
}))).toBe(true);
} else {
expect(heading!.y + heading!.height).toBeLessThanOrEqual(cards[0]!.top);
+ const inspirationBox = await inspiration.boundingBox();
+ expect(inspirationBox!.y + inspirationBox!.height).toBeLessThanOrEqual(cards[0]!.top);
}
expect(await page.evaluate(() => document.documentElement.scrollWidth - innerWidth)).toBeLessThanOrEqual(1);
expect(await moduleCards.locator('.module-option-card-title, .module-option-card-description').evaluateAll(elements =>
diff --git a/tests/e2e/pi-coding-first-chat.spec.ts b/tests/e2e/pi-coding-first-chat.spec.ts
index 0e837d45..a2cbffdc 100644
--- a/tests/e2e/pi-coding-first-chat.spec.ts
+++ b/tests/e2e/pi-coding-first-chat.spec.ts
@@ -1317,20 +1317,24 @@ test('first PI Conversation is editable under 500 ms and submits before runtime
await expect(page.getByTestId('coding-message-composer')).toBeInViewport({ ratio: 1 });
expect(await page.getByTestId('coding-message-composer').evaluate(el => innerHeight - el.getBoundingClientRect().bottom)).toBeLessThan(40);
- const welcomeCommunity = page.getByTestId('community-welcome-link');
- await expect(welcomeCommunity).toBeVisible();
+ const welcomeMark = page.locator('.coding-start-mark');
+ await expect(welcomeMark).toBeVisible();
+ await expect(page.getByTestId('coding-start-heading').getByRole('heading', { level: 1 })).toBeVisible();
+ expect(await welcomeMark.evaluate(element => element.closest('button') === null)).toBe(true);
+ await expect(page.getByTestId('community-welcome-link')).toHaveCount(0);
+ await expect(page.getByTestId('sidebar').getByRole('button', { name: '作品广场', exact: true })).toHaveCount(0);
await expect(page.getByTestId('community-invitation')).toHaveCount(0);
expect(await page.getByTestId('coding-message-composer').evaluate(el => {
const body = document.querySelector('.coding-chat-body')!.getBoundingClientRect();
const input = el.getBoundingClientRect();
return Math.abs(input.left + input.width / 2 - body.left - body.width / 2);
})).toBeLessThan(2);
- expect(await welcomeCommunity.evaluate(el => {
+ expect(await welcomeMark.evaluate(el => {
const body = document.querySelector('.coding-chat-body')!.getBoundingClientRect();
const welcome = el.getBoundingClientRect();
return Math.abs(welcome.left + welcome.width / 2 - body.left - body.width / 2);
})).toBeLessThan(2);
- expect(await welcomeCommunity.evaluate(el => {
+ expect(await welcomeMark.evaluate(el => {
const composer = document.querySelector('[data-testid="coding-message-composer"]')!;
return composer.getBoundingClientRect().top - el.getBoundingClientRect().bottom;
})).toBeGreaterThan(12);
@@ -1346,16 +1350,6 @@ test('first PI Conversation is editable under 500 ms and submits before runtime
buffer: largePng,
});
await composer.fill('Build the first PI scene');
- await electronApp.evaluate(({ shell }) => {
- const scope = globalThis as typeof globalThis & { __communityOpened?: string[] };
- scope.__communityOpened = [];
- shell.openExternal = async url => { scope.__communityOpened!.push(url); };
- });
- await page.getByTestId('sidebar').getByRole('button', { name: '作品广场', exact: true }).click();
- await welcomeCommunity.click();
- await expect.poll(() => electronApp.evaluate(() => (
- (globalThis as typeof globalThis & { __communityOpened?: string[] }).__communityOpened
- ))).toEqual(['https://square.nianxx.cn/#explore', 'https://square.nianxx.cn/#explore']);
await expect(composer).toHaveValue('Build the first PI scene');
await expect(page.getByRole('img', { name: 'large.png', exact: true })).toBeVisible();
await expect(page.getByRole('button', { name: '移除图片 large.png', exact: true })).toBeEnabled();
@@ -1365,7 +1359,7 @@ test('first PI Conversation is editable under 500 ms and submits before runtime
await page.getByTestId('coding-message-composer').evaluate(
(form: HTMLFormElement) => form.requestSubmit(),
);
- await expect(welcomeCommunity).toHaveCount(0);
+ await expect(welcomeMark).toHaveCount(0);
await expect.poll(async () => {
const state = await readState(electronApp);
diff --git a/tests/unit/community.test.tsx b/tests/unit/community.test.tsx
index 6b829f29..b9823609 100644
--- a/tests/unit/community.test.tsx
+++ b/tests/unit/community.test.tsx
@@ -12,17 +12,15 @@ afterEach(() => { cleanup(); vi.restoreAllMocks(); });
it('only opens the public explore URL after a click, without fetching or embedding works', async () => {
const fetch = vi.spyOn(globalThis, 'fetch');
- render(
);
- expect(screen.getByRole('heading', { name: '今天,你想创造什么?' })).toBeInTheDocument();
- expect(screen.getByText('一个游戏、一段故事,或是一个天马行空的想法。')).toBeInTheDocument();
+ render(
还是去「」找找灵感?
);
expect(screen.queryByTestId('community-invitation')).not.toBeInTheDocument();
expect(invokeIpc).not.toHaveBeenCalled();
expect(fetch).not.toHaveBeenCalled();
expect(document.querySelector('iframe')).toBeNull();
const button = screen.getByRole('button', { name: '作品广场' });
expect(button).toHaveAttribute('type', 'button');
- expect(button.querySelector('.coding-start-mark img')).not.toBeNull();
- expect(button.querySelectorAll('.coding-start-mark > svg')).toHaveLength(2);
+ expect(button).toHaveTextContent('作品广场');
+ expect(button.querySelector('svg')).toBeNull();
fireEvent.click(button);
await waitFor(() => expect(invokeIpc).toHaveBeenCalledExactlyOnceWith('shell:openExternal', COMMUNITY_URL));
fetch.mockRestore();
@@ -44,7 +42,7 @@ it('provides an accessible collapsed sidebar entry and a retry after opening fai
it('keeps the welcome entry available for retry without submitting its surrounding form', async () => {
const submit = vi.fn((event: FormEvent) => event.preventDefault());
vi.mocked(invokeIpc).mockRejectedValueOnce(new Error('unavailable'));
- render(
);
+ render(
);
const button = screen.getByRole('button', { name: '作品广场' });
fireEvent.click(button);
await screen.findByRole('alert');
@@ -56,3 +54,12 @@ it('keeps the welcome entry available for retry without submitting its surroundi
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
expect(submit).not.toHaveBeenCalled();
});
+
+
+it('keeps the Code welcome mark decorative after moving the gallery to home', () => {
+ render(
);
+ expect(screen.getByRole('heading', { name: '今天,你想创造什么?' })).toBeInTheDocument();
+ expect(screen.getByRole('img', { name: '麦洛' })).toBeInTheDocument();
+ expect(screen.queryByRole('button', { name: '作品广场' })).not.toBeInTheDocument();
+ expect(document.querySelectorAll('.coding-start-mark > svg')).toHaveLength(2);
+});
diff --git a/tests/unit/module-navigation.test.tsx b/tests/unit/module-navigation.test.tsx
index 433ca5a4..becc4807 100644
--- a/tests/unit/module-navigation.test.tsx
+++ b/tests/unit/module-navigation.test.tsx
@@ -105,7 +105,11 @@ describe('AI module navigation', () => {
expect(screen.getByTestId('ai-module-selection-page')).toBeInTheDocument();
expect(screen.getByRole('heading', { name: '今天,想创作什么?' })).toBeInTheDocument();
expect(screen.getByTestId('ai-module-options')).toHaveClass('module-selection-options');
- expect(await screen.findByText('小明,欢迎回来!')).toBeInTheDocument();
+ expect(await screen.findByText('小明欢迎回来!')).toBeInTheDocument();
+ const gallery = screen.getByRole('button', { name: '作品广场', exact: true });
+ expect(screen.getAllByRole('button', { name: '作品广场', exact: true })).toHaveLength(1);
+ expect(gallery.textContent).toBe('作品广场');
+ expect(gallery.closest('p')).toHaveTextContent(/^还是去「作品广场」找找灵感?$/u);
expect(screen.getByTestId('ai-module-option-programming')).toHaveClass('module-option-card');
for (const moduleId of ['programming', 'painting', 'cloud_agents'] as const) {
expect(screen.getByTestId(`ai-module-option-${moduleId}`).querySelector('.module-option-artwork')).toBeNull();
@@ -222,6 +226,7 @@ describe('AI module navigation', () => {
);
expect(screen.getByTestId('ai-module-selection-page')).toBeInTheDocument();
+ expect(screen.getByText('欢迎回来!', { exact: true })).toBeInTheDocument();
fireEvent.click(screen.getByTestId('ai-module-option-programming'));
expect(await screen.findByTestId('location-path')).toHaveTextContent('/login');
@@ -240,7 +245,7 @@ describe('AI module navigation', () => {
expect(screen.getByTestId('sidebar-module-switcher-trigger')).toHaveTextContent('绘画 Canvas');
expect(screen.getByTestId('sidebar-module-switcher-trigger')).toHaveAttribute('aria-label', '返回首页');
expect(screen.getByTestId('sidebar-module-switcher-trigger')).toHaveClass('p-3');
- expect(screen.getByTestId('sidebar-module-return-icon')).toHaveClass('lucide-arrow-left', '-translate-y-px');
+ expect(screen.getByTestId('sidebar-module-return-icon')).toHaveClass('lucide-house', '-translate-y-px');
expect(screen.queryByTestId('sidebar-module-switcher-menu')).not.toBeInTheDocument();
fireEvent.click(screen.getByTestId('sidebar-module-switcher-trigger'));
@@ -296,7 +301,7 @@ describe('AI module navigation', () => {
fireEvent.click(screen.getByRole('button', { name: '保存个人资料' }));
await waitFor(() => expect(screen.queryByRole('dialog', { name: '个人资料' })).not.toBeInTheDocument());
- expect(screen.getByText('小红,欢迎回来!')).toBeInTheDocument();
+ expect(screen.getByText('小红欢迎回来!')).toBeInTheDocument();
});
it('shows Robot as the active module and returns to the chooser from the AI hardware route', async () => {