Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ export class SnapshotServer {
headers.set('Access-Control-Allow-Origin', '*');
headers.delete('Content-Length');
headers.set('Content-Length', String(content.size));
headers.set('Cache-Control', 'public, max-age=31536000');
if (this._snapshotStorage.hasResourceOverride(resource.request.url))
headers.set('Cache-Control', 'no-store, no-cache, max-age=0');
else
headers.set('Cache-Control', 'public, max-age=31536000');
const { status } = resource.response;
const isNullBodyStatus = status === 101 || status === 204 || status === 205 || status === 304;
return new Response(isNullBodyStatus ? null : content, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export class SnapshotStorage {
}>();
private _cache = new LRUCache<SnapshotRenderer, string>(100_000_000); // 100MB per each trace
private _contextToResources = new Map<string, ResourceSnapshot[]>();
private _resourceUrlsWithOverrides = new Set<string>();

addResource(contextId: string, resource: ResourceSnapshot): void {
resource.request.url = rewriteURLForCustomProtocol(resource.request.url);
Expand Down Expand Up @@ -67,6 +68,18 @@ export class SnapshotStorage {
// Resources are not necessarily sorted in the trace file, so sort them now.
for (const resources of this._contextToResources.values())
resources.sort((a, b) => (a._monotonicTime || 0) - (b._monotonicTime || 0));
// Resources that have overrides should not be cached, otherwise we might get stale content
// while serving snapshots with different override values.
for (const frameSnapshots of this._frameSnapshots.values()) {
for (const snapshot of frameSnapshots.raw) {
for (const override of snapshot.resourceOverrides)
this._resourceUrlsWithOverrides.add(override.url);
}
}
}

hasResourceOverride(url: string) {
return this._resourceUrlsWithOverrides.has(url);
}

private _ensureResourcesForContext(contextId: string): ResourceSnapshot[] {
Expand Down
39 changes: 39 additions & 0 deletions tests/library/trace-viewer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2129,3 +2129,42 @@ test('should not navigate on anchor clicks', async ({ runAndTrace, page, server
await checkLink('link2');
await checkLink('link3');
});

test('should respect CSSOM changes', async ({ runAndTrace, page, server }) => {
const traceViewer = await runAndTrace(async () => {
await page.setContent('<style>button { color: red; }</style><button>Hello</button>');
await page.evaluate(() => { (document.styleSheets[0].cssRules[0] as any).style.color = 'blue'; });

await page.setContent('<style>@media { button { color: red; } }</style><button>Hello</button>');
await page.evaluate(() => {
window['rule'] = document.styleSheets[0].cssRules[0];
void 0;
});
await page.evaluate(() => { window['rule'].cssRules[0].style.color = 'black'; });
await page.evaluate(() => { window['rule'].insertRule('button:not(.disabled) { color: green; }', 1); });

await page.route('**/style.css', route => {
route.fulfill({ body: 'button { color: red; }', }).catch(() => {});
});
await page.goto(server.EMPTY_PAGE);
await page.setContent('<link rel="stylesheet" href="style.css"><button>Hello</button>');
await page.evaluate(() => { (document.styleSheets[0].cssRules[0] as any).style.color = 'blue'; });
});

const frame1 = await traceViewer.snapshotFrame('Set content', 0);
await expect(frame1.locator('button')).toHaveCSS('color', 'rgb(255, 0, 0)');
const frame2 = await traceViewer.snapshotFrame('Evaluate', 0);
await expect(frame2.locator('button')).toHaveCSS('color', 'rgb(0, 0, 255)');

const frame3 = await traceViewer.snapshotFrame('Set content', 1);
await expect(frame3.locator('button')).toHaveCSS('color', 'rgb(255, 0, 0)');
const frame4 = await traceViewer.snapshotFrame('Evaluate', 2);
await expect(frame4.locator('button')).toHaveCSS('color', 'rgb(0, 0, 0)');
const frame5 = await traceViewer.snapshotFrame('Evaluate', 3);
await expect(frame5.locator('button')).toHaveCSS('color', 'rgb(0, 128, 0)');

const frame6 = await traceViewer.snapshotFrame('Set content', 2);
await expect(frame6.locator('button')).toHaveCSS('color', 'rgb(255, 0, 0)');
const frame7 = await traceViewer.snapshotFrame('Evaluate', 4);
await expect(frame7.locator('button')).toHaveCSS('color', 'rgb(0, 0, 255)');
});
Loading