Skip to content

Commit 91dae54

Browse files
author
Mint de Wit
committed
chore: fix unit tests
1 parent 75b1930 commit 91dae54

File tree

2 files changed

+82
-115
lines changed

2 files changed

+82
-115
lines changed

packages/webui/src/client/ui/UserEditOperations/PropertiesPanel.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as React from 'react'
2-
import { i18nTranslator } from '../i18n'
32
import { translateMessage } from '@sofie-automation/corelib/dist/TranslatableMessage'
43
import { doUserAction, UserAction } from '../../lib/clientUserAction'
54
import { MeteorCall } from '../../lib/meteorApi'
@@ -30,6 +29,7 @@ import { StyledSchemaFormInPlace } from '../../lib/forms/SchemaFormInPlace'
3029
import { RundownUtils } from '../../lib/rundown'
3130
import * as CoreIcon from '@nrk/core-icons/jsx'
3231
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
32+
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons'
3333

3434
interface PendingChange {
3535
operationId: string
@@ -297,6 +297,8 @@ function EditingTypeAction(props: {
297297
pendingChanges: PendingChange[]
298298
setPendingChanges: React.Dispatch<React.SetStateAction<PendingChange[]>>
299299
}) {
300+
const { t } = useTranslation()
301+
300302
const getPendingState = () => {
301303
const pendingChange = props.pendingChanges.find((change) => change.operationId === props.userEditOperation.id)
302304
return pendingChange?.switchState
@@ -347,9 +349,7 @@ function EditingTypeAction(props: {
347349
className="propertiespanel-pop-up__button"
348350
onClick={addPendingChange}
349351
>
350-
<span className="propertiespanel-pop-up__label">
351-
{translateMessage(props.userEditOperation.label, i18nTranslator)}
352-
</span>
352+
<span className="propertiespanel-pop-up__label">{translateMessage(props.userEditOperation.label, t)}</span>
353353
</button>
354354
)
355355
case UserEditingButtonType.SWITCH:
@@ -363,7 +363,7 @@ function EditingTypeAction(props: {
363363
{hasBeenEdited && (
364364
<>
365365
{' '}
366-
<FontAwesomeIcon icon="pencil-alt" />
366+
<FontAwesomeIcon icon={faPencilAlt} />
367367
</>
368368
)}{' '}
369369
<a
@@ -382,9 +382,7 @@ function EditingTypeAction(props: {
382382
<div className="sb-switch"></div>
383383
</div>
384384
</a>
385-
<span className="propertiespanel-pop-up__label">
386-
{translateMessage(props.userEditOperation.label, i18nTranslator)}
387-
</span>
385+
<span className="propertiespanel-pop-up__label">{translateMessage(props.userEditOperation.label, t)}</span>
388386
</div>
389387
)
390388
case UserEditingButtonType.HIDDEN || undefined:

packages/webui/src/client/ui/UserEditOperations/__tests__/PropertiesPanel.test.tsx

Lines changed: 76 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,48 @@
1-
jest.mock('../../../../__mocks__/tracker', () => {
2-
interface TrackerComputation {
3-
stop: () => void
4-
_recompute: () => void
5-
invalidate: () => void
6-
onInvalidate: () => void
7-
}
8-
const computations = new Set<TrackerComputation>()
1+
import React from 'react'
2+
// eslint-disable-next-line node/no-unpublished-import
3+
import { renderHook, act, render, screen, waitFor, RenderOptions } from '@testing-library/react'
4+
// eslint-disable-next-line node/no-unpublished-import
5+
import '@testing-library/jest-dom'
6+
import { MeteorCall } from '../../../lib/meteorApi'
7+
import { TFunction } from 'i18next'
98

10-
return {
11-
setup: () => ({
12-
Tracker: {
13-
autorun: jest.fn((fn) => {
14-
const computation = {
15-
stop: jest.fn(),
16-
_recompute: () => fn(computation),
17-
invalidate: function () {
18-
this._recompute()
19-
},
20-
onInvalidate: jest.fn(),
21-
}
22-
computations.add(computation)
23-
fn(computation)
24-
return computation
25-
}),
26-
nonreactive: jest.fn((fn) => fn()),
27-
active: false,
28-
currentComputation: null,
29-
afterFlush: (fn: () => void) => {
30-
setTimeout(fn, 0)
31-
},
32-
flush: () => {
33-
computations.forEach((comp) => comp._recompute())
34-
},
35-
Dependency: jest.fn().mockImplementation(() => {
36-
const dependents = new Set<TrackerComputation>()
37-
return {
38-
depend: jest.fn(() => {
39-
if (Tracker.currentComputation) {
40-
dependents.add(Tracker.currentComputation as any as TrackerComputation)
41-
}
42-
}),
43-
changed: jest.fn(() => {
44-
dependents.forEach((comp) => comp.invalidate())
45-
}),
46-
hasDependents: jest.fn(() => dependents.size > 0),
47-
}
48-
}),
9+
import userEvent from '@testing-library/user-event'
10+
import { protectString } from '@sofie-automation/corelib/dist/protectedString'
11+
import { UIParts } from '../../Collections'
12+
import { Segments } from '../../../../client/collections'
13+
import { DBSegment } from '@sofie-automation/corelib/dist/dataModel/Segment'
14+
import { DBPart } from '@sofie-automation/corelib/dist/dataModel/Part'
15+
import { UserEditingType, UserEditingButtonType } from '@sofie-automation/blueprints-integration'
16+
import {
17+
SelectedElementProvider,
18+
SelectedElementsContext,
19+
SelectionContextType,
20+
useSelection,
21+
} from '../../RundownView/SelectedElementsContext'
22+
import { MongoMock } from '../../../../__mocks__/mongo'
23+
import { PropertiesPanel } from '../PropertiesPanel'
24+
import { UserAction } from '../../../lib/clientUserAction'
25+
26+
jest.mock('meteor/tracker', (...args) => require('../../../../__mocks__/tracker').setup(args), { virtual: true })
27+
28+
jest.mock('react-i18next', () => ({
29+
// this mock makes sure any components using the translate hook can use it without a warning being shown
30+
useTranslation: () => {
31+
return {
32+
t: (str: string) => str,
33+
i18n: {
34+
changeLanguage: () => new Promise(() => {}),
4935
},
50-
}),
51-
}
52-
})
36+
}
37+
},
38+
initReactI18next: {
39+
type: '3rdParty',
40+
init: () => {},
41+
},
42+
}))
5343

5444
// Mock the ReactiveDataHelper:
55-
jest.mock('../../../lib/reactiveData/ReactiveDataHelper', () => {
45+
jest.mock('../../../lib/reactiveData/reactiveDataHelper', () => {
5646
interface MockSubscription {
5747
stop: () => void
5848
ready: () => boolean
@@ -147,28 +137,6 @@ jest.mock('react-i18next', () => ({
147137
},
148138
}))
149139

150-
import React from 'react'
151-
// eslint-disable-next-line node/no-unpublished-import
152-
import { renderHook, act, render, screen, waitFor } from '@testing-library/react'
153-
// eslint-disable-next-line node/no-unpublished-import
154-
import '@testing-library/jest-dom'
155-
import { MeteorCall } from '../../../lib/meteorApi'
156-
import { TFunction } from 'i18next'
157-
158-
import userEvent from '@testing-library/user-event'
159-
import { protectString } from '@sofie-automation/corelib/dist/protectedString'
160-
import { UIParts } from '../../Collections'
161-
import { Segments } from '../../../../client/collections'
162-
import { DBSegment } from '@sofie-automation/corelib/dist/dataModel/Segment'
163-
import { DBPart } from '@sofie-automation/corelib/dist/dataModel/Part'
164-
import { UserEditingType, UserEditingButtonType } from '@sofie-automation/blueprints-integration'
165-
import { SelectedElementProvider, useSelection } from '../../RundownView/SelectedElementsContext'
166-
import { MongoMock } from '../../../../__mocks__/mongo'
167-
import { PropertiesPanel } from '../PropertiesPanel'
168-
import { UserAction } from '../../../lib/clientUserAction'
169-
import { SegmentId } from '@sofie-automation/corelib/dist/dataModel/Ids'
170-
import { Tracker } from 'meteor/tracker'
171-
172140
const mockSegmentsCollection = MongoMock.getInnerMockCollection(Segments)
173141
const mockPartsCollection = MongoMock.getInnerMockCollection(UIParts)
174142

@@ -177,6 +145,9 @@ jest.mock('../../../lib/clientUserAction', () => ({
177145
doUserAction: jest.fn((_t: TFunction, e: unknown, _action: UserAction, callback: Function) =>
178146
callback(e, Date.now())
179147
),
148+
UserAction: {
149+
EXECUTE_USER_OPERATION: 51,
150+
},
180151
}))
181152

182153
// Mock Userchange Operation:
@@ -199,11 +170,21 @@ describe('PropertiesPanel', () => {
199170
<SelectedElementProvider>{children}</SelectedElementProvider>
200171
)
201172

173+
const renderWithContext = (
174+
ui: React.ReactNode,
175+
{ ctxValue, ...renderOptions }: RenderOptions & { ctxValue: SelectionContextType }
176+
) => {
177+
return render(
178+
<SelectedElementsContext.Provider value={ctxValue}>{ui}</SelectedElementsContext.Provider>,
179+
renderOptions
180+
)
181+
}
182+
202183
beforeEach(() => {
203184
mockSegmentsCollection.remove({})
204185
mockPartsCollection.remove({})
205186
jest.clearAllMocks()
206-
jest.useFakeTimers()
187+
// jest.useFakeTimers()
207188
})
208189

209190
afterEach(() => {
@@ -257,43 +238,30 @@ describe('PropertiesPanel', () => {
257238
test('renders segment properties when segment is selected', async () => {
258239
const mockSegment = createMockSegment('segment1')
259240

260-
const mockId = mockSegmentsCollection.insert(mockSegment) as any as SegmentId
241+
const mockId = mockSegmentsCollection.insert(mockSegment)
242+
const protectedMockId = protectString(mockId)
261243

262-
const verifySegment = mockSegmentsCollection.findOne({ _id: mockId })
244+
const verifySegment = mockSegmentsCollection.findOne({ _id: protectedMockId })
263245
expect(verifySegment).toBeTruthy()
264-
console.log('Verify segment :', verifySegment?._id)
265-
266-
expect(mockSegmentsCollection.findOne({ _id: mockId })).toBeTruthy()
246+
expect(mockSegmentsCollection.findOne({ _id: protectedMockId })).toBeTruthy()
267247

268248
const { result } = renderHook(() => useSelection(), { wrapper })
269249

270250
// Update selection and wait for component to update
271251
await act(async () => {
272252
result.current.clearAndSetSelection({
273253
type: 'segment',
274-
elementId: mockId,
254+
elementId: protectedMockId,
275255
})
276256
})
277257

278-
await act(async () => {
279-
jest.advanceTimersByTime(100)
280-
})
258+
expect(result.current.listSelectedElements()).toHaveLength(1)
259+
expect(result.current.listSelectedElements()).toEqual([{ type: 'segment', elementId: mockId }])
281260

282261
// Open component after segment is selected (as used in rundownview)
283-
const { container } = render(<PropertiesPanel />, { wrapper })
284-
285-
await act(async () => {
286-
jest.advanceTimersByTime(100)
287-
})
262+
const { container } = renderWithContext(<PropertiesPanel />, { ctxValue: result.current })
288263

289-
console.log('result', result.current.listSelectedElements())
290-
// Use findByTestId instead of querySelector
291-
await waitFor(
292-
() => {
293-
expect(screen.getByText(`SEGMENT : ${mockSegment.name}`)).toBeInTheDocument()
294-
},
295-
{ timeout: 1000 }
296-
)
264+
expect(screen.getByText(`${mockSegment.name.slice(0, 30)}`)).toBeInTheDocument()
297265

298266
const button = container.querySelector('.propertiespanel-pop-up__button')
299267
expect(button).toBeInTheDocument()
@@ -304,22 +272,22 @@ describe('PropertiesPanel', () => {
304272
const mockPart = createMockPart('part1', String(mockSegment._id))
305273

306274
mockSegmentsCollection.insert(mockSegment)
307-
mockPartsCollection.insert(mockPart)
275+
const mockId = mockPartsCollection.insert(mockPart)
308276

309277
const { result } = renderHook(() => useSelection(), { wrapper })
310278

311279
await act(async () => {
312280
result.current.clearAndSetSelection({
313281
type: 'part',
314-
elementId: mockPart._id,
282+
elementId: protectString(mockId),
315283
})
316284
})
317285
// Open component after part is selected (as used in rundownview)
318-
const { container } = render(<PropertiesPanel />, { wrapper })
286+
const { container } = renderWithContext(<PropertiesPanel />, { ctxValue: result.current })
319287

320288
await waitFor(
321289
() => {
322-
expect(screen.getByText(`PART : ${mockPart.title}`)).toBeInTheDocument()
290+
expect(screen.getByText(mockPart.title.slice(0, 30))).toBeInTheDocument()
323291
},
324292
{ timeout: 1000 }
325293
)
@@ -333,7 +301,6 @@ describe('PropertiesPanel', () => {
333301
mockSegmentsCollection.insert(mockSegment)
334302

335303
const { result } = renderHook(() => useSelection(), { wrapper })
336-
const { container } = render(<PropertiesPanel />, { wrapper })
337304

338305
await act(async () => {
339306
result.current.clearAndSetSelection({
@@ -343,13 +310,14 @@ describe('PropertiesPanel', () => {
343310
})
344311

345312
// Wait for the switch button to be available
313+
const { container } = renderWithContext(<PropertiesPanel />, { ctxValue: result.current })
346314
const switchButton = await waitFor(() => container.querySelector('.propertiespanel-pop-up__switchbutton'))
347315
expect(switchButton).toBeTruthy()
348316

317+
if (!switchButton) return // above would have thrown - this is a type guard
318+
349319
// Toggle the switch
350-
await act(async () => {
351-
await userEvent.click(switchButton!)
352-
})
320+
await userEvent.click(switchButton)
353321

354322
// Check if commit button is enabled
355323
const commitButton = screen.getByText('COMMIT CHANGES')
@@ -381,7 +349,6 @@ describe('PropertiesPanel', () => {
381349
mockSegmentsCollection.insert(mockSegment)
382350

383351
const { result } = renderHook(() => useSelection(), { wrapper })
384-
const { container } = render(<PropertiesPanel />, { wrapper })
385352

386353
await act(async () => {
387354
result.current.clearAndSetSelection({
@@ -390,6 +357,8 @@ describe('PropertiesPanel', () => {
390357
})
391358
})
392359

360+
const { container } = renderWithContext(<PropertiesPanel />, { ctxValue: result.current })
361+
393362
// Wait for the switch button to be available
394363
const switchButton = await waitFor(() => container.querySelector('.propertiespanel-pop-up__switchbutton'))
395364

@@ -414,10 +383,10 @@ describe('PropertiesPanel', () => {
414383
pieceExternalId: undefined,
415384
},
416385
{
417-
id: 'REVERT_SEGMENT',
386+
id: 'revert-segment',
418387
}
419388
)
420-
}, 10000) // Increase timeout for this test
389+
})
421390

422391
test('closes panel when close button is clicked', async () => {
423392
const mockSegment = createMockSegment('segment1')

0 commit comments

Comments
 (0)