Skip to content

Commit 7782e5c

Browse files
feat: add matrix integration support
Signed-off-by: doobry <[email protected]>
1 parent bdb0e3e commit 7782e5c

File tree

7 files changed

+420
-2
lines changed

7 files changed

+420
-2
lines changed

src/api/Ticker.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface TickerFormData {
2424
telegram: TickerTelegram
2525
bluesky: TickerBluesky
2626
signalGroup: TickerSignalGroup
27+
matrix: TickerMatrix
2728
location: TickerLocation
2829
}
2930

@@ -39,6 +40,7 @@ export interface Ticker {
3940
telegram: TickerTelegram
4041
bluesky: TickerBluesky
4142
signalGroup: TickerSignalGroup
43+
matrix: TickerMatrix
4244
location: TickerLocation
4345
}
4446

@@ -118,6 +120,17 @@ export interface TickerSignalGroupAdminFormData {
118120
number: string
119121
}
120122

123+
export interface TickerMatrix {
124+
active: boolean
125+
connected: boolean
126+
roomID: string
127+
roomName: string
128+
}
129+
130+
export interface TickerMatrixFormData {
131+
active: boolean
132+
}
133+
121134
export interface TickerLocation {
122135
lat: number
123136
lon: number
@@ -249,3 +262,15 @@ export async function putTickerSignalGroupAdminApi(token: string, data: TickerSi
249262
body: JSON.stringify(data),
250263
})
251264
}
265+
266+
export async function putTickerMatrixApi(token: string, data: TickerMatrixFormData, ticker: Ticker): Promise<ApiResponse<TickerResponseData>> {
267+
return apiClient<TickerResponseData>(`${ApiUrl}/admin/tickers/${ticker.id}/matrix`, {
268+
headers: apiHeaders(token),
269+
method: 'put',
270+
body: JSON.stringify(data),
271+
})
272+
}
273+
274+
export async function deleteTickerMatrixApi(token: string, ticker: Ticker): Promise<ApiResponse<TickerResponseData>> {
275+
return apiClient<TickerResponseData>(`${ApiUrl}/admin/tickers/${ticker.id}/matrix`, { headers: apiHeaders(token), method: 'delete' })
276+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { screen } from '@testing-library/react'
2+
import userEvent from '@testing-library/user-event'
3+
import { Ticker } from '../../api/Ticker'
4+
import { renderWithProviders, setMockToken, userToken } from '../../tests/utils'
5+
import MatrixCard from './MatrixCard'
6+
7+
describe('MatrixCard', () => {
8+
beforeEach(() => {
9+
setMockToken(userToken)
10+
fetchMock.resetMocks()
11+
})
12+
13+
const ticker = ({ active, connected, roomName = '' }: { active: boolean; connected: boolean; roomName?: string }) => {
14+
return {
15+
id: 1,
16+
matrix: {
17+
active: active,
18+
connected: connected,
19+
roomName: roomName,
20+
},
21+
} as Ticker
22+
}
23+
24+
const component = ({ ticker }: { ticker: Ticker }) => {
25+
return <MatrixCard ticker={ticker} />
26+
}
27+
28+
it('should render the component', () => {
29+
renderWithProviders(component({ ticker: ticker({ active: false, connected: false }) }))
30+
31+
expect(screen.getByText('Matrix')).toBeInTheDocument()
32+
expect(screen.getByText('You are not connected with Matrix.')).toBeInTheDocument()
33+
expect(screen.getByRole('button', { name: 'Add' })).toBeInTheDocument()
34+
})
35+
36+
it('should render the component when connected and active', async () => {
37+
renderWithProviders(component({ ticker: ticker({ active: true, connected: true, roomName: '#room:matrix.org' }) }))
38+
39+
expect(screen.getByText('Matrix')).toBeInTheDocument()
40+
expect(screen.getByText('You are connected with Matrix.')).toBeInTheDocument()
41+
expect(screen.getByText('Your Room:')).toBeInTheDocument()
42+
expect(screen.getByRole('link', { name: '#room:matrix.org' })).toBeInTheDocument()
43+
expect(screen.getByRole('button', { name: 'Delete' })).toBeInTheDocument()
44+
expect(screen.getByRole('button', { name: 'Disable' })).toBeInTheDocument()
45+
46+
fetchMock.mockResponseOnce(JSON.stringify({ status: 'success' }))
47+
48+
await userEvent.click(screen.getByRole('button', { name: 'Disable' }))
49+
50+
expect(fetchMock).toHaveBeenCalledTimes(1)
51+
expect(fetchMock).toHaveBeenCalledWith('http://localhost:8080/v1/admin/tickers/1/matrix', {
52+
body: JSON.stringify({ active: false }),
53+
headers: {
54+
Accept: 'application/json',
55+
'Content-Type': 'application/json',
56+
Authorization: 'Bearer ' + userToken,
57+
},
58+
method: 'put',
59+
})
60+
})
61+
62+
it('should handle add button click', async () => {
63+
renderWithProviders(component({ ticker: ticker({ active: false, connected: false }) }))
64+
65+
fetchMock.mockResponseOnce(JSON.stringify({ status: 'success' }))
66+
67+
await userEvent.click(screen.getByRole('button', { name: 'Add' }))
68+
69+
expect(fetchMock).toHaveBeenCalledTimes(1)
70+
expect(fetchMock).toHaveBeenCalledWith('http://localhost:8080/v1/admin/tickers/1/matrix', {
71+
body: JSON.stringify({ active: true }),
72+
headers: {
73+
Accept: 'application/json',
74+
'Content-Type': 'application/json',
75+
Authorization: 'Bearer ' + userToken,
76+
},
77+
method: 'put',
78+
})
79+
})
80+
81+
it('should handle delete with dialog', async () => {
82+
renderWithProviders(component({ ticker: ticker({ active: true, connected: true, roomName: '#room:matrix.org' }) }))
83+
84+
await userEvent.click(screen.getByRole('button', { name: 'Delete' }))
85+
86+
expect(screen.getByRole('dialog')).toBeInTheDocument()
87+
expect(screen.getByText('Delete Matrix integration')).toBeInTheDocument()
88+
89+
fetchMock.mockResponseOnce(JSON.stringify({ status: 'success' }))
90+
91+
await userEvent.click(screen.getByTestId('dialog-delete'))
92+
93+
expect(fetchMock).toHaveBeenCalledTimes(1)
94+
expect(fetchMock).toHaveBeenCalledWith('http://localhost:8080/v1/admin/tickers/1/matrix', {
95+
headers: {
96+
Accept: 'application/json',
97+
'Content-Type': 'application/json',
98+
Authorization: 'Bearer ' + userToken,
99+
},
100+
method: 'delete',
101+
})
102+
})
103+
104+
it('should handle enable when inactive', async () => {
105+
renderWithProviders(component({ ticker: ticker({ active: false, connected: true, roomName: '#room:matrix.org' }) }))
106+
107+
expect(screen.getByRole('button', { name: 'Enable' })).toBeInTheDocument()
108+
109+
fetchMock.mockResponseOnce(JSON.stringify({ status: 'success' }))
110+
111+
await userEvent.click(screen.getByRole('button', { name: 'Enable' }))
112+
113+
expect(fetchMock).toHaveBeenCalledTimes(1)
114+
expect(fetchMock).toHaveBeenCalledWith('http://localhost:8080/v1/admin/tickers/1/matrix', {
115+
body: JSON.stringify({ active: true }),
116+
headers: {
117+
Accept: 'application/json',
118+
'Content-Type': 'application/json',
119+
Authorization: 'Bearer ' + userToken,
120+
},
121+
method: 'put',
122+
})
123+
})
124+
125+
it('should fail when response fails', async () => {
126+
renderWithProviders(component({ ticker: ticker({ active: true, connected: true, roomName: '#room:matrix.org' }) }))
127+
128+
fetchMock.mockResponseOnce(JSON.stringify({ status: 'error' }))
129+
130+
await userEvent.click(screen.getByRole('button', { name: 'Disable' }))
131+
132+
expect(fetchMock).toHaveBeenCalledTimes(1)
133+
})
134+
135+
it('should fail when request fails', async () => {
136+
renderWithProviders(component({ ticker: ticker({ active: true, connected: true, roomName: '#room:matrix.org' }) }))
137+
138+
fetchMock.mockReject()
139+
140+
await userEvent.click(screen.getByRole('button', { name: 'Disable' }))
141+
142+
expect(fetchMock).toHaveBeenCalledTimes(1)
143+
})
144+
})

0 commit comments

Comments
 (0)