Skip to content

Commit 0830c26

Browse files
committed
added tests for peopleSearch
1 parent fda97eb commit 0830c26

1 file changed

Lines changed: 165 additions & 0 deletions

File tree

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import { NamedNode } from 'rdflib'
2+
import { silenceDebugMessages } from '../helpers/debugger'
3+
import { createPeopleSearch } from '../../../src/widgets/peopleSearch'
4+
5+
const mockListAddressBooks = jest.fn()
6+
const mockReadAddressBook = jest.fn()
7+
8+
jest.mock('@solid-data-modules/contacts-rdflib', () => ({
9+
__esModule: true,
10+
default: class ContactsModuleRdfLib {
11+
listAddressBooks = mockListAddressBooks
12+
readAddressBook = mockReadAddressBook
13+
}
14+
}))
15+
16+
silenceDebugMessages()
17+
18+
const flushAsyncWork = async function () {
19+
await Promise.resolve()
20+
await new Promise(resolve => setTimeout(resolve, 0))
21+
await Promise.resolve()
22+
}
23+
24+
const makeKb = function () {
25+
return {
26+
fetcher: {
27+
load: jest.fn().mockResolvedValue(undefined)
28+
},
29+
updater: {},
30+
any: jest.fn((subject, predicate) => {
31+
if (
32+
subject?.value?.includes('/contacts/1#this') &&
33+
predicate?.value?.includes('url')
34+
) {
35+
return new NamedNode('https://pod.example/contacts/1#url')
36+
}
37+
return null
38+
}),
39+
anyValue: jest.fn((subject, predicate) => {
40+
if (
41+
subject?.value?.includes('/contacts/1#url') &&
42+
predicate?.value?.includes('value')
43+
) {
44+
return 'https://alice.example/profile/card#me'
45+
}
46+
return null
47+
}),
48+
each: jest.fn().mockReturnValue([])
49+
}
50+
}
51+
52+
describe('createPeopleSearch', () => {
53+
beforeEach(() => {
54+
document.body.innerHTML = ''
55+
jest.clearAllMocks()
56+
57+
mockListAddressBooks.mockResolvedValue({
58+
publicUris: ['https://pod.example/address-book-1.ttl'],
59+
privateUris: []
60+
})
61+
62+
mockReadAddressBook.mockResolvedValue({
63+
contacts: [
64+
{
65+
uri: 'https://pod.example/contacts/1#this',
66+
name: 'Alice Example'
67+
}
68+
]
69+
})
70+
})
71+
72+
it('renders a search input and hidden dropdown', () => {
73+
const kb = makeKb()
74+
const me = new NamedNode('https://user-1.example/profile/card#me')
75+
76+
const form = createPeopleSearch(document, kb as any, me)
77+
document.body.appendChild(form)
78+
79+
const input = form.querySelector('input') as HTMLInputElement | null
80+
const dropdown = form.querySelector('.people-search-dropdown') as HTMLDivElement | null
81+
82+
expect(input).not.toBeNull()
83+
expect(input?.placeholder).toBe('Search for people...')
84+
expect(dropdown).not.toBeNull()
85+
expect(dropdown?.style.display).toBe('none')
86+
})
87+
88+
it('uses onClickHandler when provided and hides dropdown', async () => {
89+
const kb = makeKb()
90+
const me = new NamedNode('https://user-2.example/profile/card#me')
91+
const onClickHandler = jest.fn()
92+
const openSpy = jest.spyOn(window, 'open').mockImplementation(() => null)
93+
94+
const form = createPeopleSearch(document, kb as any, me, onClickHandler)
95+
document.body.appendChild(form)
96+
97+
await flushAsyncWork()
98+
99+
const input = form.querySelector('input') as HTMLInputElement
100+
const dropdown = form.querySelector('.people-search-dropdown') as HTMLDivElement
101+
102+
input.dispatchEvent(new Event('focus'))
103+
await flushAsyncWork()
104+
105+
const personRow = form.querySelector('div[title="https://alice.example/profile/card#me"]') as HTMLDivElement
106+
expect(personRow).not.toBeNull()
107+
108+
personRow.dispatchEvent(new Event('click'))
109+
110+
expect(onClickHandler).toHaveBeenCalledTimes(1)
111+
expect(onClickHandler).toHaveBeenCalledWith({
112+
name: 'Alice Example',
113+
webId: 'https://alice.example/profile/card#me',
114+
relationshipLabel: 'Contact'
115+
})
116+
expect(openSpy).not.toHaveBeenCalled()
117+
expect(dropdown.style.display).toBe('none')
118+
119+
openSpy.mockRestore()
120+
})
121+
122+
it('falls back to opening webId when onClickHandler is not provided', async () => {
123+
const kb = makeKb()
124+
const me = new NamedNode('https://user-3.example/profile/card#me')
125+
const openSpy = jest.spyOn(window, 'open').mockImplementation(() => null)
126+
127+
const form = createPeopleSearch(document, kb as any, me)
128+
document.body.appendChild(form)
129+
130+
await flushAsyncWork()
131+
132+
const input = form.querySelector('input') as HTMLInputElement
133+
const dropdown = form.querySelector('.people-search-dropdown') as HTMLDivElement
134+
135+
input.dispatchEvent(new Event('focus'))
136+
await flushAsyncWork()
137+
138+
const personRow = form.querySelector('div[title="https://alice.example/profile/card#me"]') as HTMLDivElement
139+
expect(personRow).not.toBeNull()
140+
141+
personRow.dispatchEvent(new Event('click'))
142+
143+
expect(openSpy).toHaveBeenCalledTimes(1)
144+
expect(openSpy).toHaveBeenCalledWith('https://alice.example/profile/card#me', '_blank')
145+
expect(dropdown.style.display).toBe('none')
146+
147+
openSpy.mockRestore()
148+
})
149+
150+
it('shows sign-in message when me is null', async () => {
151+
const kb = makeKb()
152+
153+
const form = createPeopleSearch(document, kb as any, null)
154+
document.body.appendChild(form)
155+
156+
const input = form.querySelector('input') as HTMLInputElement
157+
const dropdown = form.querySelector('.people-search-dropdown') as HTMLDivElement
158+
159+
input.dispatchEvent(new Event('focus'))
160+
await flushAsyncWork()
161+
162+
expect(dropdown.style.display).toBe('block')
163+
expect(dropdown.textContent).toContain('Sign in to search contacts.')
164+
})
165+
})

0 commit comments

Comments
 (0)