Skip to content

Commit e2a94bd

Browse files
committed
test(reference): cover the reference widget and its script listener
Assisted-by: Claude Sonnet 5:claude-sonnet-4.5 Signed-off-by: Jos Poortvliet <jospoortvliet@gmail.com>
1 parent 18551f2 commit e2a94bd

2 files changed

Lines changed: 206 additions & 0 deletions

File tree

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { flushPromises, mount } from '@vue/test-utils'
7+
import { describe, expect, test, vi } from 'vitest'
8+
import ConversationIcon from '../ConversationIcon.vue'
9+
import { CONVERSATION, MESSAGE } from '../../constants.ts'
10+
import { fetchConversation } from '../../services/conversationsService.ts'
11+
import CallReferenceWidget from './CallReferenceWidget.vue'
12+
13+
vi.mock('../../services/conversationsService.ts', () => ({
14+
fetchConversation: vi.fn(),
15+
}))
16+
17+
// ConversationIcon reads capabilities/cached conversations from BrowserStorage at import time
18+
vi.mock('../../services/CapabilitiesManager.ts', () => ({
19+
hasTalkFeature: vi.fn(() => false),
20+
getTalkConfig: vi.fn(),
21+
}))
22+
23+
describe('CallReferenceWidget.vue', () => {
24+
const richObject = {
25+
id: 'XXTOKENXX',
26+
name: 'Fallback conversation name',
27+
link: 'https://nextcloud.local/call/XXTOKENXX',
28+
'call-type': 'group',
29+
}
30+
31+
/**
32+
* @param props additional props to merge on top of the defaults
33+
*/
34+
function mountWidget(props = {}) {
35+
return mount(CallReferenceWidget, {
36+
props: {
37+
richObject,
38+
accessible: true,
39+
...props,
40+
},
41+
})
42+
}
43+
44+
test('renders nothing when the reference is not accessible', async () => {
45+
const wrapper = mountWidget({ accessible: false })
46+
await flushPromises()
47+
48+
expect(wrapper.find('a').exists()).toBe(false)
49+
expect(fetchConversation).not.toHaveBeenCalled()
50+
})
51+
52+
test('renders the live conversation once loaded', async () => {
53+
vi.mocked(fetchConversation).mockResolvedValueOnce({
54+
data: {
55+
ocs: {
56+
data: {
57+
token: 'XXTOKENXX',
58+
displayName: 'Live conversation name',
59+
type: CONVERSATION.TYPE.GROUP,
60+
},
61+
},
62+
},
63+
})
64+
65+
const wrapper = mountWidget()
66+
await flushPromises()
67+
68+
expect(fetchConversation).toHaveBeenCalledWith('XXTOKENXX')
69+
expect(wrapper.text()).toContain('Live conversation name')
70+
expect(wrapper.findComponent(ConversationIcon).exists()).toBe(true)
71+
})
72+
73+
test('falls back to the reference metadata when the live fetch fails', async () => {
74+
vi.mocked(fetchConversation).mockRejectedValueOnce(new Error('403'))
75+
76+
const wrapper = mountWidget({ fallbackAvatarUrl: 'https://nextcloud.local/avatar.png' })
77+
await flushPromises()
78+
79+
expect(wrapper.text()).toContain('Fallback conversation name')
80+
expect(wrapper.findComponent(ConversationIcon).exists()).toBe(false)
81+
expect(wrapper.find('img').attributes('src')).toBe('https://nextcloud.local/avatar.png')
82+
})
83+
84+
test('does not show an expired last message', async () => {
85+
vi.mocked(fetchConversation).mockResolvedValueOnce({
86+
data: {
87+
ocs: {
88+
data: {
89+
token: 'XXTOKENXX',
90+
displayName: 'Live conversation name',
91+
type: CONVERSATION.TYPE.GROUP,
92+
lastMessage: {
93+
actorDisplayName: 'Alice',
94+
actorType: 'users',
95+
message: 'hello',
96+
messageParameters: {},
97+
messageType: 'comment',
98+
systemMessage: '',
99+
expirationTimestamp: 1,
100+
},
101+
},
102+
},
103+
},
104+
})
105+
106+
const wrapper = mountWidget()
107+
await flushPromises()
108+
109+
expect(wrapper.text()).not.toContain('hello')
110+
})
111+
112+
test('does not show a deleted last message', async () => {
113+
vi.mocked(fetchConversation).mockResolvedValueOnce({
114+
data: {
115+
ocs: {
116+
data: {
117+
token: 'XXTOKENXX',
118+
displayName: 'Live conversation name',
119+
type: CONVERSATION.TYPE.GROUP,
120+
lastMessage: {
121+
actorDisplayName: 'Alice',
122+
actorType: 'users',
123+
message: 'hello',
124+
messageParameters: {},
125+
messageType: MESSAGE.TYPE.COMMENT_DELETED,
126+
systemMessage: '',
127+
expirationTimestamp: 0,
128+
},
129+
},
130+
},
131+
},
132+
})
133+
134+
const wrapper = mountWidget()
135+
await flushPromises()
136+
137+
expect(wrapper.text()).not.toContain('hello')
138+
})
139+
140+
test('shows a non-expired last message with its actor', async () => {
141+
vi.mocked(fetchConversation).mockResolvedValueOnce({
142+
data: {
143+
ocs: {
144+
data: {
145+
token: 'XXTOKENXX',
146+
displayName: 'Live conversation name',
147+
type: CONVERSATION.TYPE.GROUP,
148+
lastMessage: {
149+
actorDisplayName: 'Alice',
150+
actorType: 'users',
151+
message: 'hello',
152+
messageParameters: {},
153+
messageType: 'comment',
154+
systemMessage: '',
155+
expirationTimestamp: 0,
156+
},
157+
},
158+
},
159+
},
160+
})
161+
162+
const wrapper = mountWidget()
163+
await flushPromises()
164+
165+
expect(wrapper.text()).toContain('Alice: hello')
166+
})
167+
})
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Talk\Tests\php\Collaboration\Reference;
10+
11+
use OCA\Talk\Collaboration\Reference\RenderReferenceEventListener;
12+
use OCP\Collaboration\Reference\RenderReferenceEvent;
13+
use OCP\EventDispatcher\Event;
14+
use OCP\Util;
15+
use Test\TestCase;
16+
17+
class RenderReferenceEventListenerTest extends TestCase {
18+
protected RenderReferenceEventListener $listener;
19+
20+
public function setUp(): void {
21+
parent::setUp();
22+
23+
$this->listener = new RenderReferenceEventListener();
24+
}
25+
26+
public function testHandleIgnoresUnrelatedEvents(): void {
27+
$scriptsBefore = Util::getScripts();
28+
29+
$this->listener->handle($this->createMock(Event::class));
30+
31+
self::assertSame($scriptsBefore, Util::getScripts());
32+
}
33+
34+
public function testHandleLoadsTheReferenceScriptOnRenderReferenceEvent(): void {
35+
$this->listener->handle(new RenderReferenceEvent());
36+
37+
self::assertContains('spreed/js/talk-reference', Util::getScripts());
38+
}
39+
}

0 commit comments

Comments
 (0)