diff --git a/packages/playwright-core/src/client/elementHandle.ts b/packages/playwright-core/src/client/elementHandle.ts
index 891410c58a..ae230e074b 100644
--- a/packages/playwright-core/src/client/elementHandle.ts
+++ b/packages/playwright-core/src/client/elementHandle.ts
@@ -266,13 +266,13 @@ export async function convertInputFiles(files: string | FilePayload | string[] |
const items: (string | FilePayload)[] = Array.isArray(files) ? files.slice() : [files];
const sizeLimit = 50 * 1024 * 1024;
- const hasLargeBuffer = items.find(item => typeof item === 'object' && item.buffer && item.buffer.byteLength > sizeLimit);
- if (hasLargeBuffer)
+ const totalBufferSizeExceedsLimit = items.reduce((size, item) => size + ((typeof item === 'object' && item.buffer) ? item.buffer.byteLength : 0), 0) > sizeLimit;
+ if (totalBufferSizeExceedsLimit)
throw new Error('Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead.');
const stats = await Promise.all(items.filter(isString).map(item => fs.promises.stat(item as string)));
- const hasLargeFile = !!stats.find(s => s.size > sizeLimit);
- if (hasLargeFile) {
+ const totalFileSizeExceedsLimit = stats.reduce((acc, stat) => acc + stat.size, 0) > sizeLimit;
+ if (totalFileSizeExceedsLimit) {
if (context._connection.isRemote()) {
const streams: channels.WritableStreamChannel[] = await Promise.all(items.map(async item => {
assert(isString(item));
diff --git a/tests/assets/input/fileupload-multi.html b/tests/assets/input/fileupload-multi.html
new file mode 100644
index 0000000000..05dd5a2237
--- /dev/null
+++ b/tests/assets/input/fileupload-multi.html
@@ -0,0 +1,12 @@
+
+
+
+ File upload test
+
+
+
+
+
diff --git a/tests/page/page-set-input-files.spec.ts b/tests/page/page-set-input-files.spec.ts
index de404868fe..8b5a59d7df 100644
--- a/tests/page/page-set-input-files.spec.ts
+++ b/tests/page/page-set-input-files.spec.ts
@@ -85,6 +85,44 @@ it('should upload large file', async ({ page, server, browserName, isMac, isAndr
await Promise.all([uploadFile, file1.filepath].map(fs.promises.unlink));
});
+it('should upload multiple large files', async ({ page, server, browserName, isMac, isAndroid }, testInfo) => {
+ it.skip(browserName === 'webkit' && isMac && parseInt(os.release(), 10) < 20, 'WebKit for macOS 10.15 is frozen and does not have corresponding protocol features.');
+ it.skip(isAndroid);
+ it.slow();
+ const filesCount = 10;
+ await page.goto(server.PREFIX + '/input/fileupload-multi.html');
+ const uploadFile = testInfo.outputPath('50MB_1.zip');
+ const str = 'A'.repeat(1024);
+ const stream = fs.createWriteStream(uploadFile);
+ // 49 is close to the actual limit
+ for (let i = 0; i < 49 * 1024; i++) {
+ await new Promise((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 uploadFiles = [uploadFile];
+ for (let i = 2; i <= filesCount; i++) {
+ const dstFile = testInfo.outputPath(`50MB_${i}.zip`);
+ fs.copyFileSync(uploadFile, dstFile);
+ uploadFiles.push(dstFile);
+ }
+ const fileChooserPromise = page.waitForEvent('filechooser');
+ await input.click();
+ const fileChooser = await fileChooserPromise;
+ await fileChooser.setFiles(uploadFiles);
+ const filesLen = await page.evaluate('document.getElementsByTagName("input")[0].files.length');
+ expect(fileChooser.isMultiple()).toBe(true);
+ expect(filesLen).toEqual(filesCount);
+ await Promise.all(uploadFiles.map(path => fs.promises.unlink(path)));
+});
+
it('should upload large file with relative path', async ({ page, server, browserName, isMac, isAndroid }, testInfo) => {
it.skip(browserName === 'webkit' && isMac && parseInt(os.release(), 10) < 20, 'WebKit for macOS 10.15 is frozen and does not have corresponding protocol features.');
it.skip(isAndroid);