Full document, spreadsheet, slideshow, and diagram tooling
1import { test, expect } from '@playwright/test';
2import { createNewDoc } from './helpers';
3
4test.describe('Docs - Advanced Features', () => {
5 test.beforeEach(async ({ page }) => {
6 await createNewDoc(page);
7 });
8
9 test('slash command menu appears when typing /', async ({ page }) => {
10 const editor = page.locator('.tiptap');
11 await editor.click();
12 await page.keyboard.type('/');
13
14 // Slash command menu should appear
15 await expect(page.locator('.slash-menu')).toBeVisible({ timeout: 5000 });
16
17 // Should have menu items
18 await expect(page.locator('.slash-menu-item').first()).toBeVisible();
19
20 // Dismiss with Escape
21 await page.keyboard.press('Escape');
22 await expect(page.locator('.slash-menu')).not.toBeVisible();
23 });
24
25 test('slash command inserts heading', async ({ page }) => {
26 const editor = page.locator('.tiptap');
27 await editor.click();
28 await page.keyboard.type('/');
29 await expect(page.locator('.slash-menu')).toBeVisible({ timeout: 5000 });
30
31 // Type to filter for heading
32 await page.keyboard.type('heading');
33 // Click the first heading option
34 await page.locator('.slash-menu-item').first().click();
35
36 // Should have created a heading element
37 const headings = editor.locator('h1, h2, h3');
38 await expect(headings.first()).toBeVisible();
39 });
40
41 test('find and replace opens with Cmd+F', async ({ page }) => {
42 const editor = page.locator('.tiptap');
43 await editor.click();
44 await page.keyboard.type('Find this text in the document');
45
46 // Open find
47 await page.keyboard.press('Meta+f');
48 const searchBar = page.locator('.search-bar, .find-replace-bar, [class*="search"]').first();
49 await expect(searchBar).toBeVisible({ timeout: 5000 });
50 });
51
52 test('zen mode toggles with Cmd+Shift+F', async ({ page }) => {
53 // Type some content first
54 const editor = page.locator('.tiptap');
55 await editor.click();
56 await page.keyboard.type('Zen mode content');
57
58 // Toggle zen mode
59 await page.keyboard.press('Meta+Shift+f');
60
61 // Body or app should have zen class
62 await expect(page.locator('.zen-mode, body.zen-mode, .app-shell.zen-mode')).toBeVisible({ timeout: 5000 });
63
64 // Exit button should be visible
65 await expect(page.locator('#zen-exit')).toBeVisible();
66
67 // Toggle back
68 await page.keyboard.press('Meta+Shift+f');
69 await expect(page.locator('#zen-exit')).not.toBeVisible();
70 });
71
72 test('markdown toggle with Cmd+Shift+M', async ({ page }) => {
73 const editor = page.locator('.tiptap');
74 await editor.click();
75 await page.keyboard.type('# Hello Markdown');
76
77 // Toggle to markdown view
78 await page.keyboard.press('Meta+Shift+m');
79
80 // Markdown source textarea should be visible
81 const mdSource = page.locator('#markdown-source');
82 await expect(mdSource).toBeVisible({ timeout: 5000 });
83
84 // It should contain markdown text
85 const mdValue = await mdSource.inputValue();
86 expect(mdValue).toContain('Hello Markdown');
87
88 // Toggle back
89 await page.keyboard.press('Meta+Shift+m');
90 await expect(mdSource).not.toBeVisible();
91 });
92
93 test('word count updates in footer', async ({ page }) => {
94 const editor = page.locator('.tiptap');
95 await editor.click();
96
97 // Initially should show 0 words
98 await expect(page.locator('#word-count')).toContainText('0 words');
99
100 // Type some text
101 await page.keyboard.type('one two three four five');
102
103 // Word count should update
104 await expect(page.locator('#word-count')).toContainText('5 words', { timeout: 5000 });
105 });
106
107 test('character count updates in footer', async ({ page }) => {
108 const editor = page.locator('.tiptap');
109 await editor.click();
110 await page.keyboard.type('Hello');
111
112 await expect(page.locator('#char-count')).toContainText('5 characters', { timeout: 5000 });
113 });
114
115 test('outline sidebar shows headings', async ({ page }) => {
116 const editor = page.locator('.tiptap');
117 await editor.click();
118
119 // Create some headings
120 await page.keyboard.type('# First Heading');
121 await page.keyboard.press('Enter');
122 await page.keyboard.type('Some paragraph text');
123 await page.keyboard.press('Enter');
124 await page.keyboard.type('## Second Heading');
125
126 // Open outline sidebar
127 await page.click('#btn-outline');
128
129 const sidebar = page.locator('#outline-sidebar');
130 await expect(sidebar).toBeVisible({ timeout: 5000 });
131
132 // Should list the headings
133 const items = sidebar.locator('.outline-item, .outline-link, [class*="outline"]');
134 // At least the headings should appear (wait for rendering)
135 await expect(items.first()).toBeVisible({ timeout: 5000 });
136 });
137
138 test('heading select in toolbar changes paragraph to heading', async ({ page }) => {
139 const editor = page.locator('.tiptap');
140 await editor.click();
141 await page.keyboard.type('Make me a heading');
142 await page.keyboard.press('Meta+a');
143
144 // Change heading level via toolbar select
145 await page.selectOption('#tb-heading', '1');
146 await expect(editor.locator('h1')).toContainText('Make me a heading');
147 });
148
149 test('version history panel opens with Cmd+Shift+H', async ({ page }) => {
150 // Type something to create content
151 const editor = page.locator('.tiptap');
152 await editor.click();
153 await page.keyboard.type('Version test content');
154
155 // Wait for save
156 await expect(page.locator('#save-text')).toHaveText('Saved', { timeout: 10000 });
157
158 // Open version history via button (Cmd+Shift+H might not be bound, use button)
159 await page.click('#btn-history');
160 const versionSidebar = page.locator('#version-sidebar');
161 await expect(versionSidebar).toBeVisible({ timeout: 5000 });
162
163 // Close it
164 await page.click('#version-sidebar-close');
165 await expect(versionSidebar).not.toBeVisible();
166 });
167
168 test('share dialog opens', async ({ page }) => {
169 await page.click('#btn-share');
170 const shareDialog = page.locator('#share-dialog');
171 await expect(shareDialog).toBeVisible({ timeout: 5000 });
172
173 // Should have a sharing link input
174 await expect(page.locator('#share-link-input')).toBeVisible();
175
176 // Close
177 await page.click('#share-dialog-close');
178 await expect(shareDialog).not.toBeVisible();
179 });
180});