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 - Paste Special', () => {
5 test.beforeEach(async ({ page }) => {
6 await createNewSheet(page);
7 });
8
9 // --- Dialog Opening ---
10
11 test('Cmd+Shift+V opens Paste Special dialog', async ({ page }) => {
12 // Copy something first so paste special has data
13 await typeInCell(page, 'A1', 'Data');
14 await clickCell(page, 'A1');
15 await page.keyboard.press(`${mod(page)}+c`);
16 await page.waitForTimeout(300);
17
18 // Select destination
19 await clickCell(page, 'A3');
20
21 // Open Paste Special
22 await page.keyboard.press(`${mod(page)}+Shift+v`);
23
24 const dialog = page.locator('.paste-special-dialog');
25 await expect(dialog).toBeVisible({ timeout: 3000 });
26 });
27
28 test('Paste Special dialog has all five mode options', async ({ page }) => {
29 await typeInCell(page, 'A1', 'Data');
30 await clickCell(page, 'A1');
31 await page.keyboard.press(`${mod(page)}+c`);
32 await page.waitForTimeout(300);
33
34 await clickCell(page, 'A3');
35 await page.keyboard.press(`${mod(page)}+Shift+v`);
36
37 const dialog = page.locator('.paste-special-dialog');
38 await expect(dialog).toBeVisible({ timeout: 3000 });
39
40 await expect(dialog).toContainText('All');
41 await expect(dialog).toContainText('Values Only');
42 await expect(dialog).toContainText('Formulas Only');
43 await expect(dialog).toContainText('Formatting Only');
44 await expect(dialog).toContainText('Transpose');
45 });
46
47 test('Cancel button closes Paste Special dialog', async ({ page }) => {
48 await typeInCell(page, 'A1', 'Data');
49 await clickCell(page, 'A1');
50 await page.keyboard.press(`${mod(page)}+c`);
51 await page.waitForTimeout(300);
52
53 await clickCell(page, 'A3');
54 await page.keyboard.press(`${mod(page)}+Shift+v`);
55
56 const dialog = page.locator('.paste-special-dialog');
57 await expect(dialog).toBeVisible({ timeout: 3000 });
58
59 await page.click('.paste-special-cancel');
60
61 await expect(dialog).not.toBeVisible({ timeout: 3000 });
62 });
63
64 // --- Values Only ---
65
66 test('Values Only pastes computed values without formulas', async ({ page }) => {
67 await typeInCell(page, 'A1', '10');
68 await typeInCell(page, 'A2', '=A1*2');
69
70 // Verify formula works
71 expect(await getCellText(page, 'A2')).toBe('20');
72
73 // Copy A2 (has formula)
74 await clickCell(page, 'A2');
75 await page.keyboard.press(`${mod(page)}+c`);
76 await page.waitForTimeout(300);
77
78 // Paste Special → Values Only into A4
79 await clickCell(page, 'A4');
80 await page.keyboard.press(`${mod(page)}+Shift+v`);
81
82 const dialog = page.locator('.paste-special-dialog');
83 await expect(dialog).toBeVisible({ timeout: 3000 });
84
85 await dialog.locator('input[value="values_only"]').click();
86 await page.click('.paste-special-submit');
87
88 // A4 should have value "20" but not the formula
89 expect(await getCellText(page, 'A4')).toBe('20');
90
91 // Check formula bar — should not show a formula
92 await clickCell(page, 'A4');
93 const barValue = await page.inputValue('#formula-input');
94 expect(barValue).toBe('20'); // plain value, not "=A1*2"
95 });
96
97 // --- Formatting Only ---
98
99 test('Formatting Only pastes style without values', async ({ page }) => {
100 // Create a bold cell
101 await typeInCell(page, 'A1', 'Bold');
102 await clickCell(page, 'A1');
103 await page.click('#tb-bold');
104
105 // Copy A1
106 await page.keyboard.press(`${mod(page)}+c`);
107 await page.waitForTimeout(300);
108
109 // Type something in B1 without formatting
110 await typeInCell(page, 'B1', 'Plain');
111
112 // Paste Special → Formatting Only into B1
113 await clickCell(page, 'B1');
114 await page.keyboard.press(`${mod(page)}+Shift+v`);
115
116 const dialog = page.locator('.paste-special-dialog');
117 await expect(dialog).toBeVisible({ timeout: 3000 });
118
119 await dialog.locator('input[value="formatting_only"]').click();
120 await page.click('.paste-special-submit');
121
122 // B1 should keep its value but gain bold formatting
123 expect(await getCellText(page, 'B1')).toBe('Plain');
124 const cellDisplay = page.locator('td[data-id="B1"] .cell-display');
125 await expect(cellDisplay).toHaveCSS('font-weight', '600');
126 });
127
128 // --- Transpose ---
129
130 test('Transpose paste rotates rows to columns', async ({ page }) => {
131 // Create a row of data: A1=X, B1=Y, C1=Z
132 await typeInCell(page, 'A1', 'X');
133 await typeInCell(page, 'B1', 'Y');
134 await typeInCell(page, 'C1', 'Z');
135
136 // Select A1:C1 and copy
137 await clickCell(page, 'A1');
138 await page.keyboard.down('Shift');
139 await clickCell(page, 'C1');
140 await page.keyboard.up('Shift');
141 await page.keyboard.press(`${mod(page)}+c`);
142 await page.waitForTimeout(300);
143
144 // Paste Special → Transpose into A3
145 await clickCell(page, 'A3');
146 await page.keyboard.press(`${mod(page)}+Shift+v`);
147
148 const dialog = page.locator('.paste-special-dialog');
149 await expect(dialog).toBeVisible({ timeout: 3000 });
150
151 await dialog.locator('input[value="transpose"]').click();
152 await page.click('.paste-special-submit');
153
154 // Row should be transposed to column: A3=X, A4=Y, A5=Z
155 expect(await getCellText(page, 'A3')).toBe('X');
156 expect(await getCellText(page, 'A4')).toBe('Y');
157 expect(await getCellText(page, 'A5')).toBe('Z');
158 });
159
160 // --- All (Default) ---
161
162 test('All mode pastes both values and formatting', async ({ page }) => {
163 await typeInCell(page, 'A1', 'Styled');
164 await clickCell(page, 'A1');
165 await page.click('#tb-bold');
166
167 await page.keyboard.press(`${mod(page)}+c`);
168 await page.waitForTimeout(300);
169
170 await clickCell(page, 'A3');
171 await page.keyboard.press(`${mod(page)}+Shift+v`);
172
173 const dialog = page.locator('.paste-special-dialog');
174 await expect(dialog).toBeVisible({ timeout: 3000 });
175
176 // "All" should be selected by default
177 const allRadio = dialog.locator('input[value="all"]');
178 await expect(allRadio).toBeChecked();
179
180 await page.click('.paste-special-submit');
181
182 expect(await getCellText(page, 'A3')).toBe('Styled');
183 const cellDisplay = page.locator('td[data-id="A3"] .cell-display');
184 await expect(cellDisplay).toHaveCSS('font-weight', '600');
185 });
186});