Full document, spreadsheet, slideshow, and diagram tooling
1import { test, expect } from '@playwright/test';
2import { createNewSheet, clickCell, typeInCell, getCellText, mod } from './helpers';
3
4test.describe('Sheets - Find & Replace', () => {
5 test.beforeEach(async ({ page }) => {
6 await createNewSheet(page);
7 });
8
9 // --- Opening / Closing ---
10
11 test('Cmd+F opens the find bar', async ({ page }) => {
12 await page.keyboard.press(`${mod(page)}+f`);
13
14 const findBar = page.locator('.sheets-find-bar');
15 await expect(findBar).toBeVisible({ timeout: 3000 });
16
17 // Find input should be focused
18 const findInput = page.locator('#sheets-find-input');
19 await expect(findInput).toBeVisible();
20 await expect(findInput).toBeFocused();
21 });
22
23 test('Escape closes the find bar', async ({ page }) => {
24 await page.keyboard.press(`${mod(page)}+f`);
25 await expect(page.locator('.sheets-find-bar')).toBeVisible({ timeout: 3000 });
26
27 await page.keyboard.press('Escape');
28
29 await expect(page.locator('.sheets-find-bar')).not.toBeVisible({ timeout: 3000 });
30 });
31
32 test('close button closes the find bar', async ({ page }) => {
33 await page.keyboard.press(`${mod(page)}+f`);
34 await expect(page.locator('.sheets-find-bar')).toBeVisible({ timeout: 3000 });
35
36 await page.click('#sheets-find-close');
37
38 await expect(page.locator('.sheets-find-bar')).not.toBeVisible({ timeout: 3000 });
39 });
40
41 // --- Finding ---
42
43 test('typing in find input shows match count', async ({ page }) => {
44 await typeInCell(page, 'A1', 'apple');
45 await typeInCell(page, 'A2', 'banana');
46 await typeInCell(page, 'A3', 'apple pie');
47 await typeInCell(page, 'A4', 'cherry');
48
49 await page.keyboard.press(`${mod(page)}+f`);
50 await page.locator('#sheets-find-input').fill('apple');
51
52 const countEl = page.locator('#sheets-find-count');
53 await expect(countEl).toBeVisible({ timeout: 3000 });
54 const countText = await countEl.textContent();
55 // Should show "1 of 2" or similar
56 expect(countText).toMatch(/\d+\s*of\s*2/i);
57 });
58
59 test('Enter navigates to the next match', async ({ page }) => {
60 await typeInCell(page, 'A1', 'apple');
61 await typeInCell(page, 'A2', 'banana');
62 await typeInCell(page, 'A3', 'apple');
63
64 await page.keyboard.press(`${mod(page)}+f`);
65 await page.locator('#sheets-find-input').fill('apple');
66 await page.waitForTimeout(200);
67
68 // First match should be highlighted (A1)
69 const countText1 = await page.locator('#sheets-find-count').textContent();
70 expect(countText1).toMatch(/1\s*of\s*2/i);
71
72 // Press Enter to go to next match
73 await page.keyboard.press('Enter');
74 const countText2 = await page.locator('#sheets-find-count').textContent();
75 expect(countText2).toMatch(/2\s*of\s*2/i);
76 });
77
78 test('Shift+Enter navigates to the previous match', async ({ page }) => {
79 await typeInCell(page, 'A1', 'apple');
80 await typeInCell(page, 'A2', 'banana');
81 await typeInCell(page, 'A3', 'apple');
82
83 await page.keyboard.press(`${mod(page)}+f`);
84 await page.locator('#sheets-find-input').fill('apple');
85 await page.waitForTimeout(200);
86
87 // Go to next match first
88 await page.keyboard.press('Enter');
89 const countText = await page.locator('#sheets-find-count').textContent();
90 expect(countText).toMatch(/2\s*of\s*2/i);
91
92 // Go back with Shift+Enter
93 await page.keyboard.press('Shift+Enter');
94 const countText2 = await page.locator('#sheets-find-count').textContent();
95 expect(countText2).toMatch(/1\s*of\s*2/i);
96 });
97
98 test('next/prev buttons navigate between matches', async ({ page }) => {
99 await typeInCell(page, 'A1', 'test');
100 await typeInCell(page, 'A2', 'other');
101 await typeInCell(page, 'A3', 'test');
102
103 await page.keyboard.press(`${mod(page)}+f`);
104 await page.locator('#sheets-find-input').fill('test');
105 await page.waitForTimeout(200);
106
107 // Click next button
108 await page.click('#sheets-find-next');
109 const countText = await page.locator('#sheets-find-count').textContent();
110 expect(countText).toMatch(/2\s*of\s*2/i);
111
112 // Click prev button
113 await page.click('#sheets-find-prev');
114 const countText2 = await page.locator('#sheets-find-count').textContent();
115 expect(countText2).toMatch(/1\s*of\s*2/i);
116 });
117
118 test('no matches shows 0 of 0', async ({ page }) => {
119 await typeInCell(page, 'A1', 'apple');
120
121 await page.keyboard.press(`${mod(page)}+f`);
122 await page.locator('#sheets-find-input').fill('xyz');
123 await page.waitForTimeout(200);
124
125 const countText = await page.locator('#sheets-find-count').textContent();
126 expect(countText).toMatch(/0\s*of\s*0/i);
127 });
128
129 // --- Replace ---
130
131 test('replace toggle shows replace row', async ({ page }) => {
132 await page.keyboard.press(`${mod(page)}+f`);
133 await expect(page.locator('.sheets-find-bar')).toBeVisible({ timeout: 3000 });
134
135 // Replace row should be hidden initially
136 await expect(page.locator('#sheets-replace-row')).not.toBeVisible();
137
138 // Click the toggle to show replace
139 await page.click('#sheets-find-replace-toggle');
140
141 await expect(page.locator('#sheets-replace-row')).toBeVisible();
142 await expect(page.locator('#sheets-replace-input')).toBeVisible();
143 });
144
145 test('replace single match replaces current match', async ({ page }) => {
146 await typeInCell(page, 'A1', 'old');
147 await typeInCell(page, 'A2', 'old');
148
149 await page.keyboard.press(`${mod(page)}+f`);
150 await page.locator('#sheets-find-input').fill('old');
151 await page.waitForTimeout(200);
152
153 // Show replace
154 await page.click('#sheets-find-replace-toggle');
155 await page.locator('#sheets-replace-input').fill('new');
156
157 // Replace current match
158 await page.click('#sheets-replace-one');
159
160 // First cell should be replaced, second should still be old
161 expect(await getCellText(page, 'A1')).toBe('new');
162 expect(await getCellText(page, 'A2')).toBe('old');
163 });
164
165 test('replace all replaces all matches', async ({ page }) => {
166 await typeInCell(page, 'A1', 'old');
167 await typeInCell(page, 'A2', 'old');
168 await typeInCell(page, 'A3', 'keep');
169
170 await page.keyboard.press(`${mod(page)}+f`);
171 await page.locator('#sheets-find-input').fill('old');
172 await page.waitForTimeout(200);
173
174 await page.click('#sheets-find-replace-toggle');
175 await page.locator('#sheets-replace-input').fill('new');
176
177 await page.click('#sheets-replace-all');
178
179 expect(await getCellText(page, 'A1')).toBe('new');
180 expect(await getCellText(page, 'A2')).toBe('new');
181 expect(await getCellText(page, 'A3')).toBe('keep');
182 });
183
184 test('replace all shows toast with count', async ({ page }) => {
185 await typeInCell(page, 'A1', 'old');
186 await typeInCell(page, 'A2', 'old');
187 await typeInCell(page, 'A3', 'old');
188
189 await page.keyboard.press(`${mod(page)}+f`);
190 await page.locator('#sheets-find-input').fill('old');
191 await page.waitForTimeout(200);
192
193 await page.click('#sheets-find-replace-toggle');
194 await page.locator('#sheets-replace-input').fill('new');
195 await page.click('#sheets-replace-all');
196
197 const toast = page.locator('.toast-notification');
198 await expect(toast).toBeVisible({ timeout: 3000 });
199 await expect(toast).toContainText('3');
200 });
201});