@@ -3,9 +3,11 @@ import { spy } from 'sinon';
33
44import type { TemplateResult } from 'lit' ;
55import { defineComponents } from '../common/definitions/defineComponents.js' ;
6+ import { first } from '../common/util.js' ;
67import {
8+ type ValidationContainerTestsParams ,
79 createFormAssociatedTestBed ,
8- simulateFileUpload ,
10+ runValidationContainerTests ,
911} from '../common/utils.spec.js' ;
1012import IgcFileInputComponent from './file-input.js' ;
1113
@@ -27,6 +29,10 @@ describe('File Input component', () => {
2729 input = element . renderRoot . querySelector ( 'input' ) ! ;
2830 }
2931
32+ function getDOM ( selector : string ) {
33+ return element . renderRoot . querySelector ( selector ) as HTMLElement ;
34+ }
35+
3036 describe ( 'Properties' , ( ) => {
3137 it ( 'sets the multiple property when input is of type file' , async ( ) => {
3238 await createFixture ( html `< igc-file-input multiple > </ igc-file-input > ` ) ;
@@ -58,65 +64,61 @@ describe('File Input component', () => {
5864
5965 it ( 'returns the uploaded files when input is of type file' , async ( ) => {
6066 await createFixture ( html `< igc-file-input > </ igc-file-input > ` ) ;
61- await simulateFileUpload ( element , files ) ;
6267
63- expect ( element . files ) . to . exist ;
64- expect ( element . files ! . length ) . to . equal ( 2 ) ;
65- expect ( element . files ! [ 0 ] . name ) . to . equal ( 'test.txt' ) ;
66- expect ( element . files ! [ 1 ] . name ) . to . equal ( 'image.png' ) ;
68+ simulateFileUpload ( input , files ) ;
69+ await elementUpdated ( element ) ;
70+
71+ expect ( element . files ) . lengthOf ( 2 ) ;
72+ expect ( element . files ?. item ( 0 ) ?. name ) . to . equal ( 'test.txt' ) ;
73+ expect ( element . files ?. item ( 1 ) ?. name ) . to . equal ( 'image.png' ) ;
6774 } ) ;
6875
6976 it ( 'should show placeholder text when no file is selected' , async ( ) => {
7077 await createFixture ( html `< igc-file-input > </ igc-file-input > ` ) ;
7178
72- expect (
73- element
74- . shadowRoot ! . querySelector ( '[part="file-names"]' ) !
75- . textContent ! . trim ( )
76- ) . to . equal ( 'No file chosen' ) ;
79+ expect ( getDOM ( '[part="file-names"]' ) . innerText ) . to . equal (
80+ 'No file chosen'
81+ ) ;
7782
7883 element . placeholder = 'Select a document' ;
7984 await elementUpdated ( element ) ;
8085
81- expect (
82- element
83- . shadowRoot ! . querySelector ( '[part="file-names"]' ) !
84- . textContent ! . trim ( )
85- ) . to . equal ( 'Select a document' ) ;
86+ expect ( getDOM ( '[part="file-names"]' ) . innerText ) . to . equal (
87+ 'Select a document'
88+ ) ;
8689
87- await simulateFileUpload ( element , [ files [ 0 ] ] ) ;
90+ element . focus ( ) ;
91+ simulateFileUpload ( input , [ first ( files ) ] ) ;
92+ await elementUpdated ( element ) ;
8893
89- expect (
90- element
91- . shadowRoot ! . querySelector ( '[part="file-names"]' ) !
92- . textContent ! . trim ( )
93- ) . to . equal ( 'test.txt' ) ;
94+ expect ( getDOM ( '[part="file-names"]' ) . innerText ) . to . equal (
95+ first ( files ) . name
96+ ) ;
9497 } ) ;
9598
9699 it ( 'resets the file selection when empty string is passed for value' , async ( ) => {
97- const file = files [ 0 ] ;
98100 await createFixture ( html `< igc-file-input > </ igc-file-input > ` ) ;
99- await simulateFileUpload ( element , [ file ] ) ;
101+ const file = first ( files ) ;
102+
103+ simulateFileUpload ( input , [ file ] ) ;
104+ await elementUpdated ( element ) ;
100105
101106 expect ( element . value ) . to . equal ( `C:\\fakepath\\${ file . name } ` ) ;
102- expect ( element . files ! . length ) . to . equal ( 1 ) ;
103- expect ( element . files ! [ 0 ] ) . to . equal ( file ) ;
107+ expect ( element . files ) . lengthOf ( 1 ) ;
108+ expect ( element . files ?. item ( 0 ) ) . to . equal ( file ) ;
104109
105110 element . value = '' ;
106- expect ( element . value ) . to . equal ( '' ) ;
107- expect ( element . files ! . length ) . to . equal ( 0 ) ;
111+ expect ( element . value ) . to . be . empty ;
112+ expect ( element . files ) . to . be . empty ;
108113 } ) ;
109114 } ) ;
110115
111116 describe ( 'File type layout' , ( ) => {
112117 it ( 'renders publicly documented parts when the input is of type file' , async ( ) => {
113118 await createFixture ( html `< igc-file-input > </ igc-file-input > ` ) ;
114119
115- expect (
116- element . shadowRoot ! . querySelector ( 'div[part="file-selector-button"]' )
117- ) . to . exist ;
118- expect ( element . shadowRoot ! . querySelector ( 'div[part="file-names"]' ) ) . to
119- . exist ;
120+ expect ( getDOM ( 'div[part="file-selector-button"]' ) ) . is . not . null ;
121+ expect ( getDOM ( 'div[part="file-names"]' ) ) . is . not . null ;
120122 } ) ;
121123
122124 it ( 'renders slotted contents when the input is of type file' , async ( ) => {
@@ -127,15 +129,17 @@ describe('File Input component', () => {
127129 </ igc-file-input >
128130 ` ) ;
129131
130- const selectorSlot = element . shadowRoot ! . querySelector (
132+ const selectorSlot = getDOM (
131133 'slot[name="file-selector-text"]'
132134 ) as HTMLSlotElement ;
133- const missingSlot = element . shadowRoot ! . querySelector (
135+ const missingSlot = getDOM (
134136 'slot[name="file-missing-text"]'
135137 ) as HTMLSlotElement ;
136138
137- expect ( selectorSlot ! . assignedNodes ( ) [ 0 ] . textContent ) . to . equal ( 'Upload' ) ;
138- expect ( missingSlot ! . assignedNodes ( ) [ 0 ] . textContent ) . to . equal (
139+ expect ( first ( selectorSlot . assignedNodes ( ) ) . textContent ) . to . equal (
140+ 'Upload'
141+ ) ;
142+ expect ( first ( missingSlot . assignedNodes ( ) ) . textContent ) . to . equal (
139143 'Choose a file'
140144 ) ;
141145 } ) ;
@@ -150,7 +154,8 @@ describe('File Input component', () => {
150154 await createFixture ( html `< igc-file-input > </ igc-file-input > ` ) ;
151155 const eventSpy = spy ( element , 'emitEvent' ) ;
152156
153- await simulateFileUpload ( element , [ files [ 0 ] ] ) ;
157+ simulateFileUpload ( input , [ first ( files ) ] ) ;
158+ await elementUpdated ( element ) ;
154159
155160 expect ( eventSpy ) . calledWith ( 'igcChange' , {
156161 detail : element . value ,
@@ -159,9 +164,6 @@ describe('File Input component', () => {
159164
160165 it ( 'emits igcCancel' , async ( ) => {
161166 const eventSpy = spy ( element , 'emitEvent' ) ;
162- const input = element . shadowRoot ! . querySelector (
163- 'input[type="file"]'
164- ) as HTMLInputElement ;
165167
166168 input . dispatchEvent ( new Event ( 'cancel' , { bubbles : true } ) ) ;
167169 await elementUpdated ( element ) ;
@@ -171,76 +173,28 @@ describe('File Input component', () => {
171173 } ) ;
172174 } ) ;
173175
174- it ( 'should mark as dirty on focus' , async ( ) => {
175- const input = element . shadowRoot ! . querySelector (
176- 'input[type="file"]'
177- ) as HTMLInputElement ;
178-
179- input . dispatchEvent ( new Event ( 'focus' , { bubbles : true } ) ) ;
180- await elementUpdated ( element ) ;
181-
182- const eventSpy = spy ( element as any , '_validate' ) ;
183- input . dispatchEvent ( new Event ( 'blur' , { bubbles : true } ) ) ;
184-
185- expect ( eventSpy ) . called ;
186- } ) ;
187-
188- it ( 'should validate on blur' , async ( ) => {
176+ it ( 'should update UI invalid state on blur when interacted' , async ( ) => {
189177 await createFixture ( html `< igc-file-input required > </ igc-file-input > ` ) ;
190178
191- const input = element . shadowRoot ! . querySelector (
192- 'input[type="file"]'
193- ) as HTMLInputElement ;
179+ // Internal invalid state, invalid UI is in initial state.
180+ expect ( element . validity . valueMissing ) . to . be . true ;
181+ expect ( element . invalid ) . to . be . false ;
194182
195- input . dispatchEvent ( new Event ( 'blur' , { bubbles : true } ) ) ;
196- await elementUpdated ( element ) ;
183+ // Internal invalid state, invalid UI is still in initial state.
184+ element . focus ( ) ;
185+ expect ( element . validity . valueMissing ) . to . be . true ;
186+ expect ( element . invalid ) . to . be . false ;
197187
188+ // Internal invalid state, invalid UI is updated.
189+ element . blur ( ) ;
190+ expect ( element . validity . valueMissing ) . to . be . true ;
198191 expect ( element . invalid ) . to . be . true ;
199192 } ) ;
200193 } ) ;
201194} ) ;
202195
203- describe ( 'Form Validation' , ( ) => {
204- const files = [
205- new File ( [ 'test content' ] , 'test.txt' , { type : 'text/plain' } ) ,
206- ] ;
207- const _expectedValidation = Symbol ( ) ;
208-
209- type TestBedInput = IgcFileInputComponent & {
210- [ _expectedValidation ] : boolean ;
211- } ;
212-
213- function validateInput ( event : CustomEvent < string > ) {
214- const element = event . target as TestBedInput ;
215- expect ( element . checkValidity ( ) ) . to . equal ( element [ _expectedValidation ] ) ;
216- }
217-
218- function setExpectedValidationState (
219- state : boolean ,
220- element : IgcFileInputComponent
221- ) {
222- ( element as TestBedInput ) [ _expectedValidation ] = state ;
223- }
224-
225- const spec = createFormAssociatedTestBed < IgcFileInputComponent > (
226- html `< igc-file-input
227- name ="input "
228- required
229- @igcChange =${ validateInput }
230- > </ igc-file-input > `
231- ) ;
232-
233- beforeEach ( async ( ) => {
234- await spec . setup ( IgcFileInputComponent . tagName ) ;
235- } ) ;
236-
237- it ( 'validates component' , async ( ) => {
238- setExpectedValidationState ( true , spec . element ) ;
239- await simulateFileUpload ( spec . element , files ) ;
240- } ) ;
241- } ) ;
242-
243196describe ( 'Form Integration' , ( ) => {
197+ let input : HTMLInputElement ;
244198 const files = [
245199 new File ( [ 'test content' ] , 'test.txt' , { type : 'text/plain' } ) ,
246200 ] ;
@@ -251,6 +205,7 @@ describe('Form Integration', () => {
251205
252206 beforeEach ( async ( ) => {
253207 await spec . setup ( IgcFileInputComponent . tagName ) ;
208+ input = spec . element . renderRoot . querySelector ( 'input' ) ! ;
254209 } ) ;
255210
256211 it ( 'correct initial state' , ( ) => {
@@ -267,12 +222,14 @@ describe('Form Integration', () => {
267222 } ) ;
268223
269224 it ( 'is associated on submit' , async ( ) => {
270- await simulateFileUpload ( spec . element , files ) ;
271- spec . assertSubmitHasValue ( files [ 0 ] ) ;
225+ simulateFileUpload ( input , files ) ;
226+ await elementUpdated ( spec . element ) ;
227+ spec . assertSubmitHasValue ( first ( files ) ) ;
272228 } ) ;
273229
274230 it ( 'is correctly resets on form reset' , async ( ) => {
275- await simulateFileUpload ( spec . element , files ) ;
231+ simulateFileUpload ( input , files ) ;
232+ await elementUpdated ( spec . element ) ;
276233 spec . reset ( ) ;
277234
278235 expect ( spec . element . value ) . to . be . empty ;
@@ -289,7 +246,32 @@ describe('Form Integration', () => {
289246 it ( 'fulfils required constraint' , async ( ) => {
290247 spec . assertSubmitFails ( ) ;
291248
292- await simulateFileUpload ( spec . element , files ) ;
249+ simulateFileUpload ( input , files ) ;
250+ await elementUpdated ( spec . element ) ;
251+
293252 spec . assertSubmitPasses ( ) ;
294253 } ) ;
295254} ) ;
255+
256+ describe ( 'Validation message slots' , ( ) => {
257+ it ( '' , async ( ) => {
258+ const testParameters : ValidationContainerTestsParams < IgcFileInputComponent > [ ] =
259+ [
260+ { slots : [ 'valueMissing' ] , props : { required : true } } ,
261+ { slots : [ 'customError' ] } ,
262+ ] ;
263+
264+ runValidationContainerTests ( IgcFileInputComponent , testParameters ) ;
265+ } ) ;
266+ } ) ;
267+
268+ function simulateFileUpload ( input : HTMLInputElement , files : File [ ] ) {
269+ const dataTransfer = new DataTransfer ( ) ;
270+
271+ for ( const file of files ) {
272+ dataTransfer . items . add ( file ) ;
273+ }
274+
275+ input . files = dataTransfer . files ;
276+ input . dispatchEvent ( new Event ( 'change' , { bubbles : true } ) ) ;
277+ }
0 commit comments