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 */ 16import {ClipboardModule} from '@angular/cdk/clipboard'; 17import {CommonModule} from '@angular/common'; 18import { 19 ComponentFixture, 20 ComponentFixtureAutoDetect, 21 TestBed, 22} from '@angular/core/testing'; 23import {FormsModule, ReactiveFormsModule} from '@angular/forms'; 24import {MatButtonModule} from '@angular/material/button'; 25import {MatDividerModule} from '@angular/material/divider'; 26import {MatFormFieldModule} from '@angular/material/form-field'; 27import {MatIconModule} from '@angular/material/icon'; 28import {MatInputModule} from '@angular/material/input'; 29import {MatTooltipModule} from '@angular/material/tooltip'; 30import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 31import {assertDefined} from 'common/assert_utils'; 32import {FilterFlag} from 'common/filter_flag'; 33import {PersistentStore} from 'common/persistent_store'; 34import {PropertyTreeBuilder} from 'test/unit/property_tree_builder'; 35import {TraceType} from 'trace/trace_type'; 36import {TextFilter} from 'viewers/common/text_filter'; 37import {UiPropertyTreeNode} from 'viewers/common/ui_property_tree_node'; 38import {ViewerEvents} from 'viewers/common/viewer_events'; 39import {CollapsibleSectionTitleComponent} from './collapsible_section_title_component'; 40import {PropertiesComponent} from './properties_component'; 41import {PropertyTreeNodeDataViewComponent} from './property_tree_node_data_view_component'; 42import {SearchBoxComponent} from './search_box_component'; 43import {SurfaceFlingerPropertyGroupsComponent} from './surface_flinger_property_groups_component'; 44import {TreeComponent} from './tree_component'; 45import {TreeNodeComponent} from './tree_node_component'; 46import {UserOptionsComponent} from './user_options_component'; 47 48describe('PropertiesComponent', () => { 49 let fixture: ComponentFixture<PropertiesComponent>; 50 let component: PropertiesComponent; 51 let htmlElement: HTMLElement; 52 53 beforeEach(async () => { 54 await TestBed.configureTestingModule({ 55 providers: [{provide: ComponentFixtureAutoDetect, useValue: true}], 56 declarations: [ 57 PropertiesComponent, 58 SurfaceFlingerPropertyGroupsComponent, 59 TreeComponent, 60 TreeNodeComponent, 61 PropertyTreeNodeDataViewComponent, 62 CollapsibleSectionTitleComponent, 63 UserOptionsComponent, 64 SearchBoxComponent, 65 ], 66 imports: [ 67 CommonModule, 68 MatInputModule, 69 MatFormFieldModule, 70 MatButtonModule, 71 MatDividerModule, 72 BrowserAnimationsModule, 73 FormsModule, 74 ReactiveFormsModule, 75 MatIconModule, 76 MatTooltipModule, 77 ClipboardModule, 78 ], 79 }).compileComponents(); 80 81 fixture = TestBed.createComponent(PropertiesComponent); 82 component = fixture.componentInstance; 83 htmlElement = fixture.nativeElement; 84 85 component.store = new PersistentStore(); 86 component.userOptions = { 87 showDiff: { 88 name: 'Show diff', 89 enabled: false, 90 isUnavailable: false, 91 }, 92 }; 93 component.textFilter = new TextFilter(); 94 component.traceType = TraceType.SURFACE_FLINGER; 95 96 fixture.detectChanges(); 97 }); 98 99 it('can be created', () => { 100 expect(component).toBeTruthy(); 101 }); 102 103 it('creates title', () => { 104 const title = htmlElement.querySelector('.properties-title'); 105 expect(title).toBeTruthy(); 106 }); 107 108 it('renders view controls', () => { 109 const viewControls = htmlElement.querySelector('.view-controls'); 110 expect(viewControls).toBeTruthy(); 111 const box = htmlElement.querySelector('.view-controls .user-option'); 112 expect(box).toBeTruthy(); //renders at least one view control option 113 }); 114 115 it('renders tree in proto dump upon selected item', () => { 116 const tree = new PropertyTreeBuilder() 117 .setRootId('selectedItem') 118 .setName('property') 119 .setValue(null) 120 .build(); 121 tree.setIsRoot(true); 122 component.propertiesTree = UiPropertyTreeNode.from(tree); 123 fixture.detectChanges(); 124 const treeEl = htmlElement.querySelector('tree-view'); 125 expect(treeEl).toBeTruthy(); 126 }); 127 128 it('renders placeholder text', () => { 129 component.propertiesTree = undefined; 130 component.placeholderText = 'Placeholder text'; 131 fixture.detectChanges(); 132 expect( 133 htmlElement.querySelector('.placeholder-text')?.textContent, 134 ).toContain('Placeholder text'); 135 }); 136 137 it('handles node click', () => { 138 const tree = new PropertyTreeBuilder() 139 .setRootId('selectedItem') 140 .setName('property') 141 .setValue(null) 142 .build(); 143 tree.setIsRoot(true); 144 component.propertiesTree = UiPropertyTreeNode.from(tree); 145 fixture.detectChanges(); 146 147 let highlightedItem: string | undefined; 148 htmlElement.addEventListener( 149 ViewerEvents.HighlightedPropertyChange, 150 (event) => { 151 highlightedItem = (event as CustomEvent).detail.id; 152 }, 153 ); 154 155 const node = assertDefined( 156 htmlElement.querySelector('tree-node'), 157 ) as HTMLElement; 158 node.click(); 159 fixture.detectChanges(); 160 expect(highlightedItem).toEqual(tree.id); 161 }); 162 163 it('handles change in filter', () => { 164 let textFilter: TextFilter | undefined; 165 htmlElement.addEventListener( 166 ViewerEvents.PropertiesFilterChange, 167 (event) => { 168 textFilter = (event as CustomEvent).detail; 169 }, 170 ); 171 const inputEl = assertDefined( 172 htmlElement.querySelector<HTMLInputElement>('.title-section input'), 173 ); 174 const flagButton = assertDefined( 175 htmlElement.querySelector<HTMLElement>('.search-box button'), 176 ); 177 flagButton.click(); 178 fixture.detectChanges(); 179 180 inputEl.value = 'Root'; 181 inputEl.dispatchEvent(new Event('input')); 182 fixture.detectChanges(); 183 expect(textFilter).toEqual(new TextFilter('Root', [FilterFlag.MATCH_CASE])); 184 }); 185 186 it('handles collapse button click', () => { 187 const spy = spyOn(component.collapseButtonClicked, 'emit'); 188 const collapseButton = assertDefined( 189 htmlElement.querySelector('collapsible-section-title button'), 190 ) as HTMLButtonElement; 191 collapseButton.click(); 192 fixture.detectChanges(); 193 expect(spy).toHaveBeenCalled(); 194 }); 195}); 196