Skip to content

Commit c14bf44

Browse files
authored
feat: add testId prop for individual toast components (#660)
* feat: add testId property to ToastT interface * feat: add data-testid attribute to Toast component for testing * feat: add buttons for toast notifications with test IDs * test: add tests for toast notifications with and without data-testid attributes
1 parent 05af865 commit c14bf44

File tree

4 files changed

+43
-0
lines changed

4 files changed

+43
-0
lines changed

src/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ const Toast = (props: ToastProps) => {
290290
data-swipe-out={swipeOut}
291291
data-swipe-direction={swipeOutDirection}
292292
data-expanded={Boolean(expanded || (expandByDefault && mounted))}
293+
data-testid={toast.testId}
293294
style={
294295
{
295296
'--index': index,

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export interface ToastT {
8686
classNames?: ToastClassnames;
8787
descriptionClassName?: string;
8888
position?: Position;
89+
testId?: string;
8990
}
9091

9192
export function isAction(action: Action | React.ReactNode): action is Action {

test/src/app/page.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,27 @@ export default function Home({ searchParams }: any) {
313313
>
314314
With custom ARIA labels
315315
</button>
316+
<button
317+
data-testid="testid-toast-button"
318+
className="button"
319+
onClick={() => toast('Toast with test ID', { testId: 'my-test-toast' })}
320+
>
321+
Toast with testId
322+
</button>
323+
<button
324+
data-testid="testid-promise-toast-button"
325+
className="button"
326+
onClick={() =>
327+
toast.promise(promise, {
328+
loading: 'Loading...',
329+
success: 'Loaded',
330+
error: 'Error',
331+
testId: 'promise-test-toast',
332+
})
333+
}
334+
>
335+
Promise Toast with testId
336+
</button>
316337
{showAutoClose ? <div data-testid="auto-close-el" /> : null}
317338
{showDismiss ? <div data-testid="dismiss-el" /> : null}
318339
<Toaster

test/tests/basic.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,4 +293,24 @@ test.describe('Basic functionality', () => {
293293
await expect(page.getByLabel('Notices')).toHaveCount(1);
294294
await expect(page.getByLabel('Yeet the notice', { exact: true })).toHaveCount(1);
295295
});
296+
297+
test('toast with testId renders data-testid attribute correctly', async ({ page }) => {
298+
await page.getByTestId('testid-toast-button').click();
299+
await expect(page.getByTestId('my-test-toast')).toBeVisible();
300+
await expect(page.getByTestId('my-test-toast')).toHaveText('Toast with test ID');
301+
});
302+
303+
test('toast without testId does not have data-testid attribute', async ({ page }) => {
304+
await page.getByTestId('default-button').click();
305+
const toast = page.locator('[data-sonner-toast]');
306+
await expect(toast).toBeVisible();
307+
await expect(toast).not.toHaveAttribute('data-testid');
308+
});
309+
310+
test('promise toast with testId maintains testId through state changes', async ({ page }) => {
311+
await page.getByTestId('testid-promise-toast-button').click();
312+
await expect(page.getByTestId('promise-test-toast')).toBeVisible();
313+
await expect(page.getByTestId('promise-test-toast')).toHaveText('Loading...');
314+
await expect(page.getByTestId('promise-test-toast')).toHaveText('Loaded');
315+
});
296316
});

0 commit comments

Comments
 (0)