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 {Component} from '@angular/core'; 17import {ComponentFixture, TestBed} from '@angular/core/testing'; 18import {MatButtonModule} from '@angular/material/button'; 19import {MatDividerModule} from '@angular/material/divider'; 20import {MatIconModule} from '@angular/material/icon'; 21import {MatTooltipModule} from '@angular/material/tooltip'; 22import {assertDefined} from 'common/assert_utils'; 23import {TreeNodeUtils} from 'test/unit/tree_node_utils'; 24import {ImeAdditionalProperties} from 'viewers/common/ime_additional_properties'; 25import {ViewerEvents} from 'viewers/common/viewer_events'; 26import {CollapsibleSectionTitleComponent} from './collapsible_section_title_component'; 27import {CoordinatesTableComponent} from './coordinates_table_component'; 28import {ImeAdditionalPropertiesComponent} from './ime_additional_properties_component'; 29 30describe('ImeAdditionalPropertiesComponent', () => { 31 let fixture: ComponentFixture<TestHostComponent>; 32 let component: TestHostComponent; 33 let htmlElement: HTMLElement; 34 35 beforeEach(async () => { 36 await TestBed.configureTestingModule({ 37 imports: [ 38 MatDividerModule, 39 MatIconModule, 40 MatButtonModule, 41 MatTooltipModule, 42 ], 43 declarations: [ 44 ImeAdditionalPropertiesComponent, 45 TestHostComponent, 46 CollapsibleSectionTitleComponent, 47 CoordinatesTableComponent, 48 ], 49 }).compileComponents(); 50 fixture = TestBed.createComponent(TestHostComponent); 51 component = fixture.componentInstance; 52 htmlElement = fixture.nativeElement; 53 htmlElement.addEventListener( 54 ViewerEvents.HighlightedNodeChange, 55 component.onHighlightedNodeChange, 56 ); 57 htmlElement.addEventListener( 58 ViewerEvents.HighlightedIdChange, 59 component.onHighlightedIdChange, 60 ); 61 htmlElement.addEventListener( 62 ViewerEvents.AdditionalPropertySelected, 63 component.onAdditionalPropertySelectedChange, 64 ); 65 fixture.detectChanges(); 66 }); 67 68 it('can be created', () => { 69 expect(component).toBeTruthy(); 70 }); 71 72 it('shows client or service sf properties', () => { 73 expect(htmlElement.querySelector('.ime-container')).toBeDefined(); 74 expect(htmlElement.querySelector('.input-method-surface')).toBeDefined(); 75 }); 76 77 it('renders placeholder text', () => { 78 component.additionalProperties = undefined; 79 fixture.detectChanges(); 80 expect( 81 htmlElement.querySelector('.placeholder-text')?.textContent, 82 ).toContain('No IME entry found.'); 83 }); 84 85 it('emits update additional property tree event on wm state button click', () => { 86 const button = assertDefined( 87 htmlElement.querySelector('.wm-state-button'), 88 ) as HTMLButtonElement; 89 expect(button.className).not.toContain('selected'); 90 button.click(); 91 fixture.detectChanges(); 92 expect(component.additionalPropertieTreeName).toEqual( 93 'Window Manager State', 94 ); 95 expect(button.className).toContain('selected'); 96 }); 97 98 it('propagates new ime container layer on button click', () => { 99 const button = assertDefined( 100 htmlElement.querySelector('.ime-container-button'), 101 ) as HTMLButtonElement; 102 expect(button.className).not.toContain('selected'); 103 button.click(); 104 fixture.detectChanges(); 105 expect(component.highlightedItem).toEqual('123'); 106 expect(button.className).toContain('selected'); 107 }); 108 109 it('propagates new input method surface layer on button click', () => { 110 const button = assertDefined( 111 htmlElement.querySelector('.input-method-surface-button'), 112 ) as HTMLButtonElement; 113 expect(button.className).not.toContain('selected'); 114 button.click(); 115 fixture.detectChanges(); 116 expect(component.highlightedItem).toEqual('456'); 117 expect(button.className).toContain('selected'); 118 }); 119 120 it('shows ime manager service wm properties', () => { 121 component.isImeManagerService = true; 122 fixture.detectChanges(); 123 const imeManagerService = assertDefined( 124 htmlElement.querySelector('.ime-manager-service'), 125 ); 126 expect( 127 assertDefined(imeManagerService.querySelector('.wm-state')).textContent, 128 ).toContain('1970-01-01, 00:00:00.000000000'); 129 expect( 130 imeManagerService.querySelector('.ime-control-target-button'), 131 ).toBeDefined(); 132 }); 133 134 it('propagates new property tree node window on button click', () => { 135 component.isImeManagerService = true; 136 fixture.detectChanges(); 137 const button = assertDefined( 138 htmlElement.querySelector('.ime-control-target-button'), 139 ) as HTMLButtonElement; 140 expect(button.className).not.toContain('selected'); 141 button.click(); 142 fixture.detectChanges(); 143 expect(component.additionalPropertieTreeName).toEqual('Ime Control Target'); 144 expect(button.className).toContain('selected'); 145 }); 146 147 it('handles collapse button click', () => { 148 expect(component.collapseButtonClicked).toBeFalse(); 149 const collapseButton = assertDefined( 150 htmlElement.querySelector('collapsible-section-title button'), 151 ) as HTMLButtonElement; 152 collapseButton.click(); 153 fixture.detectChanges(); 154 expect(component.collapseButtonClicked).toBeTrue(); 155 }); 156 157 @Component({ 158 selector: 'host-component', 159 template: ` 160 <ime-additional-properties 161 [highlightedItem]="highlightedItem" 162 [isImeManagerService]="isImeManagerService" 163 [additionalProperties]="additionalProperties" 164 (collapseButtonClicked)="onCollapseButtonClick()"></ime-additional-properties> 165 `, 166 }) 167 class TestHostComponent { 168 isImeManagerService = false; 169 170 additionalProperties: ImeAdditionalProperties | undefined = 171 new ImeAdditionalProperties( 172 { 173 id: 'wmStateId', 174 name: 'wmState', 175 wmStateProperties: { 176 timestamp: '1970-01-01, 00:00:00.000000000', 177 focusedApp: 'exampleFocusedApp', 178 focusedWindow: undefined, 179 focusedActivity: undefined, 180 isInputMethodWindowVisible: false, 181 imeControlTarget: TreeNodeUtils.makePropertyNode( 182 'DisplayContent.inputMethodControlTarget', 183 'inputMethodControlTarget', 184 null, 185 ), 186 imeInputTarget: undefined, 187 imeLayeringTarget: undefined, 188 imeInsetsSourceProvider: undefined, 189 }, 190 hierarchyTree: TreeNodeUtils.makeHierarchyNode({ 191 name: 'wmStateProto', 192 }), 193 }, 194 { 195 id: 'ime', 196 name: 'imeLayers', 197 properties: { 198 imeContainer: { 199 id: '123', 200 zOrderRelativeOfId: -1, 201 z: 0, 202 }, 203 inputMethodSurface: { 204 id: '456', 205 isVisible: false, 206 }, 207 focusedWindowColor: undefined, 208 root: undefined, 209 }, 210 taskLayerOfImeContainer: undefined, 211 taskLayerOfImeSnapshot: undefined, 212 }, 213 ); 214 highlightedItem = ''; 215 additionalPropertieTreeName: string | undefined; 216 collapseButtonClicked = false; 217 218 onHighlightedNodeChange = (event: Event) => { 219 this.highlightedItem = (event as CustomEvent).detail.node.id; 220 }; 221 onHighlightedIdChange = (event: Event) => { 222 this.highlightedItem = (event as CustomEvent).detail.id; 223 }; 224 onAdditionalPropertySelectedChange = (event: Event) => { 225 this.highlightedItem = ( 226 event as CustomEvent 227 ).detail.selectedItem.treeNode.id; 228 this.additionalPropertieTreeName = ( 229 event as CustomEvent 230 ).detail.selectedItem.name; 231 }; 232 233 onCollapseButtonClick() { 234 this.collapseButtonClicked = true; 235 } 236 } 237}); 238