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-
172140const mockSegmentsCollection = MongoMock . getInnerMockCollection ( Segments )
173141const 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