Skip to content

Commit edd171a

Browse files
committed
spec(file-input): add form integration tests
1 parent 9a24f1e commit edd171a

File tree

3 files changed

+133
-26
lines changed

3 files changed

+133
-26
lines changed

src/components/file-input/file-input.spec.ts

Lines changed: 120 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@ import { spy } from 'sinon';
33

44
import type { TemplateResult } from 'lit';
55
import { defineComponents } from '../common/definitions/defineComponents.js';
6-
import { simulateFileUpload } from '../common/utils.spec.js';
6+
import {
7+
createFormAssociatedTestBed,
8+
simulateFileUpload,
9+
} from '../common/utils.spec.js';
710
import IgcFileInputComponent from './file-input.js';
811

9-
describe('File input component', () => {
12+
describe('File Input component', () => {
13+
const files = [
14+
new File(['test content'], 'test.txt', { type: 'text/plain' }),
15+
new File(['image data'], 'image.png', { type: 'image/png' }),
16+
];
17+
1018
before(() => {
1119
defineComponents(IgcFileInputComponent);
1220
});
@@ -50,12 +58,6 @@ describe('File input component', () => {
5058

5159
it('returns the uploaded files when input is of type file', async () => {
5260
await createFixture(html`<igc-file-input></igc-file-input>`);
53-
54-
const files = [
55-
new File(['test content'], 'test.txt', { type: 'text/plain' }),
56-
new File(['image data'], 'image.png', { type: 'image/png' }),
57-
];
58-
5961
await simulateFileUpload(element, files);
6062

6163
expect(element.files).to.exist;
@@ -82,18 +84,28 @@ describe('File input component', () => {
8284
.textContent!.trim()
8385
).to.equal('Select a document');
8486

85-
const file = new File(['test content'], 'test.txt', {
86-
type: 'text/plain',
87-
});
88-
89-
await simulateFileUpload(element, [file]);
87+
await simulateFileUpload(element, [files[0]]);
9088

9189
expect(
9290
element
9391
.shadowRoot!.querySelector('[part="file-names"]')!
9492
.textContent!.trim()
9593
).to.equal('test.txt');
9694
});
95+
96+
it('resets the file selection when empty string is passed for value', async () => {
97+
const file = files[0];
98+
await createFixture(html`<igc-file-input></igc-file-input>`);
99+
await simulateFileUpload(element, [file]);
100+
101+
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);
104+
105+
element.value = '';
106+
expect(element.value).to.equal('');
107+
expect(element.files!.length).to.equal(0);
108+
});
97109
});
98110

99111
describe('File type layout', () => {
@@ -138,11 +150,7 @@ describe('File input component', () => {
138150
await createFixture(html`<igc-file-input></igc-file-input>`);
139151
const eventSpy = spy(element, 'emitEvent');
140152

141-
const files = [
142-
new File(['test content'], 'test.txt', { type: 'text/plain' }),
143-
];
144-
145-
await simulateFileUpload(element, files);
153+
await simulateFileUpload(element, [files[0]]);
146154

147155
expect(eventSpy).calledWith('igcChange', {
148156
detail: element.value,
@@ -191,3 +199,97 @@ describe('File input component', () => {
191199
});
192200
});
193201
});
202+
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+
243+
describe('Form Integration', () => {
244+
const files = [
245+
new File(['test content'], 'test.txt', { type: 'text/plain' }),
246+
];
247+
248+
const spec = createFormAssociatedTestBed<IgcFileInputComponent>(
249+
html`<igc-file-input name="input" required></igc-file-input>`
250+
);
251+
252+
beforeEach(async () => {
253+
await spec.setup(IgcFileInputComponent.tagName);
254+
});
255+
256+
it('correct initial state', () => {
257+
spec.assertIsPristine();
258+
expect(spec.element.value).to.equal('');
259+
});
260+
261+
it('is form associated', () => {
262+
expect(spec.element.form).to.equal(spec.form);
263+
});
264+
265+
it('is not associated on submit if no value', () => {
266+
spec.assertSubmitHasValue(null);
267+
});
268+
269+
it('is associated on submit', async () => {
270+
await simulateFileUpload(spec.element, files);
271+
spec.assertSubmitHasValue(files[0]);
272+
});
273+
274+
it('is correctly resets on form reset', async () => {
275+
await simulateFileUpload(spec.element, files);
276+
spec.reset();
277+
278+
expect(spec.element.value).to.be.empty;
279+
});
280+
281+
it('reflects disabled ancestor state', () => {
282+
spec.setAncestorDisabledState(true);
283+
expect(spec.element.disabled).to.be.true;
284+
285+
spec.setAncestorDisabledState(false);
286+
expect(spec.element.disabled).to.be.false;
287+
});
288+
289+
it('fulfils required constraint', async () => {
290+
spec.assertSubmitFails();
291+
292+
await simulateFileUpload(spec.element, files);
293+
spec.assertSubmitPasses();
294+
});
295+
});

src/components/file-input/file-input.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ export default class IgcFileInputComponent extends IgcInputBaseComponent {
8383
}
8484
}
8585

86+
public get value(): string {
87+
return this.input?.value ?? '';
88+
}
89+
8690
/**
8791
* The multiple attribute of the control.
8892
* Used to indicate that a file input allows the user to select more than one file.
@@ -132,10 +136,6 @@ export default class IgcFileInputComponent extends IgcInputBaseComponent {
132136
this._updateValidity();
133137
}
134138

135-
public get value(): string {
136-
return this.input?.value ?? '';
137-
}
138-
139139
/** @hidden */
140140
public override setSelectionRange(): void {}
141141

@@ -149,10 +149,9 @@ export default class IgcFileInputComponent extends IgcInputBaseComponent {
149149
}
150150

151151
private handleChange() {
152-
this.value = this.input.value;
153152
this._formValue.setValueAndFormState(this.files);
154-
155153
this._validate();
154+
this.requestUpdate();
156155
this.emitEvent('igcChange', { detail: this.value });
157156
}
158157

src/components/file-input/themes/file-input.base.scss

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,25 @@ $theme: $base;
3737
grid-column: 2 / 4;
3838
appearance: none;
3939
opacity: 0;
40-
cursor: pointer;
4140
}
4241

4342
[part~='input']::file-selector-button {
44-
cursor: pointer !important;
4543
width: 100%;
44+
cursor: auto;
4645
}
4746

4847
[part~='suffix'] {
4948
grid-area: 1 / 4;
5049
}
5150
}
5251

52+
:host(:not(:disabled)) {
53+
[part~='input'],
54+
[part~='input']::file-selector-button {
55+
cursor: pointer;
56+
}
57+
}
58+
5359
:host(:is(igc-file-input):disabled),
5460
:host(:is(igc-file-input)[disabled]) {
5561
[part~='file-names'] {

0 commit comments

Comments
 (0)