From 85887f20a3dea9111e9cfff8b40c4d225bdfa1fd Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 4 Jul 2024 14:20:22 +0100 Subject: [PATCH] Expose some fixture abstractions --- packages/common/src/index.ts | 2 + .../common/src/testUtil/getFixturePaths.ts | 10 +++ .../testUtil/getResultStateForComparison.ts | 46 +++++++++++ .../common/src/testUtil/runRecordedTest.ts | 80 +++---------------- .../src/util/serializedMarksToTokenHats.ts | 36 +++++++++ .../src/core/IndividualHatMap.ts | 2 +- 6 files changed, 107 insertions(+), 69 deletions(-) create mode 100644 packages/common/src/testUtil/getResultStateForComparison.ts create mode 100644 packages/common/src/util/serializedMarksToTokenHats.ts diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index de40c1b65a7..d63155641d0 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -50,6 +50,7 @@ export * from "./types/ScopeProvider"; export * from "./types/SpokenForm"; export * from "./types/commandHistory"; export * from "./util/textFormatters"; +export * from "./util/serializedMarksToTokenHats"; export * from "./types/snippet.types"; export * from "./testUtil/fromPlainObject"; export * from "./testUtil/spyToPlainObject"; @@ -68,6 +69,7 @@ export * from "./testUtil/serialize"; export * from "./testUtil/shouldUpdateFixtures"; export * from "./testUtil/TestCaseSnapshot"; export * from "./testUtil/serializeTestFixture"; +export * from "./testUtil/getResultStateForComparison"; export * from "./testUtil/asyncSafety"; export * from "./testUtil/getScopeTestPathsRecursively"; export * from "./util/typeUtils"; diff --git a/packages/common/src/testUtil/getFixturePaths.ts b/packages/common/src/testUtil/getFixturePaths.ts index 3bdcda5b304..78276915c79 100644 --- a/packages/common/src/testUtil/getFixturePaths.ts +++ b/packages/common/src/testUtil/getFixturePaths.ts @@ -1,6 +1,9 @@ +import * as yaml from "js-yaml"; import * as path from "path"; import { walkFilesSync } from "../util/walkSync"; import { getCursorlessRepoRoot } from "./getCursorlessRepoRoot"; +import { TestCaseFixtureLegacy } from "../types/TestCaseFixture"; +import { readFile } from "node:fs/promises"; export function getFixturesPath() { return path.join(getCursorlessRepoRoot(), "data", "fixtures"); @@ -67,3 +70,10 @@ function pathToName(relativeDir: string, filePath: string) { .relative(relativeDir, filePath.substring(0, filePath.lastIndexOf("."))) .replaceAll("\\", "/"); } + +export async function loadFixture( + path: string, +): Promise { + const buffer = await readFile(path); + return yaml.load(buffer.toString()) as TestCaseFixtureLegacy; +} diff --git a/packages/common/src/testUtil/getResultStateForComparison.ts b/packages/common/src/testUtil/getResultStateForComparison.ts new file mode 100644 index 00000000000..a49a87ae238 --- /dev/null +++ b/packages/common/src/testUtil/getResultStateForComparison.ts @@ -0,0 +1,46 @@ +import { storedTargetKeys } from "../StoredTargetKey"; +import SpyIDE from "../ide/spy/SpyIDE"; +import { ReadOnlyHatMap } from "../types/HatTokenMap"; +import { marksToPlainObject } from "../util/toPlainObject"; +import { ExcludableSnapshotField, TestCaseSnapshot } from "./TestCaseSnapshot"; +import { extractTargetedMarks } from "./extractTargetedMarks"; +import { TestHelpers } from "./runRecordedTest"; + +export async function getResultStateForComparison( + finalState: TestCaseSnapshot | undefined, + readableHatMap: ReadOnlyHatMap, + spyIde: SpyIDE, + takeSnapshot: TestHelpers["takeSnapshot"], +) { + const excludeFields: ExcludableSnapshotField[] = []; + + const marks = + finalState?.marks == null + ? undefined + : marksToPlainObject( + extractTargetedMarks(Object.keys(finalState.marks), readableHatMap), + ); + + if (finalState?.clipboard == null) { + excludeFields.push("clipboard"); + } + + for (const storedTargetKey of storedTargetKeys) { + const key = `${storedTargetKey}Mark` as const; + if (finalState?.[key] == null) { + excludeFields.push(key); + } + } + + // FIXME Visible ranges are not asserted, see: + // https://github.com/cursorless-dev/cursorless/issues/160 + const { visibleRanges, ...resultState } = await takeSnapshot( + excludeFields, + [], + spyIde.activeTextEditor!, + spyIde, + marks, + ); + + return resultState; +} diff --git a/packages/common/src/testUtil/runRecordedTest.ts b/packages/common/src/testUtil/runRecordedTest.ts index af2e92d0c69..45de293475d 100644 --- a/packages/common/src/testUtil/runRecordedTest.ts +++ b/packages/common/src/testUtil/runRecordedTest.ts @@ -18,23 +18,21 @@ import { TestCaseFixtureLegacy, TestCaseSnapshot, TextEditor, - TokenHat, clientSupportsFallback, - extractTargetedMarks, - marksToPlainObject, + loadFixture, omitByDeep, - plainObjectToRange, rangeToPlainObject, serializeTestFixture, + serializedMarksToTokenHats, shouldUpdateFixtures, splitKey, spyIDERecordedValuesToPlainObject, storedTargetKeys, } from "@cursorless/common"; import { assert } from "chai"; -import * as yaml from "js-yaml"; import { isUndefined } from "lodash"; import { promises as fsp } from "node:fs"; +import { getResultStateForComparison } from "./getResultStateForComparison"; function createPosition(position: PositionPlainObject) { return new Position(position.line, position.character); @@ -123,9 +121,7 @@ export async function runRecordedTest({ testHelpers, runCursorlessCommand, }: RunRecordedTestOpts) { - const buffer = await fsp.readFile(path); - const fixture = yaml.load(buffer.toString()) as TestCaseFixtureLegacy; - const excludeFields: ExcludableSnapshotField[] = []; + const fixture = await loadFixture(path); // FIXME The snapshot gets messed up with timing issues when running the recorded tests // "Couldn't find token default.a" @@ -162,7 +158,10 @@ export async function runRecordedTest({ // Ensure that the expected hats are present await hatTokenMap.allocateHats( - getTokenHats(fixture.initialState.marks, spyIde.activeTextEditor!), + serializedMarksToTokenHats( + fixture.initialState.marks, + spyIde.activeTextEditor!, + ), ); const readableHatMap = await hatTokenMap.getReadableMap(usePrePhraseSnapshot); @@ -217,35 +216,11 @@ export async function runRecordedTest({ await sleepWithBackoff(fixture.postCommandSleepTimeMs); } - const marks = - fixture.finalState?.marks == null - ? undefined - : marksToPlainObject( - extractTargetedMarks( - Object.keys(fixture.finalState.marks), - readableHatMap, - ), - ); - - if (fixture.finalState?.clipboard == null) { - excludeFields.push("clipboard"); - } - - for (const storedTargetKey of storedTargetKeys) { - const key = `${storedTargetKey}Mark` as const; - if (fixture.finalState?.[key] == null) { - excludeFields.push(key); - } - } - - // FIXME Visible ranges are not asserted, see: - // https://github.com/cursorless-dev/cursorless/issues/160 - const { visibleRanges, ...resultState } = await takeSnapshot( - excludeFields, - [], - spyIde.activeTextEditor!, + const resultState = await getResultStateForComparison( + fixture.finalState, + readableHatMap, spyIde, - marks, + takeSnapshot, ); const rawSpyIdeValues = spyIde.getSpyValues(fixture.ide?.flashes != null); @@ -313,34 +288,3 @@ function checkMarks( assert.deepStrictEqual(rangeToPlainObject(currentToken.range), token); }); } - -function getTokenHats( - marks: SerializedMarks | undefined, - editor: TextEditor, -): TokenHat[] { - if (marks == null) { - return []; - } - - return Object.entries(marks).map(([key, token]) => { - const { hatStyle, character } = splitKey(key); - const range = plainObjectToRange(token); - - return { - hatStyle, - grapheme: character, - token: { - editor, - range, - offsets: { - start: editor.document.offsetAt(range.start), - end: editor.document.offsetAt(range.end), - }, - text: editor.document.getText(range), - }, - - // NB: We don't care about the hat range for this test - hatRange: range, - }; - }); -} diff --git a/packages/common/src/util/serializedMarksToTokenHats.ts b/packages/common/src/util/serializedMarksToTokenHats.ts new file mode 100644 index 00000000000..94203c29012 --- /dev/null +++ b/packages/common/src/util/serializedMarksToTokenHats.ts @@ -0,0 +1,36 @@ +import { plainObjectToRange } from "../testUtil/fromPlainObject"; +import { splitKey } from "./splitKey"; +import { SerializedMarks } from "./toPlainObject"; +import { TextEditor } from "../types/TextEditor"; +import { TokenHat } from "../types/HatTokenMap"; + +export function serializedMarksToTokenHats( + marks: SerializedMarks | undefined, + editor: TextEditor, +): TokenHat[] { + if (marks == null) { + return []; + } + + return Object.entries(marks).map(([key, token]) => { + const { hatStyle, character } = splitKey(key); + const range = plainObjectToRange(token); + + return { + hatStyle, + grapheme: character, + token: { + editor, + range, + offsets: { + start: editor.document.offsetAt(range.start), + end: editor.document.offsetAt(range.end), + }, + text: editor.document.getText(range), + }, + + // NB: We don't care about the hat range for this test + hatRange: range, + }; + }); +} diff --git a/packages/cursorless-engine/src/core/IndividualHatMap.ts b/packages/cursorless-engine/src/core/IndividualHatMap.ts index 5b00390b4ed..334c15b5163 100644 --- a/packages/cursorless-engine/src/core/IndividualHatMap.ts +++ b/packages/cursorless-engine/src/core/IndividualHatMap.ts @@ -58,7 +58,7 @@ export class IndividualHatMap implements ReadOnlyHatMap { } /** - * Overwrites the hat assignemnt for this hat token map. + * Overwrites the hat assignment for this hat token map. * * @param tokenHats The new hat assignments */