mirror of
https://github.com/penpot/penpot.git
synced 2026-10-30 06:20:32 +00:00
100 lines
2.8 KiB
TypeScript
100 lines
2.8 KiB
TypeScript
import { expect } from '../framework/expect';
|
|
import { describe, test } from '../framework/registry';
|
|
import { waitFor } from './wait';
|
|
|
|
// Events.
|
|
// Listeners are registered with `on`, triggered by mutating state, and removed
|
|
// with `off`. Callbacks are debounced (~10ms), so the tests wait before asserting.
|
|
|
|
function sleep(ms: number): Promise<void> {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
describe('Events', () => {
|
|
test('selectionchange fires with the selected ids', async (ctx) => {
|
|
const rect = ctx.penpot.createRectangle();
|
|
ctx.board.appendChild(rect);
|
|
|
|
let received: string[] | null = null;
|
|
const listenerId = ctx.penpot.on('selectionchange', (ids) => {
|
|
received = ids;
|
|
});
|
|
|
|
ctx.penpot.selection = [rect];
|
|
await waitFor(() => received !== null);
|
|
ctx.penpot.off(listenerId);
|
|
|
|
expect(received).not.toBeNull();
|
|
if (received) {
|
|
expect((received as string[]).includes(rect.id)).toBe(true);
|
|
}
|
|
});
|
|
|
|
test('shapechange fires when the observed shape changes', async (ctx) => {
|
|
const rect = ctx.penpot.createRectangle();
|
|
ctx.board.appendChild(rect);
|
|
|
|
let fired = false;
|
|
const listenerId = ctx.penpot.on(
|
|
'shapechange',
|
|
() => {
|
|
fired = true;
|
|
},
|
|
{ shapeId: rect.id },
|
|
);
|
|
|
|
rect.name = 'changed-name';
|
|
await waitFor(() => fired);
|
|
ctx.penpot.off(listenerId);
|
|
|
|
expect(fired).toBe(true);
|
|
});
|
|
|
|
test('off stops further notifications', async (ctx) => {
|
|
const rect = ctx.penpot.createRectangle();
|
|
ctx.board.appendChild(rect);
|
|
|
|
let count = 0;
|
|
const listenerId = ctx.penpot.on('selectionchange', () => {
|
|
count += 1;
|
|
});
|
|
ctx.penpot.off(listenerId);
|
|
|
|
ctx.penpot.selection = [rect];
|
|
// Asserting a non-event: there is no positive condition to poll for, so
|
|
// give the debounce a fixed window to (not) fire.
|
|
await sleep(150);
|
|
|
|
expect(count).toBe(0);
|
|
});
|
|
|
|
test('a blank shapeId leaves the other listeners working', async (ctx) => {
|
|
const rect = ctx.penpot.createRectangle();
|
|
ctx.board.appendChild(rect);
|
|
|
|
// Listeners are store watches notified by a plain iteration, so one that
|
|
// raises aborts the loop and the listeners after it miss the change.
|
|
const blankListenerId = ctx.penpot.on('shapechange', () => {}, {
|
|
shapeId: '',
|
|
});
|
|
|
|
let received: string[] | null = null;
|
|
const listenerId = ctx.penpot.on('selectionchange', (ids) => {
|
|
received = ids;
|
|
});
|
|
|
|
ctx.penpot.selection = [rect];
|
|
await waitFor(() => received !== null);
|
|
ctx.penpot.off(listenerId);
|
|
ctx.penpot.off(blankListenerId);
|
|
|
|
expect(received).not.toBeNull();
|
|
});
|
|
|
|
test('a malformed shapeId is rejected when the listener is registered', (ctx) => {
|
|
expect(() =>
|
|
ctx.penpot.on('shapechange', () => {}, { shapeId: 'not-a-uuid' }),
|
|
).toThrow();
|
|
});
|
|
});
|