2016-06-14 00:51:40 +03:00
|
|
|
import * as assert from 'assert';
|
2016-09-13 05:55:29 +03:00
|
|
|
import * as vscode from 'vscode';
|
2017-12-11 16:05:43 +03:00
|
|
|
|
|
|
|
import { getAndUpdateModeHandler } from '../extension';
|
|
|
|
import { Position } from '../src/common/motion/position';
|
2017-12-12 14:07:31 +03:00
|
|
|
import { Globals } from '../src/globals';
|
2016-06-18 11:11:27 +03:00
|
|
|
import { ModeName } from '../src/mode/mode';
|
2016-06-14 00:51:40 +03:00
|
|
|
import { ModeHandler } from '../src/mode/modeHandler';
|
|
|
|
import { TextEditor } from '../src/textEditor';
|
2016-09-15 01:19:22 +03:00
|
|
|
import { waitForCursorUpdatesToHappen } from '../src/util';
|
2017-12-11 16:05:43 +03:00
|
|
|
import { assertEqualLines } from './testUtils';
|
2016-06-14 00:51:40 +03:00
|
|
|
|
2017-03-10 05:17:06 +03:00
|
|
|
export function getTestingFunctions() {
|
2016-07-16 20:02:31 +03:00
|
|
|
const newTest = (testObj: ITestObject): void => {
|
2017-06-28 03:02:35 +03:00
|
|
|
const stack = new Error().stack;
|
2017-08-30 20:10:31 +03:00
|
|
|
let niceStack = stack
|
|
|
|
? stack
|
|
|
|
.split('\n')
|
|
|
|
.splice(2, 1)
|
|
|
|
.join('\n')
|
|
|
|
: 'no stack available :(';
|
2016-07-16 20:02:31 +03:00
|
|
|
|
2017-06-28 03:02:35 +03:00
|
|
|
test(testObj.title, async () =>
|
2017-11-12 08:05:43 +03:00
|
|
|
testIt
|
|
|
|
.bind(null, await getAndUpdateModeHandler())(testObj)
|
|
|
|
.catch((reason: Error) => {
|
|
|
|
reason.stack = niceStack;
|
|
|
|
throw reason;
|
|
|
|
})
|
2016-07-16 20:02:31 +03:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const newTestOnly = (testObj: ITestObject): void => {
|
2017-06-28 03:02:35 +03:00
|
|
|
console.log('!!! Running single test !!!');
|
|
|
|
const stack = new Error().stack;
|
2017-08-30 20:10:31 +03:00
|
|
|
let niceStack = stack
|
|
|
|
? stack
|
|
|
|
.split('\n')
|
|
|
|
.splice(2, 1)
|
|
|
|
.join('\n')
|
|
|
|
: 'no stack available :(';
|
2016-07-16 20:02:31 +03:00
|
|
|
|
2017-06-28 03:02:35 +03:00
|
|
|
test.only(testObj.title, async () =>
|
2017-11-12 08:05:43 +03:00
|
|
|
testIt
|
|
|
|
.bind(null, await getAndUpdateModeHandler())(testObj)
|
|
|
|
.catch((reason: Error) => {
|
|
|
|
reason.stack = niceStack;
|
|
|
|
throw reason;
|
|
|
|
})
|
2016-07-16 20:02:31 +03:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
return {
|
|
|
|
newTest,
|
|
|
|
newTestOnly,
|
|
|
|
};
|
2016-06-18 11:11:27 +03:00
|
|
|
}
|
2016-06-14 00:51:40 +03:00
|
|
|
|
2016-06-18 11:11:27 +03:00
|
|
|
interface ITestObject {
|
2016-07-16 20:02:31 +03:00
|
|
|
title: string;
|
|
|
|
start: string[];
|
|
|
|
keysPressed: string;
|
|
|
|
end: string[];
|
|
|
|
endMode?: ModeName;
|
2016-06-14 00:51:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
class TestObjectHelper {
|
2016-09-13 05:55:29 +03:00
|
|
|
/**
|
|
|
|
* Position that the test says that the cursor starts at.
|
|
|
|
*/
|
2016-07-16 20:02:31 +03:00
|
|
|
startPosition = new Position(0, 0);
|
2016-09-13 05:55:29 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Position that the test says that the cursor ends at.
|
|
|
|
*/
|
2016-07-16 20:02:31 +03:00
|
|
|
endPosition = new Position(0, 0);
|
|
|
|
|
|
|
|
private _isValid = false;
|
|
|
|
private _testObject: ITestObject;
|
|
|
|
|
|
|
|
constructor(_testObject: ITestObject) {
|
|
|
|
this._testObject = _testObject;
|
|
|
|
|
|
|
|
this._parse(_testObject);
|
|
|
|
}
|
|
|
|
|
|
|
|
public get isValid(): boolean {
|
|
|
|
return this._isValid;
|
|
|
|
}
|
|
|
|
|
|
|
|
private _setStartCursorPosition(lines: string[]): boolean {
|
|
|
|
let result = this._getCursorPosition(lines);
|
|
|
|
this.startPosition = result.position;
|
|
|
|
return result.success;
|
|
|
|
}
|
|
|
|
|
|
|
|
private _setEndCursorPosition(lines: string[]): boolean {
|
|
|
|
let result = this._getCursorPosition(lines);
|
|
|
|
this.endPosition = result.position;
|
|
|
|
return result.success;
|
|
|
|
}
|
|
|
|
|
2017-06-28 03:02:35 +03:00
|
|
|
private _getCursorPosition(lines: string[]): { success: boolean; position: Position } {
|
2016-07-16 20:02:31 +03:00
|
|
|
let ret = { success: false, position: new Position(0, 0) };
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
|
|
let columnIdx = lines[i].indexOf('|');
|
|
|
|
if (columnIdx >= 0) {
|
|
|
|
ret.position = ret.position.setLocation(i, columnIdx);
|
|
|
|
ret.success = true;
|
|
|
|
}
|
2016-06-14 00:51:40 +03:00
|
|
|
}
|
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
return ret;
|
|
|
|
}
|
2016-06-18 11:11:27 +03:00
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
private _parse(t: ITestObject): void {
|
2017-05-10 03:12:56 +03:00
|
|
|
this._isValid = true;
|
2016-07-16 20:02:31 +03:00
|
|
|
if (!this._setStartCursorPosition(t.start)) {
|
|
|
|
this._isValid = false;
|
2016-06-14 00:51:40 +03:00
|
|
|
}
|
2016-07-16 20:02:31 +03:00
|
|
|
if (!this._setEndCursorPosition(t.end)) {
|
|
|
|
this._isValid = false;
|
2016-06-14 00:51:40 +03:00
|
|
|
}
|
2016-07-16 20:02:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public asVimInputText(): string[] {
|
|
|
|
let ret = 'i' + this._testObject.start.join('\n').replace('|', '');
|
|
|
|
return ret.split('');
|
|
|
|
}
|
|
|
|
|
|
|
|
public asVimOutputText(): string[] {
|
|
|
|
let ret = this._testObject.end.slice(0);
|
|
|
|
ret[this.endPosition.line] = ret[this.endPosition.line].replace('|', '');
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a sequence of Vim movement characters 'hjkl' as a string array
|
|
|
|
* which will move the cursor to the start position given in the test.
|
|
|
|
*/
|
|
|
|
public getKeyPressesToMoveToStartPosition(): string[] {
|
|
|
|
let ret = '';
|
2016-09-13 05:55:29 +03:00
|
|
|
let linesToMove = this.startPosition.line;
|
2016-07-16 20:02:31 +03:00
|
|
|
|
|
|
|
let cursorPosAfterEsc =
|
|
|
|
this._testObject.start[this._testObject.start.length - 1].replace('|', '').length - 1;
|
|
|
|
let numCharsInCursorStartLine =
|
|
|
|
this._testObject.start[this.startPosition.line].replace('|', '').length - 1;
|
2016-09-13 05:55:29 +03:00
|
|
|
let charactersToMove = this.startPosition.character;
|
2016-07-16 20:02:31 +03:00
|
|
|
|
|
|
|
if (linesToMove > 0) {
|
|
|
|
ret += Array(linesToMove + 1).join('j');
|
|
|
|
} else if (linesToMove < 0) {
|
|
|
|
ret += Array(Math.abs(linesToMove) + 1).join('k');
|
2016-06-14 00:51:40 +03:00
|
|
|
}
|
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
if (charactersToMove > 0) {
|
|
|
|
ret += Array(charactersToMove + 1).join('l');
|
|
|
|
} else if (charactersToMove < 0) {
|
|
|
|
ret += Array(Math.abs(charactersToMove) + 1).join('h');
|
2016-06-14 00:51:40 +03:00
|
|
|
}
|
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
return ret.split('');
|
|
|
|
}
|
2016-06-14 00:51:40 +03:00
|
|
|
}
|
|
|
|
|
2016-06-18 21:59:22 +03:00
|
|
|
/**
|
2016-09-01 07:09:50 +03:00
|
|
|
* Tokenize a string like "abc<Esc>d<C-c>" into ["a", "b", "c", "<Esc>", "d", "<C-c>"]
|
2016-06-18 21:59:22 +03:00
|
|
|
*/
|
|
|
|
function tokenizeKeySequence(sequence: string): string[] {
|
2016-07-16 20:02:31 +03:00
|
|
|
let isBracketedKey = false;
|
2017-06-28 03:02:35 +03:00
|
|
|
let key = '';
|
2016-07-16 20:02:31 +03:00
|
|
|
let result: string[] = [];
|
2016-06-18 21:59:22 +03:00
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
for (const char of sequence) {
|
|
|
|
key += char;
|
2016-06-18 21:59:22 +03:00
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
if (char === '<') {
|
|
|
|
isBracketedKey = true;
|
|
|
|
}
|
2016-06-18 21:59:22 +03:00
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
if (char === '>') {
|
|
|
|
isBracketedKey = false;
|
|
|
|
}
|
2016-06-18 21:59:22 +03:00
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
if (isBracketedKey) {
|
|
|
|
continue;
|
2016-06-18 21:59:22 +03:00
|
|
|
}
|
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
result.push(key);
|
2017-06-28 03:02:35 +03:00
|
|
|
key = '';
|
2016-07-16 20:02:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2016-06-18 21:59:22 +03:00
|
|
|
}
|
|
|
|
|
2016-06-18 11:11:27 +03:00
|
|
|
async function testIt(modeHandler: ModeHandler, testObj: ITestObject): Promise<void> {
|
2017-03-19 22:33:40 +03:00
|
|
|
modeHandler.vimState.editor = vscode.window.activeTextEditor!;
|
2017-03-03 10:49:54 +03:00
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
let helper = new TestObjectHelper(testObj);
|
2016-06-14 00:51:40 +03:00
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
// Don't try this at home, kids.
|
|
|
|
(modeHandler as any)._vimState.cursorPosition = new Position(0, 0);
|
2016-06-29 05:19:12 +03:00
|
|
|
|
2016-09-01 07:09:50 +03:00
|
|
|
await modeHandler.handleKeyEvent('<Esc>');
|
2016-06-18 12:16:43 +03:00
|
|
|
|
2016-09-16 02:19:07 +03:00
|
|
|
// Insert all the text as a single action.
|
2017-03-03 10:49:54 +03:00
|
|
|
await modeHandler.vimState.editor.edit(builder => {
|
2017-06-28 03:02:35 +03:00
|
|
|
builder.insert(new Position(0, 0), testObj.start.join('\n').replace('|', ''));
|
2016-09-13 05:55:29 +03:00
|
|
|
});
|
2016-06-18 11:11:27 +03:00
|
|
|
|
2016-09-13 05:55:29 +03:00
|
|
|
await modeHandler.handleMultipleKeyEvents(['<Esc>', 'g', 'g']);
|
|
|
|
|
|
|
|
await waitForCursorUpdatesToHappen();
|
|
|
|
|
2017-05-10 03:12:56 +03:00
|
|
|
// Since we bypassed VSCodeVim to add text,
|
|
|
|
// we need to tell the history tracker that we added it.
|
2016-09-15 23:42:00 +03:00
|
|
|
modeHandler.vimState.historyTracker.addChange();
|
|
|
|
modeHandler.vimState.historyTracker.finishCurrentStep();
|
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
// move cursor to start position using 'hjkl'
|
|
|
|
await modeHandler.handleMultipleKeyEvents(helper.getKeyPressesToMoveToStartPosition());
|
2016-06-14 00:51:40 +03:00
|
|
|
|
2016-09-13 05:55:29 +03:00
|
|
|
await waitForCursorUpdatesToHappen();
|
|
|
|
|
2016-10-10 06:41:28 +03:00
|
|
|
Globals.modeHandlerForTesting = modeHandler;
|
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
// assumes key presses are single characters for now
|
|
|
|
await modeHandler.handleMultipleKeyEvents(tokenizeKeySequence(testObj.keysPressed));
|
2016-06-18 11:11:27 +03:00
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
// Check valid test object input
|
|
|
|
assert(helper.isValid, "Missing '|' in test object.");
|
2016-06-18 11:11:27 +03:00
|
|
|
|
2017-07-09 17:40:11 +03:00
|
|
|
// end: check given end output is correct
|
|
|
|
//
|
|
|
|
assertEqualLines(helper.asVimOutputText());
|
2016-07-16 20:02:31 +03:00
|
|
|
// Check final cursor position
|
|
|
|
//
|
|
|
|
let actualPosition = Position.FromVSCodePosition(TextEditor.getSelection().start);
|
|
|
|
let expectedPosition = helper.endPosition;
|
2017-06-28 03:02:35 +03:00
|
|
|
assert.equal(actualPosition.line, expectedPosition.line, 'Cursor LINE position is wrong.');
|
|
|
|
assert.equal(
|
|
|
|
actualPosition.character,
|
|
|
|
expectedPosition.character,
|
|
|
|
'Cursor CHARACTER position is wrong.'
|
|
|
|
);
|
2016-06-18 11:11:27 +03:00
|
|
|
|
2016-07-16 20:02:31 +03:00
|
|
|
// endMode: check end mode is correct if given
|
|
|
|
if (typeof testObj.endMode !== 'undefined') {
|
|
|
|
let actualMode = ModeName[modeHandler.currentMode.name].toUpperCase();
|
|
|
|
let expectedMode = ModeName[testObj.endMode].toUpperCase();
|
|
|
|
assert.equal(actualMode, expectedMode, "Didn't enter correct mode.");
|
|
|
|
}
|
2016-06-14 00:51:40 +03:00
|
|
|
}
|
|
|
|
|
2017-06-28 03:02:35 +03:00
|
|
|
export { ITestObject, testIt };
|