pendingMovePreview.test.ts2.6 KBView on GitHub
/**
 * The grid's answer to a date typed into an event's details panel, BEFORE Save: the block
 * stays where it is (marked as the origin, drawn faded) and a ghost is added at the slot the
 * event would move to. Both halves matter — a preview that only moved the block would look
 * exactly like a change that had already been saved.
 */
import { applyPendingMove } from '@/modules/calendar/utils/pendingMovePreview';
import type { CalendarEvent } from '@/modules/calendar/types/calendar-types';

const event = (id: string, start: string, end: string): CalendarEvent => ({
  id,
  summary: `Event ${id}`,
  start: { dateTime: start, timeZone: 'America/Los_Angeles' },
  end: { dateTime: end, timeZone: 'America/Los_Angeles' },
});

const MONDAY = event('evt-1', '2026-08-31T17:00:00.000Z', '2026-08-31T17:30:00.000Z');
const OTHER = event('evt-2', '2026-08-31T20:00:00.000Z', '2026-08-31T21:00:00.000Z');

const MOVE = {
  eventId: 'evt-1',
  start: '2026-09-01T22:00:00.000Z',
  end: '2026-09-01T22:30:00.000Z',
};

describe('applyPendingMove', () => {
  it('leaves the list alone when nothing is being moved', () => {
    const events = [MONDAY, OTHER];
    expect(applyPendingMove(events, null)).toBe(events);
  });

  it('leaves the list alone when the moved event is outside the fetched window', () => {
    const events = [OTHER];
    expect(applyPendingMove(events, MOVE)).toBe(events);
  });

  it('marks the original and appends a ghost at the new time', () => {
    const result = applyPendingMove([MONDAY, OTHER], MOVE);

    const origin = result.find((e) => e.id === 'evt-1');
    expect(origin?.isMoveOrigin).toBe(true);
    // The original does NOT move: the panel is anchored to it, and the point of the preview
    // is that both ends of the change are on screen at once.
    expect(origin?.start?.dateTime).toBe(MONDAY.start!.dateTime);

    const ghost = result.find((e) => e.isPendingMove);
    expect(ghost?.start?.dateTime).toBe(MOVE.start);
    expect(ghost?.end?.dateTime).toBe(MOVE.end);
    expect(ghost?.summary).toBe('Event evt-1');
    // A distinct id: colliding with the original would put two blocks under one key in the
    // overlap layout and in the selected-event id.
    expect(ghost?.id).not.toBe('evt-1');

    // Every other event is untouched, and nothing is dropped.
    expect(result).toHaveLength(3);
    expect(result.find((e) => e.id === 'evt-2')).toEqual(OTHER);
  });

  it('does not mutate the events it was given', () => {
    const events = [MONDAY, OTHER];
    applyPendingMove(events, MOVE);
    expect(events).toHaveLength(2);
    expect(MONDAY.isMoveOrigin).toBeUndefined();
  });
});