@tui-sandbox/library 7.5.6 → 7.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [7.7.0](https://github.com/mikavilpas/tui-sandbox/compare/library-v7.6.0...library-v7.7.0) (2025-01-12)
4
+
5
+
6
+ ### Features
7
+
8
+ * create config-modifications directory if it doesn't exist ([1688f88](https://github.com/mikavilpas/tui-sandbox/commit/1688f88db821c73b499c1b0cb2f2ebe0fe221741))
9
+
10
+ ## [7.6.0](https://github.com/mikavilpas/tui-sandbox/compare/library-v7.5.6...library-v7.6.0) (2025-01-11)
11
+
12
+
13
+ ### Features
14
+
15
+ * can run a prepare script before starting the tests ([#225](https://github.com/mikavilpas/tui-sandbox/issues/225)) ([53d1a32](https://github.com/mikavilpas/tui-sandbox/commit/53d1a32ff292f9a931a90fddceb5ec36a8dad84a))
16
+
3
17
  ## [7.5.6](https://github.com/mikavilpas/tui-sandbox/compare/library-v7.5.5...library-v7.5.6) (2025-01-09)
4
18
 
5
19
 
@@ -34,6 +34,8 @@ declare global {
34
34
  }
35
35
  }
36
36
 
37
+ /** Arguments for starting the neovim server. They are built based on your test
38
+ * environment in a type safe manner. */
37
39
  type MyStartNeovimServerArguments = OverrideProperties<
38
40
  StartNeovimGenericArguments,
39
41
  {
@@ -1,4 +1,5 @@
1
1
  import { readFileSync, writeFileSync } from "fs";
2
+ import { mkdir, stat } from "fs/promises";
2
3
  import path from "path";
3
4
  import { createCypressSupportFileContents } from "./contents.js";
4
5
  /**
@@ -7,6 +8,15 @@ import { createCypressSupportFileContents } from "./contents.js";
7
8
  * tui-sandbox version.
8
9
  */
9
10
  export async function createCypressSupportFile({ cypressSupportDirectoryPath, supportFileName, }) {
11
+ // create config-modifications directory if it doesn't exist
12
+ const configModificationsDirectoryPath = path.join(cypressSupportDirectoryPath, "config-modifications");
13
+ try {
14
+ await stat(configModificationsDirectoryPath);
15
+ }
16
+ catch (error) {
17
+ console.log(`Creating config-modifications directory at ${configModificationsDirectoryPath}. You can put Neovim startup scripts into this directory, and load them when starting your Neovim test.`);
18
+ await mkdir(configModificationsDirectoryPath);
19
+ }
10
20
  const text = await createCypressSupportFileContents();
11
21
  let oldSchema = "";
12
22
  const outputFilePath = path.join(cypressSupportDirectoryPath, supportFileName);
@@ -1,12 +1,16 @@
1
1
  import { readFileSync, writeFileSync } from "fs";
2
+ import { mkdir, stat } from "fs/promises";
2
3
  import { createCypressSupportFileContents } from "./contents.js";
3
4
  import { createCypressSupportFile } from "./createCypressSupportFile.js";
4
5
  vi.mock("fs");
6
+ vi.mock("fs/promises");
5
7
  vi.mock("./contents.ts");
6
8
  const mocked = {
7
9
  readFileSync: vi.mocked(readFileSync),
8
10
  writeFileSync: vi.mocked(writeFileSync),
9
11
  createCypressSupportFileContents: vi.mocked(createCypressSupportFileContents),
12
+ mkdir: vi.mocked(mkdir),
13
+ stat: vi.mocked(stat),
10
14
  };
11
15
  describe("createCypressSupportFileContents", () => {
12
16
  it("should update the file if the schema has changed", async () => {
@@ -32,4 +36,21 @@ describe("createCypressSupportFileContents", () => {
32
36
  });
33
37
  expect(result).toBe("did-nothing");
34
38
  });
39
+ it("should create the config-modifications directory if it does not exist", async () => {
40
+ // This directory is required to exist so that a type safe schema of the
41
+ // neovimArguments can be built and used.
42
+ mocked.readFileSync.mockImplementationOnce(() => "");
43
+ mocked.writeFileSync.mockImplementationOnce(() => {
44
+ //
45
+ });
46
+ mocked.createCypressSupportFileContents.mockImplementationOnce(async () => "contents");
47
+ mocked.stat.mockRejectedValueOnce(new Error("ENOENT"));
48
+ const result = await createCypressSupportFile({
49
+ cypressSupportDirectoryPath: "cypress/support",
50
+ supportFileName: "tui-sandbox.ts",
51
+ });
52
+ expect(result).toBe("updated");
53
+ expect(mocked.mkdir).toHaveBeenCalledWith("cypress/support/config-modifications");
54
+ expect(mocked.mkdir).toHaveBeenCalledTimes(1);
55
+ });
35
56
  });
@@ -52,6 +52,12 @@ describe("dirtree", () => {
52
52
  extension: z.literal("lua"),
53
53
  stem: z.literal("init."),
54
54
  }),
55
+ "prepare.lua": z.object({
56
+ name: z.literal("prepare.lua"),
57
+ type: z.literal("file"),
58
+ extension: z.literal("lua"),
59
+ stem: z.literal("prepare."),
60
+ }),
55
61
  }),
56
62
  }),
57
63
  }),
@@ -104,6 +110,24 @@ describe("dirtree", () => {
104
110
  extension: z.literal("txt"),
105
111
  stem: z.literal("initial-file."),
106
112
  }),
113
+ "lua-project": z.object({
114
+ name: z.literal("lua-project/"),
115
+ type: z.literal("directory"),
116
+ contents: z.object({
117
+ "config.lua": z.object({
118
+ name: z.literal("config.lua"),
119
+ type: z.literal("file"),
120
+ extension: z.literal("lua"),
121
+ stem: z.literal("config."),
122
+ }),
123
+ "init.lua": z.object({
124
+ name: z.literal("init.lua"),
125
+ type: z.literal("file"),
126
+ extension: z.literal("lua"),
127
+ stem: z.literal("init."),
128
+ }),
129
+ }),
130
+ }),
107
131
  "other-subdirectory": z.object({
108
132
  name: z.literal("other-subdirectory/"),
109
133
  type: z.literal("directory"),
@@ -169,6 +193,7 @@ describe("dirtree", () => {
169
193
  export const testDirectoryFiles = z.enum([
170
194
  ".config/.gitkeep",
171
195
  ".config/nvim/init.lua",
196
+ ".config/nvim/prepare.lua",
172
197
  ".config/nvim",
173
198
  ".config",
174
199
  "config-modifications/add_command_to_count_open_buffers.lua",
@@ -179,6 +204,9 @@ describe("dirtree", () => {
179
204
  "dir with spaces",
180
205
  "file.txt",
181
206
  "initial-file.txt",
207
+ "lua-project/config.lua",
208
+ "lua-project/init.lua",
209
+ "lua-project",
182
210
  "other-subdirectory/other-sub-file.txt",
183
211
  "other-subdirectory",
184
212
  "routes/posts.$postId/adjacent-file.txt",
@@ -2,10 +2,9 @@ import "core-js/proposals/async-explicit-resource-management.js";
2
2
  import type { BlockingCommandInput, ExCommandInput, LuaCodeInput } from "../server.js";
3
3
  import type { BlockingShellCommandOutput, RunExCommandOutput, RunLuaCodeOutput, StartNeovimGenericArguments, TestDirectory } from "../types.js";
4
4
  import type { DirectoriesConfig } from "../updateTestdirectorySchemaFile.js";
5
- import { Lazy } from "../utilities/Lazy.js";
6
5
  import type { TabId } from "../utilities/tabId.js";
7
6
  import type { TerminalDimensions } from "./NeovimApplication.js";
8
- export declare const resources: Lazy<AsyncDisposableStack>;
7
+ export declare function installDependencies(testEnvironmentPath: string, config: DirectoriesConfig): Promise<void>;
9
8
  export declare function initializeStdout(options: {
10
9
  client: TabId;
11
10
  }, signal: AbortSignal | undefined, testEnvironmentPath: string): Promise<AsyncGenerator<string, void, unknown>>;
@@ -1,15 +1,99 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
1
53
  import assert from "assert";
2
54
  import { exec } from "child_process";
3
55
  import "core-js/proposals/async-explicit-resource-management.js";
56
+ import { access } from "fs/promises";
57
+ import path from "path";
4
58
  import util from "util";
5
59
  import { convertEventEmitterToAsyncGenerator } from "../utilities/generator.js";
6
60
  import { Lazy } from "../utilities/Lazy.js";
7
61
  import { createTempDir, removeTestDirectories } from "./environment/createTempDir.js";
8
62
  import { NeovimApplication } from "./NeovimApplication.js";
9
63
  const neovims = new Map();
10
- export const resources = new Lazy(() => {
64
+ const resources = new Lazy(() => {
11
65
  return new AsyncDisposableStack();
12
66
  });
67
+ export async function installDependencies(testEnvironmentPath, config) {
68
+ const env_1 = { stack: [], error: void 0, hasError: false };
69
+ try {
70
+ const app = __addDisposableResource(env_1, new NeovimApplication(testEnvironmentPath), true);
71
+ const testDirectory = await prepareNewTestDirectory(config);
72
+ const prepareFilePath = path.join(testDirectory.rootPathAbsolute, ".config", "nvim", "prepare.lua");
73
+ try {
74
+ await access(prepareFilePath);
75
+ }
76
+ catch (e) {
77
+ console.log(`Neovim prepareFilePath does not exist: ${prepareFilePath}. If you want to run a prepare script before starting the tests, create it.`);
78
+ return;
79
+ }
80
+ console.log(`🚀 Running Neovim prepareFilePath ${prepareFilePath}...`);
81
+ app.events.on("stdout", data => {
82
+ console.log(` neovim output: ${data}`);
83
+ });
84
+ await app.startNextAndKillCurrent(testDirectory, { filename: "empty.txt", headlessCmd: `lua dofile("${prepareFilePath}")` }, { cols: 80, rows: 24 });
85
+ await app.application.untilExit();
86
+ }
87
+ catch (e_1) {
88
+ env_1.error = e_1;
89
+ env_1.hasError = true;
90
+ }
91
+ finally {
92
+ const result_1 = __disposeResources(env_1);
93
+ if (result_1)
94
+ await result_1;
95
+ }
96
+ }
13
97
  export async function initializeStdout(options, signal, testEnvironmentPath) {
14
98
  const tabId = options.client.tabId;
15
99
  const neovim = neovims.get(tabId) ?? new NeovimApplication(testEnvironmentPath);
@@ -75,6 +75,10 @@ export async function startTestServer(config) {
75
75
  port: config.port,
76
76
  });
77
77
  const appRouter = await createAppRouter(config.directories);
78
- await testServer.startAndRun(appRouter);
78
+ await Promise.all([
79
+ // NOTE right now Neovim is always initialized
80
+ neovim.installDependencies(config.directories.testEnvironmentPath, config.directories),
81
+ testServer.startAndRun(appRouter),
82
+ ]);
79
83
  return testServer;
80
84
  }
@@ -0,0 +1 @@
1
+ export declare function timeout(ms: number): Promise<unknown>;
@@ -0,0 +1,3 @@
1
+ export function timeout(ms) {
2
+ return new Promise((_, reject) => setTimeout(reject, ms));
3
+ }