@@ -58,21 +58,47 @@ declare global {
5858 }
5959 }
6060 }
61+
62+ const getMimeTypeFromFilename = ( name : string ) : string => {
63+ const ext = ( name . split ( '.' ) . pop ( ) || '' ) . toLowerCase ( ) ;
64+
65+ if ( ext === 'csv' ) return 'text/csv' ;
66+ if ( ext === 'json' || ext === 'microbetrace' || ext === 'style' ) return 'application/json' ;
67+
68+ if ( ext === 'fasta' || ext === 'fas' || ext === 'fa' || ext === 'nwk' || ext === 'newick' ) {
69+ return 'text/plain' ;
70+ }
6171
62- Cypress . Commands . add ( 'attach_file' , ( target_selector , fixture_path , mime_type = 'text/csv' ) => {
63- return cy . fixture ( fixture_path , 'base64' ) . then ( ( base64 ) => {
64- const binary = Cypress . Blob . base64StringToBlob ( base64 , mime_type ) ;
65- const file = new File ( [ binary ] , fixture_path , { type : mime_type } ) ;
66- const data = new DataTransfer ( ) ;
72+ if ( ext === 'xlsx' ) return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ;
73+ if ( ext === 'xls' ) return 'application/vnd.ms-excel' ;
74+
75+ return 'application/octet-stream' ;
76+ } ;
77+
78+ Cypress . Commands . add ( 'attach_files' , ( target_selector , fixture_paths , mime_type ) => {
79+ const mimeTypes = ( mime_type && mime_type . length )
80+ ? mime_type
81+ : fixture_paths . map ( getMimeTypeFromFilename ) ;
82+
83+ const data = new DataTransfer ( ) ;
84+
85+ // Important: chain fixtures so the DataTransfer is fully populated before dispatching change
86+ cy . wrap ( fixture_paths , { log : false } ) . each ( ( fixture_path , idx ) => {
87+ const mt = mimeTypes [ idx ] || 'application/octet-stream' ;
88+
89+ cy . fixture ( fixture_path , 'base64' ) . then ( ( base64 ) => {
90+ const binary = Cypress . Blob . base64StringToBlob ( base64 , mt ) ;
91+ const file = new File ( [ binary ] , fixture_path , { type : mt } ) ;
6792 data . items . add ( file ) ;
68- cy . get ( target_selector ) . then ( ( $input ) => {
69- const el = $input . get ( 0 ) as HTMLInputElement ;
70- el . files = data . files ;
71- el . dispatchEvent ( new Event ( 'change' , { bubbles : true } ) ) ;
72- } ) ;
7393 } ) ;
74- }
75- ) ;
94+ } ) . then ( ( ) => {
95+ cy . get ( target_selector ) . then ( ( $input ) => {
96+ const el = $input . get ( 0 ) as HTMLInputElement ;
97+ el . files = data . files ;
98+ el . dispatchEvent ( new Event ( 'change' , { bubbles : true } ) ) ;
99+ } ) ;
100+ } ) ;
101+ } ) ;
76102
77103 // Similar to attach_files(), but allow multiple files to be attached/loaded at once
78104 Cypress . Commands . add ( 'attach_files' , ( target_selector , fixture_paths , mime_type = [ 'text/csv' ] ) => {
@@ -92,53 +118,65 @@ declare global {
92118 } ) ;
93119
94120 // haven't tested newick, might need some more work when loading json, microbetrace, or auspice datatype
95- Cypress . Commands . add ( 'loadFiles' , ( opts : { name : string , datatype : 'link' | 'node' | 'matrix' | 'fasta' | 'newick' | 'MT/other' , field1 ?: string , field2 ?: string , field3 ?: string } [ ] ) => {
96- let fileNames = opts . map ( file => file . name ) ;
97- let mime_types = fileNames . map ( name => {
98- let ext = name . split ( '.' ) . pop ( ) ;
99- if ( ext == 'fasta' || 'fas' || 'nwk' || 'newick' ) return 'text/plain' ;
100- if ( ext == 'csv' ) return 'text/csv' ;
101- if ( ext == 'json' || ext == 'microbetrace' ) return 'application/json'
102- return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
103- } )
104- cy . attach_files ( '#fileDropRef' , fileNames , mime_types ) ;
105- cy . get ( '#overlay' ) . should ( 'not.be.visible' , { timeout : 10000 } ) ;
106- cy . get ( '#launch' ) . should ( 'not.be.disabled' ) ;
107-
108- opts . forEach ( ( file , i ) => {
109- cy . contains ( '#file-table .file-table-row' , file . name )
110- . should ( 'be.visible' )
121+ Cypress . Commands . add ( 'loadFiles' , ( opts : {
122+ name : string ,
123+ datatype : 'link' | 'node' | 'matrix' | 'fasta' | 'newick' | 'MT/other' ,
124+ field1 ?: string ,
125+ field2 ?: string ,
126+ field3 ?: string
127+ } [ ] ) => {
128+ const fileNames = opts . map ( f => f . name ) ;
129+ const mimeTypes = fileNames . map ( getMimeTypeFromFilename ) ;
130+
131+ // Upload (waits until all fixtures are actually attached)
132+ cy . attach_files ( '#fileDropRef' , fileNames , mimeTypes ) ;
133+
134+ // Overlay goes away when file processing is done
135+ cy . get ( '#overlay' , { timeout : 20000 } ) . should ( 'not.be.visible' ) ;
136+
137+ // Launch enabled is a stronger signal than "row is visible"
138+ cy . get ( '#launch' , { timeout : 20000 } ) . should ( 'not.be.disabled' ) ;
139+
140+ // For each row: assert it exists (NOT visible), then set datatype/fields with force
141+ opts . forEach ( ( file ) => {
142+ cy . contains ( '#file-table .file-table-row' , file . name , { timeout : 20000 } )
143+ . should ( 'exist' )
111144 . then ( ( $fileRow ) => {
112145 const $row = cy . wrap ( $fileRow ) ;
113-
114- // First confirms/sets file. datatype
115- let activeField : string = $fileRow . find ( 'label.active input' ) . attr ( 'data-type' ) ;
116- if ( activeField ! = file . datatype ) {
117- $row . find ( `input[data-type="${ file . datatype } "]` ) . click ( { force : true } )
146+
147+ // datatype toggle
148+ const activeType = $fileRow . find ( 'label.active input' ) . attr ( 'data-type' ) ;
149+ if ( activeType != = file . datatype ) {
150+ $row . find ( `input[data-type="${ file . datatype } "]` ) . click ( { force : true } ) ;
118151 }
119- cy . wait ( 5 ) . then ( ( ) => expect ( $fileRow . find ( 'label.active input' ) . attr ( 'data-type' ) ) . to . equal ( file . datatype ) )
120-
121- // Next confirms/sets each field
122- if ( file . datatype == 'link' || file . datatype == 'node' ) {
123- let checkField = ( expectedValue : string , fieldNumber : number ) => {
152+
153+ // field mapping
154+ if ( file . datatype === 'link' || file . datatype === 'node' ) {
155+ const setField = ( expectedValue : string , fieldNumber : number ) => {
124156 const selectId = `file-${ file . name } -field-${ fieldNumber } ` ;
125- cy . wrap ( $fileRow ) . find ( `select[id="${ selectId } "]` ) . invoke ( 'val' ) . then ( ( fieldValue ) => {
126- if ( fieldValue != expectedValue ) {
127- cy . wrap ( $fileRow ) . find ( `select[id="${ selectId } "]` ) . select ( expectedValue )
128- }
129- } ) . then ( ( ) => cy . get ( `select[id="${ selectId } "]` ) . should ( 'have.value' , expectedValue ) )
130- }
131-
132- if ( file . field1 ) checkField ( file . field1 , 1 )
133- if ( file . field2 ) checkField ( file . field2 , 2 )
134- if ( file . datatype == 'link' && file . field3 ) checkField ( file . field3 , 3 )
157+
158+ cy . wrap ( $fileRow )
159+ . find ( `select[id="${ selectId } "]` )
160+ . should ( 'exist' )
161+ . then ( ( $sel ) => {
162+ const current = String ( $sel . val ( ) ) ;
163+ if ( current !== expectedValue ) {
164+ cy . wrap ( $sel ) . select ( expectedValue , { force : true } ) ;
165+ }
166+ } ) ;
167+
168+ cy . get ( `select[id="${ selectId } "]` ) . should ( 'have.value' , expectedValue ) ;
169+ } ;
170+
171+ if ( file . field1 ) setField ( file . field1 , 1 ) ;
172+ if ( file . field2 ) setField ( file . field2 , 2 ) ;
173+ if ( file . datatype === 'link' && file . field3 ) setField ( file . field3 , 3 ) ;
135174 }
136175 } ) ;
137176 } ) ;
138-
139- cy . get ( '#launch' ) . should ( 'not.be.disabled' ) ;
140- }
141- )
177+
178+ cy . get ( '#launch' , { timeout : 20000 } ) . should ( 'not.be.disabled' ) ;
179+ } ) ;
142180
143181 Cypress . Commands . add ( 'closeSettingsPane' , ( dialogTitle : string ) => {
144182 cy . contains ( '.p-dialog-title' , dialogTitle )
0 commit comments