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 */ 16 17import { 18 CdkVirtualScrollViewport, 19 ScrollingModule, 20} from '@angular/cdk/scrolling'; 21import { 22 ComponentFixture, 23 ComponentFixtureAutoDetect, 24 TestBed, 25} from '@angular/core/testing'; 26import {FormsModule} from '@angular/forms'; 27import {MatButtonModule} from '@angular/material/button'; 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 {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 33import {assertDefined} from 'common/assert_utils'; 34import {TimestampConverterUtils} from 'common/time/test_utils'; 35import {PropertyTreeBuilder} from 'test/unit/property_tree_builder'; 36import {TraceBuilder} from 'test/unit/trace_builder'; 37import {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 38import {LogSelectFilter} from 'viewers/common/log_filters'; 39import {executeScrollComponentTests} from 'viewers/common/scroll_component_tests'; 40import {LogHeader} from 'viewers/common/ui_data_log'; 41import {LogComponent} from 'viewers/components/log_component'; 42import {SearchBoxComponent} from 'viewers/components/search_box_component'; 43import {SelectWithFilterComponent} from 'viewers/components/select_with_filter_component'; 44import {ProtologScrollDirective} from './scroll_strategy/protolog_scroll_directive'; 45import {ProtologEntry, UiData} from './ui_data'; 46import {ViewerProtologComponent} from './viewer_protolog_component'; 47 48describe('ViewerProtologComponent', () => { 49 const testSpec = {name: 'Test Column', cssClass: 'test-class'}; 50 const testField = {spec: testSpec, value: 'VALUE'}; 51 let fixture: ComponentFixture<ViewerProtologComponent>; 52 let component: ViewerProtologComponent; 53 let htmlElement: HTMLElement; 54 55 describe('Main component', () => { 56 beforeEach(async () => { 57 await setUpTestEnvironment(); 58 }); 59 60 it('can be created', () => { 61 expect(component).toBeTruthy(); 62 }); 63 64 it('render headers as filters', () => { 65 expect( 66 htmlElement.querySelector( 67 `.headers .filter.${testSpec.cssClass.split(' ')[0]}`, 68 ), 69 ).toBeTruthy(); 70 }); 71 72 it('renders entries with field values and no trace timestamp', () => { 73 expect(htmlElement.querySelector('.scroll')).toBeTruthy(); 74 const entry = assertDefined( 75 htmlElement.querySelector( 76 `.scroll .entry .${testSpec.cssClass.split(' ')[0]}`, 77 ), 78 ); 79 expect(entry.textContent).toContain('VALUE'); 80 81 const entryTimestamp = assertDefined( 82 htmlElement.querySelector('.scroll .entry .time'), 83 ); 84 expect(entryTimestamp.textContent?.trim()).toEqual('10ns'); 85 }); 86 87 it('shows go to current time button', () => { 88 expect(htmlElement.querySelector('.go-to-current-time')).toBeTruthy(); 89 }); 90 }); 91 92 describe('Scroll component', () => { 93 executeScrollComponentTests(setUpTestEnvironment); 94 }); 95 96 async function setUpTestEnvironment(): Promise< 97 [ 98 ComponentFixture<ViewerProtologComponent>, 99 HTMLElement, 100 CdkVirtualScrollViewport, 101 ] 102 > { 103 await TestBed.configureTestingModule({ 104 providers: [{provide: ComponentFixtureAutoDetect, useValue: true}], 105 imports: [ 106 ScrollingModule, 107 MatFormFieldModule, 108 FormsModule, 109 MatInputModule, 110 BrowserAnimationsModule, 111 MatSelectModule, 112 MatButtonModule, 113 MatIconModule, 114 ], 115 declarations: [ 116 ViewerProtologComponent, 117 SelectWithFilterComponent, 118 LogComponent, 119 ProtologScrollDirective, 120 SearchBoxComponent, 121 ], 122 }).compileComponents(); 123 fixture = TestBed.createComponent(ViewerProtologComponent); 124 component = fixture.componentInstance; 125 htmlElement = fixture.nativeElement; 126 127 component.inputData = makeUiData(); 128 fixture.detectChanges(); 129 const viewport = assertDefined(component.logComponent?.scrollComponent); 130 return [fixture, htmlElement, viewport]; 131 } 132 133 function makeUiData(): UiData { 134 const propertiesTree = new PropertyTreeBuilder() 135 .setRootId('Protolog') 136 .setName('tree') 137 .setValue(null) 138 .build(); 139 const ts = TimestampConverterUtils.makeElapsedTimestamp(10n); 140 const trace = new TraceBuilder<PropertyTreeNode>() 141 .setEntries([propertiesTree, propertiesTree]) 142 .setTimestamps([ts, ts]) 143 .build(); 144 145 const messages: ProtologEntry[] = []; 146 const shortMessage = 'test information about message'; 147 const longMessage = shortMessage.repeat(10) + 'keep'; 148 const traceEntry = trace.getEntry(0); 149 for (let i = 0; i < 200; i++) { 150 messages.push( 151 new ProtologEntry(traceEntry, [ 152 testField, 153 testField, 154 testField, 155 { 156 spec: {name: 'Test Column Text', cssClass: 'test-class-text'}, 157 value: i % 2 === 0 ? shortMessage : longMessage, 158 }, 159 ]), 160 ); 161 } 162 return new UiData( 163 [new LogHeader(testSpec, new LogSelectFilter([]))], 164 messages, 165 150, 166 undefined, 167 undefined, 168 ); 169 } 170}); 171