@@ -231,3 +231,221 @@ test('adds bam localpath', () => {
231231 expect ( widget . wrongProtocol ) . toBe ( false )
232232 expect ( widget . assembly ) . toBe ( 'volvox' )
233233} )
234+
235+ test ( 'clearData resets all volatile state' , ( ) => {
236+ const session = standardInitializer ( )
237+ const { widget } = session
238+
239+ // Set various state
240+ widget . setTrackData ( {
241+ uri : 'test.bam' ,
242+ locationType : 'UriLocation' ,
243+ } )
244+ widget . setIndexTrackData ( {
245+ uri : 'test.bam.bai' ,
246+ locationType : 'UriLocation' ,
247+ } )
248+ widget . setTrackName ( 'Custom Name' )
249+ widget . setTrackType ( 'FeatureTrack' )
250+ widget . setAssembly ( 'customAssembly' )
251+ widget . setAdapterHint ( 'BamAdapter' )
252+ widget . setTextIndexTrack ( false )
253+ widget . setTextIndexingConf ( { attributes : [ 'Gene' ] , exclude : [ 'mRNA' ] } )
254+ widget . setMixinData ( { extra : 'data' } )
255+
256+ // Verify state was set
257+ expect ( widget . trackData ) . toBeDefined ( )
258+ expect ( widget . indexTrackData ) . toBeDefined ( )
259+ expect ( widget . altTrackName ) . toBe ( 'Custom Name' )
260+ expect ( widget . altTrackType ) . toBe ( 'FeatureTrack' )
261+ expect ( widget . altAssemblyName ) . toBe ( 'customAssembly' )
262+ expect ( widget . adapterHint ) . toBe ( 'BamAdapter' )
263+ expect ( widget . textIndexTrack ) . toBe ( false )
264+ expect ( widget . textIndexingConf ) . toEqual ( {
265+ attributes : [ 'Gene' ] ,
266+ exclude : [ 'mRNA' ] ,
267+ } )
268+ expect ( widget . mixinData ) . toEqual ( { extra : 'data' } )
269+
270+ // Clear and verify reset
271+ widget . clearData ( )
272+
273+ expect ( widget . trackData ) . toBeUndefined ( )
274+ expect ( widget . indexTrackData ) . toBeUndefined ( )
275+ expect ( widget . altTrackName ) . toBe ( '' )
276+ expect ( widget . altTrackType ) . toBe ( '' )
277+ expect ( widget . altAssemblyName ) . toBe ( '' )
278+ expect ( widget . adapterHint ) . toBe ( '' )
279+ expect ( widget . textIndexTrack ) . toBe ( true )
280+ expect ( widget . textIndexingConf ) . toBeUndefined ( )
281+ expect ( widget . mixinData ) . toEqual ( { } )
282+ } )
283+
284+ test ( 'setTrackData clears adapterHint for re-evaluation' , ( ) => {
285+ const session = standardInitializer ( )
286+ const { widget } = session
287+
288+ widget . setTrackData ( {
289+ uri : 'test.bam' ,
290+ locationType : 'UriLocation' ,
291+ } )
292+ widget . setAdapterHint ( 'BamAdapter' )
293+ expect ( widget . adapterHint ) . toBe ( 'BamAdapter' )
294+
295+ // Changing track data should clear the adapter hint
296+ widget . setTrackData ( {
297+ uri : 'test.vcf.gz' ,
298+ locationType : 'UriLocation' ,
299+ } )
300+ expect ( widget . adapterHint ) . toBe ( '' )
301+ } )
302+
303+ test ( 'setIndexTrackData clears adapterHint for re-evaluation' , ( ) => {
304+ const session = standardInitializer ( )
305+ const { widget } = session
306+
307+ widget . setTrackData ( {
308+ uri : 'test.bam' ,
309+ locationType : 'UriLocation' ,
310+ } )
311+ widget . setAdapterHint ( 'BamAdapter' )
312+ expect ( widget . adapterHint ) . toBe ( 'BamAdapter' )
313+
314+ // Changing index data should also clear the adapter hint
315+ widget . setIndexTrackData ( {
316+ uri : 'test.bam.csi' ,
317+ locationType : 'UriLocation' ,
318+ } )
319+ expect ( widget . adapterHint ) . toBe ( '' )
320+ } )
321+
322+ test ( 'detects FTP URLs in track data' , ( ) => {
323+ const session = standardInitializer ( )
324+ const { widget } = session
325+
326+ widget . setTrackData ( {
327+ uri : 'ftp://example.com/test.bam' ,
328+ locationType : 'UriLocation' ,
329+ } )
330+ expect ( widget . isFtp ) . toBe ( true )
331+ } )
332+
333+ test ( 'detects FTP URLs in index data' , ( ) => {
334+ const session = standardInitializer ( )
335+ const { widget } = session
336+
337+ widget . setTrackData ( {
338+ uri : 'https://example.com/test.bam' ,
339+ locationType : 'UriLocation' ,
340+ } )
341+ widget . setIndexTrackData ( {
342+ uri : 'ftp://example.com/test.bam.bai' ,
343+ locationType : 'UriLocation' ,
344+ } )
345+ expect ( widget . isFtp ) . toBe ( true )
346+ } )
347+
348+ test ( 'warningMessage returns FTP warning' , ( ) => {
349+ const session = standardInitializer ( )
350+ const { widget } = session
351+
352+ widget . setTrackData ( {
353+ uri : 'ftp://example.com/test.bam' ,
354+ locationType : 'UriLocation' ,
355+ } )
356+ expect ( widget . warningMessage ) . toContain ( 'ftp protocol' )
357+ } )
358+
359+ test ( 'warningMessage returns relative URL warning' , ( ) => {
360+ const session = standardInitializer ( )
361+ const { widget } = session
362+
363+ widget . setTrackData ( {
364+ uri : 'path/to/test.bam' ,
365+ locationType : 'UriLocation' ,
366+ } )
367+ expect ( widget . warningMessage ) . toContain ( 'relative URL' )
368+ } )
369+
370+ test ( 'root-relative URLs are not considered relative' , ( ) => {
371+ const session = standardInitializer ( )
372+ const { widget } = session
373+
374+ widget . setTrackData ( {
375+ uri : '/absolute/path/test.bam' ,
376+ locationType : 'UriLocation' ,
377+ } )
378+ expect ( widget . isRelativeUrl ) . toBe ( false )
379+ } )
380+
381+ test ( 'mixinData is stored and can be retrieved' , ( ) => {
382+ const session = standardInitializer ( )
383+ const { widget } = session
384+
385+ expect ( widget . mixinData ) . toEqual ( { } )
386+
387+ widget . setMixinData ( {
388+ adapter : {
389+ customOption : 'value' ,
390+ } ,
391+ metadata : {
392+ description : 'Test track' ,
393+ } ,
394+ } )
395+
396+ expect ( widget . mixinData ) . toEqual ( {
397+ adapter : {
398+ customOption : 'value' ,
399+ } ,
400+ metadata : {
401+ description : 'Test track' ,
402+ } ,
403+ } )
404+ } )
405+
406+ test ( 'trackName falls back to filename when altTrackName is empty' , ( ) => {
407+ const session = standardInitializer ( )
408+ const { widget } = session
409+
410+ widget . setTrackData ( {
411+ uri : 'https://example.com/my-track.bam' ,
412+ locationType : 'UriLocation' ,
413+ } )
414+ expect ( widget . trackName ) . toBe ( 'my-track.bam' )
415+
416+ widget . setTrackName ( 'Custom Name' )
417+ expect ( widget . trackName ) . toBe ( 'Custom Name' )
418+ } )
419+
420+ test ( 'assembly falls back to view assemblyNames when altAssemblyName is empty' , ( ) => {
421+ const session = standardInitializer ( )
422+ const { widget } = session
423+
424+ // Should use view's assembly
425+ expect ( widget . assembly ) . toBe ( 'volvox' )
426+
427+ // Setting altAssemblyName should override
428+ widget . setAssembly ( 'customAssembly' )
429+ expect ( widget . assembly ) . toBe ( 'customAssembly' )
430+ } )
431+
432+ test ( 'handles undefined view gracefully' , ( ) => {
433+ const pluginManager = new PluginManager ( [ new FakeViewPlugin ( ) ] )
434+ pluginManager . createPluggableElements ( )
435+ pluginManager . configure ( )
436+
437+ const SessionModel = types . model ( {
438+ widget : stateModelFactory ( pluginManager ) ,
439+ } )
440+
441+ const session = SessionModel . create ( {
442+ widget : {
443+ type : 'AddTrackWidget' ,
444+ // no view reference
445+ } ,
446+ } )
447+
448+ const { widget } = session
449+ // Should not throw when view is undefined
450+ expect ( widget . assembly ) . toBeUndefined ( )
451+ } )
0 commit comments