Skip to content

Commit fac1635

Browse files
committed
add test for undo functionality, currently failing
1 parent 872580f commit fac1635

File tree

1 file changed

+174
-0
lines changed

1 file changed

+174
-0
lines changed
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// Copyright 2025 Signal Messenger, LLC
2+
// SPDX-License-Identifier: AGPL-3.0-only
3+
4+
import { expect } from 'playwright/test';
5+
import { type PrimaryDevice, StorageState } from '@signalapp/mock-server';
6+
import * as path from 'node:path';
7+
8+
import type { App } from '../playwright.js';
9+
import { Bootstrap } from '../bootstrap.js';
10+
import { composerAttachImages } from '../helpers.js';
11+
import * as durations from '../../util/durations/index.js';
12+
13+
const CAT_PATH = path.join(
14+
__dirname,
15+
'..',
16+
'..',
17+
'..',
18+
'fixtures',
19+
'cat-screenshot.png'
20+
);
21+
22+
describe('MediaEditor', function (this: Mocha.Suite) {
23+
this.timeout(durations.MINUTE);
24+
25+
let bootstrap: Bootstrap;
26+
let app: App;
27+
let pinned: PrimaryDevice;
28+
29+
beforeEach(async () => {
30+
bootstrap = new Bootstrap();
31+
await bootstrap.init();
32+
33+
let state = StorageState.getEmpty();
34+
35+
const { phone, contacts } = bootstrap;
36+
[pinned] = contacts;
37+
38+
state = state.addContact(pinned, {
39+
identityKey: pinned.publicKey.serialize(),
40+
profileKey: pinned.profileKey.serialize(),
41+
whitelisted: true,
42+
});
43+
44+
state = state.pin(pinned);
45+
await phone.setStorageState(state);
46+
47+
app = await bootstrap.link();
48+
});
49+
50+
afterEach(async function (this: Mocha.Context) {
51+
if (!bootstrap) {
52+
return;
53+
}
54+
55+
await bootstrap.maybeSaveLogs(this.currentTest, app);
56+
await app.close();
57+
await bootstrap.teardown();
58+
});
59+
60+
async function openMediaEditor(page: Awaited<ReturnType<App['getWindow']>>) {
61+
await page.getByTestId(pinned.device.aci).click();
62+
63+
await composerAttachImages(page, [CAT_PATH]);
64+
65+
const AttachmentsList = page.locator('.module-attachments');
66+
await AttachmentsList.waitFor({ state: 'visible' });
67+
68+
const EditableAttachment = AttachmentsList.locator(
69+
'.module-attachments--editable'
70+
).first();
71+
await EditableAttachment.waitFor({ state: 'visible' });
72+
73+
const StagedImage = EditableAttachment.locator('.module-image--loaded');
74+
await StagedImage.waitFor({ state: 'visible' });
75+
76+
await StagedImage.click();
77+
78+
const MediaEditor = page.locator('.MediaEditor');
79+
await MediaEditor.waitFor({ state: 'visible' });
80+
81+
return MediaEditor;
82+
}
83+
84+
async function drawLineOnCanvas(
85+
page: Awaited<ReturnType<App['getWindow']>>,
86+
MediaEditor: Awaited<ReturnType<typeof openMediaEditor>>,
87+
options?: {
88+
startX?: number;
89+
startY?: number;
90+
endX?: number;
91+
endY?: number;
92+
}
93+
) {
94+
const canvas = MediaEditor.locator('.MediaEditor__media--canvas').first();
95+
const canvasBox = await canvas.boundingBox();
96+
97+
if (!canvasBox) {
98+
throw new Error('Canvas bounding box not found');
99+
}
100+
101+
// Draw diagonal line by default
102+
const startX = options?.startX ?? canvasBox.x + canvasBox.width * 0.3;
103+
const startY = options?.startY ?? canvasBox.y + canvasBox.height * 0.3;
104+
const endX = options?.endX ?? canvasBox.x + canvasBox.width * 0.7;
105+
const endY = options?.endY ?? canvasBox.y + canvasBox.height * 0.7;
106+
107+
await page.mouse.move(startX, startY);
108+
await page.mouse.down();
109+
await page.mouse.move(endX, endY);
110+
await page.mouse.up();
111+
}
112+
113+
it('can undo after drawing a line', async () => {
114+
const page = await app.getWindow();
115+
const MediaEditor = await openMediaEditor(page);
116+
117+
const canvas = MediaEditor.locator('.MediaEditor__media--canvas').first();
118+
119+
const screenshotBeforeDrawing = await canvas.screenshot();
120+
121+
const DrawButton = MediaEditor.locator('.MediaEditor__control--pen');
122+
await DrawButton.click();
123+
124+
await page.waitForTimeout(100);
125+
126+
await drawLineOnCanvas(page, MediaEditor);
127+
128+
await page.waitForTimeout(100);
129+
130+
const screenshotAfterDrawing = await canvas.screenshot();
131+
132+
const UndoButton = MediaEditor.locator('.MediaEditor__control--undo');
133+
await expect(UndoButton).toBeEnabled();
134+
await UndoButton.click();
135+
136+
await page.waitForTimeout(100);
137+
138+
const screenshotAfterUndo = await canvas.screenshot();
139+
140+
expect(
141+
Buffer.compare(screenshotBeforeDrawing, screenshotAfterDrawing),
142+
'screenshots before and after drawing should be different'
143+
).not.toBe(0);
144+
145+
expect(
146+
Buffer.compare(screenshotBeforeDrawing, screenshotAfterUndo),
147+
'screenshot before drawing should be the same as after undo'
148+
).toBe(0);
149+
});
150+
151+
it('undo button is disabled when there is nothing to undo', async () => {
152+
const page = await app.getWindow();
153+
const MediaEditor = await openMediaEditor(page);
154+
155+
const UndoButton = MediaEditor.locator('.MediaEditor__control--undo');
156+
await expect(UndoButton).toBeDisabled();
157+
158+
const DrawButton = MediaEditor.locator('.MediaEditor__control--pen');
159+
await DrawButton.click();
160+
161+
await page.waitForTimeout(100);
162+
163+
await drawLineOnCanvas(page, MediaEditor);
164+
165+
await page.waitForTimeout(100);
166+
167+
await expect(UndoButton).toBeEnabled();
168+
await UndoButton.click();
169+
170+
await page.waitForTimeout(100);
171+
172+
await expect(UndoButton).toBeDisabled();
173+
});
174+
});

0 commit comments

Comments
 (0)