Path: blob/main/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts
5248 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js';7import { IRange, Range } from '../../../../../editor/common/core/range.js';8import { CommentsPanel } from '../../browser/commentsView.js';9import { CommentService, ICommentController, ICommentInfo, ICommentService, INotebookCommentInfo } from '../../browser/commentService.js';10import { Comment, CommentInput, CommentReaction, CommentThread, CommentThreadCollapsibleState, CommentThreadState } from '../../../../../editor/common/languages.js';11import { Emitter, Event } from '../../../../../base/common/event.js';12import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';13import { IViewContainerModel, IViewDescriptor, IViewDescriptorService, IViewPaneContainer, ViewContainer, ViewContainerLocation } from '../../../../common/views.js';14import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';15import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';16import { IContextViewService } from '../../../../../platform/contextview/browser/contextView.js';17import { DisposableStore } from '../../../../../base/common/lifecycle.js';18import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';19import { CancellationToken } from '../../../../../base/common/cancellation.js';20import { URI, UriComponents } from '../../../../../base/common/uri.js';21import { IHoverService } from '../../../../../platform/hover/browser/hover.js';22import { NullHoverService } from '../../../../../platform/hover/test/browser/nullHoverService.js';23import { SyncDescriptor } from '../../../../../platform/instantiation/common/descriptors.js';2425class TestCommentThread implements CommentThread<IRange> {26isDocumentCommentThread(): this is CommentThread<IRange> {27return true;28}29constructor(public readonly commentThreadHandle: number,30public readonly controllerHandle: number,31public readonly threadId: string,32public readonly resource: string,33public readonly range: IRange,34public readonly comments: Comment[]) { }3536readonly onDidChangeComments: Event<readonly Comment[] | undefined> = new Emitter<readonly Comment[] | undefined>().event;37readonly onDidChangeInitialCollapsibleState: Event<CommentThreadCollapsibleState | undefined> = new Emitter<CommentThreadCollapsibleState | undefined>().event;38canReply: boolean = false;39readonly onDidChangeInput: Event<CommentInput | undefined> = new Emitter<CommentInput | undefined>().event;40readonly onDidChangeRange: Event<IRange> = new Emitter<IRange>().event;41readonly onDidChangeLabel: Event<string | undefined> = new Emitter<string | undefined>().event;42readonly onDidChangeCollapsibleState: Event<CommentThreadCollapsibleState | undefined> = new Emitter<CommentThreadCollapsibleState | undefined>().event;43readonly onDidChangeState: Event<CommentThreadState | undefined> = new Emitter<CommentThreadState | undefined>().event;44readonly onDidChangeCanReply: Event<boolean> = new Emitter<boolean>().event;45isDisposed: boolean = false;46isTemplate: boolean = false;47label: string | undefined = undefined;48contextValue: string | undefined = undefined;49}5051class TestCommentController implements ICommentController {52activeComment: { thread: CommentThread; comment?: Comment } | undefined;53id: string = 'test';54label: string = 'Test Comments';55owner: string = 'test';56features = {};57createCommentThreadTemplate(resource: UriComponents, range: IRange | undefined): Promise<void> {58throw new Error('Method not implemented.');59}60updateCommentThreadTemplate(threadHandle: number, range: IRange): Promise<void> {61throw new Error('Method not implemented.');62}63deleteCommentThreadMain(commentThreadId: string): void {64throw new Error('Method not implemented.');65}66toggleReaction(uri: URI, thread: CommentThread<IRange>, comment: Comment, reaction: CommentReaction, token: CancellationToken): Promise<void> {67throw new Error('Method not implemented.');68}69getDocumentComments(resource: URI, token: CancellationToken): Promise<ICommentInfo> {70throw new Error('Method not implemented.');71}72getNotebookComments(resource: URI, token: CancellationToken): Promise<INotebookCommentInfo> {73throw new Error('Method not implemented.');74}75setActiveCommentAndThread(commentInfo: { thread: CommentThread; comment: Comment } | undefined): Promise<void> {76throw new Error('Method not implemented.');77}7879}8081export class TestViewDescriptorService implements Partial<IViewDescriptorService> {82getViewLocationById(id: string): ViewContainerLocation | null {83return ViewContainerLocation.Panel;84}85readonly onDidChangeLocation: Event<{ views: IViewDescriptor[]; from: ViewContainerLocation; to: ViewContainerLocation }> = new Emitter<{ views: IViewDescriptor[]; from: ViewContainerLocation; to: ViewContainerLocation }>().event;86getViewDescriptorById(id: string): IViewDescriptor | null {87return null;88}89getViewContainerByViewId(id: string): ViewContainer | null {90return {91id: 'comments',92title: { value: 'Comments', original: 'Comments' },93ctorDescriptor: {} as SyncDescriptor<IViewPaneContainer>94};95}96getViewContainerModel(viewContainer: ViewContainer): IViewContainerModel {97const partialViewContainerModel: Partial<IViewContainerModel> = {98onDidChangeContainerInfo: new Emitter<{ title?: boolean; icon?: boolean; keybindingId?: boolean }>().event99};100return partialViewContainerModel as IViewContainerModel;101}102getDefaultContainerById(id: string): ViewContainer | null {103return null;104}105}106107suite('Comments View', function () {108teardown(() => {109instantiationService.dispose();110commentService.dispose();111disposables.dispose();112});113114ensureNoDisposablesAreLeakedInTestSuite();115116let disposables: DisposableStore;117let instantiationService: TestInstantiationService;118let commentService: CommentService;119120setup(() => {121disposables = new DisposableStore();122instantiationService = workbenchInstantiationService({}, disposables);123instantiationService.stub(IConfigurationService, new TestConfigurationService());124instantiationService.stub(IHoverService, NullHoverService);125instantiationService.stub(IContextViewService, {});126instantiationService.stub(IViewDescriptorService, new TestViewDescriptorService());127commentService = instantiationService.createInstance(CommentService);128instantiationService.stub(ICommentService, commentService);129commentService.registerCommentController('test', new TestCommentController());130});131132133134test('collapse all', async function () {135const view = instantiationService.createInstance(CommentsPanel, { id: 'comments', title: 'Comments' });136view.render();137commentService.setWorkspaceComments('test', [138new TestCommentThread(1, 1, '1', 'test1', new Range(1, 1, 1, 1), [{ body: 'test', uniqueIdInThread: 1, userName: 'alex' }]),139new TestCommentThread(2, 1, '1', 'test2', new Range(1, 1, 1, 1), [{ body: 'test', uniqueIdInThread: 1, userName: 'alex' }]),140]);141assert.strictEqual(view.getFilterStats().total, 2);142assert.strictEqual(view.areAllCommentsExpanded(), true);143view.collapseAll();144assert.strictEqual(view.isSomeCommentsExpanded(), false);145view.dispose();146});147148test('expand all', async function () {149const view = instantiationService.createInstance(CommentsPanel, { id: 'comments', title: 'Comments' });150view.render();151commentService.setWorkspaceComments('test', [152new TestCommentThread(1, 1, '1', 'test1', new Range(1, 1, 1, 1), [{ body: 'test', uniqueIdInThread: 1, userName: 'alex' }]),153new TestCommentThread(2, 1, '1', 'test2', new Range(1, 1, 1, 1), [{ body: 'test', uniqueIdInThread: 1, userName: 'alex' }]),154]);155assert.strictEqual(view.getFilterStats().total, 2);156view.collapseAll();157assert.strictEqual(view.isSomeCommentsExpanded(), false);158view.expandAll();159assert.strictEqual(view.areAllCommentsExpanded(), true);160view.dispose();161});162163test('filter by text', async function () {164const view = instantiationService.createInstance(CommentsPanel, { id: 'comments', title: 'Comments' });165view.setVisible(true);166view.render();167commentService.setWorkspaceComments('test', [168new TestCommentThread(1, 1, '1', 'test1', new Range(1, 1, 1, 1), [{ body: 'This comment is a cat.', uniqueIdInThread: 1, userName: 'alex' }]),169new TestCommentThread(2, 1, '1', 'test2', new Range(1, 1, 1, 1), [{ body: 'This comment is a dog.', uniqueIdInThread: 1, userName: 'alex' }]),170]);171assert.strictEqual(view.getFilterStats().total, 2);172assert.strictEqual(view.getFilterStats().filtered, 2);173view.getFilterWidget().setFilterText('cat');174// Setting showResolved causes the filter to trigger for the purposes of this test.175view.filters.showResolved = false;176177assert.strictEqual(view.getFilterStats().total, 2);178assert.strictEqual(view.getFilterStats().filtered, 1);179view.clearFilterText();180// Setting showResolved causes the filter to trigger for the purposes of this test.181view.filters.showResolved = true;182assert.strictEqual(view.getFilterStats().total, 2);183assert.strictEqual(view.getFilterStats().filtered, 2);184view.dispose();185});186});187188189