| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import chai from 'chai'; // not esm |
| import chaiDom from 'chai-dom'; // not esm |
| import * as constant from '../src/constants'; |
| import { FakeWebviewAPi, ffxEventForTest, logDataForTest, malformedLogForTest } |
| from './util'; |
| import { Filter } from '../src/filter'; |
| import { LogList, WRAP_LOG_TEXT_ATTR } from '../components/log_list'; |
| import { State } from '../src/state'; |
| import {LitElement} from 'lit'; |
| import {ffxLogToLogRowData} from '../src/ffxLogToLogRow'; |
| |
| before(function () { |
| chai.should(); |
| chai.use(chaiDom); |
| }); |
| |
| describe('LogList', () => { |
| let root: HTMLElement; |
| let state: State; |
| |
| beforeEach(() => { |
| root = document.createElement('div'); |
| document.body.appendChild(root); |
| state = new State(new FakeWebviewAPi()); |
| }); |
| |
| afterEach(() => { |
| root.remove(); |
| }); |
| |
| describe('#constructor', () => { |
| it('crates a new empty view', async () => { |
| const logsList = new LogList(state); |
| root.appendChild(logsList); |
| await logsList.updateComplete; |
| logsList.shadowRoot!.children.length.should.equal(1); |
| await (logsList.shadowRoot!.children[0] as LitElement).updateComplete; |
| logsList.shadowRoot!.children[0].nodeName.should.equal('LOG-HEADER'); |
| logsList.should.have.attr('aria-colcount'); |
| const expectedColumns = Object.keys(state.currentFields).length; |
| logsList.getAttribute('aria-colcount')?.should.equal(`${expectedColumns}`); |
| }); |
| |
| it('sets the log wrapping from the state', async () => { |
| state.shouldWrapLogs = true; |
| let logList = new LogList(state); |
| root.appendChild(logList); |
| await logList.updateComplete; |
| logList.hasAttribute(WRAP_LOG_TEXT_ATTR).should.be.true; |
| |
| state.shouldWrapLogs = false; |
| logList = new LogList(state); |
| logList.hasAttribute(WRAP_LOG_TEXT_ATTR).should.be.false; |
| }); |
| }); |
| |
| describe('#addLog', () => { |
| it('appends a log', async () => { |
| const logsList = new LogList(state); |
| |
| document.body.appendChild(logsList); |
| await logsList.updateComplete; |
| |
| logsList.shadowRoot!.children.length.should.equal(1); |
| await (logsList.shadowRoot!.children[0] as LitElement).updateComplete; |
| logsList.shadowRoot!.children[0].nodeName.should.equal('LOG-HEADER'); |
| logsList.addLog(ffxLogToLogRowData(logDataForTest('core/foo'))!); |
| await logsList.updateComplete; |
| logsList.shadowRoot!.children.length.should.equal(2); |
| |
| const logLine = logsList.shadowRoot!.children[1] as LitElement; |
| logLine.children[0].should.have.class('log-entry'); |
| logLine.children[0].should.have.attr('data-moniker', 'core/foo'); |
| logLine.children[0].should.have.attr('data-pid', '123'); |
| logLine.children[0].should.have.attr('data-tid', '456'); |
| (logLine.children[0] as HTMLElement).hidden.should.be.false; |
| |
| const testFields = ['000000.12', '123', '456', 'core/foo', 'my_tag', 'Info', 'msg']; |
| let field: keyof typeof testFields; |
| for (field in testFields) { |
| const logField = logLine.children[0].children[field] as HTMLElement; |
| logField.innerText.should.equal(testFields[field]); |
| } |
| logsList.remove(); |
| }); |
| |
| |
| it('hides the log if the filters require so', async () => { |
| const logsList = new LogList(state); |
| root.appendChild(logsList); |
| state.registerFilter(new Filter({ |
| category: 'moniker', |
| subCategory: undefined, |
| operator: 'contains', |
| value: 'core/bar', |
| }), 'moniker:core/bar'); |
| |
| logsList.addLog(ffxLogToLogRowData(logDataForTest('core/foo'))!); |
| logsList.addLog(ffxLogToLogRowData(logDataForTest('core/bar'))!); |
| await logsList.updateComplete; |
| // sleep a bit and wait for rerender |
| await new Promise((resolve) => {setTimeout(resolve, 10 /* 10ms */);}); |
| logsList.shadowRoot!.children.length.should.equal(3); |
| const logLine1 = logsList.shadowRoot!.children[1] as HTMLElement; |
| logLine1.hidden.should.be.true; |
| const logLine2 = logsList.shadowRoot!.children[2] as HTMLElement; |
| logLine2.hidden.should.be.false; |
| }); |
| |
| it('handles ffx events', async () => { |
| const logsList = new LogList(state); |
| let moniker = constant.FFX_MONIKER; |
| const msg = 'Logger lost connection to target. Retrying...'; |
| logsList.addLog(ffxLogToLogRowData(ffxEventForTest('TargetDisconnected'))!); |
| |
| document.body.appendChild(logsList); |
| await logsList.updateComplete; |
| logsList.shadowRoot!.children.length.should.equal(2); |
| logsList.shadowRoot!.children[0].nodeName.should.equal('LOG-HEADER'); |
| |
| const logLine = logsList.shadowRoot!.children[1] as LitElement; |
| logLine.children[0].children.length.should.equal(7); |
| logLine.children[0].should.have.class('log-entry'); |
| logLine.children[0].should.have.attr('data-moniker', '<ffx>'); |
| (logLine.children[0] as HTMLElement).hidden.should.be.false; |
| |
| let monikerEl = logLine.children[0].children[3] as HTMLElement; |
| let msgEl = logLine.children[0].children[6] as HTMLElement; |
| monikerEl.innerText.should.equal(moniker); |
| msgEl.innerText.should.equal(msg); |
| logsList.remove(); |
| }); |
| |
| it('handles malformed logs', async () => { |
| const logsList = new LogList(state); |
| const msg = 'Malformed target log: oh no something went wrong'; |
| logsList.addLog(ffxLogToLogRowData(malformedLogForTest('oh no something went wrong'))!); |
| |
| document.body.appendChild(logsList); |
| await logsList.updateComplete; |
| logsList.shadowRoot!.children.length.should.equal(2); |
| logsList.shadowRoot!.children[0].nodeName.should.equal('LOG-HEADER'); |
| |
| const logLine = logsList.shadowRoot!.children[1] as LitElement; |
| await logLine.updateComplete; |
| logLine.children[0].children.length.should.equal(7); |
| logLine.children[0].should.have.class('log-entry'); |
| (logLine.children[0] as HTMLElement).hidden.should.be.false; |
| let msgEl = logLine.children[0].children[6] as HTMLElement; |
| msgEl.innerText.should.equal(msg); |
| logsList.remove(); |
| }); |
| |
| it('detects user hovering logs', () => { |
| const view = new LogList(state); |
| view.addLog(ffxLogToLogRowData(logDataForTest('core/foo'))!); |
| const scrollArea = view.parentElement; |
| scrollArea?.matches(':hover').should.equal(false); |
| scrollArea?.dispatchEvent(new MouseEvent('onmouseenter')); |
| scrollArea?.matches(':hover').should.equal(true); |
| }); |
| }); |
| |
| describe('#reset', () => { |
| it('resets the log list', async () => { |
| const logsList = new LogList(state); |
| root.appendChild(logsList); |
| logsList.addLog(ffxLogToLogRowData(logDataForTest('core/foo'))!); |
| await logsList.updateComplete; |
| logsList.shadowRoot!.children.length.should.equal(2); |
| logsList.reset(); |
| await logsList.updateComplete; |
| logsList.shadowRoot!.children.length.should.equal(1); |
| }); |
| }); |
| }); |