Skip to content

Commit

Permalink
feedback 1
Browse files Browse the repository at this point in the history
  • Loading branch information
mxschmitt committed Jun 6, 2024
1 parent 5f0cfdf commit 091b84f
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 25 deletions.
2 changes: 1 addition & 1 deletion docs/src/api/class-elementhandle.md
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ When all steps combined have not finished during the specified [`option: timeout

Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then they
are resolved relative to the current working directory. For empty array, clears the selected files.
In order to upload a directory (`[webkitdirectory]`) pass the path to the directory.
For inputs with a `[webkitdirectory]` attribute, only a single directory path is supported.

This method expects [ElementHandle] to point to an
[input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside the `<label>` element that has an associated [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/api/class-locator.md
Original file line number Diff line number Diff line change
Expand Up @@ -2164,7 +2164,7 @@ When all steps combined have not finished during the specified [`option: timeout
* since: v1.14

Upload file or multiple files into `<input type=file>`.
In order to upload a directory (`[webkitdirectory]`) pass the path to the directory.
For inputs with a `[webkitdirectory]` attribute, only a single directory path is supported.

**Usage**

Expand Down
2 changes: 1 addition & 1 deletion docs/src/api/class-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -3927,7 +3927,7 @@ An object containing additional HTTP headers to be sent with every request. All

Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then they
are resolved relative to the current working directory. For empty array, clears the selected files.
In order to upload a directory (`[webkitdirectory]`) pass the path to the directory.
For inputs with a `[webkitdirectory]` attribute, only a single directory path is supported.

This method expects [`param: selector`] to point to an
[input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside the `<label>` element that has an associated [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead.
Expand Down
6 changes: 3 additions & 3 deletions packages/playwright-core/src/client/elementHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,9 @@ export async function convertInputFiles(files: string | FilePayload | string[] |
if (items.some(item => typeof item === 'string')) {
if (!items.every(item => typeof item === 'string'))
throw new Error('File paths cannot be mixed with buffers');
const directoryCount = (await Promise.all(items.map(async item => (await fs.promises.stat(item as string)).isDirectory()))).filter(Boolean).length;
if (directoryCount > 1)
throw new Error('Only one directory can be uploaded at a time');
const itemFileType = (await Promise.all(items.map(async item => (await fs.promises.stat(item as string)).isDirectory() ? 'directory' : 'file')));
if (new Set(itemFileType).size > 1 || itemFileType.filter(type => type === 'directory').length > 1)
throw new Error('File paths must be all files or a single directory');

if (context._connection.isRemote()) {
let streams: channels.WritableStreamChannel[] | undefined;
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright-core/src/server/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -648,8 +648,8 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
fs.promises.access(localPath, fs.constants.F_OK)
)));
const isDirectoryUpload = localPaths.length === 1 ? (await fs.promises.stat(localPaths[0])).isDirectory() : false;
const waitForChangeEvent = isDirectoryUpload ? this.evaluateInUtility(([_, node]) => new Promise<any>(fulfil => {
node.addEventListener('change', fulfil, { once: true });
const waitForChangeEvent = isDirectoryUpload ? this.evaluateInUtility(([_, node]) => new Promise<any>(fulfill => {
node.addEventListener('change', fulfill, { once: true });
}), undefined) : Promise.resolve();
await this._page._delegate.setInputFilePaths(retargeted, localPaths);
await waitForChangeEvent;
Expand Down
12 changes: 6 additions & 6 deletions packages/playwright-core/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4055,8 +4055,8 @@ export interface Page {
* instead. Read more about [locators](https://playwright.dev/docs/locators).
*
* Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then
* they are resolved relative to the current working directory. For empty array, clears the selected files. In order
* to upload a directory (`[webkitdirectory]`) pass the path to the directory.
* they are resolved relative to the current working directory. For empty array, clears the selected files. For inputs
* with a `[webkitdirectory]` attribute, only a single directory path is supported.
*
* This method expects `selector` to point to an
* [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside
Expand Down Expand Up @@ -10580,8 +10580,8 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
* instead. Read more about [locators](https://playwright.dev/docs/locators).
*
* Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then
* they are resolved relative to the current working directory. For empty array, clears the selected files. In order
* to upload a directory (`[webkitdirectory]`) pass the path to the directory.
* they are resolved relative to the current working directory. For empty array, clears the selected files. For inputs
* with a `[webkitdirectory]` attribute, only a single directory path is supported.
*
* This method expects {@link ElementHandle} to point to an
* [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside
Expand Down Expand Up @@ -12788,8 +12788,8 @@ export interface Locator {
}): Promise<void>;

/**
* Upload file or multiple files into `<input type=file>`. In order to upload a directory (`[webkitdirectory]`) pass
* the path to the directory.
* Upload file or multiple files into `<input type=file>`. For inputs with a `[webkitdirectory]` attribute, only a
* single directory path is supported.
*
* **Usage**
*
Expand Down
35 changes: 24 additions & 11 deletions tests/page/page-set-input-files.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,17 @@ it('should upload the file', async ({ page, server, asset }) => {
}, input)).toBe('contents of the file');
});

async function createTestDirectoryStructure() {
const baseDir = path.join(it.info().outputDir, 'file-upload-test');
await fs.promises.mkdir(baseDir, { recursive: true });
await fs.promises.writeFile(path.join(baseDir, 'file1.txt'), 'file1 content');
await fs.promises.writeFile(path.join(baseDir, 'file2'), 'file2 content');
await fs.promises.mkdir(path.join(baseDir, 'sub-dir'));
await fs.promises.writeFile(path.join(baseDir, 'sub-dir', 'really.txt'), 'sub-dir file content');
return baseDir;
}

it('should upload a folder', async ({ page, server, browserName, headless, browserMajorVersion }) => {
await page.goto(server.PREFIX + '/input/folderupload.html');
const input = await page.$('input');
const dir = await createTestDirectoryStructure();
const dir = path.join(it.info().outputDir, 'file-upload-test');
{
await fs.promises.mkdir(dir, { recursive: true });
await fs.promises.writeFile(path.join(dir, 'file1.txt'), 'file1 content');
await fs.promises.writeFile(path.join(dir, 'file2'), 'file2 content');
await fs.promises.mkdir(path.join(dir, 'sub-dir'));
await fs.promises.writeFile(path.join(dir, 'sub-dir', 'really.txt'), 'sub-dir file content');
}
await input.setInputFiles(dir);
expect(new Set(await page.evaluate(e => [...e.files].map(f => f.webkitRelativePath), input))).toEqual(new Set([
// https://issues.chromium.org/issues/345393164
Expand All @@ -70,6 +67,22 @@ it('should upload a folder', async ({ page, server, browserName, headless, brows
}
});

it('should upload a folder and throw for multiple directories', async ({ page, server, browserName, headless, browserMajorVersion }) => {
await page.goto(server.PREFIX + '/input/folderupload.html');
const input = await page.$('input');
const dir = path.join(it.info().outputDir, 'file-upload-test');
{
await fs.promises.mkdir(path.join(dir, 'folder1'), { recursive: true });
await fs.promises.writeFile(path.join(dir, 'folder1', 'file1.txt'), 'file1 content');
await fs.promises.mkdir(path.join(dir, 'folder2'), { recursive: true });
await fs.promises.writeFile(path.join(dir, 'folder2', 'file2.txt'), 'file2 content');
}
await expect(input.setInputFiles([
path.join(dir, 'folder1'),
path.join(dir, 'folder2'),
])).rejects.toThrow('File paths must be all files or a single directory');
});

it('should upload a file after popup', async ({ page, server, asset }) => {
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/29923' });
await page.goto(server.PREFIX + '/input/fileupload.html');
Expand Down

0 comments on commit 091b84f

Please sign in to comment.