chrome-devtools-frontend
Version:
Chrome DevTools UI
192 lines (163 loc) • 7.34 kB
text/typescript
// Copyright 2025 The Chromium 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 * as Persistence from '../../models/persistence/persistence.js';
import * as Workspace from '../../models/workspace/workspace.js';
import {createTestFilesystem, setupAutomaticFileSystem} from '../../testing/AiAssistanceHelpers.js';
import {assertScreenshot, renderElementIntoDOM, setColorScheme} from '../../testing/DOMHelpers.js';
import {describeWithEnvironment} from '../../testing/EnvironmentHelpers.js';
import {createViewFunctionStub, type ViewFunctionStub} from '../../testing/ViewFunctionHelpers.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as AiAssistance from './ai_assistance.js';
describeWithEnvironment('SelectWorkspaceDialog', () => {
const root = '/path/to/my-automatic-file-system';
beforeEach(() => {
setupAutomaticFileSystem();
});
afterEach(() => {
const workspace = Workspace.Workspace.WorkspaceImpl.instance();
for (const project of workspace.projects()) {
workspace.removeProject(project);
}
Persistence.AutomaticFileSystemManager.AutomaticFileSystemManager.removeInstance();
});
async function createComponent(): Promise<{
view: ViewFunctionStub<typeof AiAssistance.SelectWorkspaceDialog>,
component: AiAssistance.SelectWorkspaceDialog,
onProjectSelected: sinon.SinonSpy<[Workspace.Workspace.Project], void>,
hideDialogSpy: sinon.SinonSpy<[], void>,
project: Persistence.FileSystemWorkspaceBinding.FileSystem,
}> {
createTestFilesystem('file://test1');
const {project} = createTestFilesystem('file://test2');
const dialog = new UI.Dialog.Dialog('select-workspace');
const hideDialogSpy = sinon.spy(dialog, 'hide');
const view = createViewFunctionStub(AiAssistance.SelectWorkspaceDialog);
const onProjectSelected = sinon.spy() as sinon.SinonSpy<[Workspace.Workspace.Project], void>;
const component = new AiAssistance.SelectWorkspaceDialog({dialog, onProjectSelected}, view);
component.markAsRoot();
const container = document.createElement('div');
renderElementIntoDOM(container);
component.show(container);
await view.nextInput;
sinon.assert.callCount(view, 1);
assert.strictEqual(view.input.selectedIndex, 0);
return {view, component, onProjectSelected, hideDialogSpy, project};
}
describe('screenshots', () => {
function renderViewForScreenshots() {
const noop = () => {};
const target = document.createElement('div');
target.style.maxWidth = '420px';
target.style.maxHeight = '600px';
target.style.padding = '12px';
renderElementIntoDOM(target);
AiAssistance.SELECT_WORKSPACE_DIALOG_DEFAULT_VIEW(
{
folders: [
{
name: 'workspace-project',
path: 'workspace-project',
},
{
name: 'another-project',
path: 'another-project',
}
],
selectedIndex: 0,
showAutomaticWorkspaceNudge: false,
onProjectSelected: noop,
onSelectButtonClick: noop,
onCancelButtonClick: noop,
onAddFolderButtonClick: noop,
onListItemKeyDown: noop,
},
undefined, target);
}
it('should render correctly in light mode', async () => {
renderViewForScreenshots();
await assertScreenshot('ai_assistance/select-workspace-dialog-light-default.png');
});
it('should render correctly in dark mode', async () => {
setColorScheme('dark');
renderViewForScreenshots();
await assertScreenshot('ai_assistance/select-workspace-dialog-dark-default.png');
});
});
it('selects a project', async () => {
const {view, onProjectSelected, hideDialogSpy, project} = await createComponent();
view.input.onProjectSelected(1);
const input = await view.nextInput;
sinon.assert.callCount(view, 2);
assert.strictEqual(input.selectedIndex, 1);
view.input.onSelectButtonClick();
assert.isTrue(onProjectSelected.calledOnceWith(project));
sinon.assert.calledOnce(hideDialogSpy);
});
it('can be canceled', async () => {
const {view, onProjectSelected, hideDialogSpy} = await createComponent();
view.input.onProjectSelected(1);
const input = await view.nextInput;
sinon.assert.callCount(view, 2);
assert.strictEqual(input.selectedIndex, 1);
view.input.onCancelButtonClick();
sinon.assert.notCalled(onProjectSelected);
sinon.assert.calledOnce(hideDialogSpy);
});
it('listens to ArrowUp/Down', async () => {
const {view} = await createComponent();
view.input.onListItemKeyDown(new KeyboardEvent('keydown', {key: 'ArrowDown'}));
let input = await view.nextInput;
sinon.assert.callCount(view, 2);
assert.strictEqual(input.selectedIndex, 1);
view.input.onListItemKeyDown(new KeyboardEvent('keydown', {key: 'ArrowUp'}));
input = await view.nextInput;
sinon.assert.callCount(view, 3);
assert.strictEqual(input.selectedIndex, 0);
});
it('can add projects', async () => {
const addProjectSpy =
sinon.spy(Persistence.IsolatedFileSystemManager.IsolatedFileSystemManager.instance(), 'addFileSystem');
const {view} = await createComponent();
sinon.assert.callCount(view, 1);
assert.lengthOf(view.input.folders, 2);
assert.strictEqual(view.input.selectedIndex, 0);
view.input.onAddFolderButtonClick();
sinon.assert.calledOnce(addProjectSpy);
createTestFilesystem('file://test3');
const input = await view.nextInput;
sinon.assert.callCount(view, 2);
assert.lengthOf(input.folders, 3);
assert.strictEqual(input.folders[2].name, 'test3');
assert.strictEqual(input.selectedIndex, 2);
});
it('handles project removal', async () => {
const addProjectSpy =
sinon.spy(Persistence.IsolatedFileSystemManager.IsolatedFileSystemManager.instance(), 'addFileSystem');
const {view, project} = await createComponent();
view.input.onProjectSelected(1);
let input = await view.nextInput;
sinon.assert.callCount(view, 2);
assert.lengthOf(input.folders, 2);
assert.strictEqual(input.selectedIndex, 1);
input.onAddFolderButtonClick();
sinon.assert.calledOnce(addProjectSpy);
Workspace.Workspace.WorkspaceImpl.instance().removeProject(project);
input = await view.nextInput;
assert.lengthOf(input.folders, 1);
assert.strictEqual(input.selectedIndex, 0);
});
it('allows selecting an automatic workspace', async () => {
setupAutomaticFileSystem({hasFileSystem: true});
const {view, onProjectSelected, hideDialogSpy} = await createComponent();
sinon.assert.callCount(view, 1);
assert.lengthOf(view.input.folders, 3);
assert.strictEqual(view.input.selectedIndex, 0);
assert.strictEqual(view.input.folders[0].name, 'my-automatic-file-system');
view.input.onSelectButtonClick();
await new Promise(resolve => setTimeout(resolve, 0));
const {project: automaticFileSystemProject} = createTestFilesystem(`file://${root}`);
assert.isTrue(onProjectSelected.calledOnceWith(automaticFileSystemProject));
sinon.assert.calledOnce(hideDialogSpy);
});
});