From a056f889bd6c170123fc3c93f5ec23e43f0b17b0 Mon Sep 17 00:00:00 2001
From: wuyiping0628 <1106773985@qq.com>
Date: Tue, 25 Aug 2026 05:08:47 -0700
Subject: [PATCH 1/2] feat: add e2e
---
.../demos/add-toolbar-item.spec.ts | 7 +-
packages/docs/fluent-editor/demos/ai.spec.ts | 19 +++++
.../fluent-editor/demos/basic-usage.spec.ts | 2 +-
.../demos/code-block-highlight.spec.ts | 24 ++++++
.../demos/collaborative-editing.spec.ts | 81 +++++++++++++++----
.../fluent-editor/demos/counter-count.spec.ts | 2 +-
.../docs/fluent-editor/demos/counter.spec.ts | 2 +-
.../demos/custom-toolbar.spec.ts | 19 +++++
.../docs/fluent-editor/demos/emoji.spec.ts | 17 ++++
.../demos/file-upload-handle.spec.ts | 31 +++++++
.../fluent-editor/demos/file-upload.spec.ts | 2 +-
.../demos/flow-chart-background.spec.ts | 16 ++++
.../demos/flow-chart-grid.spec.ts | 15 ++++
.../demos/flow-chart-resize.spec.ts | 15 ++++
.../fluent-editor/demos/flow-chart.spec.ts | 2 +-
.../demos/format-painter.spec.ts | 25 ++++++
.../demos/formula-chemistry.spec.ts | 22 +++++
.../demos/formula-mathlive.spec.ts | 15 ++++
.../docs/fluent-editor/demos/formula.spec.ts | 20 +++++
.../demos/get-content-delta.spec.ts | 2 +-
.../demos/get-content-html.spec.ts | 2 +-
.../demos/header-list-container.spec.ts | 2 +-
.../fluent-editor/demos/header-list.spec.ts | 2 +-
.../fluent-editor/demos/i18n-custom.spec.ts | 24 ++++++
.../docs/fluent-editor/demos/i18n.spec.ts | 19 +++++
.../demos/image-toolbar-button.spec.ts | 21 +++++
.../fluent-editor/demos/image-toolbar.spec.ts | 22 +++++
.../docs/fluent-editor/demos/markdown.spec.ts | 19 +++++
.../demos/mention-custom-list.spec.ts | 19 +++++
.../fluent-editor/demos/mention-link.spec.ts | 28 +++++++
.../docs/fluent-editor/demos/mention.spec.ts | 19 +++++
.../demos/mind-map-background.spec.ts | 23 ++++++
.../fluent-editor/demos/mind-map-line.spec.ts | 15 ++++
.../demos/mind-map-resize.spec.ts | 14 ++++
.../demos/mind-map-theme.spec.ts | 15 ++++
.../docs/fluent-editor/demos/mind-map.spec.ts | 2 +-
.../docs/fluent-editor/demos/readonly.spec.ts | 2 +-
.../demos/screenshot-upload-to-server.spec.ts | 13 +++
.../fluent-editor/demos/screenshot.spec.ts | 13 +++
.../demos/set-content-delta.spec.ts | 2 +-
.../demos/set-content-html.spec.ts | 2 +-
.../fluent-editor/demos/shortcut-key.spec.ts | 18 +++++
.../demos/table-up-clipboard.spec.ts | 14 ++++
.../fluent-editor/demos/table-up-menu.spec.ts | 17 ++++
.../demos/table-up-resize.spec.ts | 18 +++++
.../demos/table-up-shortcut.spec.ts | 21 +++++
.../fluent-editor/demos/toolbar-tip.spec.ts | 18 +++++
47 files changed, 690 insertions(+), 32 deletions(-)
create mode 100644 packages/docs/fluent-editor/demos/ai.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/code-block-highlight.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/custom-toolbar.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/emoji.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/file-upload-handle.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/flow-chart-background.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/flow-chart-grid.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/flow-chart-resize.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/format-painter.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/formula-chemistry.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/formula-mathlive.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/formula.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/i18n-custom.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/i18n.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/image-toolbar-button.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/image-toolbar.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/markdown.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/mention-custom-list.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/mention-link.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/mention.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/mind-map-background.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/mind-map-line.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/mind-map-resize.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/mind-map-theme.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/screenshot-upload-to-server.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/screenshot.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/shortcut-key.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/table-up-clipboard.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/table-up-menu.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/table-up-resize.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/table-up-shortcut.spec.ts
create mode 100644 packages/docs/fluent-editor/demos/toolbar-tip.spec.ts
diff --git a/packages/docs/fluent-editor/demos/add-toolbar-item.spec.ts b/packages/docs/fluent-editor/demos/add-toolbar-item.spec.ts
index bfe2d1f1..1599f2a0 100644
--- a/packages/docs/fluent-editor/demos/add-toolbar-item.spec.ts
+++ b/packages/docs/fluent-editor/demos/add-toolbar-item.spec.ts
@@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test'
test.describe('AddToolbarItem.vue', () => {
test.beforeEach(async ({ page }) => {
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/add-toolbar-item')
+ await page.goto('/tiny-editor/docs/demo/add-toolbar-item')
})
test('should render the editor', async ({ page }) => {
@@ -36,8 +36,9 @@ test.describe('AddToolbarItem.vue', () => {
await page.keyboard.type(' Bad')
const editorHtml = await editor.innerHTML()
- expect(editorHtml).toContain(' Good')
- expect(editorHtml).toContain(' Bad')
+ // Quill stores a leading space in inline formats as
+ expect(editorHtml).toContain(' Good')
+ expect(editorHtml).toContain(' Bad')
})
// 添加更多测试用例
diff --git a/packages/docs/fluent-editor/demos/ai.spec.ts b/packages/docs/fluent-editor/demos/ai.spec.ts
new file mode 100644
index 00000000..09045296
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/ai.spec.ts
@@ -0,0 +1,19 @@
+import { expect, test } from '@playwright/test'
+
+test('has toolbar AI button and opens AI input dialog', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/ai')
+
+ const toolbar = page.locator('.ql-toolbar').first()
+ const editor = page.locator('#editor-add-toolbar-item .ql-editor')
+ const aiBtn = toolbar.locator('.ql-ai')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toContainText('我曾经跨过山和大海')
+ await expect(aiBtn).toBeVisible()
+
+ await editor.click()
+ await aiBtn.click()
+ await expect(page.locator('.ql-ai-dialog')).toBeVisible()
+ await expect(page.locator('.ql-ai-input')).toBeVisible()
+})
diff --git a/packages/docs/fluent-editor/demos/basic-usage.spec.ts b/packages/docs/fluent-editor/demos/basic-usage.spec.ts
index 7d8cfc6b..db629b5b 100644
--- a/packages/docs/fluent-editor/demos/basic-usage.spec.ts
+++ b/packages/docs/fluent-editor/demos/basic-usage.spec.ts
@@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test'
test('has toolbar', async ({ page }) => {
// page.on('pageerror', exception => expect(exception).toBeNull())
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/basic-usage')
+ await page.goto('/tiny-editor/docs/demo/basic-usage')
const toolbar = page.locator('.ql-toolbar')
const editor = page.locator('.ql-editor')
diff --git a/packages/docs/fluent-editor/demos/code-block-highlight.spec.ts b/packages/docs/fluent-editor/demos/code-block-highlight.spec.ts
new file mode 100644
index 00000000..d8e160b1
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/code-block-highlight.spec.ts
@@ -0,0 +1,24 @@
+import { expect, test } from '@playwright/test'
+
+test('has toolbar, code-block button and golang language option', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/code-block-highlight')
+
+ const toolbar = page.locator('.ql-toolbar').first()
+ const editor = page.locator('#editor .ql-editor')
+ const codeBlockBtn = toolbar.locator('.ql-code-block')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(codeBlockBtn).toBeVisible()
+
+ await editor.click()
+ await codeBlockBtn.click()
+
+ const codeBlock = editor.locator('.ql-code-block-container').first()
+ await expect(codeBlock).toBeVisible()
+ await expect(codeBlock.locator('option[value="go"]')).toHaveText('Golang')
+
+ await editor.locator('.ql-code-block').first().click()
+ await page.keyboard.type('func main() {}')
+ await expect(editor.locator('.ql-code-block').first()).toContainText('func main() {}')
+})
diff --git a/packages/docs/fluent-editor/demos/collaborative-editing.spec.ts b/packages/docs/fluent-editor/demos/collaborative-editing.spec.ts
index fbe2cfbd..fb73cc90 100644
--- a/packages/docs/fluent-editor/demos/collaborative-editing.spec.ts
+++ b/packages/docs/fluent-editor/demos/collaborative-editing.spec.ts
@@ -1,12 +1,21 @@
-import { type Browser, chromium, expect, firefox, type Page, test } from '@playwright/test'
-
-const DEMO_URL = 'http://localhost:5173/tiny-editor/docs/demo/collaborative-editing'
+import { type Browser, chromium, expect, type Page, test } from '@playwright/test'
test.describe.configure({ mode: 'serial' })
-async function openTwoPages(): Promise<[Page, Page, Browser, Browser]> {
+let DEMO_URL = ''
+let collabWsReachable = false
+
+// Must match collaborative-editing.vue: serverUrl + roomName
+const COLLAB_WS_URL = 'wss://ai.opentiny.design/tiny-editor/tiny-editor-document-demo-roomName'
+
+function collabUrl(baseURL: string): string {
+ return `${baseURL.replace(/\/$/, '')}/tiny-editor/docs/demo/collaborative-editing`
+}
+
+async function openTwoPages(baseURL: string): Promise<[Page, Page, Browser, Browser]> {
+ DEMO_URL = collabUrl(baseURL)
const browser1 = await chromium.launch()
- const browser2 = await firefox.launch()
+ const browser2 = await chromium.launch()
const page1 = await browser1.newPage()
const page2 = await browser2.newPage()
@@ -17,15 +26,17 @@ async function openTwoPages(): Promise<[Page, Page, Browser, Browser]> {
])
const editor1 = page1.locator('.ql-editor')
const editor2 = page2.locator('.ql-editor')
- await expect(editor1).toBeVisible()
- await expect(editor2).toBeVisible()
+ await expect(editor1).toBeVisible({ timeout: 30_000 })
+ await expect(editor2).toBeVisible({ timeout: 30_000 })
return [page1, page2, browser1, browser2]
}
async function typeSync(page1: Page, page2: Page, text: string): Promise {
await page1.locator('.ql-editor').click()
await page1.keyboard.type(text)
- await expect.poll(async () => (await page2.locator('.ql-editor').textContent() || '').includes(text)).toBeTruthy()
+ await expect.poll(async () => (await page2.locator('.ql-editor').textContent() || '').includes(text), {
+ timeout: 15_000,
+ }).toBeTruthy()
}
async function selectAll(page: Page): Promise {
@@ -34,24 +45,62 @@ async function selectAll(page: Page): Promise {
let p1: Page, p2: Page, b1: Browser, b2: Browser
-test.beforeEach(async () => {
- [p1, p2, b1, b2] = await openTwoPages()
+test.beforeAll(async ({ browser }) => {
+ const page = await browser.newPage()
+ try {
+ collabWsReachable = await page.evaluate((wsUrl) => new Promise((resolve) => {
+ const ws = new WebSocket(wsUrl)
+ const timer = window.setTimeout(() => {
+ ws.close()
+ resolve(false)
+ }, 5000)
+ ws.addEventListener('open', () => {
+ window.clearTimeout(timer)
+ ws.close()
+ resolve(true)
+ })
+ ws.addEventListener('error', () => {
+ window.clearTimeout(timer)
+ resolve(false)
+ })
+ }), COLLAB_WS_URL)
+ }
+ finally {
+ await page.close()
+ }
+})
+
+test.beforeEach(async ({ baseURL }, testInfo) => {
+ const needsSync = testInfo.title !== 'startup collaborative-editing test'
+ if (needsSync && !collabWsReachable)
+ test.skip(true, 'remote collaborative websocket is not reachable')
+
+ ;[p1, p2, b1, b2] = await openTwoPages(baseURL!)
})
test.afterEach(async () => {
for (const page of [p1, p2]) {
- if (!page) continue
- const editor = page.locator('.ql-editor')
- if (await editor.count()) {
- await editor.first().click({ timeout: 2000 }).catch(() => {})
- await selectAll(page)
- await page.keyboard.press('Delete').catch(() => {})
+ if (!page || page.isClosed()) continue
+ try {
+ const editor = page.locator('.ql-editor')
+ if (await editor.count()) {
+ await editor.first().click({ timeout: 2000 }).catch(() => {})
+ await selectAll(page)
+ await page.keyboard.press('Delete').catch(() => {})
+ }
+ }
+ catch {
+ // page may already be closed when a test is skipped
}
}
await Promise.all([
b1?.close().catch(() => {}),
b2?.close().catch(() => {}),
])
+ p1 = undefined as unknown as Page
+ p2 = undefined as unknown as Page
+ b1 = undefined as unknown as Browser
+ b2 = undefined as unknown as Browser
})
test('startup collaborative-editing test', async () => {
diff --git a/packages/docs/fluent-editor/demos/counter-count.spec.ts b/packages/docs/fluent-editor/demos/counter-count.spec.ts
index da8ba12c..ab307ccc 100644
--- a/packages/docs/fluent-editor/demos/counter-count.spec.ts
+++ b/packages/docs/fluent-editor/demos/counter-count.spec.ts
@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test'
test('has toolbar and custom max character counter', async ({ page }) => {
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/counter')
+ await page.goto('/tiny-editor/docs/demo/counter')
const toolbar = page.locator('.ql-toolbar').nth(1)
const editor = page.locator('.ql-editor').nth(1)
diff --git a/packages/docs/fluent-editor/demos/counter.spec.ts b/packages/docs/fluent-editor/demos/counter.spec.ts
index 68728a12..965bf033 100644
--- a/packages/docs/fluent-editor/demos/counter.spec.ts
+++ b/packages/docs/fluent-editor/demos/counter.spec.ts
@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test'
test('has toolbar and default max character counter', async ({ page }) => {
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/counter')
+ await page.goto('/tiny-editor/docs/demo/counter')
const toolbar = page.locator('.ql-toolbar').first()
const editor = page.locator('.ql-editor').first()
diff --git a/packages/docs/fluent-editor/demos/custom-toolbar.spec.ts b/packages/docs/fluent-editor/demos/custom-toolbar.spec.ts
new file mode 100644
index 00000000..56a3f1d6
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/custom-toolbar.spec.ts
@@ -0,0 +1,19 @@
+import { expect, test } from '@playwright/test'
+
+test('renders full custom toolbar with extra tools and placeholder', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/custom-toolbar')
+
+ const block = page.locator('.vp-raw').first()
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = block.locator('.ql-editor').first()
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toHaveAttribute('data-placeholder', '请输入内容...')
+ await expect(toolbar.locator('.ql-undo')).toBeVisible()
+ await expect(toolbar.locator('.ql-redo')).toBeVisible()
+ await expect(toolbar.locator('.ql-emoji')).toBeVisible()
+ await expect(toolbar.locator('.ql-screenshot')).toBeVisible()
+ await expect(toolbar.locator('.ql-format-painter')).toBeVisible()
+ await expect(toolbar.locator('.ql-picker.ql-font')).toBeVisible()
+})
diff --git a/packages/docs/fluent-editor/demos/emoji.spec.ts b/packages/docs/fluent-editor/demos/emoji.spec.ts
new file mode 100644
index 00000000..714af210
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/emoji.spec.ts
@@ -0,0 +1,17 @@
+import { expect, test } from '@playwright/test'
+
+test('has toolbar emoji button and opens emoji picker', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/emoji')
+
+ const toolbar = page.locator('.ql-toolbar').first()
+ const editor = page.locator('#editor .ql-editor')
+ const emojiBtn = toolbar.locator('.ql-emoji')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(emojiBtn).toBeVisible()
+
+ await editor.click()
+ await emojiBtn.click()
+ await expect(page.locator('#emoji-picker')).toBeVisible()
+})
diff --git a/packages/docs/fluent-editor/demos/file-upload-handle.spec.ts b/packages/docs/fluent-editor/demos/file-upload-handle.spec.ts
new file mode 100644
index 00000000..805cb5e5
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/file-upload-handle.spec.ts
@@ -0,0 +1,31 @@
+import { expect, test } from '@playwright/test'
+
+const pngBuffer = Buffer.from(
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==',
+ 'base64',
+)
+
+test('uploads image and uses fail handler placeholder', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/file-upload')
+
+ const block = page.locator('.vp-raw').nth(1)
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = block.locator('.ql-editor').first()
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(toolbar.getByLabel('image')).toBeVisible()
+ await expect(toolbar.getByLabel('video')).toBeVisible()
+ await expect(toolbar.getByLabel('file')).toBeVisible()
+
+ await toolbar.getByLabel('image').click()
+ await toolbar.locator('input.ql-image').setInputFiles({
+ name: 'a.png',
+ mimeType: 'image/png',
+ buffer: pngBuffer,
+ })
+
+ const image = editor.getByRole('img').first()
+ await expect(image).toBeVisible({ timeout: 10_000 })
+ await expect(image).toHaveAttribute('src', /edge/)
+})
diff --git a/packages/docs/fluent-editor/demos/file-upload.spec.ts b/packages/docs/fluent-editor/demos/file-upload.spec.ts
index 8a68b8a8..f33f861b 100644
--- a/packages/docs/fluent-editor/demos/file-upload.spec.ts
+++ b/packages/docs/fluent-editor/demos/file-upload.spec.ts
@@ -7,7 +7,7 @@ const currentDirectory = path.dirname(__filename)
const baseUrl = path.dirname(currentDirectory)
test('image-upload', async ({ page }) => {
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/file-upload')
+ await page.goto('/tiny-editor/docs/demo/file-upload')
// upload
await page.getByLabel('image').first().click()
diff --git a/packages/docs/fluent-editor/demos/flow-chart-background.spec.ts b/packages/docs/fluent-editor/demos/flow-chart-background.spec.ts
new file mode 100644
index 00000000..ed64069f
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/flow-chart-background.spec.ts
@@ -0,0 +1,16 @@
+import { expect, test } from '@playwright/test'
+
+test('renders flow chart background demo with initial nodes', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/flow-chart')
+
+ const block = page.locator('.vp-raw').nth(2)
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = block.locator('.ql-editor').first()
+ const chart = editor.locator('.ql-flow-chart-item')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(toolbar.locator('.ql-flow-chart')).toBeVisible()
+ await expect(chart).toBeVisible({ timeout: 30_000 })
+ await expect(editor.locator('.lf-node')).toHaveCount(2)
+ await expect(editor.locator('.lf-edge')).toHaveCount(1)
+})
diff --git a/packages/docs/fluent-editor/demos/flow-chart-grid.spec.ts b/packages/docs/fluent-editor/demos/flow-chart-grid.spec.ts
new file mode 100644
index 00000000..daa7193b
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/flow-chart-grid.spec.ts
@@ -0,0 +1,15 @@
+import { expect, test } from '@playwright/test'
+
+test('renders flow chart grid demo with initial nodes', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/flow-chart')
+
+ const block = page.locator('.vp-raw').nth(1)
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = block.locator('.ql-editor').first()
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(toolbar.locator('.ql-flow-chart')).toBeVisible()
+ await expect(editor.locator('.ql-flow-chart-item')).toBeVisible({ timeout: 30_000 })
+ await expect(editor.locator('.lf-node')).toHaveCount(2)
+ await expect(editor.locator('.lf-edge')).toHaveCount(1)
+})
diff --git a/packages/docs/fluent-editor/demos/flow-chart-resize.spec.ts b/packages/docs/fluent-editor/demos/flow-chart-resize.spec.ts
new file mode 100644
index 00000000..7eb5bb2f
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/flow-chart-resize.spec.ts
@@ -0,0 +1,15 @@
+import { expect, test } from '@playwright/test'
+
+test('renders flow chart with resize handles', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/flow-chart')
+
+ const block = page.locator('.vp-raw').nth(3)
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = block.locator('.ql-editor').first()
+ const chart = editor.locator('.ql-flow-chart-item')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(toolbar.locator('.ql-flow-chart')).toBeVisible()
+ await expect(chart).toBeVisible({ timeout: 30_000 })
+ await expect(chart.locator('.ql-flow-chart-resize-handle')).toHaveCount(4)
+})
diff --git a/packages/docs/fluent-editor/demos/flow-chart.spec.ts b/packages/docs/fluent-editor/demos/flow-chart.spec.ts
index d562c33c..26084f69 100644
--- a/packages/docs/fluent-editor/demos/flow-chart.spec.ts
+++ b/packages/docs/fluent-editor/demos/flow-chart.spec.ts
@@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test'
test.describe('FlowChart.vue', () => {
test.beforeEach(async ({ page }) => {
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/flow-chart')
+ await page.goto('/tiny-editor/docs/demo/flow-chart')
})
test('should render the editor', async ({ page }) => {
diff --git a/packages/docs/fluent-editor/demos/format-painter.spec.ts b/packages/docs/fluent-editor/demos/format-painter.spec.ts
new file mode 100644
index 00000000..ad5550e1
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/format-painter.spec.ts
@@ -0,0 +1,25 @@
+import { expect, test } from '@playwright/test'
+
+test('copies bold format with format painter', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/format-painter')
+
+ const toolbar = page.locator('.ql-toolbar').first()
+ const editor = page.locator('#editor .ql-editor')
+ const painterBtn = toolbar.locator('.ql-format-painter')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(painterBtn).toBeVisible()
+
+ await editor.click()
+ await toolbar.locator('.ql-bold').click()
+ await page.keyboard.type('Bold')
+ await page.keyboard.press('ControlOrMeta+a')
+ await painterBtn.click()
+ await expect(painterBtn).toHaveClass(/ql-active/)
+
+ await page.keyboard.press('End')
+ await page.keyboard.type(' Text')
+ await expect(editor.locator('strong')).toContainText('Bold')
+ await expect(editor.locator('strong')).toContainText('Text')
+})
diff --git a/packages/docs/fluent-editor/demos/formula-chemistry.spec.ts b/packages/docs/fluent-editor/demos/formula-chemistry.spec.ts
new file mode 100644
index 00000000..32684842
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/formula-chemistry.spec.ts
@@ -0,0 +1,22 @@
+import { expect, test } from '@playwright/test'
+
+test('inserts chemistry formula with mhchem', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/formula')
+
+ const block = page.locator('.vp-raw').filter({ has: page.locator('#chemistry-editor') })
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = page.locator('#chemistry-editor .ql-editor')
+ const formulaBtn = toolbar.locator('.ql-formula')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(formulaBtn).toBeVisible()
+
+ await editor.click()
+ await formulaBtn.click()
+ const input = page.locator('.ql-tooltip[data-mode="formula"] input[data-formula]')
+ await expect(input).toBeVisible()
+ await input.fill('\\ce{H2O}')
+ await input.blur()
+ await expect(editor.locator('.katex')).toBeVisible()
+})
diff --git a/packages/docs/fluent-editor/demos/formula-mathlive.spec.ts b/packages/docs/fluent-editor/demos/formula-mathlive.spec.ts
new file mode 100644
index 00000000..17332d04
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/formula-mathlive.spec.ts
@@ -0,0 +1,15 @@
+import { expect, test } from '@playwright/test'
+
+test('renders mathlive formulas and has formula button', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/formula')
+
+ const block = page.locator('.vp-raw').filter({ has: page.locator('#mathliveEditor') })
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = page.locator('#mathliveEditor .ql-editor')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(editor.locator('math-field.ql-math-field').first()).toBeVisible()
+ await expect(editor).toContainText('正弦交流电')
+ await expect(toolbar.locator('.ql-formula')).toBeVisible()
+})
diff --git a/packages/docs/fluent-editor/demos/formula.spec.ts b/packages/docs/fluent-editor/demos/formula.spec.ts
new file mode 100644
index 00000000..23df8cbc
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/formula.spec.ts
@@ -0,0 +1,20 @@
+import { expect, test } from '@playwright/test'
+
+test('inserts katex formula from toolbar', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/formula')
+
+ const block = page.locator('.vp-raw').first()
+ const editor = page.locator('#editor .ql-editor')
+ const formulaBtn = block.locator('.ql-formula')
+
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(formulaBtn).toBeVisible()
+
+ await editor.click()
+ await formulaBtn.click()
+ const input = page.locator('.ql-tooltip[data-mode="formula"] input[data-formula]')
+ await expect(input).toBeVisible()
+ await input.fill('e=mc^2')
+ await input.blur()
+ await expect(editor.locator('.katex')).toBeVisible()
+})
diff --git a/packages/docs/fluent-editor/demos/get-content-delta.spec.ts b/packages/docs/fluent-editor/demos/get-content-delta.spec.ts
index 728d3a86..642a6209 100644
--- a/packages/docs/fluent-editor/demos/get-content-delta.spec.ts
+++ b/packages/docs/fluent-editor/demos/get-content-delta.spec.ts
@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test'
test('has toolbar and syncs Delta preview on text change', async ({ page }) => {
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/get-content')
+ await page.goto('/tiny-editor/docs/demo/get-content')
const block = page.locator('.vp-raw').filter({ has: page.locator('#editor-get-content-delta') })
const toolbar = block.locator('.ql-toolbar').first()
diff --git a/packages/docs/fluent-editor/demos/get-content-html.spec.ts b/packages/docs/fluent-editor/demos/get-content-html.spec.ts
index 7adec3b4..6b1f50d2 100644
--- a/packages/docs/fluent-editor/demos/get-content-html.spec.ts
+++ b/packages/docs/fluent-editor/demos/get-content-html.spec.ts
@@ -9,7 +9,7 @@ const initialHtml = [
].join('')
test('has toolbar and syncs HTML preview on text change', async ({ page }) => {
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/get-content')
+ await page.goto('/tiny-editor/docs/demo/get-content')
const block = page.locator('.vp-raw').filter({ has: page.locator('#editor-get-content-html') })
const toolbar = block.locator('.ql-toolbar').first()
diff --git a/packages/docs/fluent-editor/demos/header-list-container.spec.ts b/packages/docs/fluent-editor/demos/header-list-container.spec.ts
index 42a84639..40565bb5 100644
--- a/packages/docs/fluent-editor/demos/header-list-container.spec.ts
+++ b/packages/docs/fluent-editor/demos/header-list-container.spec.ts
@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test'
test('has toolbar, headings and header list with scroll container config', async ({ page }) => {
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/header-list')
+ await page.goto('/tiny-editor/docs/demo/header-list')
const block = page.locator('.vp-raw').nth(1)
const toolbar = block.locator('.ql-toolbar').first()
diff --git a/packages/docs/fluent-editor/demos/header-list.spec.ts b/packages/docs/fluent-editor/demos/header-list.spec.ts
index 611b21b5..271696a8 100644
--- a/packages/docs/fluent-editor/demos/header-list.spec.ts
+++ b/packages/docs/fluent-editor/demos/header-list.spec.ts
@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test'
test('has toolbar, headings and toggles header list panel', async ({ page }) => {
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/header-list')
+ await page.goto('/tiny-editor/docs/demo/header-list')
const block = page.locator('.vp-raw').nth(0)
const toolbar = block.locator('.ql-toolbar').first()
diff --git a/packages/docs/fluent-editor/demos/i18n-custom.spec.ts b/packages/docs/fluent-editor/demos/i18n-custom.spec.ts
new file mode 100644
index 00000000..308987ca
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/i18n-custom.spec.ts
@@ -0,0 +1,24 @@
+import { expect, test } from '@playwright/test'
+
+test('uses custom i18n tip texts and switches locale', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/i18n')
+
+ const block = page.locator('.vp-raw').nth(1)
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = block.locator('.ql-editor').first()
+ const boldBtn = toolbar.locator('.ql-bold')
+ const switchBtn = block.getByRole('button', { name: /switch between Chinese and English/i })
+ const tip = page.locator('.toolbar-tip__tooltip:visible')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(boldBtn).toBeVisible()
+
+ await boldBtn.hover()
+ await expect(tip).toBeVisible({ timeout: 5_000 })
+ await expect(tip).toHaveText('替换粗体文本')
+
+ await switchBtn.click()
+ await boldBtn.hover()
+ await expect(tip).toHaveText('Replace bold text')
+})
diff --git a/packages/docs/fluent-editor/demos/i18n.spec.ts b/packages/docs/fluent-editor/demos/i18n.spec.ts
new file mode 100644
index 00000000..a60275bc
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/i18n.spec.ts
@@ -0,0 +1,19 @@
+import { expect, test } from '@playwright/test'
+
+test('switches counter language between English and Chinese', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/i18n')
+
+ const block = page.locator('.vp-raw').first()
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = block.locator('.ql-editor').first()
+ const counter = block.locator('.ql-counter').first()
+ const switchBtn = block.getByRole('button', { name: /switch between Chinese and English/i })
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(counter).toBeVisible()
+ await expect(counter).toHaveText('0/500 characters')
+
+ await switchBtn.click()
+ await expect(counter).toHaveText('0/500')
+})
diff --git a/packages/docs/fluent-editor/demos/image-toolbar-button.spec.ts b/packages/docs/fluent-editor/demos/image-toolbar-button.spec.ts
new file mode 100644
index 00000000..5d667bd0
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/image-toolbar-button.spec.ts
@@ -0,0 +1,21 @@
+import { expect, test } from '@playwright/test'
+
+test('custom image toolbar hides copy/download and applies clean', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/image-tool')
+
+ const block = page.locator('.vp-raw').nth(1)
+ const editor = block.locator('.ql-editor').first()
+ const image = editor.getByRole('img').first()
+
+ await expect(image).toBeVisible({ timeout: 30_000 })
+ await expect(image).toHaveAttribute('width', '400px')
+
+ await image.click()
+ const overlay = page.locator('.blot-formatter__overlay')
+ await expect(overlay).toBeVisible()
+ const buttons = overlay.locator('.blot-formatter__toolbar-button')
+ await expect(buttons).toHaveCount(5)
+
+ await buttons.last().click({ force: true })
+ await expect(image).not.toHaveAttribute('width', '400px')
+})
diff --git a/packages/docs/fluent-editor/demos/image-toolbar.spec.ts b/packages/docs/fluent-editor/demos/image-toolbar.spec.ts
new file mode 100644
index 00000000..a39223c1
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/image-toolbar.spec.ts
@@ -0,0 +1,22 @@
+import { expect, test } from '@playwright/test'
+
+test('shows image toolbar overlay and preview button', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/image-tool')
+
+ const block = page.locator('.vp-raw').first()
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = block.locator('.ql-editor').first()
+ const image = editor.getByRole('img').first()
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(image).toBeVisible({ timeout: 30_000 })
+
+ await image.click()
+ const overlay = page.locator('.blot-formatter__overlay')
+ await expect(overlay).toBeVisible()
+ const buttons = overlay.locator('.blot-formatter__toolbar-button')
+ await expect(buttons).toHaveCount(6)
+
+ await buttons.last().click({ force: true })
+ await expect(page.locator('.image-preview-overlay')).toBeVisible()
+})
diff --git a/packages/docs/fluent-editor/demos/markdown.spec.ts b/packages/docs/fluent-editor/demos/markdown.spec.ts
new file mode 100644
index 00000000..1bbfc929
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/markdown.spec.ts
@@ -0,0 +1,19 @@
+import { expect, test } from '@playwright/test'
+
+test('has toolbar and converts markdown shortcuts', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/markdown')
+
+ const toolbar = page.locator('.ql-toolbar').first()
+ const editor = page.locator('#editor .ql-editor')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+
+ await editor.click()
+ await page.keyboard.type('# Hello')
+ await expect(editor.locator('h1')).toHaveText('Hello')
+
+ await page.keyboard.press('Enter')
+ await page.keyboard.type('- item')
+ await expect(editor.locator('li').first()).toContainText('item')
+})
diff --git a/packages/docs/fluent-editor/demos/mention-custom-list.spec.ts b/packages/docs/fluent-editor/demos/mention-custom-list.spec.ts
new file mode 100644
index 00000000..cf1b7e22
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/mention-custom-list.spec.ts
@@ -0,0 +1,19 @@
+import { expect, test } from '@playwright/test'
+
+test('renders custom mention list with avatar and follower info', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/mention')
+
+ const editor = page.locator('#editor-mention-custom-list .ql-editor')
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+
+ await editor.click()
+ await editor.pressSequentially('@')
+ const list = page.locator('.ql-mention-list-container__custom-list .ql-mention-list:not(.ql-mention-list--hide)')
+ await expect(list).toBeVisible()
+ await expect(list.locator('.item-name')).toContainText(['卡哥', '超哥', '小伍哥'])
+ await expect(list.locator('.item-desc').first()).toContainText('粉丝')
+ await expect(list.locator('.item-avatar img').first()).toBeVisible()
+
+ await list.locator('.ql-mention-item').first().click()
+ await expect(editor.locator('.ql-mention-link')).toContainText('卡哥')
+})
diff --git a/packages/docs/fluent-editor/demos/mention-link.spec.ts b/packages/docs/fluent-editor/demos/mention-link.spec.ts
new file mode 100644
index 00000000..0e7845d3
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/mention-link.spec.ts
@@ -0,0 +1,28 @@
+import { expect, test } from '@playwright/test'
+
+test('inserts mention link and toggles editable state', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/mention')
+
+ const block = page.locator('.vp-raw').nth(2)
+ const editor = block.locator('.ql-editor').first()
+ const toggleBtn = block.getByRole('button', { name: /toggle editable/i })
+
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(toggleBtn).toBeVisible()
+ await expect(block).toContainText('enabled')
+
+ await editor.click()
+ await editor.pressSequentially('@')
+ const list = block.locator('.ql-mention-list:not(.ql-mention-list--hide)')
+ await expect(list).toBeVisible()
+ await expect(list).toContainText('Jack 杰克')
+ await list.locator('.ql-mention-item').first().click()
+
+ const mention = editor.locator('a.ql-mention-link')
+ await expect(mention).toHaveAttribute('href', '#/use/Jack')
+ await expect(mention).toHaveAttribute('target', '_blank')
+
+ await toggleBtn.click()
+ await expect(block).toContainText('disabled')
+ await expect(editor).toHaveAttribute('contenteditable', 'false')
+})
diff --git a/packages/docs/fluent-editor/demos/mention.spec.ts b/packages/docs/fluent-editor/demos/mention.spec.ts
new file mode 100644
index 00000000..46451685
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/mention.spec.ts
@@ -0,0 +1,19 @@
+import { expect, test } from '@playwright/test'
+
+test('shows mention list on @ and inserts selected item', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/mention')
+
+ const block = page.locator('.vp-raw').filter({ has: page.locator('#editor') })
+ const editor = page.locator('#editor .ql-editor')
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+
+ await editor.click()
+ await editor.pressSequentially('@')
+ const list = block.locator('.ql-mention-list:not(.ql-mention-list--hide)')
+ await expect(list).toBeVisible()
+ await expect(list).toContainText('Jack 杰克')
+ await expect(list).toContainText('Lucy 露西')
+
+ await list.locator('.ql-mention-item').first().click()
+ await expect(editor.locator('.ql-mention-link')).toContainText('Jack 杰克')
+})
diff --git a/packages/docs/fluent-editor/demos/mind-map-background.spec.ts b/packages/docs/fluent-editor/demos/mind-map-background.spec.ts
new file mode 100644
index 00000000..d9678762
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/mind-map-background.spec.ts
@@ -0,0 +1,23 @@
+import { expect, test } from '@playwright/test'
+
+test('renders mind map with custom background color', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/mind-map')
+
+ const block = page.locator('.vp-raw').nth(1)
+ const toolbar = block.locator('.ql-toolbar').first()
+ const map = block.locator('.ql-mind-map-item').first()
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(toolbar.locator('.ql-mind-map')).toBeVisible()
+ await expect(map).toBeVisible({ timeout: 30_000 })
+ await expect(map).toContainText('根节点')
+
+ await expect.poll(async () => {
+ const color = await map.evaluate((el) => {
+ const svg = el.querySelector('svg')
+ const style = svg ? getComputedStyle(svg) : getComputedStyle(el)
+ return `${(el as HTMLElement).style.backgroundColor} ${style.backgroundColor}`
+ })
+ return color.includes('164, 221, 0') || color.toLowerCase().includes('a4dd00')
+ }).toBeTruthy()
+})
diff --git a/packages/docs/fluent-editor/demos/mind-map-line.spec.ts b/packages/docs/fluent-editor/demos/mind-map-line.spec.ts
new file mode 100644
index 00000000..42c0b074
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/mind-map-line.spec.ts
@@ -0,0 +1,15 @@
+import { expect, test } from '@playwright/test'
+
+test('renders mind map line demo with root and child nodes', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/mind-map')
+
+ const block = page.locator('.vp-raw').nth(3)
+ const toolbar = block.locator('.ql-toolbar').first()
+ const map = block.locator('.ql-mind-map-item').first()
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(toolbar.locator('.ql-mind-map')).toBeVisible()
+ await expect(map).toBeVisible({ timeout: 30_000 })
+ await expect(map).toContainText('根节点')
+ await expect(map).toContainText('二级节点')
+})
diff --git a/packages/docs/fluent-editor/demos/mind-map-resize.spec.ts b/packages/docs/fluent-editor/demos/mind-map-resize.spec.ts
new file mode 100644
index 00000000..0a0a6bae
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/mind-map-resize.spec.ts
@@ -0,0 +1,14 @@
+import { expect, test } from '@playwright/test'
+
+test('renders mind map with resize handles', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/mind-map')
+
+ const block = page.locator('.vp-raw').nth(2)
+ const toolbar = block.locator('.ql-toolbar').first()
+ const map = block.locator('.ql-mind-map-item').first()
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(toolbar.locator('.ql-mind-map')).toBeVisible()
+ await expect(map).toBeVisible({ timeout: 30_000 })
+ await expect(map.locator('.ql-mind-map-resize-handle')).toHaveCount(4)
+})
diff --git a/packages/docs/fluent-editor/demos/mind-map-theme.spec.ts b/packages/docs/fluent-editor/demos/mind-map-theme.spec.ts
new file mode 100644
index 00000000..835ccc42
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/mind-map-theme.spec.ts
@@ -0,0 +1,15 @@
+import { expect, test } from '@playwright/test'
+
+test('renders mind map theme demo with root and child nodes', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/mind-map')
+
+ const block = page.locator('.vp-raw').nth(4)
+ const toolbar = block.locator('.ql-toolbar').first()
+ const map = block.locator('.ql-mind-map-item').first()
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(toolbar.locator('.ql-mind-map')).toBeVisible()
+ await expect(map).toBeVisible({ timeout: 30_000 })
+ await expect(map).toContainText('根节点')
+ await expect(map).toContainText('二级节点')
+})
diff --git a/packages/docs/fluent-editor/demos/mind-map.spec.ts b/packages/docs/fluent-editor/demos/mind-map.spec.ts
index ee92b0cc..ca4903dd 100644
--- a/packages/docs/fluent-editor/demos/mind-map.spec.ts
+++ b/packages/docs/fluent-editor/demos/mind-map.spec.ts
@@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test'
test.describe('MindMap.vue', () => {
test.beforeEach(async ({ page }) => {
// 根据实际的路由调整这个URL
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/mind-map')
+ await page.goto('/tiny-editor/docs/demo/mind-map')
})
test('should render the editor', async ({ page }) => {
diff --git a/packages/docs/fluent-editor/demos/readonly.spec.ts b/packages/docs/fluent-editor/demos/readonly.spec.ts
index 50ee88ba..2fa53fa6 100644
--- a/packages/docs/fluent-editor/demos/readonly.spec.ts
+++ b/packages/docs/fluent-editor/demos/readonly.spec.ts
@@ -17,7 +17,7 @@ const initialHtml = [
].join('')
test('renders readonly editor without toolbar', async ({ page }) => {
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/readonly')
+ await page.goto('/tiny-editor/docs/demo/readonly')
const block = page.locator('.vp-raw').first()
const editor = page.locator('#editor-readonly .ql-editor')
diff --git a/packages/docs/fluent-editor/demos/screenshot-upload-to-server.spec.ts b/packages/docs/fluent-editor/demos/screenshot-upload-to-server.spec.ts
new file mode 100644
index 00000000..f8238d4d
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/screenshot-upload-to-server.spec.ts
@@ -0,0 +1,13 @@
+import { expect, test } from '@playwright/test'
+
+test('has screenshot button in server-upload demo', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/screenshot')
+
+ const block = page.locator('.vp-raw').nth(1)
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = block.locator('.ql-editor').first()
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(toolbar.locator('.ql-screenshot')).toBeVisible()
+})
diff --git a/packages/docs/fluent-editor/demos/screenshot.spec.ts b/packages/docs/fluent-editor/demos/screenshot.spec.ts
new file mode 100644
index 00000000..4b2f12ac
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/screenshot.spec.ts
@@ -0,0 +1,13 @@
+import { expect, test } from '@playwright/test'
+
+test('has screenshot button in toolbar', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/screenshot')
+
+ const block = page.locator('.vp-raw').first()
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = block.locator('.ql-editor').first()
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(toolbar.locator('.ql-screenshot')).toBeVisible()
+})
diff --git a/packages/docs/fluent-editor/demos/set-content-delta.spec.ts b/packages/docs/fluent-editor/demos/set-content-delta.spec.ts
index 15b76967..6ad6a09c 100644
--- a/packages/docs/fluent-editor/demos/set-content-delta.spec.ts
+++ b/packages/docs/fluent-editor/demos/set-content-delta.spec.ts
@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test'
test('should initialize editor with Delta content', async ({ page }) => {
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/set-content')
+ await page.goto('/tiny-editor/docs/demo/set-content')
const toolbar = page.locator('.ql-toolbar').nth(1)
const editor = page.locator('#editor-set-content-delta .ql-editor')
diff --git a/packages/docs/fluent-editor/demos/set-content-html.spec.ts b/packages/docs/fluent-editor/demos/set-content-html.spec.ts
index 6b506760..1a4a6d06 100644
--- a/packages/docs/fluent-editor/demos/set-content-html.spec.ts
+++ b/packages/docs/fluent-editor/demos/set-content-html.spec.ts
@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test'
test('should initialize editor with HTML content', async ({ page }) => {
- await page.goto('http://localhost:5173/tiny-editor/docs/demo/set-content')
+ await page.goto('/tiny-editor/docs/demo/set-content')
const editor = page.locator('#editor-set-content-html .ql-editor')
await expect(editor).toBeVisible()
diff --git a/packages/docs/fluent-editor/demos/shortcut-key.spec.ts b/packages/docs/fluent-editor/demos/shortcut-key.spec.ts
new file mode 100644
index 00000000..7f54b0ed
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/shortcut-key.spec.ts
@@ -0,0 +1,18 @@
+import { expect, test } from '@playwright/test'
+
+test('shows shortcut placeholder and opens slash menu', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/shortcut-key')
+
+ const toolbar = page.locator('.ql-toolbar').first()
+ const editor = page.locator('#editor .ql-editor')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(page.locator('.qsf-placeholder__wrapper')).toBeVisible()
+
+ await editor.click()
+ await page.keyboard.type('/')
+ const menu = page.locator('.qsf-menu')
+ await expect(menu).toBeVisible({ timeout: 10_000 })
+ await expect(menu).toContainText('Heading 1')
+})
diff --git a/packages/docs/fluent-editor/demos/table-up-clipboard.spec.ts b/packages/docs/fluent-editor/demos/table-up-clipboard.spec.ts
new file mode 100644
index 00000000..11519696
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/table-up-clipboard.spec.ts
@@ -0,0 +1,14 @@
+import { expect, test } from '@playwright/test'
+
+test('has toolbar and pasteStyleSheet clipboard demo', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/table-up')
+
+ const block = page.locator('.vp-raw').nth(3)
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = block.locator('.ql-editor').first()
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(block).toContainText('pasteStyleSheet')
+ await expect(toolbar.locator('.ql-picker.ql-table-up')).toBeVisible()
+})
diff --git a/packages/docs/fluent-editor/demos/table-up-menu.spec.ts b/packages/docs/fluent-editor/demos/table-up-menu.spec.ts
new file mode 100644
index 00000000..daf60534
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/table-up-menu.spec.ts
@@ -0,0 +1,17 @@
+import { expect, test } from '@playwright/test'
+
+test('inserts table from toolbar in contextmenu and select menu editors', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/table-up')
+
+ const block = page.locator('.vp-raw').first()
+ const editors = block.locator('.ql-editor')
+ await expect(editors.first()).toBeVisible({ timeout: 30_000 })
+ await expect(editors).toHaveCount(2)
+ await expect(block).toContainText('右击菜单')
+ await expect(block).toContainText('选择菜单')
+
+ const firstToolbar = block.locator('.ql-toolbar').first()
+ await firstToolbar.locator('.ql-table-up > .ql-picker-label').click()
+ await page.locator('.table-up-select-box__item[data-row="2"][data-col="2"]:visible').click()
+ await expect(editors.first().locator('.ql-table-wrapper')).toBeVisible()
+})
diff --git a/packages/docs/fluent-editor/demos/table-up-resize.spec.ts b/packages/docs/fluent-editor/demos/table-up-resize.spec.ts
new file mode 100644
index 00000000..553edeaf
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/table-up-resize.spec.ts
@@ -0,0 +1,18 @@
+import { expect, test } from '@playwright/test'
+
+test('renders three table resize editors and inserts a table', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/table-up')
+
+ const block = page.locator('.vp-raw').nth(1)
+ const editors = block.locator('.ql-editor')
+ await expect(editors.first()).toBeVisible({ timeout: 30_000 })
+ await expect(editors).toHaveCount(3)
+ await expect(block).toContainText('包围盒')
+ await expect(block).toContainText('内部隐藏线')
+ await expect(block).toContainText('整体单元格调整')
+
+ const firstToolbar = block.locator('.ql-toolbar').first()
+ await firstToolbar.locator('.ql-table-up > .ql-picker-label').click()
+ await page.locator('.table-up-select-box__item[data-row="2"][data-col="2"]:visible').click()
+ await expect(editors.first().locator('.ql-table-wrapper')).toBeVisible()
+})
diff --git a/packages/docs/fluent-editor/demos/table-up-shortcut.spec.ts b/packages/docs/fluent-editor/demos/table-up-shortcut.spec.ts
new file mode 100644
index 00000000..7092f904
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/table-up-shortcut.spec.ts
@@ -0,0 +1,21 @@
+import { expect, test } from '@playwright/test'
+
+test('has language switcher, table button and slash menu table item', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/table-up')
+
+ const block = page.locator('.vp-raw').nth(2)
+ const toolbar = block.locator('.ql-toolbar').first()
+ const editor = block.locator('.ql-editor').first()
+ const localeSelect = block.locator('#locale-select')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(localeSelect).toBeVisible()
+ await expect(toolbar.locator('.ql-picker.ql-table-up')).toBeVisible()
+
+ await editor.click()
+ await page.keyboard.type('/')
+ const menu = page.locator('.qsf-menu')
+ await expect(menu).toBeVisible({ timeout: 10_000 })
+ await expect(menu).toContainText(/Table|表格/)
+})
diff --git a/packages/docs/fluent-editor/demos/toolbar-tip.spec.ts b/packages/docs/fluent-editor/demos/toolbar-tip.spec.ts
new file mode 100644
index 00000000..d2837881
--- /dev/null
+++ b/packages/docs/fluent-editor/demos/toolbar-tip.spec.ts
@@ -0,0 +1,18 @@
+import { expect, test } from '@playwright/test'
+
+test('has toolbar and shows tip on hover', async ({ page }) => {
+ await page.goto('/tiny-editor/docs/demo/toolbar-tip')
+
+ const toolbar = page.locator('.ql-toolbar').first()
+ const editor = page.locator('.ql-editor').first()
+ const boldBtn = toolbar.locator('.ql-bold')
+
+ await expect(toolbar).toBeVisible({ timeout: 30_000 })
+ await expect(editor).toBeVisible({ timeout: 30_000 })
+ await expect(boldBtn).toBeVisible()
+
+ await boldBtn.hover()
+ const tip = page.locator('.toolbar-tip__tooltip:visible')
+ await expect(tip).toBeVisible({ timeout: 5_000 })
+ await expect(tip).toContainText(/Bold|粗体/)
+})
From b679702448cd6072bda1a66573e6b287d31c700b Mon Sep 17 00:00:00 2001
From: wuyiping0628 <1106773985@qq.com>
Date: Tue, 25 Aug 2026 05:19:15 -0700
Subject: [PATCH 2/2] feat:add banner.svg
---
.../docs/fluent-editor/public/banner-dark.svg | 726 ++++++++++++++++++
pnpm-lock.yaml | 3 +
2 files changed, 729 insertions(+)
create mode 100644 packages/docs/fluent-editor/public/banner-dark.svg
diff --git a/packages/docs/fluent-editor/public/banner-dark.svg b/packages/docs/fluent-editor/public/banner-dark.svg
new file mode 100644
index 00000000..f52cba88
--- /dev/null
+++ b/packages/docs/fluent-editor/public/banner-dark.svg
@@ -0,0 +1,726 @@
+
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e20186bc..be902d84 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -204,6 +204,9 @@ importers:
quill-shortcut-key:
specifier: ^0.2.1
version: 0.2.1(quill@2.0.3(patch_hash=dnirvjhpzdmsvdidsclwslxxji))
+ quill-table-up:
+ specifier: ^3.5.0
+ version: 3.5.1(quill@2.0.3(patch_hash=dnirvjhpzdmsvdidsclwslxxji))
devDependencies:
'@emoji-mart/data':
specifier: ^1.2.1