• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2023 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 {
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 {MatDividerModule} from '@angular/material/divider';
29import {MatFormFieldModule} from '@angular/material/form-field';
30import {MatIconModule} from '@angular/material/icon';
31import {MatInputModule} from '@angular/material/input';
32import {MatSelectModule} from '@angular/material/select';
33import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
34import {assertDefined} from 'common/assert_utils';
35import {TimestampConverterUtils} from 'common/time/test_utils';
36import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
37import {TraceBuilder} from 'test/unit/trace_builder';
38import {UnitTestUtils} from 'test/unit/utils';
39import {Trace, TraceEntry} from 'trace/trace';
40import {TraceType} from 'trace/trace_type';
41import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
42import {LogSelectFilter} from 'viewers/common/log_filters';
43import {executeScrollComponentTests} from 'viewers/common/scroll_component_tests';
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 {SearchBoxComponent} from 'viewers/components/search_box_component';
51import {SelectWithFilterComponent} from 'viewers/components/select_with_filter_component';
52import {TreeComponent} from 'viewers/components/tree_component';
53import {TreeNodeComponent} from 'viewers/components/tree_node_component';
54import {TransitionsScrollDirective} from './scroll_strategy/transitions_scroll_directive';
55import {TransitionsEntry, UiData} from './ui_data';
56import {ViewerTransitionsComponent} from './viewer_transitions_component';
57
58describe('ViewerTransitionsComponent', () => {
59  const testSpec = {name: 'Test Column', cssClass: 'test-class'};
60  const testField = {spec: testSpec, value: 'VALUE'};
61  let transitionTree: PropertyTreeNode;
62  let trace: Trace<PropertyTreeNode>;
63  let entry: TraceEntry<PropertyTreeNode>;
64  let fixture: ComponentFixture<ViewerTransitionsComponent>;
65  let component: ViewerTransitionsComponent;
66  let htmlElement: HTMLElement;
67
68  beforeAll(() => {
69    transitionTree = new PropertyTreeBuilder()
70      .setIsRoot(true)
71      .setRootId('TransitionTraceEntry')
72      .setName('transition')
73      .build();
74    trace = new TraceBuilder<PropertyTreeNode>()
75      .setType(TraceType.TRANSITION)
76      .setEntries([transitionTree])
77      .setTimestamps([TimestampConverterUtils.makeElapsedTimestamp(20n)])
78      .build();
79    entry = trace.getEntry(0);
80  });
81
82  describe('Main component', () => {
83    beforeEach(async () => {
84      await setUpTestEnvironment();
85    });
86
87    it('can be created', () => {
88      expect(component).toBeTruthy();
89    });
90
91    it('renders headers with filters', () => {
92      expect(
93        htmlElement.querySelector(
94          `.headers .filter.${testSpec.cssClass.split(' ')[0]}`,
95        ),
96      ).toBeTruthy();
97    });
98
99    it('renders entries with field values and no trace timestamp', () => {
100      expect(htmlElement.querySelector('.scroll')).toBeTruthy();
101      const entry = assertDefined(
102        htmlElement.querySelector(
103          `.scroll .entry .${testSpec.cssClass.split(' ')[0]}`,
104        ),
105      );
106      expect(entry.textContent).toContain('VALUE');
107      expect(htmlElement.querySelector('.scroll .entry .time')).toBeNull();
108    });
109
110    it('hides go to current time button', () => {
111      expect(htmlElement.querySelector('.go-to-current-time')).toBeNull();
112    });
113
114    it('renders properties', () => {
115      expect(htmlElement.querySelector('.properties-view')).toBeTruthy();
116    });
117
118    it('shows message when no transition is selected', () => {
119      assertDefined(component.inputData).propertiesTree = undefined;
120      fixture.detectChanges();
121      expect(
122        htmlElement.querySelector('.properties-view .placeholder-text')
123          ?.innerHTML,
124      ).toContain('No current or selected transition');
125    });
126
127    it('creates collapsed sections with no buttons', () => {
128      UnitTestUtils.checkNoCollapsedSectionButtons(htmlElement);
129    });
130
131    it('handles properties section collapse/expand', () => {
132      UnitTestUtils.checkSectionCollapseAndExpand(
133        htmlElement,
134        fixture,
135        '.properties-view',
136        'SELECTED TRANSITION',
137      );
138    });
139  });
140
141  describe('Scroll component', () => {
142    executeScrollComponentTests(setUpTestEnvironment);
143  });
144
145  async function setUpTestEnvironment(): Promise<
146    [
147      ComponentFixture<ViewerTransitionsComponent>,
148      HTMLElement,
149      CdkVirtualScrollViewport,
150    ]
151  > {
152    await TestBed.configureTestingModule({
153      providers: [{provide: ComponentFixtureAutoDetect, useValue: true}],
154      imports: [
155        MatDividerModule,
156        ScrollingModule,
157        MatIconModule,
158        ClipboardModule,
159        MatFormFieldModule,
160        MatButtonModule,
161        MatInputModule,
162        BrowserAnimationsModule,
163        FormsModule,
164        MatSelectModule,
165      ],
166      declarations: [
167        ViewerTransitionsComponent,
168        TreeComponent,
169        TreeNodeComponent,
170        PropertyTreeNodeDataViewComponent,
171        PropertiesComponent,
172        CollapsedSectionsComponent,
173        CollapsibleSectionTitleComponent,
174        LogComponent,
175        SearchBoxComponent,
176        TransitionsScrollDirective,
177        SelectWithFilterComponent,
178      ],
179    }).compileComponents();
180    fixture = TestBed.createComponent(ViewerTransitionsComponent);
181    component = fixture.componentInstance;
182    htmlElement = fixture.nativeElement;
183    component.inputData = makeUiData();
184    fixture.detectChanges();
185    const viewport = assertDefined(component.logComponent?.scrollComponent);
186    return [fixture, htmlElement, viewport];
187  }
188
189  function makeUiData(): UiData {
190    const transitions = [];
191    for (let i = 0; i < 200; i++) {
192      transitions.push(createMockTransition(entry, i));
193    }
194    const uiData = UiData.createEmpty();
195    uiData.headers = [new LogHeader(testSpec, new LogSelectFilter([]))];
196    uiData.entries = transitions;
197    uiData.selectedIndex = 0;
198    return uiData;
199  }
200
201  function createMockTransition(
202    entry: TraceEntry<PropertyTreeNode>,
203    i: number,
204  ): TransitionsEntry {
205    return new TransitionsEntry(
206      entry,
207      [
208        testField,
209        testField,
210        testField,
211        testField,
212        testField,
213        testField,
214        {spec: testSpec, value: i % 2 === 0 ? 'VALUE' : 'VALUE'.repeat(40)},
215      ],
216      transitionTree,
217    );
218  }
219});
220