blob: 0e5b7cc86a86860ca4ac34112e54f9f2fed2c153 [file] [log] [blame]
// 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 {CURRENT_VERSION, MemoryStore, State} from '../src/state';
import {logDataForTest} from './util';
import { ExternalActionRequestEvent, LoggingView, PAUSING_LOG_STREAMING_LOG, RESUMING_LOG_STREAMING_LOG } from '../components/view';
import * as constant from '../src/constants';
import chai from 'chai';
import { expect } from 'chai';
import chaiDom from 'chai-dom'; // not esm
import { LogControl } from '../components/log_control';
import { Filter } from '../src/filter';
import { LogViewActions } from '../components/log_view_actions';
import { ffxLogToLogRowData } from '../src/ffxLogToLogRow';
import {LogList, WRAP_LOG_TEXT_ATTR} from '../components/log_list';
import {LitElement} from 'lit';
import {LogViewOptions} from '../src/fields';
const logOptions: LogViewOptions = {
columnFormatter: (_fieldName, text) => text,
showControls: false
};
before(function () {
chai.should();
chai.use(chaiDom);
});
describe('LoggingView', () => {
const filterText = 'moniker:core/foo';
let root: HTMLElement;
let vscode: MemoryStore;
let state: State;
beforeEach(() => {
root = document.createElement('div');
document.body.appendChild(root);
vscode = new MemoryStore();
state = new State(vscode);
});
afterEach(() => {
root.remove();
});
describe('#constructor', () => {
it('crates view with a log list container and a log action container', () => {
new LoggingView(state, root);
root.children.length.should.equal(2);
root.children[0].id.should.equal('log-action-container');
root.children[1].id.should.equal('log-list-container');
});
it('creates log action container with a log control and log view actions', () => {
new LoggingView(state, root);
const container = root.children[0];
container.children.length.should.equal(2);
container.children[0].tagName.toLowerCase().should.equal('log-control');
container.children[1].tagName.toLowerCase().should.equal('log-view-actions');
});
it('initializes log control with the current state filters', async () => {
vscode.setState({
version: CURRENT_VERSION,
filter: filterText,
fields: constant.LOGS_HEADERS,
wrappingLogs: true
});
new LoggingView(new State(vscode), root);
let logControl = root.getElementsByTagName('log-control')[0] as LogControl;
await logControl.updateComplete;
logControl.value.should.equal(filterText);
});
it('initializes log actions view with the current state wrapping logs', async () => {
vscode.setState({
version: CURRENT_VERSION,
filter: filterText,
fields: constant.LOGS_HEADERS,
wrappingLogs: true
});
new LoggingView(new State(vscode), root);
let logViewActions = root.getElementsByTagName('log-view-actions')[0] as LogViewActions;
await logViewActions.updateComplete;
logViewActions.wrappingLogs.should.be.true;
});
it('crates log list container with log list', () => {
new LoggingView(state, root);
const container = root.children[1];
container.children.length.should.equal(1);
container.children[0].nodeName.should.equal('LOG-VIEW');
});
});
describe('log control integration', () => {
it('updates the state on log control filter changes', async () => {
new LoggingView(state, root);
let logControl = root.getElementsByTagName('log-control')[0] as LogControl;
await logControl.updateComplete;
const searchBox = logControl.shadowRoot!.querySelector('#search') as HTMLInputElement;
searchBox.value = filterText;
searchBox.dispatchEvent(new KeyboardEvent('keypress', { 'key': 'Enter' }));
await logControl.updateComplete;
state.currentFilter.should.deep.equal(new Filter({
category: 'moniker',
operator: 'contains',
subCategory: undefined,
value: 'core/foo',
}));
state.currentFilterText.should.deep.equal(filterText);
});
it('log control and filter field can be disabled', async () => {
new LoggingView(state, root, logOptions);
let logView = root.getElementsByTagName('log-view')[0] as LogList;
await logView.updateComplete;
root.getElementsByTagName('log-control').length.should.equal(0);
});
it('announces the number of filtered results', async () => {
const view = new LoggingView(state, root);
const logList = root.querySelector('log-view') as LitElement;
view.addLog(ffxLogToLogRowData(logDataForTest('core/foo'))!);
view.addLog(ffxLogToLogRowData(logDataForTest('core/foo'))!);
await logList.updateComplete;
logList.shadowRoot!.children.length.should.equal(3);
let logControl = root.getElementsByTagName('log-control')[0] as LogControl;
await logControl.updateComplete;
const searchBox = logControl.shadowRoot!.querySelector('#search') as HTMLInputElement;
const filterText = 'moniker:core/foo';
searchBox.value = filterText;
searchBox.dispatchEvent(new KeyboardEvent('keypress', { 'key': 'Enter' }));
await logControl.updateComplete;
const filterResults = logControl
.shadowRoot!.querySelector('#filter-results') as HTMLParagraphElement;
filterResults.innerText.should.match(/(2 results)/);
});
});
describe('log actions view integration', () => {
it('clears the log list when the clear button is clicked', async () => {
const view = new LoggingView(state, root);
let gotResetEvent: ExternalActionRequestEvent | null = null;
view.addEventListener(LoggingView.externalActionRequestEvent, (e) => {
gotResetEvent = (e as CustomEvent).detail;
});
const logList = root.querySelector('log-view') as LitElement;
view.addLog(ffxLogToLogRowData(logDataForTest('core/foo'))!);
await logList.updateComplete;
logList.shadowRoot!.children.length.should.equal(2);
let logViewActions = root.getElementsByTagName('log-view-actions')[0] as LogViewActions;
let clearButton = logViewActions.shadowRoot!.querySelector('#clear') as HTMLDivElement;
clearButton.click();
await logViewActions.updateComplete;
expect(gotResetEvent).to.exist.and.have.property('type').equal('clear-logs');
logList.shadowRoot!.children.length.should.equal(1);
});
it('updates the state and log list when the wrap logs button is clicked', async () => {
const view = new LoggingView(state, root);
const logList = root.querySelector('log-view') as LitElement;
view.addLog(ffxLogToLogRowData(logDataForTest('core/foo'))!);
await logList.updateComplete;
logList.shadowRoot!.children.length.should.equal(2);
let logViewActions = root.getElementsByTagName('log-view-actions')[0] as LogViewActions;
await logViewActions.updateComplete;
let wrapLogsButton = logViewActions.shadowRoot!.querySelector('#wrap-logs') as HTMLDivElement;
// Clicking the button for the first should update the other elements to true.
// Since we started on true (default state value).
wrapLogsButton.click();
await logViewActions.updateComplete;
state.shouldWrapLogs.should.be.false;
logList.hasAttribute(WRAP_LOG_TEXT_ATTR).should.be.false;
// Clicking the button again should update the other elements to false.
wrapLogsButton.click();
await logViewActions.updateComplete;
state.shouldWrapLogs.should.be.true;
logList.hasAttribute(WRAP_LOG_TEXT_ATTR).should.be.true;
});
it('renders a synthetic log about pause/play and emits events', async () => {
const view = new LoggingView(state, root);
const logList = root.querySelector('log-view') as LitElement;
let logViewActions = root.getElementsByTagName('log-view-actions')[0] as LogViewActions;
await logViewActions.updateComplete;
let playPauseButton =
logViewActions.shadowRoot!.querySelector('#play-pause') as HTMLDivElement;
let promise = new Promise<ExternalActionRequestEvent>((resolve) => {
view.addEventListener(LoggingView.externalActionRequestEvent, (e) => {
const event = e as CustomEvent;
resolve(event.detail);
});
});
playPauseButton.click();
let event = await promise;
await logList.updateComplete;
event.should.deep.equal({ type: 'pause-log-streaming' });
let logLine = logList.shadowRoot!.children[1] as HTMLElement;
(logLine.querySelector('#message')! as HTMLTableCellElement)
.innerText.should.equal(PAUSING_LOG_STREAMING_LOG);
(logLine.querySelector('#moniker')! as HTMLTableCellElement)
.innerText.should.equal(constant.VSCODE_SYNTHETIC_MONIKER);
// Clicking the button again should show the "Play" state.
promise = new Promise<ExternalActionRequestEvent>((resolve) => {
view.addEventListener(LoggingView.externalActionRequestEvent, (e) => {
const event = e as CustomEvent;
resolve(event.detail);
});
});
playPauseButton.click();
event = await promise;
await logViewActions.updateComplete;
event.should.deep.equal({ type: 'resume-log-streaming' });
logLine = logList.shadowRoot!.children[2] as HTMLElement;
(logLine.querySelector('#message')! as HTMLTableCellElement)
.innerText.should.equal(RESUMING_LOG_STREAMING_LOG);
(logLine.querySelector('#moniker')! as HTMLTableCellElement)
.innerText.should.equal(constant.VSCODE_SYNTHETIC_MONIKER);
});
});
});