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 async toggleTrackGroup(locator: Locator) { 86 await locator.locator('.pf-track__shell').first().click(); 87 await this.waitForPerfettoIdle(); 88 } 89 90 locateTrack(name: string, trackGroup?: Locator): Locator { 91 return (trackGroup ?? this.page).locator(`.pf-track[ref="${name}"]`); 92 } 93 94 pinTrackUsingShellBtn(track: Locator) { 95 track.locator('button[title="Pin to top"]').click({force: true}); 96 } 97 98 // eslint-disable-next-line @typescript-eslint/no-explicit-any 99 async runCommand(cmdId: string, ...args: any[]) { 100 await this.page.evaluate( 101 (arg) => self.app.commands.runCommand(arg.cmdId, ...arg.args), 102 {cmdId, args}, 103 ); 104 } 105 106 async searchSlice(name: string) { 107 const omnibox = this.page.locator('input[ref=omnibox]'); 108 await omnibox.focus(); 109 await omnibox.fill(name); 110 await this.waitForPerfettoIdle(); 111 await omnibox.press('Enter'); 112 await this.waitForPerfettoIdle(); 113 } 114 115 getTestTracePath(fname: string): string { 116 const parts = ['test', 'data', fname]; 117 if (process.cwd().endsWith('/ui')) { 118 parts.unshift('..'); 119 } 120 const fPath = path.join(...parts); 121 if (!fs.existsSync(fPath)) { 122 throw new Error(`Could not locate file ${fPath}, cwd=${process.cwd()}`); 123 } 124 return fPath; 125 } 126 127 async clickMenuItem(text: string | RegExp) { 128 await this.page 129 .locator('.pf-popup-content .pf-menu-item', {hasText: text}) 130 .click(); 131 } 132 133 async switchToTab(text: string | RegExp) { 134 await this.page 135 .locator('.pf-tab-handle .pf-tab-handle__tab', {hasText: text}) 136 .click(); 137 } 138} 139