1/* 2 * Copyright (C) 2024 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 */ 16 17import {ClipboardModule} from '@angular/cdk/clipboard'; 18import {ScrollingModule} from '@angular/cdk/scrolling'; 19import {HttpClientModule} from '@angular/common/http'; 20import { 21 ComponentFixture, 22 ComponentFixtureAutoDetect, 23 TestBed, 24} from '@angular/core/testing'; 25import {FormsModule} from '@angular/forms'; 26import {MatButtonModule} from '@angular/material/button'; 27import {MatDividerModule} from '@angular/material/divider'; 28import {MatFormFieldModule} from '@angular/material/form-field'; 29import {MatIconModule} from '@angular/material/icon'; 30import {MatInputModule} from '@angular/material/input'; 31import {MatSelectModule} from '@angular/material/select'; 32import {MatSliderModule} from '@angular/material/slider'; 33import {MatTooltipModule} from '@angular/material/tooltip'; 34import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 35import {assertDefined} from 'common/assert_utils'; 36import {TimestampConverterUtils} from 'common/time/test_utils'; 37import {PropertyTreeBuilder} from 'test/unit/property_tree_builder'; 38import {TraceBuilder} from 'test/unit/trace_builder'; 39import {UnitTestUtils} from 'test/unit/utils'; 40import {Trace, TraceEntry} from 'trace/trace'; 41import {TraceType} from 'trace/trace_type'; 42import {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 43import {LogSelectFilter} from 'viewers/common/log_filters'; 44import {LogHeader} from 'viewers/common/ui_data_log'; 45import {CollapsedSectionsComponent} from 'viewers/components/collapsed_sections_component'; 46import {CollapsibleSectionTitleComponent} from 'viewers/components/collapsible_section_title_component'; 47import {LogComponent} from 'viewers/components/log_component'; 48import {PropertiesComponent} from 'viewers/components/properties_component'; 49import {PropertyTreeNodeDataViewComponent} from 'viewers/components/property_tree_node_data_view_component'; 50import {RectsComponent} from 'viewers/components/rects/rects_component'; 51import {SearchBoxComponent} from 'viewers/components/search_box_component'; 52import {SelectWithFilterComponent} from 'viewers/components/select_with_filter_component'; 53import {TreeComponent} from 'viewers/components/tree_component'; 54import {TreeNodeComponent} from 'viewers/components/tree_node_component'; 55import {UserOptionsComponent} from 'viewers/components/user_options_component'; 56import {InputEntry, UiData} from './ui_data'; 57import {ViewerInputComponent} from './viewer_input_component'; 58 59describe('ViewerInputComponent', () => { 60 const testSpec = {name: 'Test Column', cssClass: 'test-class'}; 61 const testField = {spec: testSpec, value: 'VALUE'}; 62 let fixture: ComponentFixture<ViewerInputComponent>; 63 let component: ViewerInputComponent; 64 let htmlElement: HTMLElement; 65 66 let tree: PropertyTreeNode; 67 let trace: Trace<PropertyTreeNode>; 68 let entry: TraceEntry<PropertyTreeNode>; 69 70 beforeAll(async () => { 71 tree = new PropertyTreeBuilder() 72 .setIsRoot(true) 73 .setRootId('AndroidMotionEvent') 74 .setName('entry') 75 .build(); 76 trace = new TraceBuilder<PropertyTreeNode>() 77 .setType(TraceType.INPUT_EVENT_MERGED) 78 .setEntries([tree]) 79 .setTimestamps([TimestampConverterUtils.makeElapsedTimestamp(20n)]) 80 .build(); 81 entry = trace.getEntry(0); 82 }); 83 84 beforeEach(async () => { 85 await TestBed.configureTestingModule({ 86 providers: [{provide: ComponentFixtureAutoDetect, useValue: true}], 87 imports: [ 88 MatSliderModule, 89 MatTooltipModule, 90 MatDividerModule, 91 ScrollingModule, 92 MatIconModule, 93 ClipboardModule, 94 MatFormFieldModule, 95 MatButtonModule, 96 MatInputModule, 97 BrowserAnimationsModule, 98 FormsModule, 99 MatSelectModule, 100 HttpClientModule, 101 ], 102 declarations: [ 103 ViewerInputComponent, 104 TreeComponent, 105 TreeNodeComponent, 106 PropertyTreeNodeDataViewComponent, 107 PropertiesComponent, 108 CollapsedSectionsComponent, 109 CollapsibleSectionTitleComponent, 110 LogComponent, 111 RectsComponent, 112 SelectWithFilterComponent, 113 SearchBoxComponent, 114 UserOptionsComponent, 115 ], 116 }).compileComponents(); 117 118 fixture = TestBed.createComponent(ViewerInputComponent); 119 component = fixture.componentInstance; 120 htmlElement = fixture.nativeElement; 121 122 component.inputData = makeUiData(); 123 fixture.detectChanges(); 124 }); 125 126 it('can be created', () => { 127 expect(component).toBeTruthy(); 128 }); 129 130 it('renders filters in header', () => { 131 expect( 132 htmlElement.querySelector( 133 `.headers .filter.${testSpec.cssClass.split(' ')[0]}`, 134 ), 135 ).toBeTruthy(); 136 expect(htmlElement.querySelector(`.title-section .filter`)).toBeNull(); 137 }); 138 139 it('renders entries with field values and no trace timestamp', () => { 140 expect(htmlElement.querySelector('.scroll')).toBeTruthy(); 141 const entry = assertDefined( 142 htmlElement.querySelector( 143 `.scroll .entry .${testSpec.cssClass.split(' ')[0]}`, 144 ), 145 ); 146 expect(entry.textContent).toContain('VALUE'); 147 expect(htmlElement.querySelector('.scroll .entry .time')).toBeNull(); 148 }); 149 150 it('hides go to current time button', () => { 151 expect(htmlElement.querySelector('.go-to-current-time')).toBeNull(); 152 }); 153 154 it('shows message when no event is selected', () => { 155 assertDefined(component.inputData).propertiesTree = undefined; 156 assertDefined(component.inputData).dispatchPropertiesTree = undefined; 157 fixture.detectChanges(); 158 expect( 159 htmlElement.querySelector('.event-properties .placeholder-text') 160 ?.innerHTML, 161 ).toContain('No selected entry'); 162 expect( 163 htmlElement.querySelector('.dispatch-properties .placeholder-text') 164 ?.innerHTML, 165 ).toContain('No selected entry'); 166 }); 167 168 it('creates collapsed sections with no buttons', () => { 169 UnitTestUtils.checkNoCollapsedSectionButtons(htmlElement); 170 }); 171 172 it('handles collapse/expand', () => { 173 UnitTestUtils.checkSectionCollapseAndExpand( 174 htmlElement, 175 fixture, 176 '.rects-view', 177 'INPUT WINDOWS', 178 ); 179 UnitTestUtils.checkSectionCollapseAndExpand( 180 htmlElement, 181 fixture, 182 '.event-properties', 183 'EVENT DETAILS', 184 ); 185 UnitTestUtils.checkSectionCollapseAndExpand( 186 htmlElement, 187 fixture, 188 '.dispatch-properties', 189 'DISPATCH DETAILS', 190 ); 191 UnitTestUtils.checkSectionCollapseAndExpand( 192 htmlElement, 193 fixture, 194 '.log-view', 195 'EVENT LOG', 196 ); 197 }); 198 199 it('shows rects view when rects are defined', () => { 200 assertDefined(component.inputData).rectsToDraw = []; 201 fixture.detectChanges(); 202 expect(htmlElement.querySelector('.rects-view')).toBeTruthy(); 203 }); 204 205 it('hides rects view when rects are not defined', () => { 206 assertDefined(component.inputData).rectsToDraw = undefined; 207 fixture.detectChanges(); 208 expect(htmlElement.querySelector('.rects-view')).toBeNull(); 209 }); 210 211 function makeUiData(): UiData { 212 const entries = [ 213 createInputEntry(entry, 1), 214 createInputEntry(entry, 2), 215 createInputEntry(entry, 3), 216 ]; 217 218 const uiData = UiData.createEmpty(); 219 uiData.headers = [new LogHeader(testSpec, new LogSelectFilter([]))]; 220 uiData.entries = entries; 221 uiData.selectedIndex = 0; 222 223 uiData.rectsToDraw = []; 224 return uiData; 225 } 226 227 function createInputEntry( 228 entry: TraceEntry<PropertyTreeNode>, 229 num: number, 230 ): InputEntry { 231 return new InputEntry( 232 entry, 233 [ 234 { 235 spec: testSpec, 236 value: 'VALUE', 237 propagateEntryTimestamp: true, 238 }, 239 testField, 240 testField, 241 testField, 242 testField, 243 testField, 244 testField, 245 ], 246 tree, 247 tree, 248 undefined, 249 ); 250 } 251}); 252