Skip to content

Commit

Permalink
Merge pull request #675 from capricorn86/task/494-typeerror-right-han…
Browse files Browse the repository at this point in the history
…d-side-of-instanceof-is-not-an-object-filelist

#494@minor: Adds support for FileList to HTMLInputElement.
  • Loading branch information
capricorn86 committed Dec 7, 2022
2 parents a85a84f + 901b701 commit 4d20b19
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 6 deletions.
6 changes: 5 additions & 1 deletion packages/happy-dom/src/index.ts
Expand Up @@ -123,6 +123,8 @@ import Attr from './nodes/attr/Attr';
import IAttr from './nodes/attr/IAttr';
import ProcessingInstruction from './nodes/processing-instruction/ProcessingInstruction';
import IProcessingInstruction from './nodes/processing-instruction/IProcessingInstruction';
import FileList from './nodes/html-input-element/FileList';
import IFileList from './nodes/html-input-element/IFileList';

export {
GlobalWindow,
Expand Down Expand Up @@ -250,5 +252,7 @@ export {
Attr,
IAttr,
ProcessingInstruction,
IProcessingInstruction
IProcessingInstruction,
FileList,
IFileList
};
35 changes: 35 additions & 0 deletions packages/happy-dom/src/nodes/html-input-element/FileList.ts
@@ -0,0 +1,35 @@
import File from '../../file/File';
import IFileList from './IFileList';

/**
* FileList.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/FileList
*/
export default class FileList extends Array implements IFileList<File> {
/**
* Constructor.
*/
constructor() {
super(0);
}

/**
* Returns `Symbol.toStringTag`.
*
* @returns `Symbol.toStringTag`.
*/
public get [Symbol.toStringTag](): string {
return this.constructor.name;
}

/**
* Returns item by index.
*
* @param index Index.
* @returns Item.
*/
public item(index: number): File | null {
return this[index] || null;
}
}
@@ -1,4 +1,3 @@
import File from '../../file/File';
import HTMLElement from '../html-element/HTMLElement';
import ValidityState from '../validity-state/ValidityState';
import DOMException from '../../exception/DOMException';
Expand All @@ -11,6 +10,9 @@ import IHTMLInputElement from './IHTMLInputElement';
import IHTMLFormElement from '../html-form-element/IHTMLFormElement';
import IHTMLElement from '../html-element/IHTMLElement';
import HTMLInputElementValueStepping from './HTMLInputElementValueStepping';
import FileList from './FileList';
import File from '../../file/File';
import IFileList from './IFileList';

/**
* HTML Input Element.
Expand All @@ -36,7 +38,7 @@ export default class HTMLInputElement extends HTMLElement implements IHTMLInputE
public defaultChecked = false;

// Type specific: file
public files: File[] = [];
public files: IFileList<File> = new FileList();

// Events
public oninput: (event: Event) => void | null = null;
Expand Down Expand Up @@ -974,7 +976,7 @@ export default class HTMLInputElement extends HTMLElement implements IHTMLInputE
clone._height = this._height;
clone._width = this._width;
clone.defaultChecked = this.defaultChecked;
clone.files = this.files.slice();
clone.files = <FileList>this.files.slice();
clone._selectionStart = this._selectionStart;
clone._selectionEnd = this._selectionEnd;
clone._selectionDirection = this._selectionDirection;
Expand Down
11 changes: 11 additions & 0 deletions packages/happy-dom/src/nodes/html-input-element/IFileList.ts
@@ -0,0 +1,11 @@
/**
* NodeList.
*/
export default interface IFileList<T> extends Array<T> {
/**
* Returns item by index.
*
* @param index Index.
*/
item(index: number): T;
}
@@ -1,9 +1,10 @@
import File from '../../file/File';
import IHTMLElement from '../html-element/IHTMLElement';
import IHTMLFormElement from '../html-form-element/IHTMLFormElement';
import HTMLInputElementSelectionModeEnum from './HTMLInputElementSelectionModeEnum';
import ValidityState from '../validity-state/ValidityState';
import Event from '../../event/Event';
import File from '../../file/File';
import IFileList from './IFileList';

/**
* HTML Input Element.
Expand All @@ -17,7 +18,7 @@ export default interface IHTMLInputElement extends IHTMLElement {
formMethod: string;
formNoValidate: boolean;
defaultChecked: boolean;
files: File[];
files: IFileList<File>;
defaultValue: string;
height: number;
width: number;
Expand Down
2 changes: 2 additions & 0 deletions packages/happy-dom/src/window/IWindow.ts
Expand Up @@ -104,6 +104,7 @@ import IElement from '../nodes/element/IElement';
import ProcessingInstruction from '../nodes/processing-instruction/ProcessingInstruction';
import IHappyDOMSettings from './IHappyDOMSettings';
import RequestInfo from '../fetch/RequestInfo';
import FileList from '../nodes/html-input-element/FileList';

/**
* Window without dependencies to server side specific packages.
Expand Down Expand Up @@ -218,6 +219,7 @@ export default interface IWindow extends IEventTarget, NodeJS.Global {
readonly XMLHttpRequest: typeof XMLHttpRequest;
readonly XMLHttpRequestUpload: typeof XMLHttpRequestUpload;
readonly XMLHttpRequestEventTarget: typeof XMLHttpRequestEventTarget;
readonly FileList: typeof FileList;

// Events
onload: (event: Event) => void;
Expand Down
2 changes: 2 additions & 0 deletions packages/happy-dom/src/window/Window.ts
Expand Up @@ -115,6 +115,7 @@ import IElement from '../nodes/element/IElement';
import ProcessingInstruction from '../nodes/processing-instruction/ProcessingInstruction';
import IHappyDOMSettings from './IHappyDOMSettings';
import RequestInfo from '../fetch/RequestInfo';
import FileList from '../nodes/html-input-element/FileList';

const ORIGINAL_SET_TIMEOUT = setTimeout;
const ORIGINAL_CLEAR_TIMEOUT = clearTimeout;
Expand Down Expand Up @@ -246,6 +247,7 @@ export default class Window extends EventTarget implements IWindow {
public readonly MimeTypeArray = MimeTypeArray;
public readonly Plugin = Plugin;
public readonly PluginArray = PluginArray;
public readonly FileList = FileList;
public readonly Headers: { new (init?: IHeadersInit): IHeaders } = Headers;
public readonly DOMRect: typeof DOMRect;
public readonly Request: {
Expand Down
33 changes: 33 additions & 0 deletions packages/happy-dom/test/nodes/html-input-element/FileList.test.ts
@@ -0,0 +1,33 @@
import Window from '../../../src/window/Window';
import IWindow from '../../../src/window/Window';
import IDocument from '../../../src/nodes/document/IDocument';
import File from '../../../src/file/File';
import IHTMLInputElement from '../../../src/nodes/html-input-element/IHTMLInputElement';

describe('FileList', () => {
let window: IWindow;
let document: IDocument;

beforeEach(() => {
window = new Window();
document = window.document;
});

afterEach(() => {
jest.restoreAllMocks();
});

describe('item()', () => {
it('Returns file at index.', () => {
const element = <IHTMLInputElement>document.createElement('input');
const file1 = new File([''], 'file.txt');
const file2 = new File([''], 'file2.txt');

element.files.push(file1);
element.files.push(file2);

expect(element.files.item(0)).toBe(file1);
expect(element.files.item(1)).toBe(file2);
});
});
});

0 comments on commit 4d20b19

Please sign in to comment.