Skip to content

Commit

Permalink
Run recorded test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
brxck committed Jul 10, 2021
1 parent 4992b25 commit 12098d6
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 12 deletions.
37 changes: 29 additions & 8 deletions src/TestCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ export function serializeRange(range: Range): SerializedRange {
}

export function serializeSelection(selection: Selection): SerializedSelection {
return { active: selection.active, anchor: selection.anchor };
return {
active: serializePosition(selection.active),
anchor: serializePosition(selection.anchor),
};
}

export function serializePosition(position: Position): SerializedPosition {
Expand All @@ -42,17 +45,25 @@ type Command = {

type Snapshot = {
document: string;
clipboard: string | undefined;
clipboard: string;
visibleRanges: SerializedRange[];
selections: SerializedSelection[];
};

type DecorationRanges = { [coloredSymbol: string]: SerializedRange };

export type TestCaseFixture = {
command: Command;
languageId: string;
decorations: DecorationRanges;
initialState: Snapshot;
finalState: Snapshot;
};

export default class TestCase {
command: Command;
languageId: string;
decorations: DecorationRanges | null;
decorations: DecorationRanges;
initialState: Snapshot | null = null;
finalState: Snapshot | null = null;

Expand Down Expand Up @@ -96,7 +107,7 @@ export default class TestCase {

extractTargetedDecorations(targets: Target[], navigationMap: NavigationMap) {
if (!navigationMap) {
return null;
return {};
}

const decorationRanges = navigationMap.serializeRanges();
Expand All @@ -108,26 +119,35 @@ export default class TestCase {
return targetedDecorations;
}

async takeSnapshot() {
static async getSnapshot(): Promise<Snapshot> {
const activeEditor = vscode.window.activeTextEditor!;
const snapshot: Snapshot = {
return {
document: activeEditor.document.getText(),
selections: activeEditor.selections.map(serializeSelection),
visibleRanges: activeEditor.visibleRanges.map(serializeRange),
clipboard: await vscode.env.clipboard.readText(),
};
}

async saveSnapshot() {
const snapshot = await TestCase.getSnapshot();

if (this.initialState == null) {
this.initialState = snapshot;
} else if (this.finalState == null) {
this.finalState = snapshot;
} else {
throw Error("Both test snapshots already taken");
throw Error("Both snapshots already taken");
}

return snapshot;
}

toYaml() {
const fixture = {
if (this.initialState == null || this.finalState == null) {
throw Error("Two snapshots must be taken before serializing");
}
const fixture: TestCaseFixture = {
command: this.command,
languageId: this.languageId,
decorations: this.decorations,
Expand All @@ -138,6 +158,7 @@ export default class TestCase {
}

async presentFixture() {
// TODO: naming convention for fixture files?
const fixture = this.toYaml();
const document = await vscode.workspace.openTextDocument({
language: "yaml",
Expand Down
14 changes: 12 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ export async function activate(context: vscode.ExtensionContext) {
() => {
console.log("Recording test case for next command");
recordTestCase = true;
// Empty clipboard to avoid accidentally cluttering fixtures
vscode.env.clipboard.writeText("");
}
);
const cursorlessSetNavigationMapDisposable = vscode.commands.registerCommand(
"cursorless.setNavigationMap",
(newNavigationMap: NavigationMap) => {
navigationMap = newNavigationMap;
}
);
const cursorlessCommandDisposable = vscode.commands.registerCommand(
Expand Down Expand Up @@ -141,7 +149,7 @@ export async function activate(context: vscode.ExtensionContext) {
if (recordTestCase) {
const command = { actionName, partialTargets, extraArgs };
testCase = new TestCase(command, targets, navigationMap!);
await testCase.takeSnapshot();
await testCase.saveSnapshot();
}

const processedTargetsContext: ProcessedTargetsContext = {
Expand All @@ -166,7 +174,7 @@ export async function activate(context: vscode.ExtensionContext) {
thatMark = newThatMark;

if (testCase != null) {
await testCase.takeSnapshot();
await testCase.saveSnapshot();
testCase.presentFixture();
recordTestCase = false;
}
Expand Down Expand Up @@ -197,6 +205,7 @@ export async function activate(context: vscode.ExtensionContext) {
// const processedTargets = processTargets(navigationMap!, targets);
} catch (e) {
vscode.window.showErrorMessage(e.message);
console.trace(e.message);
throw e;
}
}
Expand Down Expand Up @@ -224,6 +233,7 @@ export async function activate(context: vscode.ExtensionContext) {
cursorlessRecordTestCaseDisposable,
toggleDecorationsDisposable,
recomputeDecorationStylesDisposable,
cursorlessSetNavigationMapDisposable,
vscode.workspace.onDidChangeConfiguration(recomputeDecorationStyles),
vscode.window.onDidChangeTextEditorVisibleRanges(addDecorationsDebounced),
vscode.window.onDidChangeActiveTextEditor(addDecorationsDebounced),
Expand Down
2 changes: 1 addition & 1 deletion src/test/suite/inferFullTargets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as assert from "assert";
import { inferFullTargets } from "../../inferFullTargets";
import fixture from "./fixtures/inferFullTargets.fixture";

suite("inferFullTargets", () => {
suite.skip("inferFullTargets", () => {
fixture.forEach(({ input, expectedOutput }, index) => {
test(`inferFullTargets ${index}`, () => {
assert.deepStrictEqual(
Expand Down
2 changes: 1 addition & 1 deletion src/test/suite/processTargets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import processTargets from "../../processTargets";
import { Target, Token } from "../../Types";
// import * as myExtension from '../../extension';

suite("processTargets", () => {
suite.skip("processTargets", () => {
test("simple processTargets", () => {
const navigationMap = new NavigationMap();
const token: Token = {
Expand Down
95 changes: 95 additions & 0 deletions src/test/suite/recorded.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import * as assert from "assert";
import { promises as fsp } from "fs";
import * as path from "path";
import * as yaml from "js-yaml";
import * as vscode from "vscode";
import NavigationMap from "../../NavigationMap";
import TestCase, {
SerializedPosition,
SerializedRange,
SerializedSelection,
TestCaseFixture,
} from "../../TestCase";
import { Token } from "../../Types";

function deserializePosition(position: SerializedPosition) {
return new vscode.Position(position.line, position.character);
}

function deserializeRange(range: SerializedRange) {
return new vscode.Range(
deserializePosition(range.start),
deserializePosition(range.end)
);
}

function deserializeSelection(
selection: SerializedSelection
): vscode.Selection {
const active = deserializePosition(selection.active);
const anchor = deserializePosition(selection.anchor);
return new vscode.Selection(anchor, active);
}

suite("recorded test cases", async function () {
const directory = path.join(
__dirname,
// TODO What's the best way to handle this?
"../../../src/test/suite/recordings"
);
const files = await fsp.readdir(directory);

files.forEach(async (file) => {
test(file.split(".")[0], async function () {
this.timeout(100000);
const buffer = await fsp.readFile(path.join(directory, file));
const fixture = yaml.load(buffer.toString()) as TestCaseFixture;

await vscode.commands.executeCommand("workbench.action.closeAllEditors");
const document = await vscode.workspace.openTextDocument({
language: fixture.languageId,
content: fixture.initialState.document,
});
const editor = await vscode.window.showTextDocument(document);

await vscode.env.clipboard.writeText(fixture.initialState.clipboard);
editor.selections =
fixture.initialState.selections.map(deserializeSelection);

// TODO restore visible ranges?
// Not sure of a straightforward way to do this. Maybe just use to test folding?

const navigationMap = new NavigationMap();
Object.entries(fixture.decorations).forEach(([key, value], index) => {
const range = deserializeRange(value);
const text = editor.document.getText(range);
// TODO not a big fan of this, need a better way to create tokens
const token: Token = {
text,
range,
startOffset: editor.document.offsetAt(range.start) + index,
endOffset: editor.document.offsetAt(range.end) + index + text.length,
displayLine: value.start.line, // TODO depends on visible ranges? See above
editor: vscode.window.activeTextEditor!,
};
const [color, character] = key.split(".");
// @ts-ignore TODO should probably add a decoration color type?
navigationMap.addToken(color, character, token);
});

await vscode.commands.executeCommand(
"cursorless.setNavigationMap",
navigationMap
);

await vscode.commands.executeCommand(
"cursorless.command",
fixture.command.actionName,
fixture.command.partialTargets,
...fixture.command.extraArgs
);

assert.deepStrictEqual(fixture.finalState, await TestCase.getSnapshot());
});
});
});

0 comments on commit 12098d6

Please sign in to comment.