1// Copyright 2024 The Pigweed Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may not 4// use this file except in compliance with the License. You may obtain a copy of 5// the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12// License for the specific language governing permissions and limitations under 13// the License. 14 15import { MockLogSource } from '../src/custom/mock-log-source'; 16import { createLogViewer } from '../src/createLogViewer'; 17import { expect } from '@open-wc/testing'; 18import { LocalStateStorage, StateService } from '../src/shared/state'; 19import { NodeType, Orientation, ViewNode } from '../src/shared/view-node'; 20 21function setUpLogViewer() { 22 const mockLogSource = new MockLogSource(); 23 const destroyLogViewer = createLogViewer(mockLogSource, document.body); 24 const logViewer = document.querySelector('log-viewer'); 25 return { mockLogSource, destroyLogViewer, logViewer }; 26} 27 28// Handle benign ResizeObserver error caused by custom log viewer initialization 29// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors 30function handleResizeObserverError() { 31 const e = window.onerror; 32 window.onerror = function (err) { 33 if ( 34 err === 'ResizeObserver loop completed with undelivered notifications.' 35 ) { 36 console.warn( 37 'Ignored: ResizeObserver loop completed with undelivered notifications.', 38 ); 39 return false; 40 } else { 41 return e(...arguments); 42 } 43 }; 44} 45 46describe('log-view-controls', () => { 47 let mockLogSource; 48 let destroyLogViewer; 49 let logViewer; 50 let mockColumnData; 51 let mockState; 52 let stateService; 53 54 before(() => { 55 mockColumnData = [ 56 { 57 fieldName: 'test', 58 characterLength: 0, 59 manualWidth: null, 60 isVisible: false, 61 }, 62 { 63 fieldName: 'foo', 64 characterLength: 0, 65 manualWidth: null, 66 isVisible: true, 67 }, 68 { 69 fieldName: 'bar', 70 characterLength: 0, 71 manualWidth: null, 72 isVisible: false, 73 }, 74 ]; 75 mockState = { 76 rootNode: new ViewNode({ 77 type: NodeType.Split, 78 orientation: Orientation.Horizontal, 79 children: [ 80 new ViewNode({ 81 searchText: 'hello', 82 logViewId: 'child-node-1', 83 type: NodeType.View, 84 columnData: mockColumnData, 85 }), 86 new ViewNode({ 87 searchText: 'world', 88 logViewId: 'child-node-2', 89 type: NodeType.View, 90 columnData: mockColumnData, 91 }), 92 ], 93 }), 94 }; 95 96 stateService = new StateService(new LocalStateStorage()); 97 stateService.saveState(mockState); 98 handleResizeObserverError(); 99 }); 100 101 after(() => { 102 window.localStorage.clear(); 103 mockLogSource.stop(); 104 destroyLogViewer(); 105 }); 106 107 describe('state', () => { 108 it('should populate search field value on component load', async () => { 109 ({ mockLogSource, destroyLogViewer, logViewer } = setUpLogViewer()); 110 const logViews = await getLogViews(); 111 const stateSearchString = 112 mockState.rootNode.children[0].logViewState.searchText; 113 const logControls = 114 logViews[0].shadowRoot.querySelector('log-view-controls'); 115 logControls.requestUpdate(); 116 const inputEl = logControls.shadowRoot.querySelector('.search-field'); 117 118 await logViewer.updateComplete; 119 expect(inputEl.value).to.equal(stateSearchString); 120 }); 121 122 it('should populate search field values for multiple log views on component load', async () => { 123 ({ mockLogSource, destroyLogViewer, logViewer } = setUpLogViewer()); 124 125 const state = stateService.loadState(); 126 const logViews = await getLogViews(); 127 const searchInputs = []; 128 129 logViews.forEach((logView) => { 130 const logControls = 131 logView.shadowRoot.querySelector('log-view-controls'); 132 const inputEl = logControls.shadowRoot.querySelector('.search-field'); 133 searchInputs.push(inputEl.value); 134 }); 135 136 state.rootNode.children.forEach((childNode, index) => { 137 expect(childNode.logViewState.searchText).to.equal(searchInputs[index]); 138 }); 139 }); 140 141 it('should recall table column visibility on component load', async () => { 142 ({ mockLogSource, destroyLogViewer, logViewer } = setUpLogViewer()); 143 144 const state = stateService.loadState(); 145 const logViews = await getLogViews(); 146 const logControls = 147 logViews[0].shadowRoot.querySelector('log-view-controls'); 148 const colToggleMenu = logControls.shadowRoot 149 .querySelector('#col-toggle-menu') 150 .querySelectorAll('.item-checkbox'); 151 152 colToggleMenu.forEach((field, index) => { 153 const columnData = state.rootNode.children[0].logViewState.columnData; 154 expect(field.checked).to.equal(columnData[index].isVisible); 155 }); 156 157 const colToggleSubMenu = logControls.shadowRoot 158 .querySelector('#col-toggle-sub-menu') 159 .querySelectorAll('.item-checkbox'); 160 161 colToggleSubMenu.forEach((field, index) => { 162 const columnData = state.rootNode.children[0].logViewState.columnData; 163 expect(field.checked).to.equal(columnData[index].isVisible); 164 }); 165 }); 166 167 async function getLogViews() { 168 const logViewerEl = document.querySelector('log-viewer'); 169 await logViewerEl.updateComplete; 170 await new Promise((resolve) => setTimeout(resolve, 100)); 171 const logViews = logViewerEl.shadowRoot.querySelectorAll('log-view'); 172 return logViews; 173 } 174 }); 175}); 176