diff --git a/src/composables/__tests__/useInterceptNotifications.spec.js b/src/composables/__tests__/useInterceptNotifications.spec.js new file mode 100644 index 00000000000..bca8243a71a --- /dev/null +++ b/src/composables/__tests__/useInterceptNotifications.spec.js @@ -0,0 +1,67 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { emit } from '@nextcloud/event-bus' +import { mount } from '@vue/test-utils' +import { afterAll, afterEach, describe, expect, test, vi } from 'vitest' +import { useInterceptNotifications } from '../useInterceptNotifications.ts' + +const router = { push: vi.fn(() => new Promise(() => {})) } + +vi.mock('vue-router', () => ({ useRouter: () => router })) +vi.mock('vuex', () => ({ useStore: () => ({}) })) +vi.mock('../../stores/federation.ts', () => ({ useFederationStore: () => ({}) })) + +describe('useInterceptNotifications', () => { + const wrapper = mount({ + setup() { + useInterceptNotifications() + return () => null + }, + }) + + afterAll(() => { + wrapper.unmount() + }) + + afterEach(() => { + router.push.mockClear() + }) + + test('cancels a conversation link synchronously, before navigation finishes', () => { + const event = { + cancelAction: false, + notification: { app: 'spreed' }, + action: { type: 'WEB', url: 'https://cloud.example.com/index.php/call/abc123#message_42' }, + } + emit('notifications:action:execute', event) + + expect(event.cancelAction).toBe(true) + expect(router.push).toHaveBeenCalledWith('/call/abc123#message_42') + }) + + test('keeps default behavior for links without a conversation', () => { + const event = { + cancelAction: false, + notification: { app: 'spreed' }, + action: { type: 'WEB', url: 'https://cloud.example.com/index.php/settings/admin/talk' }, + } + emit('notifications:action:execute', event) + + expect(event.cancelAction).toBe(false) + expect(router.push).not.toHaveBeenCalled() + }) + + test('ignores notifications of other apps', () => { + const event = { + cancelAction: false, + notification: { app: 'files' }, + action: { type: 'WEB', url: 'https://cloud.example.com/index.php/call/abc123' }, + } + emit('notifications:action:execute', event) + + expect(event.cancelAction).toBe(false) + }) +}) diff --git a/src/composables/useInterceptNotifications.ts b/src/composables/useInterceptNotifications.ts index 0a9c3b9a8e8..87262e3bf7e 100644 --- a/src/composables/useInterceptNotifications.ts +++ b/src/composables/useInterceptNotifications.ts @@ -61,9 +61,9 @@ export function useInterceptNotifications() { } const conversationRouteLocationRaw = event.action.url.substring(conversationRouteIndex) - await router.push(conversationRouteLocationRaw) - + // Must be set before the first await, the notifications app checks it right after emitting event.cancelAction = true + await router.push(conversationRouteLocationRaw) break } case 'POST': {