xref: /aosp_15_r20/external/pigweed/pw_web/log-viewer/test/log-view.test.js (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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