diff --git a/package.json b/package.json index 2ed2ccabd7..6b269a06ee 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "extract-zip": "^1.6.6", "https-proxy-agent": "^3.0.0", "jpeg-js": "^0.3.6", - "mime": "^2.0.3", "pngjs": "^3.4.0", "progress": "^2.0.3", "proxy-from-env": "^1.0.0", @@ -56,7 +55,6 @@ "@types/debug": "0.0.31", "@types/extract-zip": "^1.6.2", "@types/jpeg-js": "^0.3.7", - "@types/mime": "^2.0.0", "@types/node": "^8.10.34", "@types/pngjs": "^3.4.0", "@types/proxy-from-env": "^1.0.0", @@ -69,6 +67,7 @@ "cross-env": "^5.0.5", "eslint": "^6.6.0", "esprima": "^4.0.0", + "formidable": "^1.2.1", "minimist": "^1.2.0", "ncp": "^2.0.0", "node-stream-zip": "^1.8.2", diff --git a/src/dom.ts b/src/dom.ts index c3d51df735..50c65809a3 100644 --- a/src/dom.ts +++ b/src/dom.ts @@ -432,7 +432,7 @@ export class ElementHandle extends js.JSHandle { if (typeof item === 'string') { const file: types.FilePayload = { name: platform.basename(item), - type: 'application/octet-stream', + type: platform.getMimeType(item), data: await platform.readFileAsync(item, 'base64') }; return file; @@ -620,7 +620,7 @@ export function waitForSelectorTask(selector: string, visibility: types.Visibili export const setFileInputFunction = async (element: HTMLInputElement, payloads: types.FilePayload[]) => { const files = await Promise.all(payloads.map(async (file: types.FilePayload) => { const result = await fetch(`data:${file.type};base64,${file.data}`); - return new File([await result.blob()], file.name); + return new File([await result.blob()], file.name, {type: file.type}); })); const dt = new DataTransfer(); for (const file of files) diff --git a/src/platform.ts b/src/platform.ts index 69009191ba..6f935b1c24 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -21,7 +21,6 @@ import * as nodeFS from 'fs'; import * as nodePath from 'path'; import * as nodeDebug from 'debug'; import * as nodeBuffer from 'buffer'; -import * as mime from 'mime'; import * as jpeg from 'jpeg-js'; import * as png from 'pngjs'; import * as http from 'http'; @@ -213,9 +212,9 @@ export async function closeFdAsync(fd: number): Promise { return await promisify(nodeFS.close)(fd); } -export function getMimeType(file: string): string | null { - assertFileAccess(); - return mime.getType(file); +export function getMimeType(file: string): string { + const extension = file.substring(file.lastIndexOf('.') + 1); + return extensionToMime[extension] || 'application/octet-stream'; } export function urlMatches(urlString: string, match: types.URLMatch | undefined): boolean { @@ -358,3 +357,203 @@ export class WebSocketTransport implements ConnectionTransport { this._ws.close(); } } + +const extensionToMime: { [key: string]: string } = { + 'ai': 'application/postscript', + 'apng': 'image/apng', + 'appcache': 'text/cache-manifest', + 'au': 'audio/basic', + 'bmp': 'image/bmp', + 'cer': 'application/pkix-cert', + 'cgm': 'image/cgm', + 'coffee': 'text/coffeescript', + 'conf': 'text/plain', + 'crl': 'application/pkix-crl', + 'css': 'text/css', + 'csv': 'text/csv', + 'def': 'text/plain', + 'doc': 'application/msword', + 'dot': 'application/msword', + 'drle': 'image/dicom-rle', + 'dtd': 'application/xml-dtd', + 'ear': 'application/java-archive', + 'emf': 'image/emf', + 'eps': 'application/postscript', + 'exr': 'image/aces', + 'fits': 'image/fits', + 'g3': 'image/g3fax', + 'gbr': 'application/rpki-ghostbusters', + 'gif': 'image/gif', + 'glb': 'model/gltf-binary', + 'gltf': 'model/gltf+json', + 'gz': 'application/gzip', + 'h261': 'video/h261', + 'h263': 'video/h263', + 'h264': 'video/h264', + 'heic': 'image/heic', + 'heics': 'image/heic-sequence', + 'heif': 'image/heif', + 'heifs': 'image/heif-sequence', + 'htm': 'text/html', + 'html': 'text/html', + 'ics': 'text/calendar', + 'ief': 'image/ief', + 'ifb': 'text/calendar', + 'iges': 'model/iges', + 'igs': 'model/iges', + 'in': 'text/plain', + 'ini': 'text/plain', + 'jade': 'text/jade', + 'jar': 'application/java-archive', + 'jls': 'image/jls', + 'jp2': 'image/jp2', + 'jpe': 'image/jpeg', + 'jpeg': 'image/jpeg', + 'jpf': 'image/jpx', + 'jpg': 'image/jpeg', + 'jpg2': 'image/jp2', + 'jpgm': 'video/jpm', + 'jpgv': 'video/jpeg', + 'jpm': 'image/jpm', + 'jpx': 'image/jpx', + 'js': 'application/javascript', + 'json': 'application/json', + 'json5': 'application/json5', + 'jsx': 'text/jsx', + 'jxr': 'image/jxr', + 'kar': 'audio/midi', + 'ktx': 'image/ktx', + 'less': 'text/less', + 'list': 'text/plain', + 'litcoffee': 'text/coffeescript', + 'log': 'text/plain', + 'm1v': 'video/mpeg', + 'm21': 'application/mp21', + 'm2a': 'audio/mpeg', + 'm2v': 'video/mpeg', + 'm3a': 'audio/mpeg', + 'm4a': 'audio/mp4', + 'm4p': 'application/mp4', + 'man': 'text/troff', + 'manifest': 'text/cache-manifest', + 'markdown': 'text/markdown', + 'mathml': 'application/mathml+xml', + 'md': 'text/markdown', + 'mdx': 'text/mdx', + 'me': 'text/troff', + 'mesh': 'model/mesh', + 'mft': 'application/rpki-manifest', + 'mid': 'audio/midi', + 'midi': 'audio/midi', + 'mj2': 'video/mj2', + 'mjp2': 'video/mj2', + 'mjs': 'application/javascript', + 'mml': 'text/mathml', + 'mov': 'video/quicktime', + 'mp2': 'audio/mpeg', + 'mp21': 'application/mp21', + 'mp2a': 'audio/mpeg', + 'mp3': 'audio/mpeg', + 'mp4': 'video/mp4', + 'mp4a': 'audio/mp4', + 'mp4s': 'application/mp4', + 'mp4v': 'video/mp4', + 'mpe': 'video/mpeg', + 'mpeg': 'video/mpeg', + 'mpg': 'video/mpeg', + 'mpg4': 'video/mp4', + 'mpga': 'audio/mpeg', + 'mrc': 'application/marc', + 'ms': 'text/troff', + 'msh': 'model/mesh', + 'n3': 'text/n3', + 'oga': 'audio/ogg', + 'ogg': 'audio/ogg', + 'ogv': 'video/ogg', + 'ogx': 'application/ogg', + 'otf': 'font/otf', + 'p10': 'application/pkcs10', + 'p7c': 'application/pkcs7-mime', + 'p7m': 'application/pkcs7-mime', + 'p7s': 'application/pkcs7-signature', + 'p8': 'application/pkcs8', + 'pdf': 'application/pdf', + 'pki': 'application/pkixcmp', + 'pkipath': 'application/pkix-pkipath', + 'png': 'image/png', + 'ps': 'application/postscript', + 'pskcxml': 'application/pskc+xml', + 'qt': 'video/quicktime', + 'rmi': 'audio/midi', + 'rng': 'application/xml', + 'roa': 'application/rpki-roa', + 'roff': 'text/troff', + 'rsd': 'application/rsd+xml', + 'rss': 'application/rss+xml', + 'rtf': 'application/rtf', + 'rtx': 'text/richtext', + 's3m': 'audio/s3m', + 'sgi': 'image/sgi', + 'sgm': 'text/sgml', + 'sgml': 'text/sgml', + 'shex': 'text/shex', + 'shtml': 'text/html', + 'sil': 'audio/silk', + 'silo': 'model/mesh', + 'slim': 'text/slim', + 'slm': 'text/slim', + 'snd': 'audio/basic', + 'spx': 'audio/ogg', + 'stl': 'model/stl', + 'styl': 'text/stylus', + 'stylus': 'text/stylus', + 'svg': 'image/svg+xml', + 'svgz': 'image/svg+xml', + 't': 'text/troff', + 't38': 'image/t38', + 'text': 'text/plain', + 'tfx': 'image/tiff-fx', + 'tif': 'image/tiff', + 'tiff': 'image/tiff', + 'tr': 'text/troff', + 'ts': 'video/mp2t', + 'tsv': 'text/tab-separated-values', + 'ttc': 'font/collection', + 'ttf': 'font/ttf', + 'ttl': 'text/turtle', + 'txt': 'text/plain', + 'uri': 'text/uri-list', + 'uris': 'text/uri-list', + 'urls': 'text/uri-list', + 'vcard': 'text/vcard', + 'vrml': 'model/vrml', + 'vtt': 'text/vtt', + 'war': 'application/java-archive', + 'wasm': 'application/wasm', + 'wav': 'audio/wav', + 'weba': 'audio/webm', + 'webm': 'video/webm', + 'webmanifest': 'application/manifest+json', + 'webp': 'image/webp', + 'wmf': 'image/wmf', + 'woff': 'font/woff', + 'woff2': 'font/woff2', + 'wrl': 'model/vrml', + 'x3d': 'model/x3d+xml', + 'x3db': 'model/x3d+fastinfoset', + 'x3dbz': 'model/x3d+binary', + 'x3dv': 'model/x3d-vrml', + 'x3dvz': 'model/x3d+vrml', + 'x3dz': 'model/x3d+xml', + 'xaml': 'application/xaml+xml', + 'xht': 'application/xhtml+xml', + 'xhtml': 'application/xhtml+xml', + 'xm': 'audio/xm', + 'xml': 'text/xml', + 'xsd': 'application/xml', + 'xsl': 'application/xml', + 'xslt': 'application/xslt+xml', + 'yaml': 'text/yaml', + 'yml': 'text/yaml', + 'zip': 'application/zip' +}; diff --git a/src/web.webpack.config.js b/src/web.webpack.config.js index 7144ab8d2c..8963d5eebd 100644 --- a/src/web.webpack.config.js +++ b/src/web.webpack.config.js @@ -46,7 +46,6 @@ module.exports = { 'path': 'dummy', 'debug': 'dummy', 'buffer': 'dummy', - 'mime': 'dummy', 'jpeg-js': 'dummy', 'pngjs': 'dummy', 'http': 'dummy', diff --git a/test/assets/input/fileupload.html b/test/assets/input/fileupload.html index 55fd7c5006..85d2c7ce83 100644 --- a/test/assets/input/fileupload.html +++ b/test/assets/input/fileupload.html @@ -4,6 +4,9 @@ File upload test +
+ +
\ No newline at end of file diff --git a/test/input.spec.js b/test/input.spec.js index d51ed762b4..8a1d58bfe0 100644 --- a/test/input.spec.js +++ b/test/input.spec.js @@ -16,6 +16,8 @@ */ const path = require('path'); +const fs = require('fs'); +const formidable = require('formidable'); const FILE_TO_UPLOAD = path.join(__dirname, '/assets/file-to-upload.txt'); @@ -114,6 +116,41 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI expect(await page.$eval('input', input => input.files.length)).toBe(1); expect(await page.$eval('input', input => input.files[0].name)).toBe('file-to-upload.txt'); }); + it('should detect mime type', async({page, server}) => { + let callback; + const result = new Promise(f => callback = f); + server.setRoute('/upload', async (req, res) => { + const form = new formidable.IncomingForm(); + form.parse(req, function(err, fields, { file1, file2 }) { + expect(file1.name).toBe('file-to-upload.txt'); + expect(file1.type).toBe('text/plain'); + expect( + fs.readFileSync(file1.path).toString() + ).toBe( + fs.readFileSync(path.join(__dirname, '/assets/file-to-upload.txt')).toString() + ); + expect(file2.name).toBe('file-to-upload.png'); + expect(file2.type).toBe('image/png'); + expect( + fs.readFileSync(file2.path).toString() + ).toBe( + fs.readFileSync(path.join(__dirname, '/assets/file-to-upload.png')).toString() + ); + callback(); + }); + }); + await page.goto(server.EMPTY_PAGE); + await page.setContent(` +
+ + + +
`) + await (await page.$('input[name=file1]')).setInputFiles(path.join(__dirname, '/assets/file-to-upload.txt')); + await (await page.$('input[name=file2]')).setInputFiles(path.join(__dirname, '/assets/file-to-upload.png')); + page.click('input[type=submit]'); + await result; + }); it('should be able to read selected file', async({page, server}) => { await page.setContent(``); page.waitForEvent('filechooser').then(({element}) => element.setInputFiles(FILE_TO_UPLOAD)); diff --git a/test/test.js b/test/test.js index 79d586db35..29406e637a 100644 --- a/test/test.js +++ b/test/test.js @@ -29,7 +29,7 @@ require('events').defaultMaxListeners *= parallel; let timeout = process.env.CI ? 30 * 1000 : 10 * 1000; if (!isNaN(process.env.TIMEOUT)) - timeout = parseInt(process.env.TIMEOUT, 10); + timeout = parseInt(process.env.TIMEOUT * 1000, 10); const testRunner = new TestRunner({ timeout, parallel,