Skip to content

Commit

Permalink
Swipe tests
Browse files Browse the repository at this point in the history
  • Loading branch information
atomiks committed Mar 11, 2025
1 parent 3080b8b commit afa920e
Showing 1 changed file with 335 additions and 0 deletions.
335 changes: 335 additions & 0 deletions packages/react/src/toast/root/ToastRoot.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,339 @@ describe('<Toast.Root />', () => {
expect(status).to.have.text('titledescription');
});
});

describe.skipIf(isJSDOM)('swipe behavior', () => {
function SwipeTestButton() {
const { add } = Toast.useToast();
return (
<button
type="button"
onClick={() => {
add({
id: 'swipe-test-toast',
title: 'Swipe Me',
description: 'Swipe to dismiss',
});
}}
>
add toast
</button>
);
}

function SwipeTestToast({
swipeDirection,
}: {
swipeDirection: Toast.Root.Props['swipeDirection'];
}) {
return Toast.useToast().toasts.map((toastItem) => (
<Toast.Root
key={toastItem.id}
toast={toastItem}
data-testid="toast-root"
swipeDirection={swipeDirection}
>
<Toast.Title>{toastItem.title}</Toast.Title>
<Toast.Description>{toastItem.description}</Toast.Description>
</Toast.Root>
));
}

function simulateSwipe(
element: HTMLElement,
startX: number,
startY: number,
endX: number,
endY: number,
) {
fireEvent.pointerDown(element, {
clientX: startX,
clientY: startY,
button: 0,
bubbles: true,
pointerId: 1,
});
fireEvent.pointerMove(element, {
clientX: endX,
clientY: endY,
bubbles: true,
pointerId: 1,
});
fireEvent.pointerUp(element, { clientX: endX, clientY: endY, bubbles: true, pointerId: 1 });
}

it('closes toast when swiping in the specified direction beyond threshold', async () => {
await render(
<Toast.Provider>
<Toast.Viewport>
<SwipeTestToast swipeDirection="up" />
</Toast.Viewport>
<SwipeTestButton />
</Toast.Provider>,
);

fireEvent.click(screen.getByRole('button', { name: 'add toast' }));

const toastElement = screen.getByTestId('toast-root');

// Swipe up (starting at y=100, ending at y=80, which is > 15px threshold)
simulateSwipe(toastElement, 100, 100, 100, 80);

await waitFor(() => {
expect(screen.queryByTestId('toast-root')).to.equal(null);
});
});

it('does not close toast when swiping in the specified direction below threshold', async () => {
await render(
<Toast.Provider>
<Toast.Viewport>
<SwipeTestToast swipeDirection="up" />
</Toast.Viewport>
<SwipeTestButton />
</Toast.Provider>,
);

fireEvent.click(screen.getByRole('button', { name: 'add toast' }));

const toastElement = screen.getByTestId('toast-root');

// Swipe up but only by 10px (below 15px threshold)
simulateSwipe(toastElement, 100, 100, 100, 90);

expect(screen.queryByTestId('toast-root')).not.to.equal(null);
});

it('does not close toast when swiping in a non-specified direction', async () => {
await render(
<Toast.Provider>
<Toast.Viewport>
<SwipeTestToast swipeDirection="up" />
</Toast.Viewport>
<SwipeTestButton />
</Toast.Provider>,
);

fireEvent.click(screen.getByRole('button', { name: 'add toast' }));

const toastElement = screen.getByTestId('toast-root');

// Swipe down (opposite of allowed direction)
simulateSwipe(toastElement, 100, 100, 100, 120);

expect(screen.queryByTestId('toast-root')).not.to.equal(null);
});

it('supports multiple swipe directions', async () => {
await render(
<Toast.Provider>
<Toast.Viewport>
<SwipeTestToast swipeDirection={['up', 'right']} />
</Toast.Viewport>
<SwipeTestButton />
</Toast.Provider>,
);

fireEvent.click(screen.getByRole('button', { name: 'add toast' }));

const toastElement = screen.getByTestId('toast-root');

// Swipe right
simulateSwipe(toastElement, 100, 100, 120, 100);

await waitFor(() => {
expect(screen.queryByTestId('toast-root')).to.equal(null);
});
});

it('cancels swipe when direction is reversed beyond threshold', async () => {
await render(
<Toast.Provider>
<Toast.Viewport>
<SwipeTestToast swipeDirection="up" />
</Toast.Viewport>
<SwipeTestButton />
</Toast.Provider>,
);

fireEvent.click(screen.getByRole('button', { name: 'add toast' }));

const toastElement = screen.getByTestId('toast-root');

// Start swiping up
fireEvent.pointerDown(toastElement, { clientX: 100, clientY: 100, button: 0, pointerId: 1 });
fireEvent.pointerMove(toastElement, { clientX: 100, clientY: 80, pointerId: 1 });

// Then reverse direction
fireEvent.pointerMove(toastElement, { clientX: 100, clientY: 90, pointerId: 1 });
fireEvent.pointerUp(toastElement, { clientX: 100, clientY: 90, pointerId: 1 });

expect(screen.queryByTestId('toast-root')).not.to.equal(null);
});

it('applies correct data attributes during swipe', async () => {
await render(
<Toast.Provider>
<Toast.Viewport>
<SwipeTestToast swipeDirection="up" />
</Toast.Viewport>
<SwipeTestButton />
</Toast.Provider>,
);

fireEvent.click(screen.getByRole('button', { name: 'add toast' }));

const toastElement = screen.getByTestId('toast-root');

fireEvent.pointerDown(toastElement, { clientX: 100, clientY: 100, button: 0, pointerId: 1 });

expect(toastElement.getAttribute('data-swipe')).to.equal('start');

// Move enough to trigger real drag
fireEvent.pointerMove(toastElement, { clientX: 100, clientY: 80, pointerId: 1 });

expect(toastElement.getAttribute('data-swipe')).to.equal('move');
expect(toastElement.getAttribute('data-swipe-direction')).to.equal('up');

fireEvent.pointerUp(toastElement, { clientX: 100, clientY: 80, pointerId: 1 });

expect(toastElement.getAttribute('data-swipe')).to.equal('end');
});

it('dismisses toast when swiped down with downward swipe direction', async () => {
await render(
<Toast.Provider>
<Toast.Viewport>
<SwipeTestToast swipeDirection="down" />
</Toast.Viewport>
<SwipeTestButton />
</Toast.Provider>,
);

fireEvent.click(screen.getByRole('button', { name: 'add toast' }));

const toastElement = screen.getByTestId('toast-root');
simulateSwipe(toastElement, 100, 100, 100, 150);

// Toast should be removed
expect(screen.queryByTestId('toast-root')).to.equal(null);
});

it('dismisses toast when swiped left with leftward swipe direction', async () => {
await render(
<Toast.Provider>
<Toast.Viewport>
<SwipeTestToast swipeDirection="left" />
</Toast.Viewport>
<SwipeTestButton />
</Toast.Provider>,
);

fireEvent.click(screen.getByRole('button', { name: 'add toast' }));

const toastElement = screen.getByTestId('toast-root');
simulateSwipe(toastElement, 100, 100, 50, 100);

// Toast should be removed
expect(screen.queryByTestId('toast-root')).to.equal(null);
});

it('dismisses toast when swiped right with rightward swipe direction', async () => {
await render(
<Toast.Provider>
<Toast.Viewport>
<SwipeTestToast swipeDirection="right" />
</Toast.Viewport>
<SwipeTestButton />
</Toast.Provider>,
);

fireEvent.click(screen.getByRole('button', { name: 'add toast' }));

const toastElement = screen.getByTestId('toast-root');
simulateSwipe(toastElement, 100, 100, 150, 100);

expect(screen.queryByTestId('toast-root')).to.equal(null);
});

it('allows swiping in multiple directions when specified', async () => {
await render(
<Toast.Provider>
<Toast.Viewport>
<SwipeTestToast swipeDirection={['up', 'right']} />
</Toast.Viewport>
<SwipeTestButton />
</Toast.Provider>,
);

fireEvent.click(screen.getByRole('button', { name: 'add toast' }));

const toastElement = screen.getByTestId('toast-root');

// First test upward swipe
simulateSwipe(toastElement, 100, 100, 100, 50);

// Toast should be removed
expect(screen.queryByTestId('toast-root')).to.equal(null);

// Add another toast to test right swipe
fireEvent.click(screen.getByRole('button', { name: 'add toast' }));
const secondToastElement = screen.getByTestId('toast-root');

simulateSwipe(secondToastElement, 100, 100, 150, 100);

// Wait for animation to complete
await new Promise((resolve) => {
setTimeout(resolve, 100);
});

expect(screen.queryByTestId('toast-root')).to.equal(null);
});

it('does not dismiss when swiped in non-specified direction', async () => {
await render(
<Toast.Provider>
<Toast.Viewport>
<SwipeTestToast swipeDirection="up" />
</Toast.Viewport>
<SwipeTestButton />
</Toast.Provider>,
);

fireEvent.click(screen.getByRole('button', { name: 'add toast' }));

const toastElement = screen.getByTestId('toast-root');

// Swipe right (not allowed)
simulateSwipe(toastElement, 100, 100, 150, 100);

// Toast should still be visible
expect(screen.queryByTestId('toast-root')).not.to.equal(null);

// Swipe down (not allowed)
simulateSwipe(toastElement, 100, 100, 100, 150);

expect(screen.queryByTestId('toast-root')).not.to.equal(null);
});

it('does not dismiss when swipe distance is below threshold', async () => {
await render(
<Toast.Provider>
<Toast.Viewport>
<SwipeTestToast swipeDirection="up" />
</Toast.Viewport>
<SwipeTestButton />
</Toast.Provider>,
);

fireEvent.click(screen.getByRole('button', { name: 'add toast' }));

const toastElement = screen.getByTestId('toast-root');

// Small upward swipe (below threshold)
simulateSwipe(toastElement, 100, 100, 100, 95);

expect(screen.queryByTestId('toast-root')).not.to.equal(null);
});
});
});

0 comments on commit afa920e

Please sign in to comment.