fix(tracker): fix dead tests (tabid prop)

This commit is contained in:
nick-delirium 2023-06-05 11:47:04 +02:00
parent 0dc3dd2210
commit c07b03cc34
9 changed files with 452 additions and 15 deletions

65
.github/workflows/tracker-tests.yaml vendored Normal file
View file

@ -0,0 +1,65 @@
# Checking unit tests for tracker and assist
name: Tracker tests
on:
workflow_dispatch:
push:
branches: [ "main" ]
paths:
- tracker/**
pull_request:
branches: [ "dev", "main" ]
paths:
- frontend/**
- tracker/**
jobs:
build-and-test:
runs-on: macos-latest
name: Build and test Tracker
strategy:
matrix:
node-version: [ 16.x ]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Cache tracker modules
uses: actions/cache@v3
with:
path: tracker/tracker/node_modules
key: ${{ runner.OS }}-test_tracker_build-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
test_tracker_build{{ runner.OS }}-build-
test_tracker_build{{ runner.OS }}-
- name: Cache tracker-assist modules
uses: actions/cache@v3
with:
path: tracker/tracker-assist/node_modules
key: ${{ runner.OS }}-test_tracker_build-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
test_tracker_build{{ runner.OS }}-build-
test_tracker_build{{ runner.OS }}-
- name: Setup Testing packages
run: |
cd tracker/tracker
npm i -g yarn
yarn
- name: Setup Testing packages
run: |
cd tracker/tracker-assist
yarn
- name: Jest tests
run: |
cd tracker/tracker
yarn test:
- name: Jest tests
run: |
cd tracker/tracker-assist
yarn test:ci
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: tracker
name: tracker

View file

@ -47,16 +47,6 @@ jobs:
cd tracker/tracker
npm i -g yarn
yarn
- name: Jest tests
run: |
cd tracker/tracker
yarn test:ci
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: tracker
name: tracker
- name: Build tracker inst
run: |
cd tracker/tracker

View file

@ -6,3 +6,4 @@ cjs
.cache
*.cache
*.DS_Store
coverage

View file

@ -0,0 +1,13 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
const config = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
collectCoverage: true,
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts',],
// .js file extension fix
moduleNameMapper: {
'(.+)\\.js': '$1',
},
}
export default config

View file

@ -23,7 +23,9 @@
"replace-req-version": "replace-in-files lib/* cjs/* --string='REQUIRED_TRACKER_VERSION' --replacement='3.5.14'",
"prepublishOnly": "npm run build",
"prepare": "cd ../../ && husky install tracker/.husky/",
"lint-front": "lint-staged"
"lint-front": "lint-staged",
"test": "jest --coverage=false",
"test:ci": "jest --coverage=true"
},
"dependencies": {
"csstype": "^3.0.10",
@ -44,7 +46,10 @@
"lint-staged": "^13.0.3",
"prettier": "^2.7.1",
"replace-in-files-cli": "^1.0.0",
"typescript": "^4.6.0-dev.20211126"
"typescript": "^4.6.0-dev.20211126",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
"ts-jest": "^29.0.3"
},
"husky": {
"hooks": {

View file

@ -513,6 +513,11 @@ export default class Assist {
})
call.answer(lStreams[call.peer].stream)
document.addEventListener('visibilitychange', () => {
initiateCallEnd()
})
this.setCallingState(CallingState.True)
if (!callEndCallback) { callEndCallback = this.options.onCallStart?.() }

View file

@ -18,8 +18,8 @@ if (nativeInputValueDescriptor && nativeInputValueDescriptor.set) {
export default class RemoteControl {
private mouse: Mouse | null
status: RCStatus = RCStatus.Disabled
private mouse: Mouse | null = null
public status: RCStatus = RCStatus.Disabled
private agentID: string | null = null
constructor(
@ -114,7 +114,9 @@ export default class RemoteControl {
input = (id, value: string) => {
if (id !== this.agentID || !this.mouse || !this.focused) { return }
if (this.focused instanceof HTMLTextAreaElement
|| this.focused instanceof HTMLInputElement) {
|| this.focused instanceof HTMLInputElement
|| this.focused.tagName === 'INPUT'
|| this.focused.tagName === 'TEXTAREA') {
setInputValue.call(this.focused, value)
const ev = new Event('input', { bubbles: true,})
this.focused.dispatchEvent(ev)

View file

@ -0,0 +1,148 @@
import AnnotationCanvas from '../src/AnnotationCanvas'
import { describe, expect, test, it, jest, beforeEach, afterEach, } from '@jest/globals'
describe('AnnotationCanvas', () => {
let annotationCanvas
let documentBody
let canvasMock
let contextMock
beforeEach(() => {
canvasMock = {
width: 0,
height: 0,
style: {},
getContext: jest.fn(() => contextMock as unknown as HTMLCanvasElement),
parentNode: document,
}
contextMock = {
globalAlpha: 1.0,
beginPath: jest.fn(),
moveTo: jest.fn(),
lineTo: jest.fn(),
lineWidth: 8,
lineCap: 'round',
lineJoin: 'round',
strokeStyle: 'red',
stroke: jest.fn(),
globalCompositeOperation: '',
fillStyle: '',
fillRect: jest.fn(),
clearRect: jest.fn(),
}
documentBody = document.body
// @ts-ignore
document['removeChild'] = (el) => jest.fn(el)
// @ts-ignore
document['createElement'] = () => canvasMock
jest.spyOn(documentBody, 'appendChild').mockImplementation(jest.fn())
jest.spyOn(documentBody, 'removeChild').mockImplementation(jest.fn())
jest.spyOn(window, 'addEventListener').mockImplementation(jest.fn())
jest.spyOn(window, 'removeEventListener').mockImplementation(jest.fn())
annotationCanvas = new AnnotationCanvas()
})
afterEach(() => {
jest.restoreAllMocks()
})
it('should create a canvas element with correct styles when initialized', () => {
const createElSpy = jest.spyOn(document, 'createElement')
annotationCanvas = new AnnotationCanvas()
expect(createElSpy).toHaveBeenCalledWith('canvas')
expect(canvasMock.style.position).toBe('fixed')
expect(canvasMock.style.left).toBe(0)
expect(canvasMock.style.top).toBe(0)
expect(canvasMock.style.pointerEvents).toBe('none')
expect(canvasMock.style.zIndex).toBe(2147483647 - 2)
})
it('should resize the canvas when calling resizeCanvas method', () => {
annotationCanvas.resizeCanvas()
expect(canvasMock.width).toBe(window.innerWidth)
expect(canvasMock.height).toBe(window.innerHeight)
})
it('should start painting and set the last position when calling start method', () => {
const position = [10, 20,]
annotationCanvas.start(position)
expect(annotationCanvas.painting).toBe(true)
expect(annotationCanvas.clrTmID).toBeNull()
expect(annotationCanvas.lastPosition).toEqual(position)
})
it('should stop painting and call fadeOut method when calling stop method', () => {
annotationCanvas.painting = true
const fadeOutSpy = jest.spyOn(annotationCanvas, 'fadeOut')
annotationCanvas.stop()
expect(annotationCanvas.painting).toBe(false)
expect(fadeOutSpy).toHaveBeenCalled()
})
it('should not stop painting or call fadeOut method when calling stop method while not painting', () => {
annotationCanvas.painting = false
const fadeOutSpy = jest.spyOn(annotationCanvas, 'fadeOut')
annotationCanvas.stop()
expect(fadeOutSpy).not.toHaveBeenCalled()
})
it('should draw a line on the canvas when calling move method', () => {
annotationCanvas.painting = true
annotationCanvas.ctx = contextMock
const initialLastPosition = [0, 0,]
const position = [10, 20,]
annotationCanvas.move(position)
expect(contextMock.globalAlpha).toBe(1.0)
expect(contextMock.beginPath).toHaveBeenCalled()
expect(contextMock.moveTo).toHaveBeenCalledWith(initialLastPosition[0], initialLastPosition[1])
expect(contextMock.lineTo).toHaveBeenCalledWith(position[0], position[1])
expect(contextMock.stroke).toHaveBeenCalled()
expect(annotationCanvas.lastPosition).toEqual(position)
})
it('should not draw a line on the canvas when calling move method while not painting', () => {
annotationCanvas.painting = false
annotationCanvas.ctx = contextMock
const position = [10, 20,]
annotationCanvas.move(position)
expect(contextMock.beginPath).not.toHaveBeenCalled()
expect(contextMock.stroke).not.toHaveBeenCalled()
expect(annotationCanvas.lastPosition).toEqual([0, 0,])
})
it('should fade out the canvas when calling fadeOut method', () => {
annotationCanvas.ctx = contextMock
jest.useFakeTimers()
const timerSpy = jest.spyOn(window, 'setTimeout')
annotationCanvas.fadeOut()
expect(timerSpy).toHaveBeenCalledTimes(2)
expect(contextMock.globalCompositeOperation).toBe('source-over')
expect(contextMock.fillStyle).toBe('rgba(255, 255, 255, 0.1)')
expect(contextMock.fillRect).toHaveBeenCalledWith(0, 0, canvasMock.width, canvasMock.height)
jest.runOnlyPendingTimers()
expect(contextMock.clearRect).toHaveBeenCalledWith(0, 0, canvasMock.width, canvasMock.height)
})
it('should remove the canvas element when calling remove method', () => {
const spyOnRemove = jest.spyOn(document, 'removeChild')
annotationCanvas.remove()
expect(spyOnRemove).toHaveBeenCalledWith(canvasMock)
expect(window.removeEventListener).toHaveBeenCalledWith('resize', annotationCanvas.resizeCanvas)
})
})

View file

@ -0,0 +1,208 @@
import RemoteControl, { RCStatus, } from '../src/RemoteControl'
import ConfirmWindow from '../src/ConfirmWindow/ConfirmWindow'
import { describe, expect, test, jest, beforeEach, afterEach, } from '@jest/globals'
describe('RemoteControl', () => {
let remoteControl
let options
let onGrand
let onRelease
let confirmWindowMountMock
let confirmWindowRemoveMock
beforeEach(() => {
options = {
/* mock options */
}
onGrand = jest.fn()
onRelease = jest.fn()
confirmWindowMountMock = jest.fn(() => Promise.resolve(true))
confirmWindowRemoveMock = jest.fn()
jest.spyOn(window, 'HTMLInputElement').mockImplementation((): any => ({
value: '',
dispatchEvent: jest.fn(),
}))
jest.spyOn(window, 'HTMLTextAreaElement').mockImplementation((): any => ({
value: '',
dispatchEvent: jest.fn(),
}))
jest
.spyOn(ConfirmWindow.prototype, 'mount')
.mockImplementation(confirmWindowMountMock)
jest
.spyOn(ConfirmWindow.prototype, 'remove')
.mockImplementation(confirmWindowRemoveMock)
remoteControl = new RemoteControl(options, onGrand, onRelease)
})
afterEach(() => {
jest.restoreAllMocks()
})
test('should initialize with disabled status', () => {
expect(remoteControl.status).toBe(RCStatus.Disabled)
expect(remoteControl.agentID).toBeNull()
expect(remoteControl.confirm).toBeNull()
expect(remoteControl.mouse).toBeNull()
})
test('should request control when calling requestControl method', () => {
const id = 'agent123'
remoteControl.requestControl(id)
expect(remoteControl.agentID).toBe(id)
expect(remoteControl.status).toBe(RCStatus.Requesting)
expect(confirmWindowMountMock).toHaveBeenCalled()
})
test('should grant control when calling grantControl method', () => {
const id = 'agent123'
remoteControl.grantControl(id)
expect(remoteControl.agentID).toBe(id)
expect(remoteControl.status).toBe(RCStatus.Enabled)
expect(onGrand).toHaveBeenCalledWith(id)
expect(remoteControl.mouse).toBeDefined()
})
test('should release control when calling releaseControl method', () => {
const isDenied = true
remoteControl['confirm'] = { remove: jest.fn(), } as unknown as ConfirmWindow
const confirmSpy = jest.spyOn(remoteControl['confirm'], 'remove')
remoteControl.releaseControl(isDenied)
expect(remoteControl.agentID).toBeNull()
expect(remoteControl.status).toBe(RCStatus.Disabled)
expect(onRelease).toHaveBeenCalledWith(null, isDenied)
expect(confirmSpy).toHaveBeenCalled()
expect(remoteControl.mouse).toBeNull()
})
test('should reset mouse when calling resetMouse method', () => {
remoteControl.resetMouse()
expect(remoteControl.mouse).toBeNull()
})
test('should call mouse.scroll when calling scroll method with correct agentID', () => {
const id = 'agent123'
const d = 10
remoteControl.agentID = id
remoteControl.mouse = {
scroll: jest.fn(),
}
remoteControl.scroll(id, d)
expect(remoteControl.mouse.scroll).toHaveBeenCalledWith(d)
})
test('should not call mouse.scroll when calling scroll method with incorrect agentID', () => {
const id = 'agent123'
const d = 10
remoteControl.agentID = 'anotherAgent'
remoteControl.mouse = {
scroll: jest.fn(),
}
remoteControl.scroll(id, d)
expect(remoteControl.mouse.scroll).not.toHaveBeenCalled()
})
test('should call mouse.move when calling move method with correct agentID', () => {
const id = 'agent123'
const xy = { x: 10, y: 20, }
remoteControl.agentID = id
remoteControl.mouse = {
move: jest.fn(),
}
remoteControl.move(id, xy)
expect(remoteControl.mouse.move).toHaveBeenCalledWith(xy)
})
test('should not call mouse.move when calling move method with incorrect agentID', () => {
const id = 'agent123'
const xy = { x: 10, y: 20, }
remoteControl.agentID = 'anotherAgent'
remoteControl.mouse = {
move: jest.fn(),
}
remoteControl.move(id, xy)
expect(remoteControl.mouse.move).not.toHaveBeenCalled()
})
test('should call mouse.click when calling click method with correct agentID', () => {
const id = 'agent123'
const xy = { x: 10, y: 20, }
remoteControl.agentID = id
remoteControl.mouse = {
click: jest.fn(),
}
remoteControl.click(id, xy)
expect(remoteControl.mouse.click).toHaveBeenCalledWith(xy)
})
test('should not call mouse.click when calling click method with incorrect agentID', () => {
const id = 'agent123'
const xy = { x: 10, y: 20, }
remoteControl.agentID = 'anotherAgent'
remoteControl.mouse = {
click: jest.fn(),
}
remoteControl.click(id, xy)
expect(remoteControl.mouse.click).not.toHaveBeenCalled()
})
test('should set the focused element when calling focus method', () => {
const id = 'agent123'
const element = document.createElement('div')
remoteControl.focus(id, element)
expect(remoteControl.focused).toBe(element)
})
test('should call setInputValue and dispatch input event when calling input method with HTMLInputElement', () => {
const id = 'agent1234'
const value = 'test_test'
const element = document.createElement('input')
const dispatchSpy = jest.spyOn(element, 'dispatchEvent')
remoteControl.agentID = id
remoteControl.mouse = true
remoteControl.focused = element
remoteControl.input(id, value)
expect(element.value).toBe(value)
expect(dispatchSpy).toHaveBeenCalledWith(
new Event('input', { bubbles: true, })
)
})
test('should update innerText when calling input method with content editable element', () => {
const id = 'agent123'
const value = 'test'
const element = document.createElement('div')
// @ts-ignore
element['isContentEditable'] = true
remoteControl.agentID = id
remoteControl.mouse = true
remoteControl.focused = element
remoteControl.input(id, value)
expect(element.innerText).toBe(value)
})
})