Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions resources/js/components/Toasts.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,9 +26,12 @@ export default class Toasts {

router.on('success', (event) => {
const toasts = event.detail.page.props._toasts;
if (toasts && Array.isArray(toasts)) {
this.#displayToasts(toasts);
}

if (!Array.isArray(toasts) || !toasts.length) return;

delete event.detail.page.props._toasts;

this.#displayToasts(toasts);
});
}

Expand Down
62 changes: 62 additions & 0 deletions resources/js/tests/Toasts.test.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
import { test, expect, beforeEach, vi } from 'vitest';

// Mock @inertiajs/vue3 router before importing the component so it captures
// the mock instead of the real router.
vi.mock('@inertiajs/vue3', () => {
const listeners = { success: [] };
return {
router: {
on: (event, callback) => {
listeners[event].push(callback);
return () => {
listeners[event] = listeners[event].filter((cb) => cb !== callback);
};
},
__listeners: listeners,
},
};
});

const fakeAxios = { interceptors: { response: { use: () => {} } } };

let toasts;
let fireSuccess;

beforeEach(async () => {
vi.resetModules();
const { router } = await import('@inertiajs/vue3');
router.__listeners.success = [];
const { default: Toasts } = await import('../components/Toasts.js');
toasts = new Toasts();
toasts.success = vi.fn();
toasts.error = vi.fn();
toasts.registerInterceptor(fakeAxios);
fireSuccess = (page) => router.__listeners.success.forEach((cb) => cb({ detail: { page } }));
});

test('toasts in the page props are displayed', () => {
fireSuccess({ props: { _toasts: [{ type: 'success', message: 'Saved' }] } });

expect(toasts.success).toHaveBeenCalledTimes(1);
expect(toasts.success).toHaveBeenCalledWith('Saved', { duration: undefined });
});

test('nothing happens without toasts in the page props', () => {
fireSuccess({ props: {} });
fireSuccess({ props: { _toasts: [] } });

expect(toasts.success).not.toHaveBeenCalled();
});

test('toasts are not replayed by partial reloads', () => {
const page = { props: { _toasts: [{ type: 'success', message: 'Saved' }] } };

fireSuccess(page);

// Simulate a partial reload of the same component: Inertia merges the partial
// response - which carries no _toasts key - into the existing page props.
page.props = { ...page.props, time: '12:00:00' };
fireSuccess(page);

expect(toasts.success).toHaveBeenCalledTimes(1);
});
Loading