1// Copyright (C) 2021 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import { 16 expect, 17 Locator, 18 Page, 19 PageAssertionsToHaveScreenshotOptions, 20} from '@playwright/test'; 21import fs from 'fs'; 22import path from 'path'; 23import {IdleDetectorWindow} from '../frontend/idle_detector_interface'; 24import {assertExists} from '../base/logging'; 25import {Size2D} from '../base/geom'; 26 27export class PerfettoTestHelper { 28 private cachedSidebarSize?: Size2D; 29 30 constructor(readonly page: Page) {} 31 32 resetFocus(): Promise<void> { 33 return this.page.click('.sidebar img.brand'); 34 } 35 36 async sidebarSize(): Promise<Size2D> { 37 if (this.cachedSidebarSize === undefined) { 38 const size = await this.page.locator('main > .sidebar').boundingBox(); 39 this.cachedSidebarSize = assertExists(size); 40 } 41 return this.cachedSidebarSize; 42 } 43 44 async navigate(fragment: string): Promise<void> { 45 await this.page.goto('/?testing=1' + fragment); 46 await this.waitForPerfettoIdle(); 47 await this.page.click('body'); 48 } 49 50 async openTraceFile(traceName: string, args?: {}): Promise<void> { 51 args = {testing: '1', ...args}; 52 const qs = Object.entries(args ?? {}) 53 .map(([k, v]) => `${k}=${v}`) 54 .join('&'); 55 await this.page.goto('/?' + qs); 56 const file = await this.page.waitForSelector('input.trace_file', { 57 state: 'attached', 58 }); 59 await this.page.evaluate(() => 60 localStorage.setItem('dismissedPanningHint', 'true'), 61 ); 62 const tracePath = this.getTestTracePath(traceName); 63 assertExists(file).setInputFiles(tracePath); 64 await this.waitForPerfettoIdle(); 65 await this.page.mouse.move(0, 0); 66 } 67 68 waitForPerfettoIdle(idleHysteresisMs?: number): Promise<void> { 69 return this.page.evaluate( 70 async (ms) => 71 (window as {} as IdleDetectorWindow).waitForPerfettoIdle(ms), 72 idleHysteresisMs, 73 ); 74 } 75 76 async waitForIdleAndScreenshot( 77 screenshotName: string, 78 opts?: PageAssertionsToHaveScreenshotOptions, 79 ) { 80 await this.page.mouse.move(0, 0); // Move mouse out of the way. 81 await this.waitForPerfettoIdle(); 82 await expect.soft(this.page).toHaveScreenshot(screenshotName, opts); 83 } 84 85 locateTrackGroup(name: string): Locator { 86 return this.page 87 .locator('.pf-panel-group') 88 .filter({has: this.page.locator(`h1[ref="${name}"]`)}); 89 } 90 91 async toggleTrackGroup(locator: Locator) { 92 await locator.locator('.pf-track-title').first().click(); 93 await this.waitForPerfettoIdle(); 94 } 95 96 locateTrack(name: string, trackGroup?: Locator): Locator { 97 return (trackGroup ?? this.page) 98 .locator('.pf-track') 99 .filter({has: this.page.locator(`h1[ref="${name}"]`)}); 100 } 101 102 pinTrackUsingShellBtn(track: Locator) { 103 track.locator('button[title="Pin to top"]').click({force: true}); 104 } 105 106 // eslint-disable-next-line @typescript-eslint/no-explicit-any 107 async runCommand(cmdId: string, ...args: any[]) { 108 await this.page.evaluate( 109 (arg) => self.app.commands.runCommand(arg.cmdId, ...arg.args), 110 {cmdId, args}, 111 ); 112 } 113 114 async searchSlice(name: string) { 115 const omnibox = this.page.locator('input[ref=omnibox]'); 116 await omnibox.focus(); 117 await omnibox.fill(name); 118 await this.waitForPerfettoIdle(); 119 await omnibox.press('Enter'); 120 await this.waitForPerfettoIdle(); 121 } 122 123 getTestTracePath(fname: string): string { 124 const parts = ['test', 'data', fname]; 125 if (process.cwd().endsWith('/ui')) { 126 parts.unshift('..'); 127 } 128 const fPath = path.join(...parts); 129 if (!fs.existsSync(fPath)) { 130 throw new Error(`Could not locate file ${fPath}, cwd=${process.cwd()}`); 131 } 132 return fPath; 133 } 134} 135