fix(test): stabilize E2E cockpit flow and add LM Studio novel integration test

Harden cockpit dismiss timing and global settings navigation in E2E helpers, tune short-chapter AI prompts for local models, fix electron-updater CJS loading, and add a 10-chapter auto-writing integration test against LM Studio.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-09 14:03:19 +08:00
parent fe421ffc55
commit 8e5045b882
24 changed files with 425 additions and 70 deletions
+13 -6
View File
@@ -25,15 +25,22 @@ async function createAndOpenBook(page: Page, name = 'AI测试书'): Promise<void
await page.locator('.dialog-content input').first().fill(name)
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
}
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
async function openGlobalSettings(page: Page): Promise<void> {
const topSettings = page.locator('#topbar button.top-btn').filter({ hasText: '⚙' })
if (await topSettings.isVisible()) {
await topSettings.click()
} else {
await page.getByRole('button', { name: /系统设置/ }).click()
}
await page.getByTestId('topbar-settings').click()
await expect(page.locator('#settings-page')).toBeVisible({ timeout: 10_000 })
}
+6 -2
View File
@@ -28,9 +28,12 @@ async function skipOnboarding(page: Page): Promise<void> {
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
if (await cockpit.isVisible({ timeout: 3000 }).catch(() => false)) {
await page.locator('[data-testid="cockpit-modal"] .modal-close').click()
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
@@ -43,6 +46,7 @@ async function createAndOpenBook(page: Page, name: string): Promise<void> {
}
async function ensureChapterEditor(page: Page): Promise<void> {
await dismissCockpitIfOpen(page)
const items = page.locator('.chapter-item')
if ((await items.count()) === 0) {
await page.getByRole('button', { name: '+ 新章' }).click()
+21 -3
View File
@@ -20,7 +20,19 @@ async function skipOnboarding(page: Page): Promise<void> {
}
}
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
async function ensureChapterEditor(page: Page): Promise<void> {
await dismissCockpitIfOpen(page)
await page.getByRole('button', { name: '+ 新章' }).click()
await expect(page.locator('.ProseMirror')).toBeVisible({ timeout: 10_000 })
}
@@ -30,6 +42,7 @@ async function openBookWithContent(page: Page): Promise<void> {
await page.locator('.dialog-content input').first().fill('自动测试书')
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
await ensureChapterEditor(page)
const editor = page.locator('.ProseMirror')
await editor.click()
@@ -48,8 +61,11 @@ async function openAutoPanel(page: Page): Promise<void> {
async function planAutoScenes(page: Page): Promise<void> {
await page.getByTestId('auto-goal-input').fill('林远通过入门考核,展现天赋震惊众人')
const wordInputs = page.locator('.auto-word-range input')
await wordInputs.nth(0).fill('180')
await wordInputs.nth(1).fill('220')
await page.getByTestId('auto-plan-scenes').click()
await expect(page.getByTestId('auto-start-generate')).toBeVisible({ timeout: 240_000 })
await expect(page.getByTestId('auto-start-generate')).toBeVisible({ timeout: 360_000 })
}
async function generateOneAutoScene(page: Page): Promise<void> {
@@ -68,6 +84,7 @@ async function generateOneAutoScene(page: Page): Promise<void> {
}
test.describe('Auto writing', () => {
test.describe.configure({ retries: 1 })
let userDataDir: string
test.beforeEach(() => {
@@ -92,13 +109,14 @@ test.describe('Auto writing', () => {
await page.locator('.dialog-content input').first().fill('空书')
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
await page.getByTestId('panel-tab-ai').click()
await expect(page.getByTestId('ai-mode-tab-auto')).toBeDisabled({ timeout: 10_000 })
await app.close()
})
test('E2E-AUTO-01: auto flow creates chapter', async () => {
test.setTimeout(300_000)
test.setTimeout(600_000)
const app = await launchApp(userDataDir)
const page = await app.firstWindow()
await skipOnboarding(page)
@@ -113,7 +131,7 @@ test.describe('Auto writing', () => {
})
test('E2E-AUTO-02: pause and handoff to interactive', async () => {
test.setTimeout(300_000)
test.setTimeout(600_000)
const app = await launchApp(userDataDir)
const page = await app.firstWindow()
await skipOnboarding(page)
+33 -15
View File
@@ -27,9 +27,12 @@ async function skipOnboarding(page: Page): Promise<void> {
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
if (await cockpit.isVisible({ timeout: 3000 }).catch(() => false)) {
await page.locator('[data-testid="cockpit-modal"] .modal-close').click()
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
@@ -47,7 +50,9 @@ async function createCharacterSetting(page: Page, name: string): Promise<void> {
await page.getByTestId('setting-name-input').fill(name)
await page.locator('.dialog-content select').selectOption('character')
await page.getByRole('button', { name: '创建' }).click()
await expect(page.getByTestId(/setting-item-/)).toContainText(name, { timeout: 10_000 })
await expect(page.getByTestId(/setting-item-/).filter({ hasText: name })).toBeVisible({
timeout: 10_000
})
}
test.describe('Character graph P7', () => {
@@ -75,11 +80,15 @@ test.describe('Character graph P7', () => {
await createCharacterSetting(page, '角色甲')
await createCharacterSetting(page, '角色乙')
await page.getByText('角色甲').click()
await page.getByTestId(/setting-item-/).filter({ hasText: '角色甲' }).click()
await expect(page.getByTestId('setting-relationships')).toBeVisible({ timeout: 10_000 })
await page.getByTestId('setting-relationship-add').click()
await page.locator('.setting-relationship-form select').selectOption({ label: '角色乙' })
await page.locator('.setting-relationship-form input[type="text"]').fill('挚友')
await expect(page.locator('.setting-relationship-form select')).toBeVisible({ timeout: 10_000 })
await page.locator('.setting-relationship-form select').selectOption({ index: 1 })
await expect(page.locator('.setting-relationship-form input.form-control')).toBeVisible({
timeout: 10_000
})
await page.locator('.setting-relationship-form input.form-control').fill('挚友')
await page.locator('.setting-relationship-form .btn-primary').click()
await expect(page.getByTestId('setting-relationships')).toContainText('挚友')
@@ -98,21 +107,30 @@ test.describe('Character graph P7', () => {
await createCharacterSetting(page, '节点A')
await createCharacterSetting(page, '节点B')
await page.getByText('节点A').click()
await page.getByTestId(/setting-item-/).filter({ hasText: '节点A' }).click()
await page.getByTestId('setting-relationship-add').click()
await page.locator('.setting-relationship-form select').selectOption({ label: '节点B' })
await page.locator('.setting-relationship-form input[type="text"]').fill('同门')
await expect(page.locator('.setting-relationship-form select')).toBeVisible({ timeout: 10_000 })
await page.locator('.setting-relationship-form select').selectOption({ index: 1 })
await expect(page.locator('.setting-relationship-form input.form-control')).toBeVisible({
timeout: 10_000
})
await page.locator('.setting-relationship-form input.form-control').fill('同门')
await page.locator('.setting-relationship-form .btn-primary').click()
await page.getByTestId('setting-open-graph').click()
await expect(page.getByTestId('character-graph-cy')).toBeVisible({ timeout: 10_000 })
const cyBox = await page.getByTestId('character-graph-cy').boundingBox()
expect(cyBox).toBeTruthy()
await page.getByTestId('character-graph-cy').click({
position: { x: (cyBox!.width ?? 200) / 2, y: (cyBox!.height ?? 200) / 2 }
await page.evaluate(() => {
const el = document.querySelector('[data-testid="character-graph-cy"]') as HTMLElement & {
__bilinCy?: { nodes: () => { trigger: (e: string) => void }[] }
}
const cy = el?.__bilinCy
if (!cy) throw new Error('cytoscape not ready')
const nodes = cy.nodes()
if (!nodes.length) throw new Error('no graph nodes')
nodes[0]!.trigger('tap')
})
await expect(page.getByTestId('character-graph-sidebar')).toContainText(/节点/, {
timeout: 5000
await expect(page.getByTestId('character-graph-sidebar')).toContainText(/节点A|节点B/, {
timeout: 10_000
})
await app.close()
})
+5 -2
View File
@@ -27,9 +27,12 @@ async function skipOnboarding(page: Page): Promise<void> {
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
if (await cockpit.isVisible({ timeout: 3000 }).catch(() => false)) {
await page.locator('[data-testid="cockpit-modal"] .modal-close').click()
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
+11 -2
View File
@@ -23,12 +23,20 @@ export async function skipOnboarding(page: Page): Promise<void> {
export async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
if (await cockpit.isVisible({ timeout: 3000 }).catch(() => false)) {
await page.locator('[data-testid="cockpit-modal"] .modal-close').click()
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
export async function openGlobalSettings(page: Page): Promise<void> {
await page.getByTestId('topbar-settings').click()
await expect(page.locator('#settings-page')).toBeVisible({ timeout: 10_000 })
}
export async function createBookAndOpenEditor(page: Page, name: string): Promise<void> {
await page.getByRole('button', { name: /新建书籍/ }).first().click()
await page.locator('.dialog-content input').first().fill(name)
@@ -38,6 +46,7 @@ export async function createBookAndOpenEditor(page: Page, name: string): Promise
}
export async function ensureChapterEditor(page: Page): Promise<void> {
await dismissCockpitIfOpen(page)
const editor = page.locator('.ProseMirror')
if (await editor.isVisible().catch(() => false)) return
await page.getByRole('button', { name: '+ 新章' }).click()
+21 -5
View File
@@ -20,7 +20,19 @@ async function skipOnboarding(page: Page): Promise<void> {
}
}
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
async function ensureChapterEditor(page: Page): Promise<void> {
await dismissCockpitIfOpen(page)
await page.getByRole('button', { name: '+ 新章' }).click()
await expect(page.locator('.ProseMirror')).toBeVisible({ timeout: 10_000 })
}
@@ -30,6 +42,7 @@ async function openBookWithContent(page: Page): Promise<void> {
await page.locator('.dialog-content input').first().fill('交互测试书')
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
await ensureChapterEditor(page)
const editor = page.locator('.ProseMirror')
await editor.click()
@@ -39,6 +52,7 @@ async function openBookWithContent(page: Page): Promise<void> {
}
test.describe('Interactive writing', () => {
test.describe.configure({ retries: 1 })
let userDataDir: string
test.beforeEach(() => {
@@ -63,13 +77,14 @@ test.describe('Interactive writing', () => {
await page.locator('.dialog-content input').first().fill('空书')
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
await page.getByTestId('panel-tab-ai').click()
await expect(page.getByTestId('ai-mode-tab-interactive')).toBeDisabled({ timeout: 10_000 })
await app.close()
})
test('E2E-INT-01: interactive flow creates chapter', async () => {
test.setTimeout(300_000)
test.setTimeout(600_000)
const app = await launchApp(userDataDir)
const page = await app.firstWindow()
await skipOnboarding(page)
@@ -85,21 +100,21 @@ test.describe('Interactive writing', () => {
await page.getByTestId('interactive-confirm-plot').click()
const naming0 = page.getByTestId('naming-option-0')
const acceptBtn = page.getByTestId('interactive-accept-scene')
await expect(naming0.or(acceptBtn)).toBeVisible({ timeout: 120_000 })
await expect(naming0.or(acceptBtn)).toBeVisible({ timeout: 240_000 })
if (await naming0.isVisible()) {
await naming0.click()
await expect(acceptBtn).toBeVisible({ timeout: 120_000 })
await expect(acceptBtn).toBeVisible({ timeout: 240_000 })
}
await acceptBtn.click()
const finishBtn = page.getByTestId('interactive-finish-chapter')
await expect(finishBtn).toBeVisible({ timeout: 120_000 })
await expect(finishBtn).toBeEnabled({ timeout: 240_000 })
await finishBtn.click()
await expect(page.locator('.ProseMirror')).not.toBeEmpty({ timeout: 30_000 })
await app.close()
})
test('E2E-INT-03: restores flow after restart', async () => {
test.setTimeout(300_000)
test.setTimeout(600_000)
const app1 = await launchApp(userDataDir)
const page1 = await app1.firstWindow()
await skipOnboarding(page1)
@@ -129,6 +144,7 @@ test.describe('Interactive writing', () => {
await skipOnboarding(page2)
await page2.getByText('交互测试书').click()
await expect(page2.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page2)
await page2.getByTestId('panel-tab-ai').click()
await expect(page2.getByTestId('ai-session-select')).not.toHaveValue('', { timeout: 10_000 })
await page2.getByTestId('ai-session-select').selectOption(sessionValue)
+9 -4
View File
@@ -27,9 +27,12 @@ async function skipOnboarding(page: Page): Promise<void> {
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
if (await cockpit.isVisible({ timeout: 3000 }).catch(() => false)) {
await page.locator('[data-testid="cockpit-modal"] .modal-close').click()
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
@@ -73,6 +76,7 @@ test.describe('Knowledge extraction P5.2', () => {
await page.locator('[data-testid="panel-tab-knowledge"]').click()
await expect(page.locator('[data-testid="knowledge-panel"]')).toBeVisible()
await page.locator('[data-testid="knowledge-tab-pending"]').click()
await expect(page.locator('[data-testid="knowledge-extract-current"]')).toBeVisible()
await expect(page.locator('[data-testid="knowledge-batch-high-confidence"]')).toBeVisible()
await app.close()
@@ -82,9 +86,10 @@ test.describe('Knowledge extraction P5.2', () => {
const app = await launchApp(userDataDir)
const page = await app.firstWindow()
await skipOnboarding(page)
await createAndOpenBook(page, '设置测试')
await createAndOpenBook(page, '全局设置测试')
await page.getByRole('button', { name: '设置' }).click()
await page.getByTestId('topbar-settings').click()
await expect(page.locator('#settings-page')).toBeVisible({ timeout: 10_000 })
await expect(page.locator('[data-testid="settings-knowledge-auto-extract"]')).toBeVisible()
await expect(page.locator('[data-testid="settings-extract-threshold"]')).toBeVisible()
await app.close()
+14 -5
View File
@@ -20,15 +20,23 @@ async function skipOnboarding(page: Page): Promise<void> {
}
}
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
async function openBookWithContent(page: Page): Promise<void> {
await page.getByRole('button', { name: /新建书籍/ }).first().click()
await page.locator('.dialog-content input').first().fill('注入历史书')
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
const cockpit = page.locator('[data-testid="cockpit-modal"]')
if (await cockpit.isVisible({ timeout: 3000 }).catch(() => false)) {
await page.locator('[data-testid="cockpit-modal"] .modal-close').click()
}
await dismissCockpitIfOpen(page)
await page.getByRole('button', { name: '+ 新章' }).click()
const editor = page.locator('.ProseMirror')
await expect(editor).toBeVisible({ timeout: 10_000 })
@@ -96,7 +104,8 @@ test.describe('Knowledge injection history P6.1', () => {
await page.getByTestId('context-tab-knowledge').click()
await page.locator(`[data-testid="context-knowledge-item-${entryId}"] input`).check()
await page.getByTestId('context-save').click()
await page.getByRole('button', { name: '下一步' }).click()
await expect(page.getByTestId('context-editor-modal')).toBeHidden({ timeout: 10_000 })
await page.locator('[data-testid="wizard-panel"] button.btn.primary').click()
await page.locator('[data-testid="panel-tab-knowledge"]').click()
await page.locator('[data-testid="knowledge-tab-all"]').click()
+12
View File
@@ -20,11 +20,23 @@ async function skipOnboarding(page: Page): Promise<void> {
}
}
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
async function createAndOpenBook(page: Page): Promise<void> {
await page.getByRole('button', { name: /新建书籍/ }).first().click()
await page.locator('.dialog-content input').first().fill('覆盖度测试')
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
}
test.describe('Outline coverage', () => {
+12
View File
@@ -26,11 +26,23 @@ async function skipOnboarding(page: Page): Promise<void> {
}
}
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
async function createAndOpenBook(page: Page, name: string): Promise<void> {
await page.getByRole('button', { name: /新建书籍/ }).first().click()
await page.locator('.dialog-content input').first().fill(name)
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
}
test.describe('Outline persistence', () => {
+13
View File
@@ -20,14 +20,27 @@ async function skipOnboarding(page: Page): Promise<void> {
}
}
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
async function createAndOpenBook(page: Page, name = 'P2测试书'): Promise<void> {
await page.getByRole('button', { name: /新建书籍/ }).first().click()
await page.locator('.dialog-content input').first().fill(name)
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
}
async function ensureChapterEditor(page: Page): Promise<void> {
await dismissCockpitIfOpen(page)
await page.getByRole('button', { name: '+ 新章' }).click()
await expect(page.locator('.ProseMirror')).toBeVisible({ timeout: 10_000 })
}
+14
View File
@@ -20,14 +20,27 @@ async function skipOnboarding(page: Page): Promise<void> {
}
}
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
async function createAndOpenBook(page: Page): Promise<void> {
await page.getByRole('button', { name: /新建书籍/ }).first().click()
await page.locator('.dialog-content input').first().fill('拖拽测试')
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
}
async function ensureChapterEditor(page: Page): Promise<void> {
await dismissCockpitIfOpen(page)
await page.getByRole('button', { name: '+ 新章' }).click()
await expect(page.locator('.ProseMirror')).toBeVisible({ timeout: 10_000 })
}
@@ -150,6 +163,7 @@ test.describe('P2.1 features', () => {
await page.getByTestId('inspiration-save-btn').click()
await expect(page.getByTestId('inspiration-modal')).toBeHidden({ timeout: 10_000 })
await page.getByTestId('sidebar-tab-inspiration').click()
await expect(page.getByTestId('inspiration-list')).toBeVisible()
await expect(page.getByTestId('inspiration-list').getByText(idea)).toBeVisible({ timeout: 10_000 })
await app.close()
+12
View File
@@ -26,11 +26,23 @@ async function skipOnboarding(page: Page): Promise<void> {
}
}
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
async function createBookAndOpenEditor(page: Page, name: string): Promise<void> {
await page.getByRole('button', { name: /新建书籍/ }).first().click()
await page.locator('.dialog-content input').first().fill(name)
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
await page.getByRole('button', { name: '+ 新章' }).click()
await expect(page.locator('.ProseMirror')).toBeVisible({ timeout: 10_000 })
}
+17
View File
@@ -20,7 +20,19 @@ async function skipOnboarding(page: Page): Promise<void> {
}
}
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
async function ensureChapterEditor(page: Page): Promise<void> {
await dismissCockpitIfOpen(page)
await page.getByRole('button', { name: '+ 新章' }).click()
await expect(page.locator('.ProseMirror')).toBeVisible({ timeout: 10_000 })
}
@@ -30,6 +42,7 @@ async function openBookWithContent(page: Page): Promise<void> {
await page.locator('.dialog-content input').first().fill('向导测试书')
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
await ensureChapterEditor(page)
const editor = page.locator('.ProseMirror')
await editor.click()
@@ -81,6 +94,7 @@ test.describe('Wizard writing', () => {
await page.locator('.dialog-content input').first().fill('空书')
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
await page.getByTestId('panel-tab-ai').click()
await expect(page.getByTestId('ai-mode-tab-wizard')).toBeDisabled({ timeout: 10_000 })
await app.close()
@@ -95,6 +109,9 @@ test.describe('Wizard writing', () => {
await openWizardPanel(page)
await page.getByTestId('wizard-goal-input').fill('林远通过入门考核,展现天赋震惊众人')
const wordInputs = page.locator('.auto-word-range input')
await wordInputs.nth(0).fill('180')
await wordInputs.nth(1).fill('220')
await page.getByRole('button', { name: '下一步' }).click()
await page.getByTestId('wizard-rhythm-mixed').click()
+10 -4
View File
@@ -28,9 +28,12 @@ async function skipOnboarding(page: Page): Promise<void> {
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
if (await cockpit.isVisible({ timeout: 3000 }).catch(() => false)) {
await page.locator('[data-testid="cockpit-modal"] .modal-close').click()
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
@@ -43,6 +46,7 @@ async function createAndOpenBook(page: Page, name: string): Promise<void> {
}
async function ensureChapterEditor(page: Page): Promise<void> {
await dismissCockpitIfOpen(page)
const items = page.locator('.chapter-item')
if ((await items.count()) === 0) {
await page.getByRole('button', { name: '+ 新章' }).click()
@@ -104,9 +108,11 @@ test.describe('Writing analytics P9', () => {
await page.getByTestId('setting-name-input').fill('主角视角')
await page.locator('.dialog-content select').selectOption('character')
await page.getByRole('button', { name: '创建' }).click()
await expect(page.getByText('主角视角')).toBeVisible({ timeout: 10_000 })
await expect(page.getByTestId(/setting-item-/).filter({ hasText: '主角视角' })).toBeVisible({
timeout: 10_000
})
await page.getByTestId('sidebar-tab-chapter').click()
await page.getByTestId('sidebar-tab-chapters').click()
await ensureChapterEditor(page)
await page.getByTestId('chapter-pov-select').selectOption({ label: '主角视角' })
+12 -7
View File
@@ -30,9 +30,12 @@ async function skipOnboarding(page: Page): Promise<void> {
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
if (await cockpit.isVisible({ timeout: 3000 }).catch(() => false)) {
await page.locator('[data-testid="cockpit-modal"] .modal-close').click()
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
@@ -45,6 +48,7 @@ async function createAndOpenBook(page: Page, name: string): Promise<void> {
}
async function ensureChapterEditor(page: Page): Promise<void> {
await dismissCockpitIfOpen(page)
const items = page.locator('.chapter-item')
if ((await items.count()) === 0) {
await page.getByRole('button', { name: '+ 新章' }).click()
@@ -88,17 +92,18 @@ test.describe('Writing goals P5.3', () => {
await createAndOpenBook(page, '目标测试书')
await ensureChapterEditor(page)
await page.getByRole('button', { name: '设置' }).click()
await page.locator('[data-testid="settings-daily-word-goal"]').fill('100')
await page.getByRole('button', { name: '书籍' }).click()
await page.getByTestId('topbar-settings').click()
await expect(page.locator('#settings-page')).toBeVisible({ timeout: 10_000 })
await page.locator('[data-testid="settings-daily-word-goal"]').fill('30')
await page.getByRole('button', { name: '目标测试书' }).click()
await ensureChapterEditor(page)
const editor = page.locator('.ProseMirror')
await editor.click()
await editor.pressSequentially(
'这是一段用于测试每日写作目标达标的示例文字,需要足够长度才能触发字数统计与达标提醒通知。'
'这是一段用于测试每日写作目标达标的示例文字,需要足够长度才能触发字数统计与达标提醒通知。继续书写更多内容以确保超过三十字的目标阈值。'
)
await page.waitForTimeout(1500)
await page.waitForTimeout(2500)
await expect(page.locator('[data-testid="status-daily-goal"].goal-met')).toBeVisible({
timeout: 15_000
+11 -6
View File
@@ -27,9 +27,12 @@ async function skipOnboarding(page: Page): Promise<void> {
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
if (await cockpit.isVisible({ timeout: 3000 }).catch(() => false)) {
await page.locator('[data-testid="cockpit-modal"] .modal-close').click()
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
@@ -64,9 +67,10 @@ test.describe('Writing logs P5.2', () => {
await skipOnboarding(page)
await createAndOpenBook(page, '日志测试书')
await page.getByRole('button', { name: '设置' }).click()
await page.getByTestId('topbar-settings').click()
await expect(page.locator('#settings-page')).toBeVisible({ timeout: 10_000 })
await page.locator('[data-testid="settings-daily-word-goal"]').fill('100')
await page.getByRole('button', { name: '书' }).click()
await page.getByRole('button', { name: '日志测试书' }).click()
const items = page.locator('.chapter-item')
if ((await items.count()) === 0) {
@@ -92,9 +96,10 @@ test.describe('Writing logs P5.2', () => {
await skipOnboarding(page)
await createAndOpenBook(page, '状态栏测试')
await page.getByRole('button', { name: '设置' }).click()
await page.getByTestId('topbar-settings').click()
await expect(page.locator('#settings-page')).toBeVisible({ timeout: 10_000 })
await page.locator('[data-testid="settings-daily-word-goal"]').fill('500')
await page.getByRole('button', { name: '书籍' }).click()
await page.getByRole('button', { name: '状态栏测试' }).click()
const items = page.locator('.chapter-item')
if ((await items.count()) === 0) {
+13
View File
@@ -25,11 +25,23 @@ async function skipOnboarding(page: Page): Promise<void> {
await expect(page.getByText('欢迎使用笔临')).toBeHidden({ timeout: 10_000 })
}
async function dismissCockpitIfOpen(page: Page): Promise<void> {
const cockpit = page.locator('[data-testid="cockpit-modal"]')
try {
await cockpit.waitFor({ state: 'visible', timeout: 15_000 })
await cockpit.locator('.modal-close').click()
await expect(cockpit).toBeHidden({ timeout: 5000 })
} catch {
/* cockpit did not open */
}
}
async function createAndOpenBook(page: Page, name: string): Promise<void> {
await page.getByRole('button', { name: /新建书籍/ }).first().click()
await page.locator('.dialog-content input').first().fill(name)
await page.getByRole('button', { name: '创建' }).click()
await expect(page.locator('#editor-layout')).toBeVisible({ timeout: 15_000 })
await dismissCockpitIfOpen(page)
}
test.describe('Writing', () => {
@@ -54,6 +66,7 @@ test.describe('Writing', () => {
const page1 = await app1.firstWindow()
await skipOnboarding(page1)
await createAndOpenBook(page1, '测试书')
await dismissCockpitIfOpen(page1)
await page1.getByRole('button', { name: '+ 新章' }).click()
const editor = page1.locator('.ProseMirror')