| // 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 * as constant from '../src/constants'; |
| import {ffxEventForTest, malformedLogForTest, symbolizedDataForTest} from './util'; |
| import { FfxLogData, LogPayload } from '../src/log_data'; |
| import { LogRow } from '../components/log_row'; |
| import { messageForEvent } from '../src/format_log_text'; |
| import {ffxLogToLogRowData} from '../src/ffxLogToLogRow'; |
| import {State} from '../src/state'; |
| import {LogColumnFormatter} from '../src/fields'; |
| |
| const columnFormatter: LogColumnFormatter = (_fieldName, text) => text; |
| |
| before(() => { |
| chai.should(); |
| }); |
| |
| /** |
| * Creates a fake log data to be used in tests. |
| * |
| * @param payload payload to use for the fake log data. |
| * @returns a log data to use for testing. |
| */ |
| function logDataForTest(payload: LogPayload): FfxLogData { |
| return { |
| data: { |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| TargetLog: { |
| moniker: 'core/foo', |
| metadata: { |
| errors: [], |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| component_url: 'fuchsia-pkg://fuchsia.com/foo#metafoo.cm', |
| timestamp: 12345678900, |
| severity: 'Info', |
| tags: ['bar', 'baz'], |
| pid: 123, |
| tid: 456, |
| file: 'foo/main.rs', |
| line: 25, |
| }, |
| payload, |
| }, |
| }, |
| timestamp: 12345, |
| version: 1 |
| }; |
| }; |
| |
| describe('LogRow', () => { |
| let state: State; |
| beforeEach(() => { |
| state = new State(); |
| }); |
| |
| describe('#constructor', () => { |
| it('adds title to to non-message cells', async () => { |
| const logRow = new LogRow(ffxLogToLogRowData( |
| logDataForTest({ |
| root: { |
| message: { |
| value: 'hello vscode', |
| }, |
| keys: null, |
| printf: null, |
| } |
| }), false)!, state.currentFields, columnFormatter); |
| document.body.appendChild(logRow); |
| await logRow.updateComplete; |
| const testFields = [ |
| '000012.35', |
| '123', |
| '456', |
| 'core/foo', |
| 'bar,baz', |
| 'Info', |
| 'hello vscode']; |
| let field: keyof typeof testFields; |
| for (field in testFields) { |
| if (testFields[field] === 'hello vscode') { |
| return; |
| } |
| const logField = logRow.children[0].children[field] as HTMLElement; |
| logField.title.should.equal(testFields[field]); |
| } |
| logRow.remove(); |
| }); |
| |
| it('formats target logs', async () => { |
| const logRow = new LogRow(ffxLogToLogRowData( |
| logDataForTest({ |
| root: { |
| message: { |
| value: 'hello vscode', |
| }, |
| keys: null, |
| printf: null, |
| } |
| }), false)!, state.currentFields, columnFormatter); |
| document.body.appendChild(logRow); |
| await logRow.updateComplete; |
| const testFields = [ |
| '000012.35', |
| '123', |
| '456', |
| 'core/foo', |
| 'bar,baz', |
| 'Info', |
| 'hello vscode']; |
| let field: keyof typeof testFields; |
| for (field in testFields) { |
| const logField = logRow.children[0].children[field] as HTMLElement; |
| logField.innerText.should.equal(testFields[field]); |
| } |
| logRow.remove(); |
| }); |
| |
| it('formats html logs', async () => { |
| const logRow = new LogRow( |
| {fields: [{key: 'message', text: 'hello vscode'}]} |
| , { |
| 'timestamp': {displayName: 'timestamp', width: ''}, |
| 'pid': {displayName: 'pid', width: ''}, |
| 'tid': {displayName: 'tid', width: ''}, |
| 'tags': {displayName: 'tags', width: ''}, |
| 'moniker': {displayName: 'moniker', width: ''}, |
| 'severity': {displayName: 'severity', width: ''}, |
| 'message': {displayName: 'messagepw', width: ''}, |
| }, (fieldName, text) => { |
| const el = document.createElement('span'); |
| if (fieldName === 'message') { |
| el.innerHTML = text.replace('vscode', '<b>vscode</b>'); |
| } |
| return el; |
| }); |
| document.body.appendChild(logRow); |
| await logRow.updateComplete; |
| const msgField = logRow.children[0].children[6] as HTMLElement; |
| msgField.querySelector('b')!.innerText.should.equal('vscode'); |
| logRow.remove(); |
| }); |
| |
| it('formats structured logs', async () => { |
| const logRow = new LogRow(ffxLogToLogRowData( |
| logDataForTest({ |
| root: { |
| message: { |
| value: 'hello vscode', |
| }, |
| keys: { |
| os: 'fuchsia', |
| number: 1, |
| }, |
| printf: null, |
| } |
| }), false)!, state.currentFields, columnFormatter); |
| document.body.appendChild(logRow); |
| await logRow.updateComplete; |
| const testFields = |
| ['000012.35', '123', '456', 'core/foo', 'bar,baz', |
| 'Info', 'hello vscodeos=fuchsia number=1']; |
| let field: keyof typeof testFields; |
| for (field in testFields) { |
| const logField = logRow.children[0].children[field] as HTMLElement; |
| logField.innerText.should.equal(testFields[field]); |
| } |
| logRow.remove(); |
| }); |
| |
| it('formats printf logs', async () => { |
| const logRow = new LogRow(ffxLogToLogRowData( |
| logDataForTest({ |
| root: { |
| message: null, |
| keys: null, |
| printf: { |
| format: '%s is #%d', |
| args: ['Fuchsia', 1] |
| }, |
| } |
| }), false)!, state.currentFields, columnFormatter); |
| document.body.appendChild(logRow); |
| await logRow.updateComplete; |
| const testFields = |
| ['000012.35', |
| '123', |
| '456', |
| 'core/foo', |
| 'bar,baz', |
| 'Info', |
| '%s is #%d args=[Fuchsia, 1]']; |
| let field: keyof typeof testFields; |
| for (field in testFields) { |
| const logField = logRow.children[0].children[field] as HTMLElement; |
| logField.innerText.should.equal(testFields[field]); |
| } |
| logRow.remove(); |
| }); |
| |
| it('formats symbolized log', async () => { |
| const logRow = new LogRow(ffxLogToLogRowData( |
| symbolizedDataForTest({ |
| root: { |
| message: { |
| value: 'this is ignored', |
| }, |
| keys: null, |
| printf: null, |
| } |
| }), false)!, state.currentFields, columnFormatter); |
| document.body.appendChild(logRow); |
| await logRow.updateComplete; |
| const testFields = |
| ['000000.12', '123', '456', 'core/foo', 'my_tag', 'Info', 'symbolized']; |
| let field: keyof typeof testFields; |
| for (field in testFields) { |
| const logField = logRow.children[0].children[field] as HTMLElement; |
| logField.innerText.should.equal(testFields[field]); |
| } |
| logRow.remove(); |
| }); |
| |
| it('formats logging started events', async () => { |
| let logRow = new LogRow( |
| ffxLogToLogRowData( |
| ffxEventForTest('LoggingStarted'), false)!, |
| state.currentFields, |
| columnFormatter); |
| document.body.appendChild(logRow); |
| await logRow.updateComplete; |
| let moniker = constant.FFX_MONIKER; |
| let msg = messageForEvent('LoggingStarted', false); |
| let monikerEl = logRow.children[0].children[3] as HTMLElement; |
| let msgEl = logRow.children[0].children[6] as HTMLElement; |
| |
| monikerEl.innerText.should.equal(moniker); |
| msgEl.innerText.should.equal(msg); |
| logRow.remove(); |
| |
| logRow = new LogRow( |
| ffxLogToLogRowData( |
| ffxEventForTest('LoggingStarted'), true)!, |
| state.currentFields, |
| columnFormatter); |
| document.body.appendChild(logRow); |
| await logRow.updateComplete; |
| msg = messageForEvent('LoggingStarted', true); |
| monikerEl = logRow.children[0].children[3] as HTMLElement; |
| msgEl = logRow.children[0].children[6] as HTMLElement; |
| |
| monikerEl.innerText.should.equal(moniker); |
| msgEl.innerText.should.equal(msg); |
| logRow.remove(); |
| }); |
| |
| it('formats target disconnected events', async () => { |
| const logRow = new LogRow( |
| ffxLogToLogRowData( |
| ffxEventForTest('TargetDisconnected'), false)!, |
| state.currentFields, |
| columnFormatter); |
| document.body.appendChild(logRow); |
| await logRow.updateComplete; |
| let moniker = constant.FFX_MONIKER; |
| let msg = messageForEvent('TargetDisconnected', true); |
| let monikerEl = logRow.children[0].children[3] as HTMLElement; |
| let msgEl = logRow.children[0].children[6] as HTMLElement; |
| |
| monikerEl.innerText.should.equal(moniker); |
| msgEl.innerText.should.equal(msg); |
| logRow.remove(); |
| }); |
| |
| it('formats malformed log', async () => { |
| const logRow = new LogRow( |
| ffxLogToLogRowData( |
| malformedLogForTest('hello world'), false)!, |
| state.currentFields, |
| columnFormatter); |
| document.body.appendChild(logRow); |
| await logRow.updateComplete; |
| let msg = 'Malformed target log: hello world'; |
| let msgEl = logRow.children[0].children[6] as HTMLElement; |
| msgEl.innerText.should.equal(msg); |
| logRow.remove(); |
| }); |
| |
| it('formats viewer synthesized messages', async () => { |
| const msg = 'Logs are cool'; |
| const data = { |
| data: { |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| ViewerEvent: msg, |
| }, |
| timestamp: 0, |
| version: 0 |
| }; |
| const logRow = new LogRow( |
| ffxLogToLogRowData(data, false)!, state.currentFields, columnFormatter); |
| document.body.appendChild(logRow); |
| await logRow.updateComplete; |
| const monikerEl = logRow.children[0].children[3] as HTMLElement; |
| const msgEl = logRow.children[0].children[6] as HTMLElement; |
| monikerEl.innerText.should.equal(constant.VSCODE_SYNTHETIC_MONIKER); |
| msgEl.innerText.should.equal(msg); |
| logRow.remove(); |
| }); |
| }); |
| }); |