mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(trace-viewer): redirect, time and missing snapshot bugfixes (#10055)
This commit is contained in:
parent
5c9dcffd67
commit
806b5706a5
@ -80,9 +80,10 @@ export class SnapshotRenderer {
|
|||||||
if (!html)
|
if (!html)
|
||||||
return { html: '', pageId: snapshot.pageId, frameId: snapshot.frameId, index: this._index };
|
return { html: '', pageId: snapshot.pageId, frameId: snapshot.frameId, index: this._index };
|
||||||
|
|
||||||
if (snapshot.doctype)
|
// Hide the document in order to prevent flickering. We will unhide once script has processed shadow.
|
||||||
html = `<!DOCTYPE ${snapshot.doctype}>` + html;
|
const hideAllStyle = '<style>*,*::before,*::after { visibility: hidden }</style>';
|
||||||
html += `
|
const prefix = snapshot.doctype ? `<!DOCTYPE ${snapshot.doctype}>` + hideAllStyle : hideAllStyle;
|
||||||
|
html = prefix + html + `
|
||||||
<style>*[__playwright_target__="${this.snapshotName}"] { background-color: #6fa8dc7f; }</style>
|
<style>*[__playwright_target__="${this.snapshotName}"] { background-color: #6fa8dc7f; }</style>
|
||||||
<script>${snapshotScript()}</script>
|
<script>${snapshotScript()}</script>
|
||||||
`;
|
`;
|
||||||
@ -240,6 +241,7 @@ function snapshotScript() {
|
|||||||
pointElement.style.top = pointY + 'px';
|
pointElement.style.top = pointY + 'px';
|
||||||
document.documentElement.appendChild(pointElement);
|
document.documentElement.appendChild(pointElement);
|
||||||
}
|
}
|
||||||
|
document.styleSheets[0].disabled = true;
|
||||||
};
|
};
|
||||||
window.addEventListener('load', onLoad);
|
window.addEventListener('load', onLoad);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ResourceSnapshot } from '../../server/trace/common/snapshotTypes';
|
|
||||||
import { SnapshotStorage } from './snapshotStorage';
|
import { SnapshotStorage } from './snapshotStorage';
|
||||||
import type { Point } from '../../common/types';
|
import type { Point } from '../../common/types';
|
||||||
import { URLSearchParams } from 'url';
|
import { URLSearchParams } from 'url';
|
||||||
@ -44,7 +43,9 @@ export class SnapshotServer {
|
|||||||
return this._respondWithJson(snapshot ? {
|
return this._respondWithJson(snapshot ? {
|
||||||
viewport: snapshot.viewport(),
|
viewport: snapshot.viewport(),
|
||||||
url: snapshot.snapshot().frameUrl
|
url: snapshot.snapshot().frameUrl
|
||||||
} : {});
|
} : {
|
||||||
|
error: 'No snapshot found'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _snapshot(pathname: string, params: URLSearchParams) {
|
private _snapshot(pathname: string, params: URLSearchParams) {
|
||||||
@ -70,15 +71,7 @@ export class SnapshotServer {
|
|||||||
return new Response(null, { status: 404 });
|
return new Response(null, { status: 404 });
|
||||||
|
|
||||||
const sha1 = resource.response.content._sha1;
|
const sha1 = resource.response.content._sha1;
|
||||||
if (!sha1)
|
const content = sha1 ? await this._snapshotStorage.resourceContent(sha1) || new Blob([]) : new Blob([]);
|
||||||
return new Response(null, { status: 404 });
|
|
||||||
return this._innerServeResource(sha1, resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _innerServeResource(sha1: string, resource: ResourceSnapshot): Promise<Response> {
|
|
||||||
const content = await this._snapshotStorage.resourceContent(sha1);
|
|
||||||
if (!content)
|
|
||||||
return new Response(null, { status: 404 });
|
|
||||||
|
|
||||||
let contentType = resource.response.content.mimeType;
|
let contentType = resource.response.content.mimeType;
|
||||||
const isTextEncoding = /^text\/|^application\/(javascript|json)/.test(contentType);
|
const isTextEncoding = /^text\/|^application\/(javascript|json)/.test(contentType);
|
||||||
@ -95,7 +88,11 @@ export class SnapshotServer {
|
|||||||
headers.delete('Content-Length');
|
headers.delete('Content-Length');
|
||||||
headers.set('Content-Length', String(content.size));
|
headers.set('Content-Length', String(content.size));
|
||||||
headers.set('Cache-Control', 'public, max-age=31536000');
|
headers.set('Cache-Control', 'public, max-age=31536000');
|
||||||
return new Response(content, { headers });
|
return new Response(content, {
|
||||||
|
headers,
|
||||||
|
status: resource.response.status,
|
||||||
|
statusText: resource.response.statusText,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ export const CallTab: React.FunctionComponent<{
|
|||||||
<div className='call-line'>{action.metadata.apiName}</div>
|
<div className='call-line'>{action.metadata.apiName}</div>
|
||||||
{<>
|
{<>
|
||||||
<div className='call-section'>Time</div>
|
<div className='call-section'>Time</div>
|
||||||
<div className='call-line'>wall time: <span className='datetime' title={wallTime}>{wallTime}</span></div>
|
{action.metadata.wallTime && <div className='call-line'>wall time: <span className='datetime' title={wallTime}>{wallTime}</span></div>}
|
||||||
<div className='call-line'>duration: <span className='datetime' title={duration}>{duration}</span></div>
|
<div className='call-line'>duration: <span className='datetime' title={duration}>{duration}</span></div>
|
||||||
</>}
|
</>}
|
||||||
{ !!paramKeys.length && <div className='call-section'>Parameters</div> }
|
{ !!paramKeys.length && <div className='call-section'>Parameters</div> }
|
||||||
|
@ -63,7 +63,8 @@ export const SnapshotTab: React.FunctionComponent<{
|
|||||||
if (snapshotInfoUrl) {
|
if (snapshotInfoUrl) {
|
||||||
const response = await fetch(snapshotInfoUrl);
|
const response = await fetch(snapshotInfoUrl);
|
||||||
const info = await response.json();
|
const info = await response.json();
|
||||||
setSnapshotInfo(info);
|
if (!info.error)
|
||||||
|
setSnapshotInfo(info);
|
||||||
}
|
}
|
||||||
if (!iframeRef.current)
|
if (!iframeRef.current)
|
||||||
return;
|
return;
|
||||||
|
@ -216,6 +216,7 @@ function distillSnapshot(snapshot, distillTarget = true) {
|
|||||||
if (distillTarget)
|
if (distillTarget)
|
||||||
html = html.replace(/\s__playwright_target__="[^"]+"/g, '');
|
html = html.replace(/\s__playwright_target__="[^"]+"/g, '');
|
||||||
return html
|
return html
|
||||||
|
.replace(/<style>\*,\*::before,\*::after { visibility: hidden }<\/style>/, '')
|
||||||
.replace(/<script>[.\s\S]+<\/script>/, '')
|
.replace(/<script>[.\s\S]+<\/script>/, '')
|
||||||
.replace(/<style>.*__playwright_target__.*<\/style>/, '')
|
.replace(/<style>.*__playwright_target__.*<\/style>/, '')
|
||||||
.replace(/<BASE href="about:blank">/, '')
|
.replace(/<BASE href="about:blank">/, '')
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type { Browser, Frame, Locator, Page } from 'playwright-core';
|
import type { Browser, Frame, Locator, Page } from 'playwright-core';
|
||||||
import { showTraceViewer } from '../../packages/playwright-core/lib/server/trace/viewer/traceViewer';
|
import { showTraceViewer } from '../../packages/playwright-core/lib/server/trace/viewer/traceViewer';
|
||||||
@ -552,3 +553,29 @@ test('should show action source', async ({ showTraceViewer }) => {
|
|||||||
await expect(page.locator('.source-line-running')).toContainText('page.click');
|
await expect(page.locator('.source-line-running')).toContainText('page.click');
|
||||||
await expect(page.locator('.stack-trace-frame.selected')).toHaveText(/doClick.*trace-viewer\.spec\.ts:[\d]+/);
|
await expect(page.locator('.stack-trace-frame.selected')).toHaveText(/doClick.*trace-viewer\.spec\.ts:[\d]+/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should follow redirects', async ({ page, runAndTrace, server, asset }) => {
|
||||||
|
server.setRoute('/empty.html', (req, res) => {
|
||||||
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||||
|
res.end(`<div><img id=img src="image.png"></img></div>`);
|
||||||
|
});
|
||||||
|
server.setRoute('/image.png', (req, res) => {
|
||||||
|
res.writeHead(301, { location: '/image-301.png' });
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
server.setRoute('/image-301.png', (req, res) => {
|
||||||
|
res.writeHead(302, { location: '/image-302.png' });
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
server.setRoute('/image-302.png', (req, res) => {
|
||||||
|
res.writeHead(200, { 'content-type': 'image/png' });
|
||||||
|
res.end(fs.readFileSync(asset('digits/0.png')));
|
||||||
|
});
|
||||||
|
|
||||||
|
const traceViewer = await runAndTrace(async () => {
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
expect(await page.evaluate(() => (window as any).img.naturalWidth)).toBe(10);
|
||||||
|
});
|
||||||
|
const snapshotFrame = await traceViewer.snapshotFrame('page.evaluate');
|
||||||
|
await expect(snapshotFrame.locator('img')).toHaveJSProperty('naturalWidth', 10);
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user