blob: 59aa10ac5683102bddb2490f8742398698346f18 [file] [log] [blame]
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
import * as React from 'react'
import Node from '../../components/Node/Node'
import NodeInfo from '../../models/node-info'
import OsInfo from '../../models/os-info'
import StereotypeInfo from '../../models/stereotype-info'
import { render, screen, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import '@testing-library/jest-dom'
jest.mock('../../components/LiveView/LiveView', () => {
return {
__esModule: true,
default: React.forwardRef((props: { url: string, scaleViewport?: boolean, onClose?: () => void }, ref) => {
React.useImperativeHandle(ref, () => ({
disconnect: jest.fn()
}))
return <div data-testid="mock-live-view" data-url={props.url}>LiveView Mock</div>
})
}
})
const osInfo: OsInfo = {
name: 'Mac OS X',
version: '10.16',
arch: 'x86_64'
}
const slotStereotype: StereotypeInfo = {
browserName: 'chrome',
browserVersion: 'v. 88',
slotCount: 12,
rawData: ['stereotype: {"browserName": "chrome"}'],
platformName: 'macos'
}
const node: NodeInfo = {
uri: 'http://192.168.1.7:4444',
id: '9e92a45a-0de3-4424-824a-ff7b6aa57b16',
status: 'UP',
maxSession: 12,
slotCount: 50,
version: '4.0.0-beta-1',
osInfo: osInfo,
sessionCount: 2,
slotStereotypes: [slotStereotype]
}
const sessionWithVnc = {
id: 'session-with-vnc',
capabilities: JSON.stringify({
'browserName': 'chrome',
'browserVersion': '88.0',
'se:vnc': 'ws://172.17.0.2:4444/session/4d31d065-6cb9-4545-ac8d-d70ed5f5168b/se/vnc'
}),
nodeId: node.id
}
const sessionWithoutVnc = {
id: 'session-without-vnc',
capabilities: JSON.stringify({
'browserName': 'chrome',
'browserVersion': '88.0'
}),
nodeId: node.id
}
describe('Node component', () => {
it('renders basic node information', () => {
render(<Node node={node} />)
expect(screen.getByText(node.uri)).toBeInTheDocument()
expect(
screen.getByText(`Sessions: ${node.sessionCount}`)).toBeInTheDocument()
expect(screen.getByText(
`Max. Concurrency: ${node.maxSession}`)).toBeInTheDocument()
})
it('renders detailed node information', async () => {
render(<Node node={node}/>)
const user = userEvent.setup()
await user.click(screen.getByRole('button'))
expect(screen.getByText(`Node Id: ${node.id}`)).toBeInTheDocument()
expect(
screen.getByText(`Total slots: ${node.slotCount}`)).toBeInTheDocument()
expect(screen.getByText(`OS Arch: ${node.osInfo.arch}`)).toBeInTheDocument()
expect(screen.getByText(`OS Name: ${node.osInfo.name}`)).toBeInTheDocument()
expect(
screen.getByText(`OS Version: ${node.osInfo.version}`)).toBeInTheDocument()
})
it('does not render live view icon when no VNC session is available', () => {
render(<Node node={node} sessions={[sessionWithoutVnc]} origin="http://localhost:4444" />)
expect(screen.queryByTestId('VideocamIcon')).not.toBeInTheDocument()
})
it('renders live view icon when VNC session is available', () => {
render(<Node node={node} sessions={[sessionWithVnc]} origin="http://localhost:4444" />)
expect(screen.getByTestId('VideocamIcon')).toBeInTheDocument()
})
it('opens live view dialog when camera icon is clicked', async () => {
render(<Node node={node} sessions={[sessionWithVnc]} origin="http://localhost:4444" />)
const user = userEvent.setup()
await user.click(screen.getByTestId('VideocamIcon'))
expect(screen.getByText('Node Session Live View')).toBeInTheDocument()
const dialogTitle = screen.getByText('Node Session Live View')
const dialog = dialogTitle.closest('.MuiDialog-root')
expect(dialog).not.toBeNull()
if (dialog) {
expect(within(dialog as HTMLElement).getAllByText(node.uri).length).toBeGreaterThan(0)
}
expect(screen.getByTestId('mock-live-view')).toBeInTheDocument()
})
it('closes live view dialog when close button is clicked', async () => {
render(<Node node={node} sessions={[sessionWithVnc]} origin="http://localhost:4444" />)
const user = userEvent.setup()
await user.click(screen.getByTestId('VideocamIcon'))
expect(screen.getByText('Node Session Live View')).toBeInTheDocument()
await user.click(screen.getByRole('button', { name: /close/i }))
expect(screen.queryByText('Node Session Live View')).not.toBeInTheDocument()
})
it('correctly transforms VNC URL for WebSocket connection', async () => {
const origin = 'https://grid.example.com'
render(<Node node={node} sessions={[sessionWithVnc]} origin={origin} />)
const user = userEvent.setup()
await user.click(screen.getByTestId('VideocamIcon'))
const liveView = screen.getByTestId('mock-live-view')
const url = liveView.getAttribute('data-url')
expect(url).toBe('wss://grid.example.com/session/4d31d065-6cb9-4545-ac8d-d70ed5f5168b/se/vnc')
})
it('handles HTTP to WS protocol conversion correctly', async () => {
const httpOrigin = 'http://grid.example.com'
render(<Node node={node} sessions={[sessionWithVnc]} origin={httpOrigin} />)
const user = userEvent.setup()
await user.click(screen.getByTestId('VideocamIcon'))
const liveView = screen.getByTestId('mock-live-view')
const url = liveView.getAttribute('data-url')
expect(url).toContain('ws:')
expect(url).not.toContain('wss:')
})
it('handles invalid VNC URLs gracefully', async () => {
const invalidVncSession = {
id: 'session-invalid-vnc',
capabilities: JSON.stringify({
'browserName': 'chrome',
'browserVersion': '88.0',
'se:vnc': 'invalid-url'
}),
nodeId: node.id
}
render(<Node node={node} sessions={[invalidVncSession]} origin="http://localhost:4444" />)
const user = userEvent.setup()
await user.click(screen.getByTestId('VideocamIcon'))
const liveView = screen.getByTestId('mock-live-view')
const url = liveView.getAttribute('data-url')
expect(url).toBe('')
})
})