blob: 011381a6d46d378b1daabc70120744fdf9dd8625 [file] [log] [blame]
// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview unit tests for terminal_info.js
*/
import {DEFAULT_VM_NAME, DEFAULT_CONTAINER_NAME} from './terminal_common.js';
import {LaunchInfo, TerminalInfoTracker, getInitialTitleCacheKey,
setUpTitleHandler, resolveLaunchInfo} from './terminal_info.js';
import {MockObject} from './terminal_test_mocks.js';
const DEFAULT_CONTAINER = {
vmName: DEFAULT_VM_NAME,
containerName: DEFAULT_CONTAINER_NAME,
};
describe('terminal_info_tests.js', () => {
describe('resolveLaunchInfo for home', function() {
it('follows url', function() {
const parentLaunchInfo = /** @type {!LaunchInfo} */({
ssh: {},
});
assert.deepEqual(
resolveLaunchInfo(parentLaunchInfo,
new URL('/html/terminal.html#home', location.href)).home,
{});
// The ssh page without hash should be considered the home page.
assert.deepEqual(
resolveLaunchInfo(parentLaunchInfo,
new URL('/html/terminal_ssh.html', location.href)).home,
{});
});
it('follows parent', function() {
const parentLaunchInfo = /** @type {!LaunchInfo} */({
home: {},
});
const url = new URL(location.href);
url.search = '';
// No parent.
assert.isUndefined(resolveLaunchInfo(/** @type {!LaunchInfo} */({}),
url).home);
// Has parent at the home page.
assert.deepEqual(resolveLaunchInfo(parentLaunchInfo, url).home, {});
// Has parent but there is a url param.
url.search = '?vm=penguin';
assert.isUndefined(
resolveLaunchInfo(parentLaunchInfo, url).home);
});
});
describe('resolveLaunchInfo for tmux', function() {
it('follows parent', function() {
const parentLaunchInfo = /** @type {!LaunchInfo} */({
tmux: {
driverChannelName: 'abcd',
},
});
const url = new URL(location.href);
url.search = '';
// No parent.
assert.isUndefined(resolveLaunchInfo(/** @type {!LaunchInfo} */({}),
url).tmux);
// Has parent with driver channel.
assert.deepEqual(
resolveLaunchInfo(parentLaunchInfo, url).tmux,
{driverChannelName: 'abcd'},
);
// Has parent but there is a url param.
url.search = '?vm=penguin';
assert.isUndefined(
resolveLaunchInfo(parentLaunchInfo, url).tmux);
});
});
describe('resolveLaunchInfo() for vsh', function() {
const emptyParent = /** @type {!LaunchInfo} */({});
const parentWithoutTerminalId = /** @type {!LaunchInfo} */({
vsh: {
args: [],
containerId: {
vmName: 'vm0',
containerName: 'container0',
},
},
});
const parentWithTerminalId = /** @type {!LaunchInfo} */({
vsh: {
args: [],
terminalId: 'tid0',
containerId: {
vmName: 'vm0',
containerName: 'container0',
},
},
});
/**
* A helper function which builds a url from `args` and calls
* resolveLaunchInfo().
*
* @param {!LaunchInfo} parentLaunchInfo
* @param {!Array<string>} args Arguments to be put into url params 'args[]'
* @return {!LaunchInfo}
*/
const resolveLaunchInfoWithArgs = (parentLaunchInfo, args) => {
const url = new URL(location.href);
url.search = (new URLSearchParams(args.map((value) => ['args[]', value])))
.toString();
return resolveLaunchInfo(parentLaunchInfo, url);
};
function assertVshInfoEqual(vshInfo0, vshInfo1) {
// resolveLaunchInfo() might change the order of some args, so we sort
// them first.
assert.deepEqual(
{...vshInfo0, args: [...vshInfo0.args].sort()},
{...vshInfo1, args: [...vshInfo1.args].sort()},
);
}
describe('containerId', function() {
it('always uses args\' if it exists', function() {
assertVshInfoEqual(
resolveLaunchInfoWithArgs(
parentWithoutTerminalId,
['a', 'b', '--vm_name=aaa'],
).vsh,
{
args: ['a', 'b', '--vm_name=aaa'],
containerId: {vmName: 'aaa'},
hasCwd: false,
},
);
assertVshInfoEqual(
resolveLaunchInfoWithArgs(
parentWithoutTerminalId,
['a', 'b', '--target_container=bbb'],
).vsh,
{
args: ['a', 'b', '--target_container=bbb'],
containerId: {containerName: 'bbb'},
hasCwd: false,
},
);
assertVshInfoEqual(
resolveLaunchInfoWithArgs(
parentWithoutTerminalId,
['a', 'b', '--vm_name=aaa', '--target_container=bbb'],
).vsh,
{
args: ['a', 'b', '--vm_name=aaa', '--target_container=bbb'],
containerId: {vmName: 'aaa', containerName: 'bbb'},
hasCwd: false,
},
);
});
it('uses parent\'s if args\' is missing', function() {
assertVshInfoEqual(
resolveLaunchInfoWithArgs(
parentWithoutTerminalId,
['a', 'b'],
).vsh,
{
args: ['a', 'b', '--vm_name=vm0',
'--target_container=container0'],
containerId: {
vmName: 'vm0',
containerName: 'container0',
},
hasCwd: false,
},
);
});
it('uses default as a fallback', function() {
assertVshInfoEqual(
resolveLaunchInfoWithArgs(emptyParent, ['a', 'b']).vsh,
{
args: ['a', 'b', `--vm_name=${DEFAULT_VM_NAME}`,
`--target_container=${DEFAULT_CONTAINER_NAME}`],
containerId: {
vmName: DEFAULT_VM_NAME,
containerName: DEFAULT_CONTAINER_NAME,
},
hasCwd: false,
},
);
});
});
describe('cwd', function() {
it('always uses args\' if it exists', function() {
assertVshInfoEqual(
resolveLaunchInfoWithArgs(
emptyParent,
['a', 'b', '--cwd=some-cwd'],
).vsh,
{
args: ['a', 'b', `--vm_name=${DEFAULT_VM_NAME}`,
`--target_container=${DEFAULT_CONTAINER_NAME}`,
'--cwd=some-cwd'],
containerId: {
vmName: DEFAULT_VM_NAME,
containerName: DEFAULT_CONTAINER_NAME,
},
hasCwd: true,
},
);
assertVshInfoEqual(
resolveLaunchInfoWithArgs(
parentWithTerminalId,
['a', 'b', '--cwd=some-cwd'],
).vsh,
{
args: ['a', 'b', '--vm_name=vm0',
'--target_container=container0', '--cwd=some-cwd'],
containerId: {vmName: 'vm0', containerName: 'container0'},
hasCwd: true,
},
);
});
it('follows parent\'s if has the same container', function() {
assertVshInfoEqual(
resolveLaunchInfoWithArgs(
parentWithTerminalId,
['a', 'b'],
).vsh,
{
args: ['a', 'b', '--vm_name=vm0',
'--target_container=container0', '--cwd=terminal_id:tid0'],
containerId: {vmName: 'vm0', containerName: 'container0'},
hasCwd: true,
},
);
assertVshInfoEqual(
resolveLaunchInfoWithArgs(
parentWithTerminalId,
['a', 'b', '--vm_name=vm0', '--target_container=container0'],
).vsh,
{
args: ['a', 'b', '--vm_name=vm0',
'--target_container=container0', '--cwd=terminal_id:tid0'],
containerId: {vmName: 'vm0', containerName: 'container0'},
hasCwd: true,
},
);
// Not the same container.
assertVshInfoEqual(
resolveLaunchInfoWithArgs(
parentWithTerminalId,
['a', 'b', '--vm_name=vm0', '--target_container=container1'],
).vsh,
{
args: ['a', 'b', '--vm_name=vm0',
'--target_container=container1'],
containerId: {vmName: 'vm0', containerName: 'container1'},
hasCwd: false,
},
);
});
});
});
describe('TerminalInfoTracker', function() {
beforeEach(function() {
this.mockChannel = new MockObject({onmessage: null});
document.title = 'TerminalInfoTracker';
this.newTracker = () => new TerminalInfoTracker({
tabId: 123,
channel: this.mockChannel.proxy,
launchInfo: {crosh: {}},
parentTitle: '',
});
});
it('postInfo upon construction', function() {
this.newTracker();
assert.deepEqual(this.mockChannel.getMethodHistory('postMessage'), [[{
tabId: 123,
title: 'TerminalInfoTracker',
launchInfo: {crosh: {}},
}]]);
});
it('postInfo upon request', function() {
this.newTracker();
this.mockChannel.popMethodHistory('postMessage');
document.title = 'TerminalInfoTracker 2';
this.mockChannel.proxy.onmessage({data: 123});
assert.deepEqual(this.mockChannel.getMethodHistory('postMessage'), [[{
tabId: 123,
title: 'TerminalInfoTracker 2',
launchInfo: {crosh: {}},
}]]);
});
it('requestTerminalInfo', async function() {
assert.isNull(await TerminalInfoTracker.requestTerminalInfo(
this.mockChannel.proxy, null));
assert.deepEqual(this.mockChannel.popMethodHistory('postMessage'), []);
const promise =
TerminalInfoTracker.requestTerminalInfo(this.mockChannel.proxy, 123);
assert.deepEqual(this.mockChannel.popMethodHistory('postMessage'),
[[123]]);
this.mockChannel.proxy.onmessage({data: 123});
this.mockChannel.proxy.onmessage({data: {tabId: 789}});
this.mockChannel.proxy.onmessage({data: {tabId: 123}});
assert.deepEqual(await promise, {tabId: 123});
});
it('requestTerminalInfo timeout', async function() {
const promise = TerminalInfoTracker.requestTerminalInfo(
this.mockChannel.proxy, 123, 0);
await Promise.resolve();
assert.isNull(await promise);
});
});
describe('setupTitleHandler() for vsh', function() {
beforeEach(function() {
window.localStorage.clear();
});
it('default container with no cache', async function() {
const key = getInitialTitleCacheKey(DEFAULT_CONTAINER);
window.localStorage.removeItem(key);
document.title = 'test title';
await setUpTitleHandler(/** @type {!TerminalInfoTracker} */({
launchInfo: {
vsh: {
args: [],
containerId: DEFAULT_CONTAINER,
hasCwd: false,
},
},
}));
assert.equal(document.title, 'test title',
'no cache, title should not change');
assert.isNull(window.localStorage.getItem(key));
document.title = 'test title 2';
await Promise.resolve();
assert.equal(window.localStorage.getItem(key),
'test title 2');
document.title = 'test title 3';
await Promise.resolve();
assert.equal(window.localStorage.getItem(key),
'test title 2',
'only the first changed title should be written to the cache');
});
[
DEFAULT_CONTAINER,
{vmName: 'vm0', containerName: 'container0'},
].forEach(function(containerId) {
it(`has cache (${JSON.stringify(containerId)})`, async function() {
const key = getInitialTitleCacheKey(containerId);
window.localStorage.setItem(key, 'cached title');
document.title = 'test title';
await setUpTitleHandler(/** @type {!TerminalInfoTracker} */({
launchInfo: {
vsh: {
containerId,
hasCwd: false,
// args does not matter.
args: [],
},
},
}));
assert.equal(document.title, 'cached title',
'title should be set to cache');
await Promise.resolve();
assert.equal(window.localStorage.getItem(key),
'cached title');
document.title = 'test title 2';
await Promise.resolve();
assert.equal(window.localStorage.getItem(key),
'test title 2');
document.title = 'test title 3';
await Promise.resolve();
assert.equal(window.localStorage.getItem(key),
'test title 2',
'only the first changed title should be written to the cache');
});
});
});
});