246 lines
9.9 KiB
TypeScript
246 lines
9.9 KiB
TypeScript
// *****************************************************************************
|
|
// Copyright (C) 2018 TypeFox and others.
|
|
//
|
|
// This program and the accompanying materials are made available under the
|
|
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
// http://www.eclipse.org/legal/epl-2.0.
|
|
//
|
|
// This Source Code may also be made available under the following Secondary
|
|
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
// with the GNU Classpath Exception which is available at
|
|
// https://www.gnu.org/software/classpath/license.html.
|
|
//
|
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
// *****************************************************************************
|
|
import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
|
|
|
|
let disableJSDOM = enableJSDOM();
|
|
|
|
import { expect } from 'chai';
|
|
import { Container } from '@theia/core/shared/inversify';
|
|
import URI from '@theia/core/lib/common/uri';
|
|
import { ILogger } from '@theia/core/lib/common/logger';
|
|
import { MockLogger } from '@theia/core/lib/common/test/mock-logger';
|
|
import { OpenerService } from '@theia/core/lib/browser/opener-service';
|
|
import { MockOpenerService } from '@theia/core/lib/browser/test/mock-opener-service';
|
|
import { NavigationLocationUpdater } from './navigation-location-updater';
|
|
import { NoopNavigationLocationUpdater } from './test/mock-navigation-location-updater';
|
|
import { NavigationLocationSimilarity } from './navigation-location-similarity';
|
|
import { CursorLocation, Position, NavigationLocation, RecentlyClosedEditor } from './navigation-location';
|
|
import { NavigationLocationService } from './navigation-location-service';
|
|
|
|
disableJSDOM();
|
|
|
|
describe('navigation-location-service', () => {
|
|
|
|
let stack: NavigationLocationService;
|
|
|
|
before(() => {
|
|
disableJSDOM = enableJSDOM();
|
|
});
|
|
|
|
after(() => {
|
|
disableJSDOM();
|
|
});
|
|
|
|
beforeEach(() => {
|
|
stack = init();
|
|
});
|
|
|
|
it('should not allow navigating back when the stack is empty', () => {
|
|
expect(stack.canGoBack()).to.be.false;
|
|
});
|
|
|
|
it('should not allow navigating back when the stack has a single location', () => {
|
|
stack.register(createCursorLocation());
|
|
expect(stack.canGoBack()).to.be.false;
|
|
});
|
|
|
|
it('should allow navigating back when the stack has more than one locations', () => {
|
|
stack.register(
|
|
createCursorLocation(),
|
|
createCursorLocation({ line: 100, character: 100 })
|
|
);
|
|
expect(stack.canGoBack()).to.be.true;
|
|
});
|
|
|
|
it('should not allow navigating forward when the stack is empty', () => {
|
|
expect(stack.canGoForward()).to.be.false;
|
|
});
|
|
|
|
it('should not allow navigating forward when the pointer points to the end last element of the stack', () => {
|
|
stack.register(
|
|
createCursorLocation(),
|
|
createCursorLocation({ line: 100, character: 100 })
|
|
);
|
|
expect(stack.canGoForward()).to.be.false;
|
|
});
|
|
|
|
it('should not exceed the max stack item', () => {
|
|
const max = NavigationLocationService['MAX_STACK_ITEMS'];
|
|
const locations: NavigationLocation[] = [...Array(max + 10).keys()].map(i => createCursorLocation({ line: i * 10, character: i }, `file://${i}`));
|
|
stack.register(...locations);
|
|
expect(stack.locations().length).to.not.be.greaterThan(max);
|
|
});
|
|
|
|
it('should successfully clear the history', () => {
|
|
expect(stack['recentlyClosedEditors'].length).equal(0);
|
|
const editor = createMockClosedEditor(new URI('file://foo/a.ts'));
|
|
stack.addClosedEditor(editor);
|
|
expect(stack['recentlyClosedEditors'].length).equal(1);
|
|
|
|
expect(stack['stack'].length).equal(0);
|
|
stack.register(
|
|
createCursorLocation(),
|
|
);
|
|
expect(stack['stack'].length).equal(1);
|
|
|
|
stack['clearHistory']();
|
|
|
|
expect(stack['recentlyClosedEditors'].length).equal(0);
|
|
expect(stack['stack'].length).equal(0);
|
|
});
|
|
|
|
describe('last-edit-location', async () => {
|
|
|
|
it('should return with undefined if the stack contains no modifications', () => {
|
|
stack.register(
|
|
createCursorLocation(),
|
|
createCursorLocation({ line: 100, character: 100 })
|
|
);
|
|
expect(stack.lastEditLocation()).to.be.undefined;
|
|
});
|
|
|
|
it('should return with the location of the last modification', () => {
|
|
const expected = NavigationLocation.create('file://path/to/file', {
|
|
text: '',
|
|
range: { start: { line: 200, character: 0 }, end: { line: 500, character: 0 } },
|
|
rangeLength: 0
|
|
});
|
|
stack.register(
|
|
createCursorLocation(),
|
|
expected,
|
|
createCursorLocation({ line: 100, character: 100 })
|
|
);
|
|
expect(stack.lastEditLocation()).to.be.deep.equal(expected);
|
|
});
|
|
|
|
it('should return with the location of the last modification even if the pointer is not on the head', async () => {
|
|
const modificationLocation = NavigationLocation.create('file://path/to/file', {
|
|
text: '',
|
|
range: { start: { line: 300, character: 0 }, end: { line: 500, character: 0 } },
|
|
rangeLength: 0
|
|
});
|
|
const expected = NavigationLocation.create('file://path/to/file', {
|
|
text: '',
|
|
range: { start: { line: 700, character: 0 }, end: { line: 800, character: 0 } },
|
|
rangeLength: 0
|
|
});
|
|
stack.register(
|
|
createCursorLocation(),
|
|
modificationLocation,
|
|
createCursorLocation({ line: 100, character: 100 }),
|
|
expected
|
|
);
|
|
await stack.back();
|
|
await stack.back();
|
|
expect(stack.currentLocation()).to.be.deep.equal(modificationLocation);
|
|
expect(stack.lastEditLocation()).to.be.deep.equal(expected);
|
|
});
|
|
|
|
});
|
|
|
|
describe('recently-closed-editors', () => {
|
|
|
|
describe('#getLastClosedEditor', () => {
|
|
|
|
it('should return the last closed editor from the history', () => {
|
|
const uri = new URI('file://foo/a.ts');
|
|
stack.addClosedEditor(createMockClosedEditor(uri));
|
|
const editor = stack.getLastClosedEditor();
|
|
expect(editor?.uri).equal(uri);
|
|
});
|
|
|
|
it('should return `undefined` when no history is found', () => {
|
|
expect(stack['recentlyClosedEditors'].length).equal(0);
|
|
const editor = stack.getLastClosedEditor();
|
|
expect(editor).equal(undefined);
|
|
});
|
|
|
|
it('should not exceed the max history', () => {
|
|
expect(stack['recentlyClosedEditors'].length).equal(0);
|
|
const max = NavigationLocationService['MAX_RECENTLY_CLOSED_EDITORS'];
|
|
for (let i = 0; i < max + 10; i++) {
|
|
const uri = new URI(`file://foo/bar-${i}.ts`);
|
|
stack.addClosedEditor(createMockClosedEditor(uri));
|
|
}
|
|
expect(stack['recentlyClosedEditors'].length <= max).be.true;
|
|
});
|
|
|
|
});
|
|
|
|
describe('#addToRecentlyClosedEditors', () => {
|
|
|
|
it('should include unique recently closed editors in the history', () => {
|
|
expect(stack['recentlyClosedEditors'].length).equal(0);
|
|
const a = createMockClosedEditor(new URI('file://foo/a.ts'));
|
|
const b = createMockClosedEditor(new URI('file://foo/b.ts'));
|
|
stack.addClosedEditor(a);
|
|
stack.addClosedEditor(b);
|
|
expect(stack['recentlyClosedEditors'].length).equal(2);
|
|
});
|
|
|
|
it('should not include duplicate recently closed editors in the history', () => {
|
|
const uri = new URI('file://foo/a.ts');
|
|
[1, 2, 3].forEach(i => {
|
|
stack.addClosedEditor(createMockClosedEditor(uri));
|
|
});
|
|
expect(stack['recentlyClosedEditors'].length).equal(1);
|
|
});
|
|
|
|
});
|
|
|
|
describe('#removeFromRecentlyClosedEditors', () => {
|
|
|
|
it('should successfully remove editors from the history that match the given editor uri', () => {
|
|
expect(stack['recentlyClosedEditors'].length).equal(0);
|
|
const editor = createMockClosedEditor(new URI('file://foo/a.ts'));
|
|
|
|
[1, 2, 3].forEach(() => {
|
|
stack['recentlyClosedEditors'].push(editor);
|
|
});
|
|
expect(stack['recentlyClosedEditors'].length).equal(3);
|
|
|
|
// Remove the given editor from the recently closed history.
|
|
stack['removeClosedEditor'](editor.uri);
|
|
expect(stack['recentlyClosedEditors'].length).equal(0);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
function createCursorLocation(context: Position = { line: 0, character: 0, }, uri: string = 'file://path/to/file'): CursorLocation {
|
|
return NavigationLocation.create(uri, context);
|
|
}
|
|
|
|
function init(): NavigationLocationService {
|
|
const container = new Container({ defaultScope: 'Singleton' });
|
|
container.bind(NavigationLocationService).toSelf();
|
|
container.bind(NavigationLocationSimilarity).toSelf();
|
|
container.bind(MockOpenerService).toSelf();
|
|
container.bind(MockLogger).toSelf();
|
|
container.bind(ILogger).toService(MockLogger);
|
|
container.bind(NoopNavigationLocationUpdater).toSelf();
|
|
container.bind(NavigationLocationUpdater).toService(NoopNavigationLocationUpdater);
|
|
container.bind(OpenerService).toService(MockOpenerService);
|
|
return container.get(NavigationLocationService);
|
|
}
|
|
|
|
function createMockClosedEditor(uri: URI): RecentlyClosedEditor {
|
|
return { uri, viewState: {} };
|
|
}
|
|
|
|
});
|