1/* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16import {NO_ERRORS_SCHEMA} from '@angular/core'; 17import {ComponentFixture, TestBed} from '@angular/core/testing'; 18import {MatButtonModule} from '@angular/material/button'; 19import {MatCardModule} from '@angular/material/card'; 20import {MatDividerModule} from '@angular/material/divider'; 21import {MatIconModule} from '@angular/material/icon'; 22import {MatListModule} from '@angular/material/list'; 23import {MatProgressBarModule} from '@angular/material/progress-bar'; 24import {MatSnackBar, MatSnackBarModule} from '@angular/material/snack-bar'; 25import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 26import {assertDefined} from 'common/assert_utils'; 27import {InMemoryStorage} from 'common/in_memory_storage'; 28import {PersistentStoreProxy} from 'common/persistent_store_proxy'; 29import { 30 TraceConfigurationMap, 31 TRACES, 32} from 'trace_collection/trace_collection_utils'; 33import {AdbProxyComponent} from './adb_proxy_component'; 34import {CollectTracesComponent} from './collect_traces_component'; 35import {LoadProgressComponent} from './load_progress_component'; 36import {TraceConfigComponent} from './trace_config_component'; 37import {WebAdbComponent} from './web_adb_component'; 38 39describe('CollectTracesComponent', () => { 40 let fixture: ComponentFixture<CollectTracesComponent>; 41 let component: CollectTracesComponent; 42 let htmlElement: HTMLElement; 43 44 beforeEach(async () => { 45 await TestBed.configureTestingModule({ 46 imports: [ 47 MatIconModule, 48 MatCardModule, 49 MatListModule, 50 MatButtonModule, 51 MatDividerModule, 52 MatProgressBarModule, 53 BrowserAnimationsModule, 54 MatSnackBarModule, 55 ], 56 providers: [MatSnackBar], 57 declarations: [ 58 CollectTracesComponent, 59 AdbProxyComponent, 60 WebAdbComponent, 61 TraceConfigComponent, 62 LoadProgressComponent, 63 ], 64 schemas: [NO_ERRORS_SCHEMA], 65 }).compileComponents(); 66 fixture = TestBed.createComponent(CollectTracesComponent); 67 component = fixture.componentInstance; 68 htmlElement = fixture.nativeElement; 69 component.isAdbProxy = true; 70 component.storage = new InMemoryStorage(); 71 component.traceConfig = PersistentStoreProxy.new<TraceConfigurationMap>( 72 'TracingSettings', 73 TRACES['default'], 74 component.storage, 75 ); 76 component.dumpConfig = PersistentStoreProxy.new<TraceConfigurationMap>( 77 'DumpSettings', 78 { 79 window_dump: { 80 name: 'Window Manager', 81 run: true, 82 config: undefined, 83 }, 84 layers_dump: { 85 name: 'Surface Flinger', 86 run: true, 87 config: undefined, 88 }, 89 }, 90 component.storage, 91 ); 92 fixture.detectChanges(); 93 }); 94 95 it('can be created', () => { 96 expect(component).toBeTruthy(); 97 }); 98 99 it('renders the expected card title', () => { 100 const title = assertDefined(htmlElement.querySelector('.title')); 101 expect(title.innerHTML).toContain('Collect Traces'); 102 }); 103 104 it('displays connecting message', () => { 105 assertDefined(component.connect).isConnectingState = jasmine 106 .createSpy() 107 .and.returnValue(true); 108 fixture.detectChanges(); 109 110 const connectingMessage = assertDefined( 111 htmlElement.querySelector('.connecting-message'), 112 ); 113 expect(connectingMessage.innerHTML).toContain('Connecting...'); 114 }); 115 116 it('displays adb set up', () => { 117 assertDefined(component.connect).adbSuccess = jasmine 118 .createSpy() 119 .and.returnValue(false); 120 fixture.detectChanges(); 121 122 const setUpAdbEl = assertDefined(htmlElement.querySelector('.set-up-adb')); 123 expect(setUpAdbEl.querySelector('.proxy-tab')).toBeTruthy(); 124 }); 125 126 it('displays adb proxy element', () => { 127 assertDefined(component.connect).adbSuccess = jasmine 128 .createSpy() 129 .and.returnValue(false); 130 component.isAdbProxy = true; 131 fixture.detectChanges(); 132 133 expect(htmlElement.querySelector('adb-proxy')).toBeTruthy(); 134 }); 135 136 it('displays no connected devices', () => { 137 const connect = assertDefined(component.connect); 138 connect.isDevicesState = jasmine.createSpy().and.returnValue(true); 139 connect.devices = jasmine.createSpy().and.returnValue({}); 140 fixture.detectChanges(); 141 142 const el = assertDefined(htmlElement.querySelector('.devices-connecting')); 143 expect(el.innerHTML).toContain('No devices detected'); 144 }); 145 146 it('displays connected authorised devices', () => { 147 const connect = assertDefined(component.connect); 148 connect.isDevicesState = jasmine.createSpy().and.returnValue(true); 149 connect.devices = jasmine 150 .createSpy() 151 .and.returnValue({'35562': {model: 'Pixel 6', authorised: true}}); 152 fixture.detectChanges(); 153 154 const el = assertDefined(htmlElement.querySelector('.devices-connecting')); 155 expect(el.innerHTML).toContain('Pixel 6'); 156 expect(el.innerHTML).toContain('smartphone'); 157 }); 158 159 it('displays connected unauthorised devices', () => { 160 const connect = assertDefined(component.connect); 161 connect.isDevicesState = jasmine.createSpy().and.returnValue(true); 162 connect.devices = jasmine 163 .createSpy() 164 .and.returnValue({'35562': {model: 'Pixel 6', authorised: false}}); 165 fixture.detectChanges(); 166 167 const el = assertDefined(htmlElement.querySelector('.devices-connecting')); 168 expect(el.innerHTML).toContain('unauthorised'); 169 expect(el.innerHTML).toContain('screen_lock_portrait'); 170 }); 171 172 it('auto detects changes in devices', async () => { 173 const connect = assertDefined(component.connect); 174 connect.isDevicesState = jasmine.createSpy().and.returnValue(true); 175 fixture.detectChanges(); 176 177 const el = assertDefined(htmlElement.querySelector('.devices-connecting')); 178 expect(el.textContent).toContain('No devices detected'); 179 180 connect.devices = jasmine 181 .createSpy() 182 .and.returnValue({'35562': {model: 'Pixel 6', authorised: true}}); 183 184 await fixture.whenStable(); 185 expect(el.textContent).toContain( 186 'Select a device: smartphone Pixel 6 (35562)', 187 ); 188 }); 189 190 it('displays trace collection config elements', () => { 191 const connect = assertDefined(component.connect); 192 connect.isStartTraceState = jasmine.createSpy().and.returnValue(true); 193 const mock = {model: 'Pixel 6', authorised: true}; 194 connect.devices = jasmine.createSpy().and.returnValue({'35562': mock}); 195 connect.selectedDevice = jasmine.createSpy().and.returnValue(mock); 196 connect.selectedDeviceId = jasmine.createSpy().and.returnValue('35562'); 197 fixture.detectChanges(); 198 199 const el = assertDefined( 200 htmlElement.querySelector('.trace-collection-config'), 201 ); 202 expect(el.innerHTML).toContain('smartphone'); 203 expect(el.innerHTML).toContain('Pixel 6'); 204 expect(el.innerHTML).toContain('35562'); 205 206 const traceSection = htmlElement.querySelector('.trace-section'); 207 expect(traceSection).toBeTruthy(); 208 209 const dumpSection = htmlElement.querySelector('.dump-section'); 210 expect(dumpSection).toBeTruthy(); 211 }); 212 213 it('start trace button works as expected', () => { 214 const connect = assertDefined(component.connect); 215 connect.isStartTraceState = jasmine.createSpy().and.returnValue(true); 216 const mock = {model: 'Pixel 6', authorised: true}; 217 connect.devices = jasmine.createSpy().and.returnValue({'35562': mock}); 218 connect.selectedDevice = jasmine.createSpy().and.returnValue(mock); 219 fixture.detectChanges(); 220 221 const spy = spyOn(connect, 'startTrace'); 222 const start = assertDefined( 223 htmlElement.querySelector('.start-btn button'), 224 ) as HTMLButtonElement; 225 start.click(); 226 expect(spy).toHaveBeenCalled(); 227 }); 228 229 it('dump state button works as expected', () => { 230 const connect = assertDefined(component.connect); 231 connect.isStartTraceState = jasmine.createSpy().and.returnValue(true); 232 const mock = {model: 'Pixel 6', authorised: true}; 233 connect.devices = jasmine.createSpy().and.returnValue({'35562': mock}); 234 connect.selectedDevice = jasmine.createSpy().and.returnValue(mock); 235 fixture.detectChanges(); 236 237 const spy = spyOn(connect, 'dumpState'); 238 const dump = assertDefined( 239 htmlElement.querySelector('.dump-btn button'), 240 ) as HTMLButtonElement; 241 dump.click(); 242 expect(spy).toHaveBeenCalled(); 243 }); 244 245 it('change device button works as expected', () => { 246 const connect = assertDefined(component.connect); 247 connect.isStartTraceState = jasmine.createSpy().and.returnValue(true); 248 const mock = {model: 'Pixel 6', authorised: true}; 249 connect.devices = jasmine.createSpy().and.returnValue({'35562': mock}); 250 connect.selectedDevice = jasmine.createSpy().and.returnValue(mock); 251 fixture.detectChanges(); 252 253 const spy = spyOn(connect, 'resetLastDevice'); 254 const change = assertDefined( 255 htmlElement.querySelector('.change-btn'), 256 ) as HTMLButtonElement; 257 change.click(); 258 expect(spy).toHaveBeenCalled(); 259 }); 260 261 it('displays unknown error message', () => { 262 const connect = assertDefined(component.connect); 263 connect.isErrorState = jasmine.createSpy().and.returnValue(true); 264 fixture.detectChanges(); 265 266 const testErrorMessage = 'bad things are happening'; 267 assertDefined(connect.proxy).errorText = testErrorMessage; 268 fixture.detectChanges(); 269 270 const el = assertDefined(htmlElement.querySelector('.unknown-error')); 271 expect(el.innerHTML).toContain('Error:'); 272 expect(el.innerHTML).toContain(testErrorMessage); 273 274 const spy = spyOn(connect, 'restart').and.callThrough(); 275 const retryButton = assertDefined( 276 htmlElement.querySelector('.retry-btn'), 277 ) as HTMLButtonElement; 278 retryButton.click(); 279 expect(spy).toHaveBeenCalled(); 280 }); 281 282 it('displays starting trace elements', () => { 283 assertDefined(component.connect).isStartingTraceState = jasmine 284 .createSpy() 285 .and.returnValue(true); 286 fixture.detectChanges(); 287 288 const el = assertDefined(htmlElement.querySelector('.starting-trace')); 289 const progress = assertDefined(el.querySelector('load-progress')); 290 expect(progress.innerHTML).toContain('Starting trace...'); 291 292 const endButton = assertDefined( 293 el.querySelector('.end-btn button'), 294 ) as HTMLButtonElement; 295 expect(endButton.disabled).toBeTrue(); 296 }); 297 298 it('displays end tracing elements', () => { 299 const connect = assertDefined(component.connect); 300 connect.isEndTraceState = jasmine.createSpy().and.returnValue(true); 301 fixture.detectChanges(); 302 303 const el = assertDefined(htmlElement.querySelector('.end-tracing')); 304 const progress = assertDefined(el.querySelector('load-progress')); 305 expect(progress.innerHTML).toContain('Tracing...'); 306 expect(progress.innerHTML).toContain('cable'); 307 308 const spy = spyOn(connect, 'endTrace'); 309 const endButton = assertDefined( 310 el.querySelector('.end-btn button'), 311 ) as HTMLButtonElement; 312 expect(endButton.disabled).toBeFalse(); 313 endButton.click(); 314 expect(spy).toHaveBeenCalled(); 315 }); 316 317 it('displays loading data elements', () => { 318 assertDefined(component.connect).isLoadDataState = jasmine 319 .createSpy() 320 .and.returnValue(true); 321 fixture.detectChanges(); 322 323 const el = assertDefined(htmlElement.querySelector('.load-data')); 324 const progress = assertDefined(el.querySelector('load-progress')); 325 expect(progress.innerHTML).toContain('Fetching...'); 326 327 const endButton = assertDefined( 328 el.querySelector('.end-btn button'), 329 ) as HTMLButtonElement; 330 expect(endButton.disabled).toBeTrue(); 331 }); 332}); 333