input[type=”file”]の要素では、ファイルを選択させることができない。 しかし、jsdomを利用してテストを書きたいときの解決方法です。
何かの参考になれば幸いです。 また、issueがすでにopenになっているので将来的にjsdomで別の方法で修正されるかもしれません。
https://github.com/jsdom/jsdom/issues/1272
解決方法まとめ 解決方法としては、次の手順を踏んでinput[type=”file”]要素でファイルを選択した状態を作る。
FileListクラスをFile[]のprototypeとして設定する。
Object.definePropertyで、input[type=”file”]のvalueに設定する。
環境 必要なパッケージ
JS用
1 npm install --save-dev jsdom mime-types
TypeScript用
1 npm install --save-dev jsdom mime-types @types/node @types/mime-types
解決方法詳細 FileListクラスのモック作成用クラスの作成 次のコードの様に、ファイルパスの配列からFileListクラスのモックを作成するクラスを実装した。 参考: https://bitbucket.org/william_rusnack/addfilelist/src/master/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import * as fs from "fs" ;import * as path from "path" ;import * as mime from "mime-types" ;export default class FileMockFactory{ private window ; constructor (window : Window ) { this .window = window ; } setFileList(input: HTMLInputElement, file_paths: string ); setFileList(input: HTMLInputElement, file_paths: string []); setFileList(input: HTMLInputElement, file_paths: string []|string ) { if (typeof file_paths === 'string' ) file_paths = [file_paths] else if (!Array .isArray(file_paths)) { throw new Error ('file_paths needs to be a file path string or an Array of file path strings' ) } const file_list = file_paths.map(fp => this .createFile(fp)); Object .setPrototypeOf(file_list, Object .create(this .window.FileList.prototype)); Object .defineProperty(input, 'files' , { value: file_list, writable: false , }) return input } createFile(file_path: string ) { const { mtimeMs: lastModified } = fs.statSync(file_path) return new this .window.File( [fs.readFileSync(file_path)], path.basename(file_path), { lastModified, type : mime.lookup(file_path) || '' , } ) } }
使い方 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import * as assert from 'power-assert' ;import * as JSDOM from 'jsdom' ;import * as path from "path" ;import FileMockFactory from '../../TestHelpers/FileMockFactory' ;const fixture = `<html> <head></head> <body> <input type="file" id="file-input"> </body> </html>` ;describe('file attached test' , () => { let jsdom: JSDOM.JSDOM; let fileMockFactory: FileMockFactory; before(() => { jsdom = new JSDOM.JSDOM(fixture); fileMockFactory = new FileMockFactory(jsdom.window); }); describe('file attached test' , () => { it('should setting FileList.' , () => { const targetElement = <HTMLInputElement>jsdom.window.document.getElementById('file-input' ); const generator = new FileUpload(parser); fileMockFactory.setFileList(targetElement, [__filename]); assert.equal(targetElement.files[0 ].name, path.basename(__filename)); }); }); });
参考 jsdom issue