Skip to content

Converted test recorder for bulk recordings #169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 1, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 30 additions & 59 deletions src/TestCaseRecorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { walkDirsSync } from "./test/suite/walkSync";

export class TestCaseRecorder {
active: boolean = false;
outPath: string | null = null;
spokenForm: string | null = null;
workspacePath: string | null;
workSpaceFolder: string | null;
fixtureRoot: string | null;
Expand All @@ -28,23 +26,19 @@ export class TestCaseRecorder {
: null;
}

start(): Promise<void> {
this.active = true;
return this.promptSpokenForm();
async start(): Promise<boolean> {
this.active = await this.promptSubdirectory();
return this.active;
}

async finish(testCase: TestCase): Promise<string | null> {
stop() {
this.active = false;
const outPath = await this.promptSubdirectory();
const fixture = testCase.toYaml();

if (outPath) {
this.writeToFile(outPath, fixture);
} else {
this.showFixture(fixture);
}
}

return outPath;
async finish(testCase: TestCase): Promise<void> {
const outPath = this.calculateFilePath(testCase);
const fixture = testCase.toYaml();
await this.writeToFile(outPath, fixture);
}

private async writeToFile(outPath: string, fixture: string) {
Expand All @@ -59,39 +53,13 @@ export class TestCaseRecorder {
});
}

private async showFixture(fixture: string) {
const document = await vscode.workspace.openTextDocument({
language: "yaml",
content: fixture,
});
await vscode.window.showTextDocument(document, {
viewColumn: vscode.ViewColumn.Beside,
});
}

private async promptSpokenForm(): Promise<void> {
const result = await vscode.window.showInputBox({
prompt: "Talon Command",
ignoreFocusOut: true,
validateInput: (input) => (input.trim().length > 0 ? null : "Required"),
});

// Inputs return undefined when a user cancels by hitting 'escape'
if (result === undefined) {
this.active = false;
return;
}

this.spokenForm = result;
}

private async promptSubdirectory(): Promise<string | null> {
private async promptSubdirectory(): Promise<boolean> {
if (
this.workspacePath == null ||
this.fixtureRoot == null ||
this.workSpaceFolder !== "cursorless-vscode"
) {
return null;
return false;
}

const subdirectories = walkDirsSync(this.fixtureRoot).concat("/");
Expand All @@ -103,16 +71,16 @@ export class TestCaseRecorder {
]);

if (subdirectorySelection === undefined) {
return null;
return false;
} else if (subdirectorySelection === createNewSubdirectory) {
return this.promptNewSubdirectory();
} else {
this.fixtureSubdirectory = subdirectorySelection;
return this.promptFileName();
return true;
}
}

private async promptNewSubdirectory(): Promise<string | null> {
private async promptNewSubdirectory(): Promise<boolean> {
if (this.fixtureRoot == null) {
throw new Error("Missing fixture root. Not in cursorless workspace?");
}
Expand All @@ -128,32 +96,35 @@ export class TestCaseRecorder {
}

this.fixtureSubdirectory = subdirectory;
return this.promptFileName();
return true;
}

private async promptFileName(): Promise<string | null> {
private calculateFilePath(testCase: TestCase): string {
if (this.fixtureRoot == null) {
throw new Error("Missing fixture root. Not in cursorless workspace?");
}

const filename = await vscode.window.showInputBox({
prompt: "Fixture Filename",
});

if (filename === undefined || this.fixtureSubdirectory == null) {
return this.promptSubdirectory(); // go back a prompt
}

const targetDirectory = path.join(
this.fixtureRoot,
this.fixtureSubdirectory
this.fixtureSubdirectory!
);

if (!fs.existsSync(targetDirectory)) {
fs.mkdirSync(targetDirectory);
}

this.outPath = path.join(targetDirectory, `${filename}.yml`);
return this.outPath;
const filename = camelize(testCase.spokenForm);
return path.join(targetDirectory, `${filename}.yml`);
}
}

function camelize(str: string) {
return str
.split(" ")
.map((str, index) => (index === 0 ? str : capitalize(str)))
.join("");
}

function capitalize(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
29 changes: 18 additions & 11 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,33 @@ export async function activate(context: vscode.ExtensionContext) {
const graph = makeGraph(graphConstructors);
const thatMark = new ThatMark();
const testCaseRecorder = new TestCaseRecorder(context);

const cursorlessRecordTestCaseDisposable = vscode.commands.registerCommand(
"cursorless.recordTestCase",
async () => {
console.log("Recording test case for next command");
testCaseRecorder.start();
if (testCaseRecorder.active) {
vscode.window.showInformationMessage("Stopped recording test cases");
testCaseRecorder.stop();
} else {
if (await testCaseRecorder.start()) {
vscode.window.showInformationMessage(
"Recording test cases for following commands"
);
}
}
}
);

const cursorlessCommandDisposable = vscode.commands.registerCommand(
"cursorless.command",
async (
spokenForm: string,
actionName: ActionType,
partialTargets: PartialTarget[],
...extraArgs: any[]
) => {
try {
console.debug(`spokenForm: ${spokenForm}`);
console.debug(`action: ${actionName}`);
console.debug(`partialTargets:`);
console.debug(JSON.stringify(partialTargets, null, 3));
Expand All @@ -109,14 +121,9 @@ export async function activate(context: vscode.ExtensionContext) {

const isPaste = actionName === "paste";

var clipboardContents: string | undefined;

if (isPaste) {
clipboardContents = await Clipboard.readText();
// clipboardContents = "hello";
// clipboardContents = "hello\n";
// clipboardContents = "\nhello\n";
}
const clipboardContents = isPaste
? await Clipboard.readText()
: undefined;

const inferenceContext = {
selectionContents,
Expand Down Expand Up @@ -151,7 +158,7 @@ export async function activate(context: vscode.ExtensionContext) {
targets,
thatMark: thatMark,
navigationMap: graph.navigationMap!,
spokenForm: testCaseRecorder.spokenForm ?? "",
spokenForm,
};
testCase = new TestCase(command, context);
await testCase.recordInitialState();
Expand Down
27 changes: 27 additions & 0 deletions src/test/suite/fixtures/recorded/actions/breakpointCap.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
spokenForm: breakpoint cap
languageId: typescript
command:
actionName: setBreakpoint
partialTargets:
- type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: c}
extraArgs: []
marks:
default.c:
start: {line: 0, character: 0}
end: {line: 0, character: 5}
initialState:
documentContents: "const myVariableValue = \"hello\";\r\n"
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
finalState:
documentContents: "const myVariableValue = \"hello\";\r\n"
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
thatMark:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 5}
returnValue: null
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: c}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}]
33 changes: 33 additions & 0 deletions src/test/suite/fixtures/recorded/actions/bringMade.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
spokenForm: bring made
languageId: typescript
command:
actionName: bring
partialTargets:
- type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: m}
- type: primitive
mark: {type: cursor}
selectionType: token
position: contents
modifier: {type: identity}
insideOutsideType: inside
extraArgs: []
marks:
default.m:
start: {line: 0, character: 6}
end: {line: 0, character: 21}
initialState:
documentContents: "const myVariableValue = \"hello\";\r\n"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. So I don't think it's worth re-recording everything, but I believe if you don't use line feeds these will get turned into block output and be a bit nicer to read. At least I think that's what's happening here. Check out the example yamls

selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
finalState:
documentContents: "const myVariableValue = \"hello\";\r\nmyVariableValue"
selections:
- anchor: {line: 1, character: 15}
active: {line: 1, character: 15}
thatMark:
- anchor: {line: 1, character: 15}
active: {line: 1, character: 0}
returnValue: null
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: m}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: null}, {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}]
32 changes: 32 additions & 0 deletions src/test/suite/fixtures/recorded/actions/bringMadeToCap.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
spokenForm: bring made to cap
languageId: typescript
command:
actionName: bring
partialTargets:
- type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: m}
- type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: c}
extraArgs: []
marks:
default.m:
start: {line: 0, character: 6}
end: {line: 0, character: 21}
default.c:
start: {line: 0, character: 0}
end: {line: 0, character: 5}
initialState:
documentContents: "const myVariableValue = \"hello\";\r\n"
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
finalState:
documentContents: "myVariableValue myVariableValue = \"hello\";\r\n"
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
thatMark:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 15}
returnValue: null
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: m}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: null}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: c}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: null}]
27 changes: 27 additions & 0 deletions src/test/suite/fixtures/recorded/actions/carveMade.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
spokenForm: carve made
languageId: typescript
command:
actionName: cut
partialTargets:
- type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: m}
extraArgs: []
marks:
default.m:
start: {line: 0, character: 6}
end: {line: 0, character: 21}
initialState:
documentContents: "const myVariableValue = \"hello\";\r\n"
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
finalState:
documentContents: "const = \"hello\";\r\n"
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
thatMark:
- anchor: {line: 0, character: 6}
active: {line: 0, character: 6}
returnValue: null
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: m}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: null}]
27 changes: 27 additions & 0 deletions src/test/suite/fixtures/recorded/actions/chuckMade.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
spokenForm: chuck made
languageId: typescript
command:
actionName: delete
partialTargets:
- type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: m}
extraArgs: []
marks:
default.m:
start: {line: 0, character: 6}
end: {line: 0, character: 21}
initialState:
documentContents: "const myVariableValue = \"hello\";\r\n"
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
finalState:
documentContents: "const = \"hello\";\r\n"
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
thatMark:
- anchor: {line: 0, character: 6}
active: {line: 0, character: 6}
returnValue: null
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: m}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: outside}]
Loading