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 - Undo/Redo', () => {
5 test.beforeEach(async ({ page }) => {
6 await createNewSheet(page);
7 });
8
9 // --- Basic Undo/Redo ---
10
11 test('undo reverses the last cell edit', async ({ page }) => {
12 await typeInCell(page, 'A1', 'Hello');
13 expect(await getCellText(page, 'A1')).toBe('Hello');
14
15 await page.keyboard.press(`${mod(page)}+z`);
16
17 expect(await getCellText(page, 'A1')).toBe('');
18 });
19
20 test('redo restores the undone cell edit', async ({ page }) => {
21 await typeInCell(page, 'A1', 'Hello');
22 await page.keyboard.press(`${mod(page)}+z`);
23 expect(await getCellText(page, 'A1')).toBe('');
24
25 await page.keyboard.press(`${mod(page)}+Shift+z`);
26
27 expect(await getCellText(page, 'A1')).toBe('Hello');
28 });
29
30 test('multiple undos reverse multiple edits in order', async ({ page }) => {
31 await typeInCell(page, 'A1', 'First');
32 await typeInCell(page, 'A2', 'Second');
33 await typeInCell(page, 'A3', 'Third');
34
35 // Undo Third
36 await page.keyboard.press(`${mod(page)}+z`);
37 expect(await getCellText(page, 'A3')).toBe('');
38 expect(await getCellText(page, 'A2')).toBe('Second');
39
40 // Undo Second
41 await page.keyboard.press(`${mod(page)}+z`);
42 expect(await getCellText(page, 'A2')).toBe('');
43 expect(await getCellText(page, 'A1')).toBe('First');
44
45 // Undo First
46 await page.keyboard.press(`${mod(page)}+z`);
47 expect(await getCellText(page, 'A1')).toBe('');
48 });
49
50 test('redo after multiple undos restores in order', async ({ page }) => {
51 await typeInCell(page, 'A1', 'First');
52 await typeInCell(page, 'A2', 'Second');
53
54 // Undo both
55 await page.keyboard.press(`${mod(page)}+z`);
56 await page.keyboard.press(`${mod(page)}+z`);
57
58 // Redo First
59 await page.keyboard.press(`${mod(page)}+Shift+z`);
60 expect(await getCellText(page, 'A1')).toBe('First');
61
62 // Redo Second
63 await page.keyboard.press(`${mod(page)}+Shift+z`);
64 expect(await getCellText(page, 'A2')).toBe('Second');
65 });
66
67 // --- Button State ---
68
69 test('undo button is disabled when there is no history', async ({ page }) => {
70 const undoBtn = page.locator('#tb-undo');
71 // On fresh sheet, undo should be disabled
72 await expect(undoBtn).toHaveClass(/btn-disabled/);
73 });
74
75 test('redo button is disabled when there is no redo history', async ({ page }) => {
76 const redoBtn = page.locator('#tb-redo');
77 await expect(redoBtn).toHaveClass(/btn-disabled/);
78 });
79
80 test('undo button becomes enabled after making an edit', async ({ page }) => {
81 const undoBtn = page.locator('#tb-undo');
82 await expect(undoBtn).toHaveClass(/btn-disabled/);
83
84 await typeInCell(page, 'A1', 'Change');
85
86 await expect(undoBtn).not.toHaveClass(/btn-disabled/);
87 });
88
89 test('redo button becomes enabled after undoing', async ({ page }) => {
90 const redoBtn = page.locator('#tb-redo');
91 await typeInCell(page, 'A1', 'Change');
92
93 await page.keyboard.press(`${mod(page)}+z`);
94
95 await expect(redoBtn).not.toHaveClass(/btn-disabled/);
96 });
97
98 test('undo button title shows stack count', async ({ page }) => {
99 await typeInCell(page, 'A1', 'One');
100 await typeInCell(page, 'A2', 'Two');
101
102 const undoBtn = page.locator('#tb-undo');
103 const title = await undoBtn.getAttribute('title');
104 // Should mention the count (e.g., "Undo (2)")
105 expect(title).toMatch(/\d/);
106 });
107
108 // --- Undo via Toolbar Button ---
109
110 test('clicking undo button undoes last change', async ({ page }) => {
111 await typeInCell(page, 'A1', 'ByButton');
112 expect(await getCellText(page, 'A1')).toBe('ByButton');
113
114 await page.click('#tb-undo');
115
116 expect(await getCellText(page, 'A1')).toBe('');
117 });
118
119 test('clicking redo button redoes last undone change', async ({ page }) => {
120 await typeInCell(page, 'A1', 'ByButton');
121 await page.click('#tb-undo');
122 expect(await getCellText(page, 'A1')).toBe('');
123
124 await page.click('#tb-redo');
125
126 expect(await getCellText(page, 'A1')).toBe('ByButton');
127 });
128
129 // --- Undo formatting changes ---
130
131 test('undo reverses bold formatting', async ({ page }) => {
132 await typeInCell(page, 'A1', 'Bold');
133 await clickCell(page, 'A1');
134 await page.click('#tb-bold');
135
136 const cellDisplay = page.locator('td[data-id="A1"] .cell-display');
137 await expect(cellDisplay).toHaveCSS('font-weight', '600');
138
139 await page.keyboard.press(`${mod(page)}+z`);
140
141 const weight = await cellDisplay.evaluate(el => getComputedStyle(el).fontWeight);
142 expect(parseInt(weight)).toBeLessThanOrEqual(400);
143 });
144});