@tui-sandbox/library 2.2.0 → 3.0.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 +18 -0
- package/dist/src/server/dirtree/index.js +1 -1
- package/dist/src/server/dirtree/index.test.js +7 -7
- package/dist/src/server/neovim/NeovimApplication.d.ts +1 -1
- package/dist/src/server/neovim/NeovimApplication.js +28 -7
- package/dist/src/server/neovim/NeovimJavascriptApiClient.d.ts +5 -0
- package/dist/src/server/neovim/NeovimJavascriptApiClient.js +21 -0
- package/dist/src/server/neovim/NeovimJavascriptApiClient.test.d.ts +1 -0
- package/dist/src/server/neovim/NeovimJavascriptApiClient.test.js +33 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
- package/src/server/dirtree/index.test.ts +7 -7
- package/src/server/dirtree/index.ts +1 -1
- package/src/server/neovim/NeovimApplication.ts +44 -7
- package/src/server/neovim/NeovimJavascriptApiClient.test.ts +42 -0
- package/src/server/neovim/NeovimJavascriptApiClient.ts +27 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.0.0](https://github.com/mikavilpas/tui-sandbox/compare/library-v2.3.0...library-v3.0.0) (2024-11-08)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* This change modifies the directory names in the MyTestDirectory file. To upgrade, run your tests once, and then commit the changes to that file. That's all.
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* add `/` to the end of directory names to make it clear ([#114](https://github.com/mikavilpas/tui-sandbox/issues/114)) ([f40c912](https://github.com/mikavilpas/tui-sandbox/commit/f40c9129ad8ac5ec62bdf3276caead1606b2fedc))
|
|
13
|
+
|
|
14
|
+
## [2.3.0](https://github.com/mikavilpas/tui-sandbox/compare/library-v2.2.0...library-v2.3.0) (2024-11-03)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* **wip:** allow access to neovim via a type safe socket connection ([#109](https://github.com/mikavilpas/tui-sandbox/issues/109)) ([1c4d919](https://github.com/mikavilpas/tui-sandbox/commit/1c4d9194ec8961bde0e8f84d500c27363c66a61b))
|
|
20
|
+
|
|
3
21
|
## [2.2.0](https://github.com/mikavilpas/tui-sandbox/compare/library-v2.1.0...library-v2.2.0) (2024-11-03)
|
|
4
22
|
|
|
5
23
|
|
|
@@ -29,11 +29,11 @@ describe("dirtree", () => {
|
|
|
29
29
|
import { z } from "zod"
|
|
30
30
|
|
|
31
31
|
export const MyDirectoryTreeSchema = z.object({
|
|
32
|
-
name: z.literal("test-environment"),
|
|
32
|
+
name: z.literal("test-environment/"),
|
|
33
33
|
type: z.literal("directory"),
|
|
34
34
|
contents: z.object({
|
|
35
35
|
"config-modifications": z.object({
|
|
36
|
-
name: z.literal("config-modifications"),
|
|
36
|
+
name: z.literal("config-modifications/"),
|
|
37
37
|
type: z.literal("directory"),
|
|
38
38
|
contents: z.object({
|
|
39
39
|
"add_command_to_count_open_buffers.lua": z.object({
|
|
@@ -45,7 +45,7 @@ describe("dirtree", () => {
|
|
|
45
45
|
}),
|
|
46
46
|
}),
|
|
47
47
|
"dir with spaces": z.object({
|
|
48
|
-
name: z.literal("dir with spaces"),
|
|
48
|
+
name: z.literal("dir with spaces/"),
|
|
49
49
|
type: z.literal("directory"),
|
|
50
50
|
contents: z.object({
|
|
51
51
|
"file1.txt": z.object({
|
|
@@ -75,7 +75,7 @@ describe("dirtree", () => {
|
|
|
75
75
|
stem: z.literal("initial-file."),
|
|
76
76
|
}),
|
|
77
77
|
"other-subdirectory": z.object({
|
|
78
|
-
name: z.literal("other-subdirectory"),
|
|
78
|
+
name: z.literal("other-subdirectory/"),
|
|
79
79
|
type: z.literal("directory"),
|
|
80
80
|
contents: z.object({
|
|
81
81
|
"other-sub-file.txt": z.object({
|
|
@@ -87,11 +87,11 @@ describe("dirtree", () => {
|
|
|
87
87
|
}),
|
|
88
88
|
}),
|
|
89
89
|
routes: z.object({
|
|
90
|
-
name: z.literal("routes"),
|
|
90
|
+
name: z.literal("routes/"),
|
|
91
91
|
type: z.literal("directory"),
|
|
92
92
|
contents: z.object({
|
|
93
93
|
"posts.$postId": z.object({
|
|
94
|
-
name: z.literal("posts.$postId"),
|
|
94
|
+
name: z.literal("posts.$postId/"),
|
|
95
95
|
type: z.literal("directory"),
|
|
96
96
|
contents: z.object({
|
|
97
97
|
"adjacent-file.txt": z.object({
|
|
@@ -117,7 +117,7 @@ describe("dirtree", () => {
|
|
|
117
117
|
}),
|
|
118
118
|
}),
|
|
119
119
|
subdirectory: z.object({
|
|
120
|
-
name: z.literal("subdirectory"),
|
|
120
|
+
name: z.literal("subdirectory/"),
|
|
121
121
|
type: z.literal("directory"),
|
|
122
122
|
contents: z.object({
|
|
123
123
|
"subdirectory-file.txt": z.object({
|
|
@@ -15,7 +15,7 @@ export type StartNeovimGenericArguments = {
|
|
|
15
15
|
export declare class NeovimApplication {
|
|
16
16
|
private readonly testEnvironmentPath;
|
|
17
17
|
readonly application: DisposableSingleApplication;
|
|
18
|
-
private
|
|
18
|
+
private state;
|
|
19
19
|
readonly events: EventEmitter;
|
|
20
20
|
constructor(testEnvironmentPath: string, application?: DisposableSingleApplication);
|
|
21
21
|
/**
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import assert from "assert";
|
|
2
2
|
import { exec } from "child_process";
|
|
3
3
|
import EventEmitter from "events";
|
|
4
|
-
import {
|
|
4
|
+
import { access } from "fs/promises";
|
|
5
|
+
import { tmpdir } from "os";
|
|
5
6
|
import path from "path";
|
|
6
7
|
import { DisposableSingleApplication } from "../utilities/DisposableSingleApplication";
|
|
7
8
|
import { TerminalApplication } from "../utilities/TerminalApplication";
|
|
9
|
+
import { connectNeovimApi } from "./NeovimJavascriptApiClient";
|
|
8
10
|
export class NeovimApplication {
|
|
9
11
|
testEnvironmentPath;
|
|
10
12
|
application;
|
|
11
|
-
|
|
13
|
+
state;
|
|
12
14
|
events;
|
|
13
15
|
constructor(testEnvironmentPath, application = new DisposableSingleApplication()) {
|
|
14
16
|
this.testEnvironmentPath = testEnvironmentPath;
|
|
@@ -20,13 +22,16 @@ export class NeovimApplication {
|
|
|
20
22
|
*/
|
|
21
23
|
async startNextAndKillCurrent(testDirectory, startArgs) {
|
|
22
24
|
await this[Symbol.asyncDispose]();
|
|
23
|
-
this.
|
|
25
|
+
assert(this.state === undefined, "NeovimApplication state should be undefined after disposing so that no previous state is reused.");
|
|
24
26
|
const neovimArguments = ["-u", "test-setup.lua"];
|
|
25
27
|
if (startArgs.startupScriptModifications) {
|
|
26
28
|
for (const modification of startArgs.startupScriptModifications) {
|
|
27
29
|
const file = path.join(testDirectory.rootPathAbsolute, "config-modifications", modification);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
try {
|
|
31
|
+
await access(file);
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
throw new Error(`startupScriptModifications file does not exist: ${file}. Error: ${String(e)}`);
|
|
30
35
|
}
|
|
31
36
|
neovimArguments.push("-c", `lua dofile('${file}')`);
|
|
32
37
|
}
|
|
@@ -43,6 +48,9 @@ export class NeovimApplication {
|
|
|
43
48
|
neovimArguments.push(filePath);
|
|
44
49
|
}
|
|
45
50
|
}
|
|
51
|
+
const id = Math.random().toString().slice(2, 8);
|
|
52
|
+
const socketPath = `${tmpdir()}/tui-sandbox-nvim-socket-${id}`;
|
|
53
|
+
neovimArguments.push("--listen", socketPath);
|
|
46
54
|
const stdout = this.events;
|
|
47
55
|
await this.application.startNextAndKillCurrent(async () => {
|
|
48
56
|
return TerminalApplication.start({
|
|
@@ -59,12 +67,25 @@ export class NeovimApplication {
|
|
|
59
67
|
});
|
|
60
68
|
const processId = this.application.processId();
|
|
61
69
|
assert(processId !== undefined, "Neovim was started without a process ID. This is a bug - please open an issue.");
|
|
70
|
+
this.state = {
|
|
71
|
+
testDirectory,
|
|
72
|
+
socketPath,
|
|
73
|
+
client: connectNeovimApi(socketPath),
|
|
74
|
+
};
|
|
62
75
|
console.log(`🚀 Started Neovim instance ${processId}`);
|
|
63
76
|
}
|
|
64
77
|
async [Symbol.asyncDispose]() {
|
|
65
78
|
await this.application[Symbol.asyncDispose]();
|
|
66
|
-
if (this.
|
|
67
|
-
|
|
79
|
+
if (!this.state)
|
|
80
|
+
return;
|
|
81
|
+
exec(`rm -rf ${this.state.testDirectory.rootPathAbsolute}`);
|
|
82
|
+
try {
|
|
83
|
+
await access(this.state.socketPath);
|
|
84
|
+
throw new Error(`Socket file ${this.state.socketPath} should have been removed by neovim when it exited.`);
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
// all good
|
|
68
88
|
}
|
|
89
|
+
this.state = undefined;
|
|
69
90
|
}
|
|
70
91
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { NeovimClient as NeovimApiClient } from "neovim";
|
|
2
|
+
import { Lazy } from "../utilities/Lazy";
|
|
3
|
+
export type NeovimJavascriptApiClient = NeovimApiClient;
|
|
4
|
+
export type PollingInterval = 100;
|
|
5
|
+
export declare function connectNeovimApi(socketPath: string): Lazy<Promise<NeovimJavascriptApiClient>>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { access } from "fs/promises";
|
|
2
|
+
import { attach } from "neovim";
|
|
3
|
+
import { Lazy } from "../utilities/Lazy";
|
|
4
|
+
export function connectNeovimApi(socketPath) {
|
|
5
|
+
// it takes about 100ms for the socket file to be created - best make this
|
|
6
|
+
// Lazy so that we don't wait for it unnecessarily.
|
|
7
|
+
return new Lazy(async () => {
|
|
8
|
+
for (let i = 0; i < 100; i++) {
|
|
9
|
+
try {
|
|
10
|
+
await access(socketPath);
|
|
11
|
+
console.log(`socket file ${socketPath} created after at attempt ${i + 1}`);
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
console.log(`polling for socket file ${socketPath} to be created (attempt ${i + 1})`);
|
|
16
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return attach({ socket: socketPath });
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { access } from "fs/promises";
|
|
2
|
+
import { attach } from "neovim";
|
|
3
|
+
import { connectNeovimApi } from "./NeovimJavascriptApiClient";
|
|
4
|
+
vi.mock("neovim");
|
|
5
|
+
vi.mock("fs/promises");
|
|
6
|
+
const mocked = {
|
|
7
|
+
attach: vi.mocked(attach),
|
|
8
|
+
access: vi.mocked(access),
|
|
9
|
+
log: vi.spyOn(console, "log").mockImplementation(() => {
|
|
10
|
+
//
|
|
11
|
+
}),
|
|
12
|
+
};
|
|
13
|
+
const pollingInterval = 100;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
vi.useFakeTimers();
|
|
16
|
+
});
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
vi.useRealTimers();
|
|
19
|
+
});
|
|
20
|
+
it("is lazy - does not connect right away", async () => {
|
|
21
|
+
mocked.access.mockRejectedValue(new Error("no such file or directory"));
|
|
22
|
+
connectNeovimApi("foosocket");
|
|
23
|
+
vi.advanceTimersByTime(pollingInterval);
|
|
24
|
+
expect(mocked.attach).not.toHaveBeenCalled();
|
|
25
|
+
});
|
|
26
|
+
it("connects right away if the socket file is already there", async () => {
|
|
27
|
+
mocked.access.mockResolvedValue(undefined);
|
|
28
|
+
const lazyClient = connectNeovimApi("foosocket");
|
|
29
|
+
await lazyClient.get();
|
|
30
|
+
vi.advanceTimersByTime(pollingInterval);
|
|
31
|
+
expect(mocked.attach).toHaveBeenCalledWith({ socket: "foosocket" });
|
|
32
|
+
expect(mocked.attach).toHaveBeenCalledTimes(1);
|
|
33
|
+
});
|