2020-08-05 02:32:10 +03:00
/ * *
* Copyright 2017 Google Inc . All rights reserved .
* Modifications copyright ( c ) Microsoft Corporation .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
2020-08-20 07:32:12 +03:00
2021-05-06 17:08:22 +03:00
import { test as it , expect } from './pageTest' ;
2021-08-27 19:28:24 +03:00
import { attachFrame } from '../config/utils' ;
2020-08-05 02:32:10 +03:00
2020-08-12 01:50:53 +03:00
import path from 'path' ;
import fs from 'fs' ;
2022-03-25 23:26:12 +03:00
import os from 'os' ;
2020-08-12 01:50:53 +03:00
import formidable from 'formidable' ;
2020-08-05 02:32:10 +03:00
2021-09-27 19:58:08 +03:00
it ( 'should upload the file' , async ( { page , server , asset } ) = > {
2020-08-05 02:32:10 +03:00
await page . goto ( server . PREFIX + '/input/fileupload.html' ) ;
2021-04-06 01:51:45 +03:00
const filePath = path . relative ( process . cwd ( ) , asset ( 'file-to-upload.txt' ) ) ;
2020-08-05 02:32:10 +03:00
const input = await page . $ ( 'input' ) ;
await input . setInputFiles ( filePath ) ;
expect ( await page . evaluate ( e = > e . files [ 0 ] . name , input ) ) . toBe ( 'file-to-upload.txt' ) ;
expect ( await page . evaluate ( e = > {
const reader = new FileReader ( ) ;
const promise = new Promise ( fulfill = > reader . onload = fulfill ) ;
reader . readAsText ( e . files [ 0 ] ) ;
return promise . then ( ( ) = > reader . result ) ;
} , input ) ) . toBe ( 'contents of the file' ) ;
} ) ;
2022-05-19 18:24:20 +03:00
it ( 'should upload large file' , async ( { page , server , browserName , isMac , isAndroid } , testInfo ) = > {
2022-03-25 23:26:12 +03:00
it . skip ( browserName === 'webkit' && isMac && parseInt ( os . release ( ) , 10 ) < 20 , 'WebKit for macOS 10.15 is frozen and does not have corresponding protocol features.' ) ;
2022-05-19 18:24:20 +03:00
it . skip ( isAndroid ) ;
2022-03-18 19:00:52 +03:00
it . slow ( ) ;
await page . goto ( server . PREFIX + '/input/fileupload.html' ) ;
const uploadFile = testInfo . outputPath ( '200MB.zip' ) ;
const str = 'A' . repeat ( 4 * 1024 ) ;
const stream = fs . createWriteStream ( uploadFile ) ;
for ( let i = 0 ; i < 50 * 1024 ; i ++ ) {
await new Promise < void > ( ( fulfill , reject ) = > {
stream . write ( str , err = > {
if ( err )
reject ( err ) ;
else
fulfill ( ) ;
} ) ;
} ) ;
}
await new Promise ( f = > stream . end ( f ) ) ;
const input = page . locator ( 'input[type="file"]' ) ;
const events = await input . evaluateHandle ( e = > {
const events = [ ] ;
e . addEventListener ( 'input' , ( ) = > events . push ( 'input' ) ) ;
e . addEventListener ( 'change' , ( ) = > events . push ( 'change' ) ) ;
return events ;
} ) ;
await input . setInputFiles ( uploadFile ) ;
expect ( await input . evaluate ( e = > ( e as HTMLInputElement ) . files [ 0 ] . name ) ) . toBe ( '200MB.zip' ) ;
expect ( await events . evaluate ( e = > e ) ) . toEqual ( [ 'input' , 'change' ] ) ;
2022-04-14 21:19:36 +03:00
const serverFilePromise = new Promise < formidable.File > ( fulfill = > {
server . setRoute ( '/upload' , async ( req , res ) = > {
const form = new formidable . IncomingForm ( { uploadDir : testInfo.outputPath ( ) } ) ;
form . parse ( req , function ( err , fields , f ) {
res . end ( ) ;
const files = f as Record < string , formidable.File > ;
fulfill ( files . file1 ) ;
} ) ;
} ) ;
} ) ;
const [ file1 ] = await Promise . all ( [
serverFilePromise ,
page . click ( 'input[type=submit]' )
] ) ;
expect ( file1 . originalFilename ) . toBe ( '200MB.zip' ) ;
expect ( file1 . size ) . toBe ( 200 * 1024 * 1024 ) ;
await Promise . all ( [ uploadFile , file1 . filepath ] . map ( fs . promises . unlink ) ) ;
} ) ;
2022-05-19 18:24:20 +03:00
it ( 'should upload large file with relative path' , async ( { page , server , browserName , isMac , isAndroid } , testInfo ) = > {
2022-04-14 21:19:36 +03:00
it . skip ( browserName === 'webkit' && isMac && parseInt ( os . release ( ) , 10 ) < 20 , 'WebKit for macOS 10.15 is frozen and does not have corresponding protocol features.' ) ;
2022-05-19 18:24:20 +03:00
it . skip ( isAndroid ) ;
2022-04-14 21:19:36 +03:00
it . slow ( ) ;
await page . goto ( server . PREFIX + '/input/fileupload.html' ) ;
const uploadFile = testInfo . outputPath ( '200MB.zip' ) ;
const str = 'A' . repeat ( 4 * 1024 ) ;
const stream = fs . createWriteStream ( uploadFile ) ;
for ( let i = 0 ; i < 50 * 1024 ; i ++ ) {
await new Promise < void > ( ( fulfill , reject ) = > {
stream . write ( str , err = > {
if ( err )
reject ( err ) ;
else
fulfill ( ) ;
} ) ;
} ) ;
}
await new Promise ( f = > stream . end ( f ) ) ;
const input = page . locator ( 'input[type="file"]' ) ;
const events = await input . evaluateHandle ( e = > {
const events = [ ] ;
e . addEventListener ( 'input' , ( ) = > events . push ( 'input' ) ) ;
e . addEventListener ( 'change' , ( ) = > events . push ( 'change' ) ) ;
return events ;
} ) ;
const relativeUploadPath = path . relative ( process . cwd ( ) , uploadFile ) ;
expect ( path . isAbsolute ( relativeUploadPath ) ) . toBeFalsy ( ) ;
await input . setInputFiles ( relativeUploadPath ) ;
expect ( await input . evaluate ( e = > ( e as HTMLInputElement ) . files [ 0 ] . name ) ) . toBe ( '200MB.zip' ) ;
expect ( await events . evaluate ( e = > e ) ) . toEqual ( [ 'input' , 'change' ] ) ;
2022-03-18 19:00:52 +03:00
const serverFilePromise = new Promise < formidable.File > ( fulfill = > {
server . setRoute ( '/upload' , async ( req , res ) = > {
const form = new formidable . IncomingForm ( { uploadDir : testInfo.outputPath ( ) } ) ;
form . parse ( req , function ( err , fields , f ) {
res . end ( ) ;
const files = f as Record < string , formidable.File > ;
fulfill ( files . file1 ) ;
} ) ;
} ) ;
} ) ;
const [ file1 ] = await Promise . all ( [
serverFilePromise ,
page . click ( 'input[type=submit]' )
] ) ;
expect ( file1 . originalFilename ) . toBe ( '200MB.zip' ) ;
expect ( file1 . size ) . toBe ( 200 * 1024 * 1024 ) ;
await Promise . all ( [ uploadFile , file1 . filepath ] . map ( fs . promises . unlink ) ) ;
} ) ;
2022-03-10 21:42:52 +03:00
it ( 'should work @smoke' , async ( { page , asset } ) = > {
2020-08-05 02:32:10 +03:00
await page . setContent ( ` <input type=file> ` ) ;
2021-04-06 01:51:45 +03:00
await page . setInputFiles ( 'input' , asset ( 'file-to-upload.txt' ) ) ;
2020-08-05 02:32:10 +03:00
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' ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should work with label' , async ( { page , asset } ) = > {
2021-06-29 00:18:01 +03:00
await page . setContent ( ` <label for=target>Choose a file</label><input id=target type=file> ` ) ;
await page . setInputFiles ( 'text=Choose a file' , asset ( 'file-to-upload.txt' ) ) ;
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' ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should set from memory' , async ( { page } ) = > {
2020-08-05 02:32:10 +03:00
await page . setContent ( ` <input type=file> ` ) ;
await page . setInputFiles ( 'input' , {
name : 'test.txt' ,
mimeType : 'text/plain' ,
buffer : Buffer.from ( 'this is a test' )
} ) ;
expect ( await page . $eval ( 'input' , input = > input . files . length ) ) . toBe ( 1 ) ;
expect ( await page . $eval ( 'input' , input = > input . files [ 0 ] . name ) ) . toBe ( 'test.txt' ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should emit event once' , async ( { page , server } ) = > {
2020-08-05 02:32:10 +03:00
await page . setContent ( ` <input type=file> ` ) ;
const [ chooser ] = await Promise . all ( [
new Promise ( f = > page . once ( 'filechooser' , f ) ) ,
page . click ( 'input' ) ,
] ) ;
expect ( chooser ) . toBeTruthy ( ) ;
2021-08-27 19:28:24 +03:00
} ) ;
2022-06-16 02:15:45 +03:00
it ( 'should emit event via prepend' , async ( { page , server } ) = > {
await page . setContent ( ` <input type=file> ` ) ;
const [ chooser ] = await Promise . all ( [
new Promise ( f = > page . prependListener ( 'filechooser' , f ) ) ,
page . click ( 'input' ) ,
] ) ;
expect ( chooser ) . toBeTruthy ( ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should emit event for iframe' , async ( { page , server , browserName } ) = > {
2021-08-27 19:28:24 +03:00
it . skip ( browserName === 'firefox' ) ;
const frame = await attachFrame ( page , 'frame1' , server . EMPTY_PAGE ) ;
await frame . setContent ( ` <input type=file> ` ) ;
const [ chooser ] = await Promise . all ( [
new Promise ( f = > page . once ( 'filechooser' , f ) ) ,
frame . click ( 'input' ) ,
] ) ;
expect ( chooser ) . toBeTruthy ( ) ;
2020-08-05 02:32:10 +03:00
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should emit event on/off' , async ( { page , server } ) = > {
2020-08-15 04:24:36 +03:00
await page . setContent ( ` <input type=file> ` ) ;
const [ chooser ] = await Promise . all ( [
new Promise ( f = > {
const listener = chooser = > {
page . off ( 'filechooser' , listener ) ;
f ( chooser ) ;
2020-08-28 14:20:29 +03:00
} ;
2020-08-15 04:24:36 +03:00
page . on ( 'filechooser' , listener ) ;
} ) ,
page . click ( 'input' ) ,
] ) ;
expect ( chooser ) . toBeTruthy ( ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should emit event addListener/removeListener' , async ( { page , server } ) = > {
2020-08-15 04:24:36 +03:00
await page . setContent ( ` <input type=file> ` ) ;
const [ chooser ] = await Promise . all ( [
new Promise ( f = > {
const listener = chooser = > {
page . removeListener ( 'filechooser' , listener ) ;
f ( chooser ) ;
2020-08-28 14:20:29 +03:00
} ;
2020-08-15 04:24:36 +03:00
page . addListener ( 'filechooser' , listener ) ;
} ) ,
page . click ( 'input' ) ,
] ) ;
expect ( chooser ) . toBeTruthy ( ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should work when file input is attached to DOM' , async ( { page , server } ) = > {
2020-08-05 02:32:10 +03:00
await page . setContent ( ` <input type=file> ` ) ;
const [ chooser ] = await Promise . all ( [
page . waitForEvent ( 'filechooser' ) ,
page . click ( 'input' ) ,
] ) ;
expect ( chooser ) . toBeTruthy ( ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should work when file input is not attached to DOM' , async ( { page , asset } ) = > {
2021-02-16 21:22:46 +03:00
const [ , content ] = await Promise . all ( [
2021-04-06 01:51:45 +03:00
page . waitForEvent ( 'filechooser' ) . then ( chooser = > chooser . setFiles ( asset ( 'file-to-upload.txt' ) ) ) ,
2021-02-16 21:22:46 +03:00
page . evaluate ( async ( ) = > {
2020-08-05 02:32:10 +03:00
const el = document . createElement ( 'input' ) ;
el . type = 'file' ;
el . click ( ) ;
2021-02-16 21:22:46 +03:00
await new Promise ( x = > el . oninput = x ) ;
const reader = new FileReader ( ) ;
const promise = new Promise ( fulfill = > reader . onload = fulfill ) ;
reader . readAsText ( el . files [ 0 ] ) ;
return promise . then ( ( ) = > reader . result ) ;
2020-08-05 02:32:10 +03:00
} ) ,
] ) ;
2021-02-16 21:22:46 +03:00
expect ( content ) . toBe ( 'contents of the file' ) ;
2020-08-05 02:32:10 +03:00
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should not throw when filechooser belongs to iframe' , async ( { page , server , browserName } ) = > {
2021-02-05 22:30:44 +03:00
await page . goto ( server . PREFIX + '/frames/one-frame.html' ) ;
const frame = page . mainFrame ( ) . childFrames ( ) [ 0 ] ;
await frame . setContent ( `
< div > Click me < / div >
< script >
document . querySelector ( 'div' ) . addEventListener ( 'click' , ( ) = > {
const input = document . createElement ( 'input' ) ;
input . type = 'file' ;
input . click ( ) ;
window . parent . __done = true ;
} ) ;
< / script >
` );
await Promise . all ( [
page . waitForEvent ( 'filechooser' ) ,
frame . click ( 'div' )
] ) ;
await page . waitForFunction ( ( ) = > ( window as any ) . __done ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should not throw when frame is detached immediately' , async ( { page , server } ) = > {
2021-02-05 22:30:44 +03:00
await page . goto ( server . PREFIX + '/frames/one-frame.html' ) ;
const frame = page . mainFrame ( ) . childFrames ( ) [ 0 ] ;
await frame . setContent ( `
< div > Click me < / div >
< script >
document . querySelector ( 'div' ) . addEventListener ( 'click' , ( ) = > {
const input = document . createElement ( 'input' ) ;
input . type = 'file' ;
input . click ( ) ;
window . parent . __done = true ;
const iframe = window . parent . document . querySelector ( 'iframe' ) ;
iframe . remove ( ) ;
} ) ;
< / script >
` );
page . on ( 'filechooser' , ( ) = > { } ) ; // To ensure we handle file choosers.
await frame . click ( 'div' ) ;
await page . waitForFunction ( ( ) = > ( window as any ) . __done ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should work with CSP' , async ( { page , server , asset } ) = > {
2020-09-03 20:09:03 +03:00
server . setCSP ( '/empty.html' , 'default-src "none"' ) ;
await page . goto ( server . EMPTY_PAGE ) ;
await page . setContent ( ` <input type=file> ` ) ;
2021-04-06 01:51:45 +03:00
await page . setInputFiles ( 'input' , asset ( 'file-to-upload.txt' ) ) ;
2020-09-03 20:09:03 +03:00
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' ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should respect timeout' , async ( { page , playwright } ) = > {
2020-08-05 02:32:10 +03:00
let error = null ;
2021-09-27 19:58:08 +03:00
await page . waitForEvent ( 'filechooser' , { timeout : 1 } ) . catch ( e = > error = e ) ;
2020-08-05 02:32:10 +03:00
expect ( error ) . toBeInstanceOf ( playwright . errors . TimeoutError ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should respect default timeout when there is no custom timeout' , async ( { page , playwright } ) = > {
2020-08-05 02:32:10 +03:00
page . setDefaultTimeout ( 1 ) ;
let error = null ;
await page . waitForEvent ( 'filechooser' ) . catch ( e = > error = e ) ;
expect ( error ) . toBeInstanceOf ( playwright . errors . TimeoutError ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should prioritize exact timeout over default timeout' , async ( { page , playwright } ) = > {
2020-08-05 02:32:10 +03:00
page . setDefaultTimeout ( 0 ) ;
let error = null ;
2021-09-27 19:58:08 +03:00
await page . waitForEvent ( 'filechooser' , { timeout : 1 } ) . catch ( e = > error = e ) ;
2020-08-05 02:32:10 +03:00
expect ( error ) . toBeInstanceOf ( playwright . errors . TimeoutError ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should work with no timeout' , async ( { page , server } ) = > {
2020-08-05 02:32:10 +03:00
const [ chooser ] = await Promise . all ( [
2021-09-27 19:58:08 +03:00
page . waitForEvent ( 'filechooser' , { timeout : 0 } ) ,
2020-08-05 02:32:10 +03:00
page . evaluate ( ( ) = > setTimeout ( ( ) = > {
const el = document . createElement ( 'input' ) ;
el . type = 'file' ;
el . click ( ) ;
} , 50 ) )
] ) ;
expect ( chooser ) . toBeTruthy ( ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should return the same file chooser when there are many watchdogs simultaneously' , async ( { page , server } ) = > {
2020-08-05 02:32:10 +03:00
await page . setContent ( ` <input type=file> ` ) ;
const [ fileChooser1 , fileChooser2 ] = await Promise . all ( [
page . waitForEvent ( 'filechooser' ) ,
page . waitForEvent ( 'filechooser' ) ,
page . $eval ( 'input' , input = > input . click ( ) ) ,
] ) ;
expect ( fileChooser1 === fileChooser2 ) . toBe ( true ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should accept single file' , async ( { page , asset } ) = > {
2020-08-05 02:32:10 +03:00
await page . setContent ( ` <input type=file oninput='javascript:console.timeStamp()'> ` ) ;
const [ fileChooser ] = await Promise . all ( [
page . waitForEvent ( 'filechooser' ) ,
page . click ( 'input' ) ,
] ) ;
expect ( fileChooser . page ( ) ) . toBe ( page ) ;
expect ( fileChooser . element ( ) ) . toBeTruthy ( ) ;
2021-04-06 01:51:45 +03:00
await fileChooser . setFiles ( asset ( 'file-to-upload.txt' ) ) ;
2020-08-05 02:32:10 +03:00
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' ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should detect mime type' , async ( { page , server , asset , isAndroid } ) = > {
2021-04-09 17:59:09 +03:00
it . fixme ( isAndroid ) ;
2021-04-05 05:32:14 +03:00
2022-02-01 19:12:11 +03:00
let files : Record < string , formidable.File > ;
2020-08-05 02:32:10 +03:00
server . setRoute ( '/upload' , async ( req , res ) = > {
const form = new formidable . IncomingForm ( ) ;
form . parse ( req , function ( err , fields , f ) {
2022-02-01 19:12:11 +03:00
files = f as Record < string , formidable.File > ;
2020-08-05 02:32:10 +03:00
res . end ( ) ;
} ) ;
} ) ;
await page . goto ( server . EMPTY_PAGE ) ;
await page . setContent ( `
< form action = "/upload" method = "post" enctype = "multipart/form-data" >
< input type = "file" name = "file1" >
< input type = "file" name = "file2" >
< input type = "submit" value = "Submit" >
2020-08-28 14:20:29 +03:00
< / form > ` );
2021-04-06 01:51:45 +03:00
await ( await page . $ ( 'input[name=file1]' ) ) . setInputFiles ( asset ( 'file-to-upload.txt' ) ) ;
await ( await page . $ ( 'input[name=file2]' ) ) . setInputFiles ( asset ( 'pptr.png' ) ) ;
2020-08-05 02:32:10 +03:00
await Promise . all ( [
page . click ( 'input[type=submit]' ) ,
server . waitForRequest ( '/upload' ) ,
] ) ;
const { file1 , file2 } = files ;
2022-02-01 19:12:11 +03:00
expect ( file1 . originalFilename ) . toBe ( 'file-to-upload.txt' ) ;
expect ( file1 . mimetype ) . toBe ( 'text/plain' ) ;
expect ( fs . readFileSync ( file1 . filepath ) . toString ( ) ) . toBe (
2021-04-06 01:51:45 +03:00
fs . readFileSync ( asset ( 'file-to-upload.txt' ) ) . toString ( ) ) ;
2022-02-01 19:12:11 +03:00
expect ( file2 . originalFilename ) . toBe ( 'pptr.png' ) ;
expect ( file2 . mimetype ) . toBe ( 'image/png' ) ;
expect ( fs . readFileSync ( file2 . filepath ) . toString ( ) ) . toBe (
2021-04-06 01:51:45 +03:00
fs . readFileSync ( asset ( 'pptr.png' ) ) . toString ( ) ) ;
2020-08-05 02:32:10 +03:00
} ) ;
2021-05-13 23:42:25 +03:00
// @see https://github.com/microsoft/playwright/issues/4704
2021-09-27 19:58:08 +03:00
it ( 'should not trim big uploaded files' , async ( { page , server , asset , isAndroid } ) = > {
2021-05-13 23:42:25 +03:00
it . fixme ( isAndroid ) ;
2022-02-01 19:12:11 +03:00
let files : Record < string , formidable.File > ;
2021-05-13 23:42:25 +03:00
server . setRoute ( '/upload' , async ( req , res ) = > {
const form = new formidable . IncomingForm ( ) ;
form . parse ( req , function ( err , fields , f ) {
2022-02-01 19:12:11 +03:00
files = f as Record < string , formidable.File > ;
2021-05-13 23:42:25 +03:00
res . end ( ) ;
} ) ;
} ) ;
await page . goto ( server . EMPTY_PAGE ) ;
const DATA_SIZE = Math . pow ( 2 , 20 ) ;
await Promise . all ( [
page . evaluate ( async size = > {
const body = new FormData ( ) ;
body . set ( 'file' , new Blob ( [ new Uint8Array ( size ) ] ) ) ;
await fetch ( '/upload' , { method : 'POST' , body } ) ;
} , DATA_SIZE ) ,
server . waitForRequest ( '/upload' ) ,
] ) ;
expect ( files . file . size ) . toBe ( DATA_SIZE ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should be able to read selected file' , async ( { page , asset } ) = > {
2020-08-05 02:32:10 +03:00
await page . setContent ( ` <input type=file> ` ) ;
const [ , content ] = await Promise . all ( [
2021-04-06 01:51:45 +03:00
page . waitForEvent ( 'filechooser' ) . then ( fileChooser = > fileChooser . setFiles ( asset ( 'file-to-upload.txt' ) ) ) ,
2020-08-05 02:32:10 +03:00
page . $eval ( 'input' , async picker = > {
picker . click ( ) ;
await new Promise ( x = > picker . oninput = x ) ;
const reader = new FileReader ( ) ;
const promise = new Promise ( fulfill = > reader . onload = fulfill ) ;
reader . readAsText ( picker . files [ 0 ] ) ;
return promise . then ( ( ) = > reader . result ) ;
} ) ,
] ) ;
expect ( content ) . toBe ( 'contents of the file' ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should be able to reset selected files with empty file list' , async ( { page , asset } ) = > {
2020-08-05 02:32:10 +03:00
await page . setContent ( ` <input type=file> ` ) ;
const [ , fileLength1 ] = await Promise . all ( [
2021-04-06 01:51:45 +03:00
page . waitForEvent ( 'filechooser' ) . then ( fileChooser = > fileChooser . setFiles ( asset ( 'file-to-upload.txt' ) ) ) ,
2020-08-05 02:32:10 +03:00
page . $eval ( 'input' , async picker = > {
picker . click ( ) ;
await new Promise ( x = > picker . oninput = x ) ;
return picker . files . length ;
} ) ,
] ) ;
expect ( fileLength1 ) . toBe ( 1 ) ;
const [ , fileLength2 ] = await Promise . all ( [
page . waitForEvent ( 'filechooser' ) . then ( fileChooser = > fileChooser . setFiles ( [ ] ) ) ,
page . $eval ( 'input' , async picker = > {
picker . click ( ) ;
await new Promise ( x = > picker . oninput = x ) ;
return picker . files . length ;
} ) ,
] ) ;
expect ( fileLength2 ) . toBe ( 0 ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should not accept multiple files for single-file input' , async ( { page , asset } ) = > {
2020-08-05 02:32:10 +03:00
await page . setContent ( ` <input type=file> ` ) ;
const [ fileChooser ] = await Promise . all ( [
page . waitForEvent ( 'filechooser' ) ,
page . click ( 'input' ) ,
] ) ;
let error = null ;
await fileChooser . setFiles ( [
2021-04-06 01:51:45 +03:00
asset ( 'file-to-upload.txt' ) ,
asset ( 'pptr.png' )
2020-08-05 02:32:10 +03:00
] ) . catch ( e = > error = e ) ;
expect ( error ) . not . toBe ( null ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should emit input and change events' , async ( { page , asset } ) = > {
2020-08-05 02:32:10 +03:00
const events = [ ] ;
await page . exposeFunction ( 'eventHandled' , e = > events . push ( e ) ) ;
await page . setContent ( `
< input id = input type = file > < / input >
< script >
input . addEventListener ( 'input' , e = > eventHandled ( { type : e . type } ) ) ;
input . addEventListener ( 'change' , e = > eventHandled ( { type : e . type } ) ) ;
< / script > ` );
2021-04-06 01:51:45 +03:00
await ( await page . $ ( 'input' ) ) . setInputFiles ( asset ( 'file-to-upload.txt' ) ) ;
2020-08-05 02:32:10 +03:00
expect ( events . length ) . toBe ( 2 ) ;
expect ( events [ 0 ] . type ) . toBe ( 'input' ) ;
expect ( events [ 1 ] . type ) . toBe ( 'change' ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should work for single file pick' , async ( { page , server } ) = > {
2020-08-05 02:32:10 +03:00
await page . setContent ( ` <input type=file> ` ) ;
const [ fileChooser ] = await Promise . all ( [
page . waitForEvent ( 'filechooser' ) ,
page . click ( 'input' ) ,
] ) ;
expect ( fileChooser . isMultiple ( ) ) . toBe ( false ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should work for "multiple"' , async ( { page , server } ) = > {
2020-08-05 02:32:10 +03:00
await page . setContent ( ` <input multiple type=file> ` ) ;
const [ fileChooser ] = await Promise . all ( [
page . waitForEvent ( 'filechooser' ) ,
page . click ( 'input' ) ,
] ) ;
expect ( fileChooser . isMultiple ( ) ) . toBe ( true ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should work for "webkitdirectory"' , async ( { page , server } ) = > {
2020-08-05 02:32:10 +03:00
await page . setContent ( ` <input multiple webkitdirectory type=file> ` ) ;
const [ fileChooser ] = await Promise . all ( [
page . waitForEvent ( 'filechooser' ) ,
page . click ( 'input' ) ,
] ) ;
expect ( fileChooser . isMultiple ( ) ) . toBe ( true ) ;
} ) ;
2022-01-13 22:24:21 +03:00
2022-01-26 23:39:59 +03:00
it ( 'should emit event after navigation' , async ( { page , server , browserName , browserMajorVersion } ) = > {
2022-01-13 22:24:21 +03:00
it . info ( ) . annotations . push ( { type : 'issue' , description : 'https://github.com/microsoft/playwright/issues/11375' } ) ;
2022-01-26 23:39:59 +03:00
it . skip ( browserName === 'chromium' && browserMajorVersion < 99 ) ;
2022-01-13 22:24:21 +03:00
const logs = [ ] ;
page . on ( 'filechooser' , ( ) = > logs . push ( 'filechooser' ) ) ;
await page . goto ( server . PREFIX + '/empty.html' ) ;
await page . setContent ( ` <input type=file> ` ) ;
await Promise . all ( [
page . waitForEvent ( 'filechooser' ) ,
page . click ( 'input' ) ,
] ) ;
await page . goto ( server . CROSS_PROCESS_PREFIX + '/empty.html' ) ;
await page . setContent ( ` <input type=file> ` ) ;
await Promise . all ( [
page . waitForEvent ( 'filechooser' ) ,
page . click ( 'input' ) ,
] ) ;
expect ( logs ) . toEqual ( [ 'filechooser' , 'filechooser' ] ) ;
} ) ;
2022-05-27 23:04:58 +03:00
2022-07-28 16:41:32 +03:00
it ( 'should trigger listener added before navigation' , async ( { page , server , browserMajorVersion , isElectron } ) = > {
it . skip ( isElectron && browserMajorVersion <= 98 ) ;
2022-05-27 23:04:58 +03:00
// Add listener before cross process navigation.
const chooserPromise = new Promise ( f = > page . once ( 'filechooser' , f ) ) ;
await page . goto ( server . PREFIX + '/empty.html' ) ;
await page . goto ( server . CROSS_PROCESS_PREFIX + '/empty.html' ) ;
await page . setContent ( ` <input type=file> ` ) ;
const [ chooser ] = await Promise . all ( [
chooserPromise ,
page . click ( 'input' ) ,
] ) ;
expect ( chooser ) . toBeTruthy ( ) ;
} ) ;