Full document, spreadsheet, slideshow, and diagram tooling
1import { test, expect } from '@playwright/test';
2import { createNewSheet, clickCell, typeInCell, getCellText } from './helpers';
3
4test.describe('Sheets - Context Menu', () => {
5 test.beforeEach(async ({ page }) => {
6 await createNewSheet(page);
7 });
8
9 test('right-click on cell shows context menu with relevant options', async ({ page }) => {
10 await clickCell(page, 'B2');
11 await page.click('td[data-id="B2"]', { button: 'right' });
12
13 const contextMenu = page.locator('.context-menu');
14 await expect(contextMenu).toBeVisible({ timeout: 5000 });
15
16 // Should have Cut, Copy, Paste options
17 await expect(contextMenu).toContainText('Cut');
18 await expect(contextMenu).toContainText('Copy');
19 await expect(contextMenu).toContainText('Paste');
20
21 // Should have Insert/Delete row/column options
22 await expect(contextMenu).toContainText('Insert Row');
23 await expect(contextMenu).toContainText('Insert Column');
24 await expect(contextMenu).toContainText('Delete Row');
25 await expect(contextMenu).toContainText('Delete Column');
26
27 // Dismiss
28 await page.keyboard.press('Escape');
29 await expect(contextMenu).not.toBeVisible();
30 });
31
32 test('right-click on row header shows insert/delete row options', async ({ page }) => {
33 await page.click('th.row-header[data-row="3"]', { button: 'right' });
34
35 const contextMenu = page.locator('.context-menu');
36 await expect(contextMenu).toBeVisible({ timeout: 5000 });
37
38 await expect(contextMenu).toContainText('Insert Row Above');
39 await expect(contextMenu).toContainText('Insert Row Below');
40 await expect(contextMenu).toContainText('Delete Row');
41
42 await page.keyboard.press('Escape');
43 });
44
45 test('right-click on column header shows insert/delete column and sort options', async ({ page }) => {
46 await page.click('thead th[data-col="2"]', { button: 'right' });
47
48 const contextMenu = page.locator('.context-menu');
49 await expect(contextMenu).toBeVisible({ timeout: 5000 });
50
51 await expect(contextMenu).toContainText('Insert Column Left');
52 await expect(contextMenu).toContainText('Insert Column Right');
53 await expect(contextMenu).toContainText('Delete Column');
54
55 // Sort options should also appear for column headers
56 await expect(contextMenu).toContainText('Sort');
57
58 await page.keyboard.press('Escape');
59 });
60
61 test('insert row above inserts a new row and shifts data down', async ({ page }) => {
62 await typeInCell(page, 'A1', 'Row1');
63 await typeInCell(page, 'A2', 'Row2');
64 await typeInCell(page, 'A3', 'Row3');
65
66 // Right-click on row 2 header
67 await page.click('th.row-header[data-row="2"]', { button: 'right' });
68 const contextMenu = page.locator('.context-menu');
69 await expect(contextMenu).toBeVisible({ timeout: 5000 });
70
71 // Click "Insert Row Above"
72 await page.click('.context-menu >> text=Insert Row Above');
73
74 // Row1 should still be in A1
75 expect(await getCellText(page, 'A1')).toBe('Row1');
76
77 // Row2 should have been pushed down to A3 (new empty row at A2)
78 const a2Text = await getCellText(page, 'A2');
79 expect(a2Text).toBe('');
80
81 expect(await getCellText(page, 'A3')).toBe('Row2');
82 expect(await getCellText(page, 'A4')).toBe('Row3');
83 });
84
85 test('insert row below inserts a new row after the clicked row', async ({ page }) => {
86 await typeInCell(page, 'A1', 'Row1');
87 await typeInCell(page, 'A2', 'Row2');
88 await typeInCell(page, 'A3', 'Row3');
89
90 // Right-click on row 1 header
91 await page.click('th.row-header[data-row="1"]', { button: 'right' });
92 const contextMenu = page.locator('.context-menu');
93 await expect(contextMenu).toBeVisible({ timeout: 5000 });
94
95 // Click "Insert Row Below"
96 await page.click('.context-menu >> text=Insert Row Below');
97
98 // Row1 should still be in A1
99 expect(await getCellText(page, 'A1')).toBe('Row1');
100
101 // A2 should be the new empty row
102 expect(await getCellText(page, 'A2')).toBe('');
103
104 // Original A2 content should now be in A3
105 expect(await getCellText(page, 'A3')).toBe('Row2');
106 });
107
108 test('delete row removes the row and shifts data up', async ({ page }) => {
109 await typeInCell(page, 'A1', 'Keep');
110 await typeInCell(page, 'A2', 'Delete');
111 await typeInCell(page, 'A3', 'Keep Too');
112
113 // Right-click on row 2 header
114 await page.click('th.row-header[data-row="2"]', { button: 'right' });
115 const contextMenu = page.locator('.context-menu');
116 await expect(contextMenu).toBeVisible({ timeout: 5000 });
117
118 await page.click('.context-menu >> text=Delete Row');
119
120 // A1 should be unchanged
121 expect(await getCellText(page, 'A1')).toBe('Keep');
122
123 // "Keep Too" should have moved up to A2
124 expect(await getCellText(page, 'A2')).toBe('Keep Too');
125 });
126
127 test('insert column left inserts a new column and shifts data right', async ({ page }) => {
128 await typeInCell(page, 'A1', 'ColA');
129 await typeInCell(page, 'B1', 'ColB');
130 await typeInCell(page, 'C1', 'ColC');
131
132 // Right-click on column B header
133 await page.click('thead th[data-col="2"]', { button: 'right' });
134 const contextMenu = page.locator('.context-menu');
135 await expect(contextMenu).toBeVisible({ timeout: 5000 });
136
137 await page.click('.context-menu >> text=Insert Column Left');
138
139 // A1 should still have ColA
140 expect(await getCellText(page, 'A1')).toBe('ColA');
141
142 // B1 should be empty (new column)
143 expect(await getCellText(page, 'B1')).toBe('');
144
145 // ColB should have shifted to C1
146 expect(await getCellText(page, 'C1')).toBe('ColB');
147 });
148
149 test('insert column right inserts a new column after the clicked column', async ({ page }) => {
150 await typeInCell(page, 'A1', 'ColA');
151 await typeInCell(page, 'B1', 'ColB');
152
153 // Right-click on column A header
154 await page.click('thead th[data-col="1"]', { button: 'right' });
155 const contextMenu = page.locator('.context-menu');
156 await expect(contextMenu).toBeVisible({ timeout: 5000 });
157
158 await page.click('.context-menu >> text=Insert Column Right');
159
160 // A1 should still be ColA
161 expect(await getCellText(page, 'A1')).toBe('ColA');
162
163 // B1 should be empty (new column)
164 expect(await getCellText(page, 'B1')).toBe('');
165
166 // ColB should have shifted to C1
167 expect(await getCellText(page, 'C1')).toBe('ColB');
168 });
169
170 test('delete column removes the column and shifts data left', async ({ page }) => {
171 await typeInCell(page, 'A1', 'Keep');
172 await typeInCell(page, 'B1', 'Delete');
173 await typeInCell(page, 'C1', 'Keep Too');
174
175 // Right-click on column B header
176 await page.click('thead th[data-col="2"]', { button: 'right' });
177 const contextMenu = page.locator('.context-menu');
178 await expect(contextMenu).toBeVisible({ timeout: 5000 });
179
180 await page.click('.context-menu >> text=Delete Column');
181
182 // A1 should be unchanged
183 expect(await getCellText(page, 'A1')).toBe('Keep');
184
185 // B1 should now have "Keep Too" (shifted left)
186 expect(await getCellText(page, 'B1')).toBe('Keep Too');
187 });
188
189 test('context menu Clear Cells option clears selected cells', async ({ page }) => {
190 await typeInCell(page, 'A1', 'ClearMe');
191 expect(await getCellText(page, 'A1')).toBe('ClearMe');
192
193 // Right-click on the cell
194 await page.click('td[data-id="A1"]', { button: 'right' });
195 const contextMenu = page.locator('.context-menu');
196 await expect(contextMenu).toBeVisible({ timeout: 5000 });
197
198 await page.click('.context-menu >> text=Clear Cells');
199
200 expect(await getCellText(page, 'A1')).toBe('');
201 });
202
203 test('context menu disappears when clicking outside of it', async ({ page }) => {
204 await page.click('td[data-id="A1"]', { button: 'right' });
205 const contextMenu = page.locator('.context-menu');
206 await expect(contextMenu).toBeVisible({ timeout: 5000 });
207
208 // Click somewhere else on the page
209 await page.mouse.click(10, 10);
210
211 await expect(contextMenu).not.toBeVisible({ timeout: 5000 });
212 });
213
214 test('context menu shows Add Note option for cells', async ({ page }) => {
215 await page.click('td[data-id="A1"]', { button: 'right' });
216 const contextMenu = page.locator('.context-menu');
217 await expect(contextMenu).toBeVisible({ timeout: 5000 });
218
219 await expect(contextMenu).toContainText('Add Note');
220
221 await page.keyboard.press('Escape');
222 });
223});