Skip to content

Commit 06cb579

Browse files
committed
Add component test
Signed-off-by: Viet Nguyen Duc <[email protected]>
1 parent b3f2a68 commit 06cb579

File tree

2 files changed

+479
-20
lines changed

2 files changed

+479
-20
lines changed

javascript/grid-ui/src/tests/components/Node.test.tsx

Lines changed: 144 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,21 @@ import Node from '../../components/Node/Node'
2020
import NodeInfo from '../../models/node-info'
2121
import OsInfo from '../../models/os-info'
2222
import StereotypeInfo from '../../models/stereotype-info'
23-
import { render, screen } from '@testing-library/react'
23+
import { render, screen, within } from '@testing-library/react'
2424
import userEvent from '@testing-library/user-event'
25+
import '@testing-library/jest-dom'
26+
27+
jest.mock('../../components/LiveView/LiveView', () => {
28+
return {
29+
__esModule: true,
30+
default: React.forwardRef((props: { url: string, scaleViewport?: boolean, onClose?: () => void }, ref) => {
31+
React.useImperativeHandle(ref, () => ({
32+
disconnect: jest.fn()
33+
}))
34+
return <div data-testid="mock-live-view" data-url={props.url}>LiveView Mock</div>
35+
})
36+
}
37+
})
2538

2639
const osInfo: OsInfo = {
2740
name: 'Mac OS X',
@@ -49,24 +62,135 @@ const node: NodeInfo = {
4962
slotStereotypes: [slotStereotype]
5063
}
5164

52-
it('renders basic node information', () => {
53-
render(<Node node={node} />)
54-
expect(screen.getByText(node.uri)).toBeInTheDocument()
55-
expect(
56-
screen.getByText(`Sessions: ${node.sessionCount}`)).toBeInTheDocument()
57-
expect(screen.getByText(
58-
`Max. Concurrency: ${node.maxSession}`)).toBeInTheDocument()
59-
})
65+
const sessionWithVnc = {
66+
id: 'session-with-vnc',
67+
capabilities: JSON.stringify({
68+
'browserName': 'chrome',
69+
'browserVersion': '88.0',
70+
'se:vnc': 'ws://192.168.1.7:5900/websockify'
71+
}),
72+
nodeId: node.id
73+
}
74+
75+
const sessionWithoutVnc = {
76+
id: 'session-without-vnc',
77+
capabilities: JSON.stringify({
78+
'browserName': 'chrome',
79+
'browserVersion': '88.0'
80+
}),
81+
nodeId: node.id
82+
}
83+
84+
describe('Node component', () => {
85+
it('renders basic node information', () => {
86+
render(<Node node={node} />)
87+
expect(screen.getByText(node.uri)).toBeInTheDocument()
88+
expect(
89+
screen.getByText(`Sessions: ${node.sessionCount}`)).toBeInTheDocument()
90+
expect(screen.getByText(
91+
`Max. Concurrency: ${node.maxSession}`)).toBeInTheDocument()
92+
})
93+
94+
it('renders detailed node information', async () => {
95+
render(<Node node={node}/>)
96+
const user = userEvent.setup()
97+
await user.click(screen.getByRole('button'))
98+
expect(screen.getByText(`Node Id: ${node.id}`)).toBeInTheDocument()
99+
expect(
100+
screen.getByText(`Total slots: ${node.slotCount}`)).toBeInTheDocument()
101+
expect(screen.getByText(`OS Arch: ${node.osInfo.arch}`)).toBeInTheDocument()
102+
expect(screen.getByText(`OS Name: ${node.osInfo.name}`)).toBeInTheDocument()
103+
expect(
104+
screen.getByText(`OS Version: ${node.osInfo.version}`)).toBeInTheDocument()
105+
})
106+
107+
it('does not render live view icon when no VNC session is available', () => {
108+
render(<Node node={node} sessions={[sessionWithoutVnc]} origin="http://localhost:4444" />)
109+
expect(screen.queryByTestId('VideocamIcon')).not.toBeInTheDocument()
110+
})
111+
112+
it('renders live view icon when VNC session is available', () => {
113+
render(<Node node={node} sessions={[sessionWithVnc]} origin="http://localhost:4444" />)
114+
expect(screen.getByTestId('VideocamIcon')).toBeInTheDocument()
115+
})
116+
117+
it('opens live view dialog when camera icon is clicked', async () => {
118+
render(<Node node={node} sessions={[sessionWithVnc]} origin="http://localhost:4444" />)
119+
120+
const user = userEvent.setup()
121+
await user.click(screen.getByTestId('VideocamIcon'))
122+
123+
expect(screen.getByText('Node Session Live View')).toBeInTheDocument()
124+
const dialogTitle = screen.getByText('Node Session Live View')
125+
const dialog = dialogTitle.closest('.MuiDialog-root')
126+
expect(dialog).not.toBeNull()
127+
if (dialog) {
128+
expect(within(dialog as HTMLElement).getAllByText(node.uri).length).toBeGreaterThan(0)
129+
}
130+
expect(screen.getByTestId('mock-live-view')).toBeInTheDocument()
131+
})
132+
133+
it('closes live view dialog when close button is clicked', async () => {
134+
render(<Node node={node} sessions={[sessionWithVnc]} origin="http://localhost:4444" />)
135+
136+
const user = userEvent.setup()
137+
await user.click(screen.getByTestId('VideocamIcon'))
138+
139+
expect(screen.getByText('Node Session Live View')).toBeInTheDocument()
140+
141+
await user.click(screen.getByRole('button', { name: /close/i }))
142+
143+
expect(screen.queryByText('Node Session Live View')).not.toBeInTheDocument()
144+
})
145+
146+
it('correctly transforms VNC URL for WebSocket connection', async () => {
147+
const origin = 'https://grid.example.com'
148+
render(<Node node={node} sessions={[sessionWithVnc]} origin={origin} />)
149+
150+
const user = userEvent.setup()
151+
await user.click(screen.getByTestId('VideocamIcon'))
152+
153+
const liveView = screen.getByTestId('mock-live-view')
154+
const url = liveView.getAttribute('data-url')
155+
156+
expect(url).toContain('wss:')
157+
expect(url).toContain('grid.example.com')
158+
expect(url).toContain('/websockify')
159+
})
160+
161+
it('handles HTTP to WS protocol conversion correctly', async () => {
162+
const httpOrigin = 'http://grid.example.com'
163+
render(<Node node={node} sessions={[sessionWithVnc]} origin={httpOrigin} />)
164+
165+
const user = userEvent.setup()
166+
await user.click(screen.getByTestId('VideocamIcon'))
167+
168+
const liveView = screen.getByTestId('mock-live-view')
169+
const url = liveView.getAttribute('data-url')
170+
171+
expect(url).toContain('ws:')
172+
expect(url).not.toContain('wss:')
173+
})
60174

61-
it('renders detailed node information', async () => {
62-
render(<Node node={node}/>)
63-
const user = userEvent.setup()
64-
await user.click(screen.getByRole('button'))
65-
expect(screen.getByText(`Node Id: ${node.id}`)).toBeInTheDocument()
66-
expect(
67-
screen.getByText(`Total slots: ${node.slotCount}`)).toBeInTheDocument()
68-
expect(screen.getByText(`OS Arch: ${node.osInfo.arch}`)).toBeInTheDocument()
69-
expect(screen.getByText(`OS Name: ${node.osInfo.name}`)).toBeInTheDocument()
70-
expect(
71-
screen.getByText(`OS Version: ${node.osInfo.version}`)).toBeInTheDocument()
175+
it('handles invalid VNC URLs gracefully', async () => {
176+
const invalidVncSession = {
177+
id: 'session-invalid-vnc',
178+
capabilities: JSON.stringify({
179+
'browserName': 'chrome',
180+
'browserVersion': '88.0',
181+
'se:vnc': 'invalid-url'
182+
}),
183+
nodeId: node.id
184+
}
185+
186+
render(<Node node={node} sessions={[invalidVncSession]} origin="http://localhost:4444" />)
187+
188+
const user = userEvent.setup()
189+
await user.click(screen.getByTestId('VideocamIcon'))
190+
191+
const liveView = screen.getByTestId('mock-live-view')
192+
const url = liveView.getAttribute('data-url')
193+
194+
expect(url).toBe('')
195+
})
72196
})

0 commit comments

Comments
 (0)