diff --git a/packages/web/src/components/toolbarButton.tsx b/packages/web/src/components/toolbarButton.tsx
index 521951bfee..fbdfb738ab 100644
--- a/packages/web/src/components/toolbarButton.tsx
+++ b/packages/web/src/components/toolbarButton.tsx
@@ -31,7 +31,7 @@ export interface ToolbarButtonProps {
ariaLabel?: string,
}
-export const ToolbarButton: React.FC
> = ({
+export const ToolbarButton = React.forwardRef>(function ToolbarButton({
children,
title = '',
icon,
@@ -42,8 +42,9 @@ export const ToolbarButton: React.FC
testId,
className,
ariaLabel,
-}) => {
+}, ref) {
return ;
-};
+});
export const ToolbarSeparator: React.FC<{ style?: React.CSSProperties }> = ({
style,
diff --git a/tests/config/traceViewerFixtures.ts b/tests/config/traceViewerFixtures.ts
index 40737f6bc1..e3947ff092 100644
--- a/tests/config/traceViewerFixtures.ts
+++ b/tests/config/traceViewerFixtures.ts
@@ -49,6 +49,10 @@ class TraceViewerPage {
snapshotContainer: Locator;
sourceCodeTab: Locator;
+ settingsDialog: Locator;
+ darkModeSetting: Locator;
+ displayCanvasContentSetting: Locator;
+
constructor(public page: Page) {
this.actionTitles = page.locator('.action-title');
this.actionsTree = page.getByTestId('actions-tree');
@@ -63,6 +67,10 @@ class TraceViewerPage {
this.snapshotContainer = page.locator('.snapshot-container iframe.snapshot-visible[name=snapshot]');
this.metadataTab = page.getByTestId('metadata-view');
this.sourceCodeTab = page.getByTestId('source-code');
+
+ this.settingsDialog = page.getByTestId('settings-toolbar-dialog');
+ this.darkModeSetting = page.locator('.setting').getByText('Dark mode');
+ this.displayCanvasContentSetting = page.locator('.setting').getByText('Display canvas content');
}
async actionIconsText(action: string) {
@@ -115,6 +123,10 @@ class TraceViewerPage {
await this.page.click('text="Metadata"');
}
+ async showSettings() {
+ await this.page.locator('.settings-gear').click();
+ }
+
@step
async snapshotFrame(actionName: string, ordinal: number = 0, hasSubframe: boolean = false): Promise {
await this.selectAction(actionName, ordinal);
diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts
index 53c6bcf6c4..59441935cf 100644
--- a/tests/library/trace-viewer.spec.ts
+++ b/tests/library/trace-viewer.spec.ts
@@ -1521,12 +1521,26 @@ test('should serve css without content-type', async ({ page, runAndTrace, server
await expect(snapshotFrame.locator('body')).toHaveCSS('background-color', 'rgb(255, 0, 0)', { timeout: 0 });
});
+test('canvas disabled title', async ({ runAndTrace, page, server }) => {
+ const traceViewer = await runAndTrace(async () => {
+ await page.goto(server.PREFIX + '/screenshots/canvas.html#canvas-on-edge');
+ await rafraf(page, 5);
+ });
+
+ const snapshot = await traceViewer.snapshotFrame('page.goto');
+ await expect(snapshot.locator('canvas')).toHaveAttribute('title', `Canvas content display is disabled.`);
+});
+
test('canvas clipping', async ({ runAndTrace, page, server }) => {
const traceViewer = await runAndTrace(async () => {
await page.goto(server.PREFIX + '/screenshots/canvas.html#canvas-on-edge');
await rafraf(page, 5);
});
+ // Enable canvas display
+ await traceViewer.showSettings();
+ await traceViewer.displayCanvasContentSetting.click();
+
const msg = await traceViewer.page.waitForEvent('console', { predicate: msg => msg.text().startsWith('canvas drawn:') });
expect(msg.text()).toEqual('canvas drawn: [0,91,11,20]');
@@ -1543,6 +1557,10 @@ test('canvas clipping in iframe', async ({ runAndTrace, page, server }) => {
await rafraf(page, 5);
});
+ // Enable canvas display
+ await traceViewer.showSettings();
+ await traceViewer.displayCanvasContentSetting.click();
+
const msg = await traceViewer.page.waitForEvent('console', { predicate: msg => msg.text().startsWith('canvas drawn:') });
expect(msg.text()).toEqual('canvas drawn: [1,1,11,20]');
@@ -1593,3 +1611,60 @@ test('should show a popover', async ({ runAndTrace, page, server }) => {
const popover = snapshot.locator('#pop');
await expect.poll(() => popover.evaluate(e => e.matches(':popover-open'))).toBe(true);
});
+
+test('should open settings dialog', async ({ showTraceViewer }) => {
+ const traceViewer = await showTraceViewer([traceFile]);
+ await traceViewer.selectAction('http://localhost');
+ await traceViewer.showSettings();
+ await expect(traceViewer.settingsDialog).toBeVisible();
+});
+
+test('should toggle theme color', async ({ showTraceViewer, page }) => {
+ const traceViewer = await showTraceViewer([traceFile]);
+ await traceViewer.selectAction('http://localhost');
+ await traceViewer.showSettings();
+
+ await expect(traceViewer.darkModeSetting).toBeChecked({ checked: false });
+
+ await traceViewer.darkModeSetting.click();
+ await expect(traceViewer.darkModeSetting).toBeChecked({ checked: true });
+ await expect(traceViewer.page.locator('.dark-mode')).toBeVisible();
+
+ await traceViewer.darkModeSetting.click();
+ await expect(traceViewer.darkModeSetting).toBeChecked({ checked: false });
+ await expect(traceViewer.page.locator('.light-mode')).toBeVisible();
+});
+
+test('should toggle canvas rendering', async ({ runAndTrace, page }) => {
+ const traceViewer = await runAndTrace(async () => {
+ await page.goto(`data:text/html,Hello world
`);
+ await page.goto(`data:text/html,Hello world
`);
+ });
+
+ let snapshotRequestPromise = traceViewer.page.waitForRequest(request => request.url().includes('/snapshot/'));
+
+ // Click on the action with a canvas snapshot
+ await traceViewer.selectAction('goto', 0);
+
+ let snapshotRequest = await snapshotRequestPromise;
+
+ expect(snapshotRequest.url()).not.toContain('shouldPopulateCanvasFromScreenshot');
+
+ await traceViewer.showSettings();
+
+ await expect(traceViewer.displayCanvasContentSetting).toBeChecked({ checked: false });
+ await traceViewer.displayCanvasContentSetting.click();
+ await expect(traceViewer.displayCanvasContentSetting).toBeChecked({ checked: true });
+
+ // Deselect canvas
+ await traceViewer.selectAction('goto', 1);
+
+ snapshotRequestPromise = traceViewer.page.waitForRequest(request => request.url().includes('/snapshot/'));
+
+ // Select canvas again
+ await traceViewer.selectAction('goto', 0);
+
+ snapshotRequest = await snapshotRequestPromise;
+
+ expect(snapshotRequest.url()).toContain('shouldPopulateCanvasFromScreenshot');
+});