1*61c4878aSAndroid Build Coastguard Worker// Copyright 2024 The Pigweed Authors 2*61c4878aSAndroid Build Coastguard Worker// 3*61c4878aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); you may not 4*61c4878aSAndroid Build Coastguard Worker// use this file except in compliance with the License. You may obtain a copy of 5*61c4878aSAndroid Build Coastguard Worker// the License at 6*61c4878aSAndroid Build Coastguard Worker// 7*61c4878aSAndroid Build Coastguard Worker// https://www.apache.org/licenses/LICENSE-2.0 8*61c4878aSAndroid Build Coastguard Worker// 9*61c4878aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*61c4878aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11*61c4878aSAndroid Build Coastguard Worker// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12*61c4878aSAndroid Build Coastguard Worker// License for the specific language governing permissions and limitations under 13*61c4878aSAndroid Build Coastguard Worker// the License. 14*61c4878aSAndroid Build Coastguard Worker 15*61c4878aSAndroid Build Coastguard Workerimport { assert } from '@open-wc/testing'; 16*61c4878aSAndroid Build Coastguard Workerimport { MockLogSource } from '../src/custom/mock-log-source'; 17*61c4878aSAndroid Build Coastguard Workerimport { createLogViewer } from '../src/createLogViewer'; 18*61c4878aSAndroid Build Coastguard Workerimport { LocalStateStorage, StateService } from '../src/shared/state'; 19*61c4878aSAndroid Build Coastguard Workerimport { NodeType, Orientation, ViewNode } from '../src/shared/view-node'; 20*61c4878aSAndroid Build Coastguard Worker 21*61c4878aSAndroid Build Coastguard Workerfunction setUpLogViewer(logSources) { 22*61c4878aSAndroid Build Coastguard Worker const destroyLogViewer = createLogViewer(logSources, document.body); 23*61c4878aSAndroid Build Coastguard Worker const logViewer = document.querySelector('log-viewer'); 24*61c4878aSAndroid Build Coastguard Worker return { logSources, destroyLogViewer, logViewer }; 25*61c4878aSAndroid Build Coastguard Worker} 26*61c4878aSAndroid Build Coastguard Worker 27*61c4878aSAndroid Build Coastguard Worker// Handle benign ResizeObserver error caused by custom log viewer initialization 28*61c4878aSAndroid Build Coastguard Worker// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors 29*61c4878aSAndroid Build Coastguard Workerfunction handleResizeObserverError() { 30*61c4878aSAndroid Build Coastguard Worker const e = window.onerror; 31*61c4878aSAndroid Build Coastguard Worker window.onerror = function (err) { 32*61c4878aSAndroid Build Coastguard Worker if ( 33*61c4878aSAndroid Build Coastguard Worker err === 'ResizeObserver loop completed with undelivered notifications.' 34*61c4878aSAndroid Build Coastguard Worker ) { 35*61c4878aSAndroid Build Coastguard Worker console.warn( 36*61c4878aSAndroid Build Coastguard Worker 'Ignored: ResizeObserver loop completed with undelivered notifications.', 37*61c4878aSAndroid Build Coastguard Worker ); 38*61c4878aSAndroid Build Coastguard Worker return false; 39*61c4878aSAndroid Build Coastguard Worker } else { 40*61c4878aSAndroid Build Coastguard Worker return e(...arguments); 41*61c4878aSAndroid Build Coastguard Worker } 42*61c4878aSAndroid Build Coastguard Worker }; 43*61c4878aSAndroid Build Coastguard Worker} 44*61c4878aSAndroid Build Coastguard Worker 45*61c4878aSAndroid Build Coastguard Workerdescribe('log-view', () => { 46*61c4878aSAndroid Build Coastguard Worker let logSources; 47*61c4878aSAndroid Build Coastguard Worker let destroyLogViewer; 48*61c4878aSAndroid Build Coastguard Worker let logViewer; 49*61c4878aSAndroid Build Coastguard Worker let stateService; 50*61c4878aSAndroid Build Coastguard Worker let mockColumnData; 51*61c4878aSAndroid Build Coastguard Worker let mockState; 52*61c4878aSAndroid Build Coastguard Worker 53*61c4878aSAndroid Build Coastguard Worker async function getLogViews() { 54*61c4878aSAndroid Build Coastguard Worker const logViewerEl = document.querySelector('log-viewer'); 55*61c4878aSAndroid Build Coastguard Worker await logViewerEl.updateComplete; 56*61c4878aSAndroid Build Coastguard Worker await new Promise((resolve) => setTimeout(resolve, 100)); 57*61c4878aSAndroid Build Coastguard Worker const logViews = logViewerEl.shadowRoot.querySelectorAll('log-view'); 58*61c4878aSAndroid Build Coastguard Worker return logViews; 59*61c4878aSAndroid Build Coastguard Worker } 60*61c4878aSAndroid Build Coastguard Worker 61*61c4878aSAndroid Build Coastguard Worker describe('state', () => { 62*61c4878aSAndroid Build Coastguard Worker beforeEach(() => { 63*61c4878aSAndroid Build Coastguard Worker mockColumnData = [ 64*61c4878aSAndroid Build Coastguard Worker { 65*61c4878aSAndroid Build Coastguard Worker fieldName: 'test', 66*61c4878aSAndroid Build Coastguard Worker characterLength: 0, 67*61c4878aSAndroid Build Coastguard Worker manualWidth: null, 68*61c4878aSAndroid Build Coastguard Worker isVisible: false, 69*61c4878aSAndroid Build Coastguard Worker }, 70*61c4878aSAndroid Build Coastguard Worker { 71*61c4878aSAndroid Build Coastguard Worker fieldName: 'foo', 72*61c4878aSAndroid Build Coastguard Worker characterLength: 0, 73*61c4878aSAndroid Build Coastguard Worker manualWidth: null, 74*61c4878aSAndroid Build Coastguard Worker isVisible: true, 75*61c4878aSAndroid Build Coastguard Worker }, 76*61c4878aSAndroid Build Coastguard Worker { 77*61c4878aSAndroid Build Coastguard Worker fieldName: 'bar', 78*61c4878aSAndroid Build Coastguard Worker characterLength: 0, 79*61c4878aSAndroid Build Coastguard Worker manualWidth: null, 80*61c4878aSAndroid Build Coastguard Worker isVisible: false, 81*61c4878aSAndroid Build Coastguard Worker }, 82*61c4878aSAndroid Build Coastguard Worker ]; 83*61c4878aSAndroid Build Coastguard Worker 84*61c4878aSAndroid Build Coastguard Worker mockState = { 85*61c4878aSAndroid Build Coastguard Worker rootNode: new ViewNode({ 86*61c4878aSAndroid Build Coastguard Worker type: NodeType.Split, 87*61c4878aSAndroid Build Coastguard Worker orientation: Orientation.Horizontal, 88*61c4878aSAndroid Build Coastguard Worker children: [ 89*61c4878aSAndroid Build Coastguard Worker new ViewNode({ 90*61c4878aSAndroid Build Coastguard Worker searchText: 'hello', 91*61c4878aSAndroid Build Coastguard Worker logViewId: 'child-node-1', 92*61c4878aSAndroid Build Coastguard Worker type: NodeType.View, 93*61c4878aSAndroid Build Coastguard Worker columnData: mockColumnData, 94*61c4878aSAndroid Build Coastguard Worker }), 95*61c4878aSAndroid Build Coastguard Worker new ViewNode({ 96*61c4878aSAndroid Build Coastguard Worker searchText: 'world', 97*61c4878aSAndroid Build Coastguard Worker logViewId: 'child-node-2', 98*61c4878aSAndroid Build Coastguard Worker type: NodeType.View, 99*61c4878aSAndroid Build Coastguard Worker columnData: mockColumnData, 100*61c4878aSAndroid Build Coastguard Worker }), 101*61c4878aSAndroid Build Coastguard Worker ], 102*61c4878aSAndroid Build Coastguard Worker }), 103*61c4878aSAndroid Build Coastguard Worker }; 104*61c4878aSAndroid Build Coastguard Worker 105*61c4878aSAndroid Build Coastguard Worker stateService = new StateService(new LocalStateStorage()); 106*61c4878aSAndroid Build Coastguard Worker stateService.saveState(mockState); 107*61c4878aSAndroid Build Coastguard Worker handleResizeObserverError(); 108*61c4878aSAndroid Build Coastguard Worker }); 109*61c4878aSAndroid Build Coastguard Worker 110*61c4878aSAndroid Build Coastguard Worker afterEach(() => { 111*61c4878aSAndroid Build Coastguard Worker destroyLogViewer(); 112*61c4878aSAndroid Build Coastguard Worker }); 113*61c4878aSAndroid Build Coastguard Worker 114*61c4878aSAndroid Build Coastguard Worker it('should default to single view when state is cleared', async () => { 115*61c4878aSAndroid Build Coastguard Worker window.localStorage.clear(); 116*61c4878aSAndroid Build Coastguard Worker 117*61c4878aSAndroid Build Coastguard Worker ({ logSources, destroyLogViewer, logViewer } = setUpLogViewer([ 118*61c4878aSAndroid Build Coastguard Worker new MockLogSource(), 119*61c4878aSAndroid Build Coastguard Worker ])); 120*61c4878aSAndroid Build Coastguard Worker const logViews = await getLogViews(); 121*61c4878aSAndroid Build Coastguard Worker 122*61c4878aSAndroid Build Coastguard Worker assert.lengthOf(logViews, 1); 123*61c4878aSAndroid Build Coastguard Worker }); 124*61c4878aSAndroid Build Coastguard Worker 125*61c4878aSAndroid Build Coastguard Worker it('should populate correct number of views from state', async () => { 126*61c4878aSAndroid Build Coastguard Worker ({ logSources, destroyLogViewer, logViewer } = setUpLogViewer([ 127*61c4878aSAndroid Build Coastguard Worker new MockLogSource(), 128*61c4878aSAndroid Build Coastguard Worker ])); 129*61c4878aSAndroid Build Coastguard Worker 130*61c4878aSAndroid Build Coastguard Worker const logViews = await getLogViews(); 131*61c4878aSAndroid Build Coastguard Worker assert.lengthOf(logViews, 2); 132*61c4878aSAndroid Build Coastguard Worker }); 133*61c4878aSAndroid Build Coastguard Worker }); 134*61c4878aSAndroid Build Coastguard Worker 135*61c4878aSAndroid Build Coastguard Worker describe('sources', () => { 136*61c4878aSAndroid Build Coastguard Worker before(() => { 137*61c4878aSAndroid Build Coastguard Worker window.localStorage.clear(); 138*61c4878aSAndroid Build Coastguard Worker ({ logSources, destroyLogViewer, logViewer } = setUpLogViewer([ 139*61c4878aSAndroid Build Coastguard Worker new MockLogSource('Source 1'), 140*61c4878aSAndroid Build Coastguard Worker new MockLogSource('Source 2'), 141*61c4878aSAndroid Build Coastguard Worker ])); 142*61c4878aSAndroid Build Coastguard Worker }); 143*61c4878aSAndroid Build Coastguard Worker 144*61c4878aSAndroid Build Coastguard Worker after(() => { 145*61c4878aSAndroid Build Coastguard Worker destroyLogViewer(); 146*61c4878aSAndroid Build Coastguard Worker window.localStorage.clear(); 147*61c4878aSAndroid Build Coastguard Worker }); 148*61c4878aSAndroid Build Coastguard Worker 149*61c4878aSAndroid Build Coastguard Worker it('registers a new source upon receiving its first log entry', async () => { 150*61c4878aSAndroid Build Coastguard Worker const logSource1 = logSources[0]; 151*61c4878aSAndroid Build Coastguard Worker 152*61c4878aSAndroid Build Coastguard Worker logSource1.publishLogEntry({ 153*61c4878aSAndroid Build Coastguard Worker timestamp: new Date(), 154*61c4878aSAndroid Build Coastguard Worker fields: [{ key: 'message', value: 'Message from Source 1' }], 155*61c4878aSAndroid Build Coastguard Worker }); 156*61c4878aSAndroid Build Coastguard Worker 157*61c4878aSAndroid Build Coastguard Worker await logViewer.updateComplete; 158*61c4878aSAndroid Build Coastguard Worker await new Promise((resolve) => setTimeout(resolve, 100)); 159*61c4878aSAndroid Build Coastguard Worker 160*61c4878aSAndroid Build Coastguard Worker const logViews = await getLogViews(); 161*61c4878aSAndroid Build Coastguard Worker const sources = logViews[0]?.sources; 162*61c4878aSAndroid Build Coastguard Worker const sourceNames = Array.from(sources.values()).map( 163*61c4878aSAndroid Build Coastguard Worker (source) => source.name, 164*61c4878aSAndroid Build Coastguard Worker ); 165*61c4878aSAndroid Build Coastguard Worker 166*61c4878aSAndroid Build Coastguard Worker assert.include( 167*61c4878aSAndroid Build Coastguard Worker sourceNames, 168*61c4878aSAndroid Build Coastguard Worker 'Source 1', 169*61c4878aSAndroid Build Coastguard Worker 'New source should be registered after emitting its first log entry', 170*61c4878aSAndroid Build Coastguard Worker ); 171*61c4878aSAndroid Build Coastguard Worker }); 172*61c4878aSAndroid Build Coastguard Worker 173*61c4878aSAndroid Build Coastguard Worker it('keeps a record of multiple log sources', async () => { 174*61c4878aSAndroid Build Coastguard Worker const logSource2 = logSources[1]; 175*61c4878aSAndroid Build Coastguard Worker 176*61c4878aSAndroid Build Coastguard Worker logSource2.publishLogEntry({ 177*61c4878aSAndroid Build Coastguard Worker timestamp: new Date(), 178*61c4878aSAndroid Build Coastguard Worker fields: [{ key: 'message', value: 'Message from Source 2' }], 179*61c4878aSAndroid Build Coastguard Worker }); 180*61c4878aSAndroid Build Coastguard Worker 181*61c4878aSAndroid Build Coastguard Worker await logViewer.updateComplete; 182*61c4878aSAndroid Build Coastguard Worker await new Promise((resolve) => setTimeout(resolve, 100)); 183*61c4878aSAndroid Build Coastguard Worker 184*61c4878aSAndroid Build Coastguard Worker const logViews = await getLogViews(); 185*61c4878aSAndroid Build Coastguard Worker const sources = logViews[0]?.sources; 186*61c4878aSAndroid Build Coastguard Worker const sourceNames = Array.from(sources.values()).map( 187*61c4878aSAndroid Build Coastguard Worker (source) => source.name, 188*61c4878aSAndroid Build Coastguard Worker ); 189*61c4878aSAndroid Build Coastguard Worker 190*61c4878aSAndroid Build Coastguard Worker assert.includeMembers( 191*61c4878aSAndroid Build Coastguard Worker sourceNames, 192*61c4878aSAndroid Build Coastguard Worker ['Source 1', 'Source 2'], 193*61c4878aSAndroid Build Coastguard Worker 'Both sources should be present', 194*61c4878aSAndroid Build Coastguard Worker ); 195*61c4878aSAndroid Build Coastguard Worker }); 196*61c4878aSAndroid Build Coastguard Worker }); 197*61c4878aSAndroid Build Coastguard Worker}); 198