mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-23 08:39:54 +01:00
tests for #11475
This commit is contained in:
@@ -12,9 +12,9 @@ export interface IIterator<T> {
|
||||
export class ArrayIterator<T> implements IIterator<T> {
|
||||
|
||||
private items: T[];
|
||||
private start: number;
|
||||
private end: number;
|
||||
private index: number;
|
||||
protected start: number;
|
||||
protected end: number;
|
||||
protected index: number;
|
||||
|
||||
constructor(items: T[], start: number = 0, end: number = items.length) {
|
||||
this.items = items;
|
||||
@@ -25,8 +25,11 @@ export class ArrayIterator<T> implements IIterator<T> {
|
||||
|
||||
public next(): T {
|
||||
this.index = Math.min(this.index + 1, this.end);
|
||||
return this.current();
|
||||
}
|
||||
|
||||
if (this.index === this.end) {
|
||||
protected current(): T {
|
||||
if (this.index === this.start - 1 || this.index === this.end) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -34,6 +37,37 @@ export class ArrayIterator<T> implements IIterator<T> {
|
||||
}
|
||||
}
|
||||
|
||||
export class ArrayNavigator<T> extends ArrayIterator<T> implements INavigator<T> {
|
||||
|
||||
constructor(items: T[], start: number = 0, end: number = items.length) {
|
||||
super(items, start, end);
|
||||
}
|
||||
|
||||
public current(): T {
|
||||
return super.current();
|
||||
}
|
||||
|
||||
public previous(): T {
|
||||
this.index = Math.max(this.index - 1, this.start - 1);
|
||||
return this.current();
|
||||
}
|
||||
|
||||
public first(): T {
|
||||
this.index = this.start;
|
||||
return this.current();
|
||||
}
|
||||
|
||||
public last(): T {
|
||||
this.index = this.end - 1;
|
||||
return this.current();
|
||||
}
|
||||
|
||||
public parent(): T {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class MappedIterator<T, R> implements IIterator<R> {
|
||||
|
||||
constructor(protected iterator: IIterator<T>, protected fn: (item:T)=>R) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import * as sinon from 'sinon';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { LinkedMap } from 'vs/base/common/map';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
@@ -26,6 +27,8 @@ import { MarkerService } from 'vs/platform/markers/common/markerService';
|
||||
import { IMarkerService } from 'vs/platform/markers/common/markers';
|
||||
import { IReplaceService } from 'vs/workbench/parts/search/common/replace';
|
||||
import { ReplaceService } from 'vs/workbench/parts/search/browser/replaceService';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
|
||||
|
||||
interface IServiceMock<T> {
|
||||
id: ServiceIdentifier<T>;
|
||||
@@ -48,6 +51,7 @@ export class TestInstantiationService extends InstantiationService {
|
||||
this._servciesMap.set(IExtensionService, SimpleExtensionService);
|
||||
this._servciesMap.set(IMarkerService, MarkerService);
|
||||
this._servciesMap.set(IReplaceService, ReplaceService);
|
||||
this._servciesMap.set(IKeybindingService, WorkbenchKeybindingService);
|
||||
}
|
||||
|
||||
public mock<T>(service:ServiceIdentifier<T>): T | sinon.SinonMock {
|
||||
@@ -136,6 +140,13 @@ export class TestInstantiationService extends InstantiationService {
|
||||
}
|
||||
}
|
||||
|
||||
export function stubFunction<T>(ctor: any, fnProperty: string, value: any): T | sinon.SinonStub {
|
||||
let stub = sinon.createStubInstance(ctor);
|
||||
stub[fnProperty].restore();
|
||||
sinon.stub(stub, fnProperty, types.isFunction(value) ? value : () => { return value; });
|
||||
return stub;
|
||||
}
|
||||
|
||||
interface SinonOptions {
|
||||
mock?: boolean;
|
||||
stub?: boolean;
|
||||
|
||||
@@ -24,7 +24,7 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Keybinding, KeyCode, KeyMod, CommonKeybindings } from 'vs/base/common/keyCodes';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput';
|
||||
import { asFileEditorInput } from 'vs/workbench/common/editor';
|
||||
|
||||
export function isSearchViewletFocussed(viewletService: IViewletService): boolean {
|
||||
let activeViewlet = viewletService.getActiveViewlet();
|
||||
@@ -132,7 +132,7 @@ export abstract class AbstractSearchAndReplaceAction extends Action {
|
||||
/**
|
||||
* Returns element to focus after removing the given element
|
||||
*/
|
||||
protected getElementToFocusAfterRemoved(viewer: ITree, elementToBeRemoved: FileMatchOrMatch): FileMatchOrMatch {
|
||||
public getElementToFocusAfterRemoved(viewer: ITree, elementToBeRemoved: FileMatchOrMatch): FileMatchOrMatch {
|
||||
let elementToFocus = this.getNextElementAfterRemoved(viewer, elementToBeRemoved);
|
||||
if (!elementToFocus) {
|
||||
elementToFocus = this.getPreviousElementAfterRemoved(viewer, elementToBeRemoved);
|
||||
@@ -140,7 +140,7 @@ export abstract class AbstractSearchAndReplaceAction extends Action {
|
||||
return elementToFocus;
|
||||
}
|
||||
|
||||
protected getNextElementAfterRemoved(viewer: ITree, element: FileMatchOrMatch): FileMatchOrMatch {
|
||||
public getNextElementAfterRemoved(viewer: ITree, element: FileMatchOrMatch): FileMatchOrMatch {
|
||||
let navigator: INavigator<any> = this.getNavigatorAt(element, viewer);
|
||||
if (element instanceof FileMatch) {
|
||||
// If file match is removed then next element is the next file match
|
||||
@@ -151,7 +151,7 @@ export abstract class AbstractSearchAndReplaceAction extends Action {
|
||||
return navigator.current();
|
||||
}
|
||||
|
||||
protected getPreviousElementAfterRemoved(viewer: ITree, element: FileMatchOrMatch): FileMatchOrMatch {
|
||||
public getPreviousElementAfterRemoved(viewer: ITree, element: FileMatchOrMatch): FileMatchOrMatch {
|
||||
let navigator: INavigator<any> = this.getNavigatorAt(element, viewer);
|
||||
let previousElement = navigator.previous();
|
||||
if (element instanceof Match && element.parent().matches().length === 1) {
|
||||
@@ -272,9 +272,9 @@ export class ReplaceAction extends AbstractSearchAndReplaceAction {
|
||||
}
|
||||
|
||||
private hasToOpenFile(): boolean {
|
||||
let activeInput = this.editorService.getActiveEditorInput();
|
||||
if (activeInput instanceof FileEditorInput) {
|
||||
return activeInput.getResource().fsPath === this.element.parent().resource().fsPath;
|
||||
const editorInput = asFileEditorInput(this.editorService.getActiveEditorInput());
|
||||
if (editorInput) {
|
||||
return editorInput.getResource().fsPath === this.element.parent().resource().fsPath;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TestInstantiationService, stubFunction } from 'vs/test/utils/instantiationTestUtils';
|
||||
import { Match, FileMatch, FileMatchOrMatch } from 'vs/workbench/parts/search/common/searchModel';
|
||||
import { ReplaceAction } from 'vs/workbench/parts/search/browser/searchActions';
|
||||
import { ArrayNavigator } from 'vs/base/common/iterator';
|
||||
import { IFileMatch } from 'vs/platform/search/common/search';
|
||||
import { createMockModelService } from 'vs/test/utils/servicesTestUtils';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
|
||||
suite('Search Actions', () => {
|
||||
|
||||
let instantiationService: TestInstantiationService;
|
||||
let counter: number;
|
||||
|
||||
setup(() => {
|
||||
instantiationService = new TestInstantiationService();
|
||||
instantiationService.stub(IModelService, createMockModelService(instantiationService));
|
||||
instantiationService.stub(IKeybindingService);
|
||||
counter = 0;
|
||||
});
|
||||
|
||||
test('get next element to focus after removing a match when it has next sibling match', function () {
|
||||
let fileMatch1 = aFileMatch();
|
||||
let fileMatch2 = aFileMatch();
|
||||
let data = [fileMatch1, aMatch(fileMatch1), aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), aMatch(fileMatch2)];
|
||||
let tree = aTree(data);
|
||||
let target = data[2];
|
||||
let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null);
|
||||
|
||||
let actual = testObject.getElementToFocusAfterRemoved(tree, target);
|
||||
|
||||
assert.equal(data[3], actual);
|
||||
});
|
||||
|
||||
test('get next element to focus after removing a match when it does not have next sibling match', function () {
|
||||
let fileMatch1 = aFileMatch();
|
||||
let fileMatch2 = aFileMatch();
|
||||
let data = [fileMatch1, aMatch(fileMatch1), aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), aMatch(fileMatch2)];
|
||||
let tree = aTree(data);
|
||||
let target = data[5];
|
||||
let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null);
|
||||
|
||||
let actual = testObject.getElementToFocusAfterRemoved(tree, target);
|
||||
|
||||
assert.equal(data[4], actual);
|
||||
});
|
||||
|
||||
test('get next element to focus after removing a match when it does not have next sibling match and previous match is file match', function () {
|
||||
let fileMatch1 = aFileMatch();
|
||||
let fileMatch2 = aFileMatch();
|
||||
let data = [fileMatch1, aMatch(fileMatch1), aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2)];
|
||||
let tree = aTree(data);
|
||||
let target = data[4];
|
||||
let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null);
|
||||
|
||||
let actual = testObject.getElementToFocusAfterRemoved(tree, target);
|
||||
|
||||
assert.equal(data[2], actual);
|
||||
});
|
||||
|
||||
test('get next element to focus after removing a match when it is the only match', function () {
|
||||
let fileMatch1 = aFileMatch();
|
||||
let data = [fileMatch1, aMatch(fileMatch1)];
|
||||
let tree = aTree(data);
|
||||
let target = data[1];
|
||||
let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null);
|
||||
|
||||
let actual = testObject.getElementToFocusAfterRemoved(tree, target);
|
||||
|
||||
assert.equal(void 0, actual);
|
||||
});
|
||||
|
||||
test('get next element to focus after removing a file match when it has next sibling', function () {
|
||||
let fileMatch1 = aFileMatch();
|
||||
let fileMatch2 = aFileMatch();
|
||||
let fileMatch3 = aFileMatch();
|
||||
let data = [fileMatch1, aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), fileMatch3, aMatch(fileMatch3)];
|
||||
let tree = aTree(data);
|
||||
let target = data[2];
|
||||
let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null);
|
||||
|
||||
let actual = testObject.getElementToFocusAfterRemoved(tree, target);
|
||||
|
||||
assert.equal(data[4], actual);
|
||||
});
|
||||
|
||||
test('get next element to focus after removing a file match when it has no next sibling', function () {
|
||||
let fileMatch1 = aFileMatch();
|
||||
let fileMatch2 = aFileMatch();
|
||||
let fileMatch3 = aFileMatch();
|
||||
let data = [fileMatch1, aMatch(fileMatch1), fileMatch2, aMatch(fileMatch2), fileMatch3, aMatch(fileMatch3)];
|
||||
let tree = aTree(data);
|
||||
let target = data[4];
|
||||
let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null);
|
||||
|
||||
let actual = testObject.getElementToFocusAfterRemoved(tree, target);
|
||||
|
||||
assert.equal(data[3], actual);
|
||||
});
|
||||
|
||||
test('get next element to focus after removing a file match when it is only match', function () {
|
||||
let fileMatch1 = aFileMatch();
|
||||
let data = [fileMatch1, aMatch(fileMatch1)];
|
||||
let tree = aTree(data);
|
||||
let target = data[0];
|
||||
let testObject: ReplaceAction = instantiationService.createInstance(ReplaceAction, tree, target, null);
|
||||
|
||||
let actual = testObject.getElementToFocusAfterRemoved(tree, target);
|
||||
|
||||
assert.equal(void 0, actual);
|
||||
});
|
||||
|
||||
function aFileMatch(): FileMatch {
|
||||
let rawMatch: IFileMatch = {
|
||||
resource: URI.file('somepath' + ++counter),
|
||||
lineMatches: []
|
||||
};
|
||||
return instantiationService.createInstance(FileMatch, null, null, rawMatch);
|
||||
}
|
||||
|
||||
function aMatch(fileMatch: FileMatch): Match {
|
||||
let match = new Match(fileMatch, 'some match', ++counter, 0, 2);
|
||||
fileMatch.add(match);
|
||||
return match;
|
||||
}
|
||||
|
||||
function aTree(elements: FileMatchOrMatch[]): any {
|
||||
return stubFunction(Tree, 'getNavigator', () => { return new ArrayNavigator(elements); });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user