feat(downloads): expose suggested filename (#2062)
This commit is contained in:
Родитель
84f966c301
Коммит
f63ea3ffd2
|
@ -3050,6 +3050,7 @@ const path = await download.path();
|
|||
- [download.delete()](#downloaddelete)
|
||||
- [download.failure()](#downloadfailure)
|
||||
- [download.path()](#downloadpath)
|
||||
- [download.suggestedFilename()](#downloadsuggestedfilename)
|
||||
- [download.url()](#downloadurl)
|
||||
<!-- GEN:stop -->
|
||||
|
||||
|
@ -3073,6 +3074,11 @@ Returns download error if any.
|
|||
|
||||
Returns path to the downloaded file in case of successful download.
|
||||
|
||||
#### download.suggestedFilename()
|
||||
- returns: <[string]>
|
||||
|
||||
Returns suggested filename for this download. It is typically computed by the browser from the [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) response header or the `download` attribute. See the spec on [whatwg](https://html.spec.whatwg.org/#downloading-resources). Different browsers can use different logic for computing it.
|
||||
|
||||
#### download.url()
|
||||
- returns: <[string]>
|
||||
|
||||
|
|
|
@ -53,11 +53,18 @@ export abstract class BrowserBase extends EventEmitter implements Browser, Inner
|
|||
return page;
|
||||
}
|
||||
|
||||
_downloadCreated(page: Page, uuid: string, url: string) {
|
||||
const download = new Download(page, this._downloadsPath, uuid, url);
|
||||
_downloadCreated(page: Page, uuid: string, url: string, suggestedFilename?: string) {
|
||||
const download = new Download(page, this._downloadsPath, uuid, url, suggestedFilename);
|
||||
this._downloads.set(uuid, download);
|
||||
}
|
||||
|
||||
_downloadFilenameSuggested(uuid: string, suggestedFilename: string) {
|
||||
const download = this._downloads.get(uuid);
|
||||
if (!download)
|
||||
return;
|
||||
download._filenameSuggested(suggestedFilename);
|
||||
}
|
||||
|
||||
_downloadFinished(uuid: string, error?: string) {
|
||||
const download = this._downloads.get(uuid);
|
||||
if (!download)
|
||||
|
|
|
@ -670,7 +670,7 @@ class FrameSession {
|
|||
}
|
||||
if (!originPage)
|
||||
return;
|
||||
this._crPage._browserContext._browser._downloadCreated(originPage, payload.guid, payload.url);
|
||||
this._crPage._browserContext._browser._downloadCreated(originPage, payload.guid, payload.url, payload.suggestedFilename);
|
||||
}
|
||||
|
||||
_onDownloadProgress(payload: Protocol.Page.downloadProgressPayload) {
|
||||
|
|
|
@ -20,6 +20,7 @@ import * as util from 'util';
|
|||
import { Page } from './page';
|
||||
import { Events } from './events';
|
||||
import { Readable } from 'stream';
|
||||
import { assert } from './helper';
|
||||
|
||||
export class Download {
|
||||
private _downloadsPath: string;
|
||||
|
@ -31,16 +32,25 @@ export class Download {
|
|||
private _failure: string | null = null;
|
||||
private _deleted = false;
|
||||
private _url: string;
|
||||
private _suggestedFilename: string | undefined;
|
||||
|
||||
constructor(page: Page, downloadsPath: string, uuid: string, url: string) {
|
||||
constructor(page: Page, downloadsPath: string, uuid: string, url: string, suggestedFilename?: string) {
|
||||
this._page = page;
|
||||
this._downloadsPath = downloadsPath;
|
||||
this._uuid = uuid;
|
||||
this._url = url;
|
||||
this._suggestedFilename = suggestedFilename;
|
||||
this._finishedCallback = () => {};
|
||||
this._finishedPromise = new Promise(f => this._finishedCallback = f);
|
||||
page._browserContext._downloads.add(this);
|
||||
this._acceptDownloads = !!this._page._browserContext._options.acceptDownloads;
|
||||
if (suggestedFilename !== undefined)
|
||||
this._page.emit(Events.Page.Download, this);
|
||||
}
|
||||
|
||||
_filenameSuggested(suggestedFilename: string) {
|
||||
assert(this._suggestedFilename === undefined);
|
||||
this._suggestedFilename = suggestedFilename;
|
||||
this._page.emit(Events.Page.Download, this);
|
||||
}
|
||||
|
||||
|
@ -48,6 +58,10 @@ export class Download {
|
|||
return this._url;
|
||||
}
|
||||
|
||||
suggestedFilename(): string {
|
||||
return this._suggestedFilename!;
|
||||
}
|
||||
|
||||
async path(): Promise<string | null> {
|
||||
if (!this._acceptDownloads)
|
||||
throw new Error('Pass { acceptDownloads: true } when you are creating your browser context.');
|
||||
|
|
|
@ -130,7 +130,7 @@ export class FFBrowser extends BrowserBase {
|
|||
}
|
||||
if (!originPage)
|
||||
return;
|
||||
this._downloadCreated(originPage, payload.uuid, payload.url);
|
||||
this._downloadCreated(originPage, payload.uuid, payload.url, payload.suggestedFileName);
|
||||
}
|
||||
|
||||
_onDownloadFinished(payload: Protocol.Browser.downloadFinishedPayload) {
|
||||
|
|
|
@ -56,6 +56,7 @@ export class WKBrowser extends BrowserBase {
|
|||
helper.addEventListener(this._browserSession, 'Playwright.pageProxyDestroyed', this._onPageProxyDestroyed.bind(this)),
|
||||
helper.addEventListener(this._browserSession, 'Playwright.provisionalLoadFailed', event => this._onProvisionalLoadFailed(event)),
|
||||
helper.addEventListener(this._browserSession, 'Playwright.downloadCreated', this._onDownloadCreated.bind(this)),
|
||||
helper.addEventListener(this._browserSession, 'Playwright.downloadFilenameSuggested', this._onDownloadFilenameSuggested.bind(this)),
|
||||
helper.addEventListener(this._browserSession, 'Playwright.downloadFinished', this._onDownloadFinished.bind(this)),
|
||||
helper.addEventListener(this._browserSession, kPageProxyMessageReceived, this._onPageProxyMessageReceived.bind(this)),
|
||||
];
|
||||
|
@ -111,6 +112,10 @@ export class WKBrowser extends BrowserBase {
|
|||
this._downloadCreated(originPage, payload.uuid, payload.url);
|
||||
}
|
||||
|
||||
_onDownloadFilenameSuggested(payload: Protocol.Playwright.downloadFilenameSuggestedPayload) {
|
||||
this._downloadFilenameSuggested(payload.uuid, payload.suggestedFilename);
|
||||
}
|
||||
|
||||
_onDownloadFinished(payload: Protocol.Playwright.downloadFinishedPayload) {
|
||||
this._downloadFinished(payload.uuid, payload.error);
|
||||
}
|
||||
|
|
|
@ -25,16 +25,22 @@ describe('Download', function() {
|
|||
res.setHeader('Content-Disposition', 'attachment');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
state.server.setRoute('/downloadWithFilename', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should report downloads with acceptDownloads: false', async({page, server}) => {
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
await page.setContent(`<a href="${server.PREFIX}/downloadWithFilename">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
let error;
|
||||
expect(download.url()).toBe(`${server.PREFIX}/download`);
|
||||
expect(download.url()).toBe(`${server.PREFIX}/downloadWithFilename`);
|
||||
expect(download.suggestedFilename()).toBe(`file.txt`);
|
||||
await download.path().catch(e => error = e);
|
||||
expect(await download.failure()).toContain('acceptDownloads');
|
||||
expect(error.message).toContain('acceptDownloads: true');
|
||||
|
@ -51,8 +57,8 @@ describe('Download', function() {
|
|||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it.fail(WEBKIT)('should report non-navigation downloads', async({browser, server}) => {
|
||||
// Our WebKit embedder does not download in this case, although Safari does.
|
||||
it.fail(WEBKIT && MAC)('should report non-navigation downloads', async({browser, server}) => {
|
||||
// Mac WebKit embedder does not download in this case, although Safari does.
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.end(`Hello world`);
|
||||
|
@ -65,6 +71,7 @@ describe('Download', function() {
|
|||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
expect(download.suggestedFilename()).toBe(`file.txt`);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
|
|
Загрузка…
Ссылка в новой задаче