@tui-sandbox/library 11.11.2 → 12.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/dist/browser/assets/index-CYUPHpRk.css +1 -0
- package/dist/browser/assets/index-DPQpUaDL.js +9 -0
- package/dist/browser/index.html +2 -2
- package/dist/src/browser/neovim-client.d.ts +2 -0
- package/dist/src/browser/neovim-client.js +2 -0
- package/dist/src/browser/neovim-client.js.map +1 -1
- package/dist/src/client/index.d.ts +1 -0
- package/dist/src/client/neovim-terminal-client.d.ts +0 -2
- package/dist/src/client/neovim-terminal-client.js +0 -2
- package/dist/src/client/neovim-terminal-client.js.map +1 -1
- package/dist/src/client/startTerminal.d.ts +0 -2
- package/dist/src/client/startTerminal.js +0 -2
- package/dist/src/client/startTerminal.js.map +1 -1
- package/dist/src/client/terminal-terminal-client.d.ts +0 -1
- package/dist/src/client/terminal-terminal-client.js +0 -1
- package/dist/src/client/terminal-terminal-client.js.map +1 -1
- package/dist/src/server/cypress-support/contents.js +9 -11
- package/dist/src/server/cypress-support/contents.js.map +1 -1
- package/dist/src/server/index.d.ts +4 -0
- package/dist/src/server/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +34 -8
- package/CHANGELOG.md +0 -941
- package/dist/browser/assets/index-BQzArJW3.js +0 -9
- package/dist/browser/assets/index-hkmOP7rU.css +0 -1
- package/index.html +0 -11
- package/src/browser/neovim-client.ts +0 -126
- package/src/client/MyNeovimConfigModification.test.ts +0 -25
- package/src/client/MyNeovimConfigModification.ts +0 -8
- package/src/client/color-utilities.test.ts +0 -10
- package/src/client/color-utilities.ts +0 -9
- package/src/client/cypress-assertions.ts +0 -35
- package/src/client/index.ts +0 -4
- package/src/client/neovim-terminal-client.ts +0 -130
- package/src/client/public/DejaVuSansMNerdFontMono-Regular.ttf +0 -0
- package/src/client/startTerminal.ts +0 -97
- package/src/client/style.css +0 -32
- package/src/client/terminal-config.ts +0 -25
- package/src/client/terminal-terminal-client.ts +0 -106
- package/src/client/validateMouseEvent.ts +0 -22
- package/src/scripts/commands/commandRun.ts +0 -68
- package/src/scripts/commands/commandTuiNeovimExec.ts +0 -36
- package/src/scripts/commands/commandTuiNeovimPrepare.ts +0 -12
- package/src/scripts/commands/commandTuiStart.ts +0 -36
- package/src/scripts/parseArguments.test.ts +0 -28
- package/src/scripts/parseArguments.ts +0 -66
- package/src/scripts/resolveConfig.test.ts +0 -78
- package/src/scripts/resolveTuiConfig.ts +0 -65
- package/src/scripts/tui.ts +0 -77
- package/src/server/TestServer.ts +0 -73
- package/src/server/applications/neovim/NeovimApplication.ts +0 -236
- package/src/server/applications/neovim/NeovimJavascriptApiClient.test.ts +0 -48
- package/src/server/applications/neovim/NeovimJavascriptApiClient.ts +0 -68
- package/src/server/applications/neovim/api.ts +0 -257
- package/src/server/applications/neovim/environment/TempDirectory.ts +0 -18
- package/src/server/applications/neovim/environment/createTempDir.test.ts +0 -29
- package/src/server/applications/neovim/environment/createTempDir.ts +0 -91
- package/src/server/applications/neovim/neovimRouter.ts +0 -100
- package/src/server/applications/neovim/prepareNewTestDirectory.test.ts +0 -32
- package/src/server/applications/neovim/prepareNewTestDirectory.ts +0 -21
- package/src/server/applications/terminal/TerminalTestApplication.ts +0 -101
- package/src/server/applications/terminal/api.ts +0 -89
- package/src/server/applications/terminal/runBlockingShellCommand.test.ts +0 -41
- package/src/server/applications/terminal/runBlockingShellCommand.ts +0 -64
- package/src/server/applications/terminal/terminalRouter.ts +0 -47
- package/src/server/blockingCommandInputSchema.test.ts +0 -24
- package/src/server/blockingCommandInputSchema.ts +0 -27
- package/src/server/config.test.ts +0 -7
- package/src/server/config.ts +0 -18
- package/src/server/connection/trpc.ts +0 -3
- package/src/server/cypress-support/contents.ts +0 -245
- package/src/server/cypress-support/createCypressSupportFile.test.ts +0 -69
- package/src/server/cypress-support/createCypressSupportFile.ts +0 -56
- package/src/server/dirtree/index.test.ts +0 -352
- package/src/server/dirtree/index.ts +0 -144
- package/src/server/dirtree/json-to-zod.ts +0 -60
- package/src/server/index.ts +0 -6
- package/src/server/server.ts +0 -34
- package/src/server/types.ts +0 -98
- package/src/server/updateTestdirectorySchemaFile.test.ts +0 -49
- package/src/server/updateTestdirectorySchemaFile.ts +0 -71
- package/src/server/utilities/DisposableSingleApplication.test.ts +0 -92
- package/src/server/utilities/DisposableSingleApplication.ts +0 -49
- package/src/server/utilities/Lazy.ts +0 -16
- package/src/server/utilities/TerminalApplication.ts +0 -100
- package/src/server/utilities/generator.test.ts +0 -50
- package/src/server/utilities/generator.ts +0 -12
- package/src/server/utilities/tabId.ts +0 -4
- package/src/server/utilities/timeout.ts +0 -3
- package/src/server/utilities/timeoutable.ts +0 -19
- package/tsconfig.json +0 -31
- package/vite.config.js +0 -27
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import assert from "assert"
|
|
2
|
-
import { execSync } from "child_process"
|
|
3
|
-
import { constants, readdirSync, statSync } from "fs"
|
|
4
|
-
import { access, mkdir, mkdtemp, symlink } from "fs/promises"
|
|
5
|
-
import path from "path"
|
|
6
|
-
import { debuglog } from "util"
|
|
7
|
-
import { convertDree, getDirectoryTree } from "../../../dirtree/index.js"
|
|
8
|
-
import type { TestDirectory } from "../../../types.js"
|
|
9
|
-
import type { TestServerConfig } from "../../../updateTestdirectorySchemaFile.js"
|
|
10
|
-
import { updateTestdirectorySchemaFile } from "../../../updateTestdirectorySchemaFile.js"
|
|
11
|
-
|
|
12
|
-
const log = debuglog("tui-sandbox.createTempDir")
|
|
13
|
-
|
|
14
|
-
export async function createTempDir(config: TestServerConfig): Promise<TestDirectory> {
|
|
15
|
-
try {
|
|
16
|
-
// before calling this function, the testEnvironmentPath should already exist
|
|
17
|
-
statSync(config.directories.testEnvironmentPath)
|
|
18
|
-
const dir = await createUniqueDirectory(config.directories.testEnvironmentPath)
|
|
19
|
-
|
|
20
|
-
readdirSync(config.directories.testEnvironmentPath).forEach(entry => {
|
|
21
|
-
if (entry === ("testdirs" satisfies TestDirsPath)) return
|
|
22
|
-
if (entry === ".repro") return
|
|
23
|
-
|
|
24
|
-
execSync(`cp -R '${path.join(config.directories.testEnvironmentPath, entry)}' ${dir}/`)
|
|
25
|
-
})
|
|
26
|
-
log(`Created test directory at ${dir}`)
|
|
27
|
-
|
|
28
|
-
const tree = convertDree(getDirectoryTree(dir).dree)
|
|
29
|
-
assert(tree.type === "directory")
|
|
30
|
-
|
|
31
|
-
const [_, latestEnvironmentSymlink] = await Promise.all([
|
|
32
|
-
updateTestdirectorySchemaFile(config),
|
|
33
|
-
createLatestSymlink(config, dir),
|
|
34
|
-
])
|
|
35
|
-
return {
|
|
36
|
-
rootPathAbsolute: dir,
|
|
37
|
-
contents: tree.contents,
|
|
38
|
-
testEnvironmentPath: config.directories.testEnvironmentPath,
|
|
39
|
-
testEnvironmentPathRelative: path.relative(config.directories.testEnvironmentPath, dir),
|
|
40
|
-
latestEnvironmentSymlink,
|
|
41
|
-
}
|
|
42
|
-
} catch (err) {
|
|
43
|
-
console.error(err)
|
|
44
|
-
throw err
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export type TestDirsPath = "testdirs"
|
|
49
|
-
|
|
50
|
-
async function createUniqueDirectory(testEnvironmentPath: string): Promise<string> {
|
|
51
|
-
const testdirs = path.join(testEnvironmentPath, "testdirs" satisfies TestDirsPath)
|
|
52
|
-
try {
|
|
53
|
-
await access(testdirs, constants.F_OK)
|
|
54
|
-
} catch {
|
|
55
|
-
await mkdir(testdirs)
|
|
56
|
-
}
|
|
57
|
-
const dir = await mkdtemp(path.join(testdirs, "dir-"))
|
|
58
|
-
assert(typeof dir === "string")
|
|
59
|
-
|
|
60
|
-
return dir
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function createLatestSymlink(config: TestServerConfig, uniqueTestDirectory: string): Promise<string> {
|
|
64
|
-
const latestSymlinkPath = path.join(
|
|
65
|
-
config.directories.testEnvironmentPath,
|
|
66
|
-
"testdirs" satisfies TestDirsPath,
|
|
67
|
-
config.directories.latestSymlinkName
|
|
68
|
-
)
|
|
69
|
-
try {
|
|
70
|
-
await access(latestSymlinkPath, constants.F_OK)
|
|
71
|
-
// If it already exists, remove it
|
|
72
|
-
execSync(`rm -f '${latestSymlinkPath}'`)
|
|
73
|
-
} catch {
|
|
74
|
-
// It doesn't exist, that's fine
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// recreate it
|
|
78
|
-
await symlink(uniqueTestDirectory, latestSymlinkPath, "junction")
|
|
79
|
-
|
|
80
|
-
return latestSymlinkPath
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export async function removeTestDirectories(testEnvironmentPath: string): Promise<void> {
|
|
84
|
-
try {
|
|
85
|
-
const testdirs = path.join(testEnvironmentPath, "testdirs" satisfies TestDirsPath)
|
|
86
|
-
await access(testdirs, constants.F_OK)
|
|
87
|
-
execSync(`rm -rf ${testdirs}/*`)
|
|
88
|
-
} catch (e) {
|
|
89
|
-
console.debug("Could not remove test directories, maybe they don't exist yet", e)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import type { Except } from "type-fest"
|
|
2
|
-
import * as z from "zod"
|
|
3
|
-
import { blockingCommandInputSchema } from "../../blockingCommandInputSchema.js"
|
|
4
|
-
import { trpc } from "../../connection/trpc.js"
|
|
5
|
-
import { serverTestDirectorySchema } from "../../types.js"
|
|
6
|
-
import type { NeovimIntegrationDefaultAppName, TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
|
|
7
|
-
import { tabIdSchema } from "../../utilities/tabId.js"
|
|
8
|
-
import { timeoutable } from "../../utilities/timeoutable.js"
|
|
9
|
-
import * as neovim from "./api.js"
|
|
10
|
-
|
|
11
|
-
const luaCodeInputSchema = z.object({ tabId: tabIdSchema, luaCode: z.string() })
|
|
12
|
-
export type LuaCodeClientInput = Except<LuaCodeInput, "tabId">
|
|
13
|
-
export type LuaCodeInput = z.infer<typeof luaCodeInputSchema>
|
|
14
|
-
|
|
15
|
-
const pollLuaCodeInputSchema = z.object({
|
|
16
|
-
tabId: tabIdSchema,
|
|
17
|
-
luaAssertion: z.string(),
|
|
18
|
-
timeoutMs: z.number().optional().default(10_000),
|
|
19
|
-
})
|
|
20
|
-
export type PollLuaCodeClientInput = Except<z.input<typeof pollLuaCodeInputSchema>, "tabId">
|
|
21
|
-
|
|
22
|
-
const exCommandInputSchema = z.object({
|
|
23
|
-
tabId: tabIdSchema,
|
|
24
|
-
command: z.string(),
|
|
25
|
-
log: z.boolean().optional(),
|
|
26
|
-
})
|
|
27
|
-
export type ExCommandClientInput = Except<ExCommandInput, "tabId">
|
|
28
|
-
export type ExCommandInput = z.infer<typeof exCommandInputSchema>
|
|
29
|
-
|
|
30
|
-
const runLuaFileInputSchema = z.object({
|
|
31
|
-
tabId: tabIdSchema,
|
|
32
|
-
luaFile: z.string(),
|
|
33
|
-
log: z.boolean().optional(),
|
|
34
|
-
})
|
|
35
|
-
export type RunLuaFileInput = z.output<typeof runLuaFileInputSchema>
|
|
36
|
-
export type RunLuaFileClientInput = Except<RunLuaFileInput, "tabId">
|
|
37
|
-
|
|
38
|
-
// let trpc infer the type as that is what it is designed to do
|
|
39
|
-
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
40
|
-
export function createNeovimRouter(config: TestServerConfig) {
|
|
41
|
-
return trpc.router({
|
|
42
|
-
start: trpc.procedure
|
|
43
|
-
.input(
|
|
44
|
-
z.object({
|
|
45
|
-
tabId: tabIdSchema,
|
|
46
|
-
startNeovimArguments: z.object({
|
|
47
|
-
filename: z.union([
|
|
48
|
-
z.string(),
|
|
49
|
-
z.object({
|
|
50
|
-
openInVerticalSplits: z.array(z.string()),
|
|
51
|
-
}),
|
|
52
|
-
]),
|
|
53
|
-
startupScriptModifications: z.array(z.string()).optional(),
|
|
54
|
-
additionalEnvironmentVariables: z.record(z.string(), z.string()).optional(),
|
|
55
|
-
NVIM_APPNAME: z
|
|
56
|
-
.string()
|
|
57
|
-
.optional()
|
|
58
|
-
.default("nvim" satisfies NeovimIntegrationDefaultAppName),
|
|
59
|
-
}),
|
|
60
|
-
terminalDimensions: z.object({
|
|
61
|
-
cols: z.number(),
|
|
62
|
-
rows: z.number(),
|
|
63
|
-
}),
|
|
64
|
-
})
|
|
65
|
-
)
|
|
66
|
-
.output(serverTestDirectorySchema)
|
|
67
|
-
.mutation(async options => {
|
|
68
|
-
return neovim.start(
|
|
69
|
-
options.input.startNeovimArguments,
|
|
70
|
-
options.input.terminalDimensions,
|
|
71
|
-
options.input.tabId,
|
|
72
|
-
config
|
|
73
|
-
)
|
|
74
|
-
}),
|
|
75
|
-
|
|
76
|
-
onStdout: trpc.procedure.input(z.object({ client: tabIdSchema })).subscription(options => {
|
|
77
|
-
return neovim.initializeStdout(options.input, options.signal, config.directories.testEnvironmentPath)
|
|
78
|
-
}),
|
|
79
|
-
sendStdin: trpc.procedure.input(z.object({ tabId: tabIdSchema, data: z.string() })).mutation(options => {
|
|
80
|
-
return neovim.sendStdin(options.input)
|
|
81
|
-
}),
|
|
82
|
-
|
|
83
|
-
runBlockingShellCommand: trpc.procedure.input(blockingCommandInputSchema).mutation(async options => {
|
|
84
|
-
return neovim.runBlockingShellCommand(options.signal, options.input, options.input.allowFailure ?? false)
|
|
85
|
-
}),
|
|
86
|
-
|
|
87
|
-
runLuaCode: trpc.procedure.input(luaCodeInputSchema).mutation(options => {
|
|
88
|
-
return timeoutable(10_000, neovim.runLuaCode(options.input))
|
|
89
|
-
}),
|
|
90
|
-
|
|
91
|
-
waitForLuaCode: trpc.procedure.input(pollLuaCodeInputSchema).mutation(async options => {
|
|
92
|
-
const result = await timeoutable(options.input.timeoutMs, neovim.waitForLuaCode(options.input, options.signal))
|
|
93
|
-
return result
|
|
94
|
-
}),
|
|
95
|
-
|
|
96
|
-
runExCommand: trpc.procedure.input(exCommandInputSchema).mutation(options => {
|
|
97
|
-
return timeoutable(10_000, neovim.runExCommand(options.input))
|
|
98
|
-
}),
|
|
99
|
-
})
|
|
100
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { rm } from "fs/promises"
|
|
2
|
-
import path from "path"
|
|
3
|
-
import type { PartialDeep } from "type-fest"
|
|
4
|
-
import { assert, describe, expect, it } from "vitest"
|
|
5
|
-
import { createDefaultConfig } from "../../config.js"
|
|
6
|
-
import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
|
|
7
|
-
import { prepareNewTestDirectory } from "./prepareNewTestDirectory.js"
|
|
8
|
-
|
|
9
|
-
describe("prepareNewTestDirectory when the testEnvironmentPath does not exist", () => {
|
|
10
|
-
it("should be able to create a new test directory", async () => {
|
|
11
|
-
// this happens when starting a new project, and the directory structure
|
|
12
|
-
// has not been created yet
|
|
13
|
-
const testEnvironmentPath = "/tmp/test"
|
|
14
|
-
const testDirectory = await prepareNewTestDirectory({
|
|
15
|
-
...createDefaultConfig("cwd", {}),
|
|
16
|
-
directories: {
|
|
17
|
-
outputFilePath: path.join(testEnvironmentPath, "foo.ts"),
|
|
18
|
-
testEnvironmentPath,
|
|
19
|
-
latestSymlinkName: "latest",
|
|
20
|
-
},
|
|
21
|
-
} satisfies PartialDeep<TestServerConfig>)
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
expect(testDirectory.contents).toEqual({})
|
|
25
|
-
expect(testDirectory.testEnvironmentPath).toEqual("/tmp/test")
|
|
26
|
-
} finally {
|
|
27
|
-
// for safety, only remove inside /tmp
|
|
28
|
-
assert(testEnvironmentPath.startsWith("/tmp/"))
|
|
29
|
-
await rm(testEnvironmentPath, { recursive: true })
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
})
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { access, mkdir } from "fs/promises"
|
|
2
|
-
import { debuglog } from "util"
|
|
3
|
-
import type { TestDirectory } from "../../types.js"
|
|
4
|
-
import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
|
|
5
|
-
import { createTempDir, removeTestDirectories } from "./environment/createTempDir.js"
|
|
6
|
-
|
|
7
|
-
const log = debuglog("tui-sandbox.neovim.prepareNewTestDirectory")
|
|
8
|
-
|
|
9
|
-
export async function prepareNewTestDirectory(config: TestServerConfig): Promise<TestDirectory> {
|
|
10
|
-
try {
|
|
11
|
-
// if the directory does not exist, create it
|
|
12
|
-
await access(config.directories.testEnvironmentPath)
|
|
13
|
-
} catch {
|
|
14
|
-
log(`Creating testEnvironmentPath directory at ${config.directories.testEnvironmentPath}`)
|
|
15
|
-
await mkdir(config.directories.testEnvironmentPath, { recursive: true })
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
await removeTestDirectories(config.directories.testEnvironmentPath)
|
|
19
|
-
const testDirectory = await createTempDir(config)
|
|
20
|
-
return testDirectory
|
|
21
|
-
}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import assert from "assert"
|
|
2
|
-
import { exec } from "child_process"
|
|
3
|
-
import EventEmitter from "events"
|
|
4
|
-
import { join } from "path"
|
|
5
|
-
import { debuglog } from "util"
|
|
6
|
-
import type { TestDirectory, TestEnvironmentCommonEnvironmentVariables } from "../../types.js"
|
|
7
|
-
import { DisposableSingleApplication } from "../../utilities/DisposableSingleApplication.js"
|
|
8
|
-
import { TerminalApplication } from "../../utilities/TerminalApplication.js"
|
|
9
|
-
import type { StdoutOrStderrMessage, TerminalDimensions } from "../neovim/NeovimApplication.js"
|
|
10
|
-
|
|
11
|
-
export type { StdoutOrStderrMessage }
|
|
12
|
-
|
|
13
|
-
const log = debuglog("tui-sandbox.terminal.TerminalTestApplication")
|
|
14
|
-
|
|
15
|
-
type ResettableState = {
|
|
16
|
-
testDirectory: TestDirectory
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type StartTerminalGenericArguments = {
|
|
20
|
-
commandToRun: string[]
|
|
21
|
-
additionalEnvironmentVariables?: Record<string, string> | undefined
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default class TerminalTestApplication implements AsyncDisposable {
|
|
25
|
-
public state: ResettableState | undefined
|
|
26
|
-
public readonly events: EventEmitter
|
|
27
|
-
|
|
28
|
-
public constructor(public readonly application: DisposableSingleApplication = new DisposableSingleApplication()) {
|
|
29
|
-
this.events = new EventEmitter()
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public async startNextAndKillCurrent(
|
|
33
|
-
testDirectory: TestDirectory,
|
|
34
|
-
startArgs: StartTerminalGenericArguments,
|
|
35
|
-
terminalDimensions: TerminalDimensions
|
|
36
|
-
): Promise<void> {
|
|
37
|
-
await this[Symbol.asyncDispose]()
|
|
38
|
-
assert(
|
|
39
|
-
this.state === undefined,
|
|
40
|
-
"TerminalTestApplication state should be undefined after disposing so that no previous state is reused."
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
const command = startArgs.commandToRun[0]
|
|
44
|
-
assert(command, "No command to run was provided.")
|
|
45
|
-
// TODO could check if the command is executable
|
|
46
|
-
const terminalArguments = startArgs.commandToRun.slice(1)
|
|
47
|
-
|
|
48
|
-
const stdout = this.events
|
|
49
|
-
|
|
50
|
-
await this.application.startNextAndKillCurrent(async () => {
|
|
51
|
-
const env = this.getEnvironmentVariables(testDirectory, startArgs.additionalEnvironmentVariables)
|
|
52
|
-
return TerminalApplication.start({
|
|
53
|
-
command,
|
|
54
|
-
args: terminalArguments,
|
|
55
|
-
|
|
56
|
-
cwd: testDirectory.rootPathAbsolute,
|
|
57
|
-
env,
|
|
58
|
-
dimensions: terminalDimensions,
|
|
59
|
-
|
|
60
|
-
onStdoutOrStderr(data) {
|
|
61
|
-
data satisfies string
|
|
62
|
-
stdout.emit("stdout" satisfies StdoutOrStderrMessage, data)
|
|
63
|
-
},
|
|
64
|
-
})
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
const processId = this.application.processId()
|
|
68
|
-
assert(
|
|
69
|
-
processId !== undefined,
|
|
70
|
-
"TerminalApplication was started without a process ID. This is a bug - please open an issue."
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
this.state = { testDirectory }
|
|
74
|
-
|
|
75
|
-
log(`🚀 Started Terminal instance ${processId}`)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
public getEnvironmentVariables(
|
|
79
|
-
testDirectory: TestDirectory,
|
|
80
|
-
additionalEnvironmentVariables?: Record<string, string>
|
|
81
|
-
): NodeJS.ProcessEnv {
|
|
82
|
-
return {
|
|
83
|
-
...process.env,
|
|
84
|
-
HOME: testDirectory.rootPathAbsolute,
|
|
85
|
-
XDG_CONFIG_HOME: join(testDirectory.rootPathAbsolute, ".config"),
|
|
86
|
-
XDG_DATA_HOME: join(testDirectory.testEnvironmentPath, ".repro", "data"),
|
|
87
|
-
TUI_SANDBOX_TEST_ENVIRONMENT_PATH: testDirectory.testEnvironmentPath,
|
|
88
|
-
...additionalEnvironmentVariables,
|
|
89
|
-
} satisfies TestEnvironmentCommonEnvironmentVariables
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async [Symbol.asyncDispose](): Promise<void> {
|
|
93
|
-
await this.application[Symbol.asyncDispose]()
|
|
94
|
-
|
|
95
|
-
if (!this.state) return
|
|
96
|
-
|
|
97
|
-
exec(`rm -rf ${this.state.testDirectory.rootPathAbsolute}`)
|
|
98
|
-
|
|
99
|
-
this.state = undefined
|
|
100
|
-
}
|
|
101
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import assert from "assert"
|
|
2
|
-
import type { BlockingCommandInput } from "../../blockingCommandInputSchema.js"
|
|
3
|
-
import type { BlockingShellCommandOutput, TestDirectory } from "../../types.js"
|
|
4
|
-
import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
|
|
5
|
-
import { convertEventEmitterToAsyncGenerator } from "../../utilities/generator.js"
|
|
6
|
-
import { Lazy } from "../../utilities/Lazy.js"
|
|
7
|
-
import type { TabId } from "../../utilities/tabId.js"
|
|
8
|
-
import { prepareNewTestDirectory } from "../neovim/prepareNewTestDirectory.js"
|
|
9
|
-
import { executeBlockingShellCommand } from "./runBlockingShellCommand.js"
|
|
10
|
-
import type { StartTerminalInput } from "./terminalRouter.js"
|
|
11
|
-
import type { StdoutOrStderrMessage } from "./TerminalTestApplication.js"
|
|
12
|
-
import TerminalTestApplication from "./TerminalTestApplication.js"
|
|
13
|
-
|
|
14
|
-
const terminals = new Map<TabId["tabId"], TerminalTestApplication>()
|
|
15
|
-
const resources: Lazy<AsyncDisposableStack> = new Lazy(() => {
|
|
16
|
-
return new AsyncDisposableStack()
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
export async function start(
|
|
20
|
-
{ tabId, startTerminalArguments }: StartTerminalInput,
|
|
21
|
-
config: TestServerConfig
|
|
22
|
-
): Promise<TestDirectory> {
|
|
23
|
-
const app = terminals.get(tabId.tabId)
|
|
24
|
-
assert(app, `Terminal with tabId ${tabId.tabId} not found.`)
|
|
25
|
-
const testDirectory = await prepareNewTestDirectory(config)
|
|
26
|
-
await app.startNextAndKillCurrent(
|
|
27
|
-
testDirectory,
|
|
28
|
-
{
|
|
29
|
-
commandToRun: startTerminalArguments.commandToRun,
|
|
30
|
-
additionalEnvironmentVariables: startTerminalArguments.additionalEnvironmentVariables,
|
|
31
|
-
},
|
|
32
|
-
startTerminalArguments.terminalDimensions
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
return testDirectory
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export async function initializeStdout(
|
|
39
|
-
options: { client: TabId },
|
|
40
|
-
signal: AbortSignal | undefined
|
|
41
|
-
): Promise<AsyncGenerator<string, void, unknown>> {
|
|
42
|
-
const tabId = options.client.tabId
|
|
43
|
-
const app = terminals.get(tabId) ?? new TerminalTestApplication()
|
|
44
|
-
if (terminals.get(tabId) === undefined) {
|
|
45
|
-
terminals.set(tabId, app)
|
|
46
|
-
resources.get().use(app)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const stdout = convertEventEmitterToAsyncGenerator(app.events, "stdout" satisfies StdoutOrStderrMessage)
|
|
50
|
-
signal?.addEventListener("abort", () => {
|
|
51
|
-
void app[Symbol.asyncDispose]().finally(() => {
|
|
52
|
-
terminals.delete(tabId)
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
return stdout
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export async function sendStdin(options: { tabId: TabId; data: string }): Promise<void> {
|
|
60
|
-
const tabId = options.tabId.tabId
|
|
61
|
-
const app = terminals.get(tabId)
|
|
62
|
-
assert(app !== undefined, `Terminal instance for tabId not found - cannot send stdin. Maybe it's not started yet?`)
|
|
63
|
-
assert(
|
|
64
|
-
app.application,
|
|
65
|
-
`Terminal application not found for client id ${options.tabId.tabId}. Maybe it's not started yet?`
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
await app.application.write(options.data)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export async function runBlockingShellCommand(
|
|
72
|
-
signal: AbortSignal | undefined,
|
|
73
|
-
input: BlockingCommandInput,
|
|
74
|
-
allowFailure: boolean
|
|
75
|
-
): Promise<BlockingShellCommandOutput> {
|
|
76
|
-
const tabId = input.tabId.tabId
|
|
77
|
-
const app = terminals.get(tabId)
|
|
78
|
-
assert(
|
|
79
|
-
app !== undefined,
|
|
80
|
-
`Terminal instance for tabId ${input.tabId.tabId} not found - cannot send stdin. Maybe it's not started yet?`
|
|
81
|
-
)
|
|
82
|
-
assert(app.application, `Terminal application not found for tabId ${input.tabId.tabId}. Maybe it's not started yet?`)
|
|
83
|
-
|
|
84
|
-
const testDirectory = app.state?.testDirectory
|
|
85
|
-
assert(testDirectory, `Test directory not found for client id ${input.tabId.tabId}. Maybe neovim's not started yet?`)
|
|
86
|
-
|
|
87
|
-
const env = app.getEnvironmentVariables(testDirectory, input.envOverrides)
|
|
88
|
-
return executeBlockingShellCommand(testDirectory, input, signal, allowFailure, env)
|
|
89
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest"
|
|
2
|
-
import { getCwd } from "./runBlockingShellCommand.js"
|
|
3
|
-
|
|
4
|
-
describe("getCwd", () => {
|
|
5
|
-
it("prefers cwdRelative if it is defined", () => {
|
|
6
|
-
// in many cases this is what the user wants to do, so prefer that
|
|
7
|
-
|
|
8
|
-
const result = getCwd({
|
|
9
|
-
rootPathAbsolute: "/root-absolute",
|
|
10
|
-
cwdRelative: "test-cwd-relative",
|
|
11
|
-
cwdAbsolute: "/test-cwd",
|
|
12
|
-
home: "/test-home",
|
|
13
|
-
})
|
|
14
|
-
expect(result).toEqual("/root-absolute/test-cwd-relative")
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it("falls back to cwd if cwdRelative is not defined", () => {
|
|
18
|
-
// this is used when the user wants to use absolute paths
|
|
19
|
-
|
|
20
|
-
const result = getCwd({
|
|
21
|
-
rootPathAbsolute: "/root-absolute",
|
|
22
|
-
cwdRelative: undefined,
|
|
23
|
-
cwdAbsolute: "/test-cwd",
|
|
24
|
-
home: "/test-home",
|
|
25
|
-
})
|
|
26
|
-
expect(result).toEqual("/test-cwd")
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it("falls back to HOME if neither cwdRelative nor cwd are defined", () => {
|
|
30
|
-
// if the user doesn't specify anything, we default to the home directory
|
|
31
|
-
// because a directory must be present when running the command
|
|
32
|
-
|
|
33
|
-
const result = getCwd({
|
|
34
|
-
rootPathAbsolute: "/root-absolute",
|
|
35
|
-
cwdRelative: undefined,
|
|
36
|
-
cwdAbsolute: undefined,
|
|
37
|
-
home: "/test-home",
|
|
38
|
-
})
|
|
39
|
-
expect(result).toEqual("/test-home")
|
|
40
|
-
})
|
|
41
|
-
})
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { exec } from "child_process"
|
|
2
|
-
import { join } from "path"
|
|
3
|
-
import util, { debuglog } from "util"
|
|
4
|
-
import type { BlockingCommandInput } from "../../blockingCommandInputSchema.js"
|
|
5
|
-
import type { BlockingShellCommandOutput, TestDirectory } from "../../types.js"
|
|
6
|
-
|
|
7
|
-
const log = debuglog("tui-sandbox.terminal.runBlockingShellCommand")
|
|
8
|
-
|
|
9
|
-
export async function executeBlockingShellCommand(
|
|
10
|
-
testDirectory: TestDirectory,
|
|
11
|
-
input: BlockingCommandInput,
|
|
12
|
-
signal: AbortSignal | undefined,
|
|
13
|
-
allowFailure: boolean,
|
|
14
|
-
env: NodeJS.ProcessEnv
|
|
15
|
-
): Promise<BlockingShellCommandOutput> {
|
|
16
|
-
const execPromise = util.promisify(exec)
|
|
17
|
-
|
|
18
|
-
const cwd = getCwd({
|
|
19
|
-
rootPathAbsolute: testDirectory.rootPathAbsolute,
|
|
20
|
-
cwdRelative: input.cwdRelative,
|
|
21
|
-
cwdAbsolute: input.cwd,
|
|
22
|
-
home: testDirectory.rootPathAbsolute,
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
const result = await execPromise(input.command, {
|
|
27
|
-
signal,
|
|
28
|
-
shell: input.shell,
|
|
29
|
-
uid: input.uid,
|
|
30
|
-
gid: input.gid,
|
|
31
|
-
cwd,
|
|
32
|
-
env,
|
|
33
|
-
})
|
|
34
|
-
log(
|
|
35
|
-
`Successfully ran shell blockingCommand (${input.command}) in cwd: '${cwd}' with stdout: ${result.stdout}, stderr: ${result.stderr}`
|
|
36
|
-
)
|
|
37
|
-
return {
|
|
38
|
-
type: "success",
|
|
39
|
-
stdout: result.stdout,
|
|
40
|
-
stderr: result.stderr,
|
|
41
|
-
} satisfies BlockingShellCommandOutput
|
|
42
|
-
} catch (e) {
|
|
43
|
-
console.warn(`Error running shell blockingCommand (${input.command})`, e)
|
|
44
|
-
if (allowFailure) {
|
|
45
|
-
return {
|
|
46
|
-
type: "failed",
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
throw new Error(`Error running shell blockingCommand (${input.command})`, { cause: e })
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export type GetCwdArguments = {
|
|
54
|
-
rootPathAbsolute: string
|
|
55
|
-
cwdRelative: string | undefined
|
|
56
|
-
cwdAbsolute: string | undefined
|
|
57
|
-
home: string
|
|
58
|
-
}
|
|
59
|
-
export function getCwd(args: GetCwdArguments): string {
|
|
60
|
-
if (args.cwdRelative) {
|
|
61
|
-
return join(args.rootPathAbsolute, args.cwdRelative)
|
|
62
|
-
}
|
|
63
|
-
return args.cwdAbsolute ?? args.home
|
|
64
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import * as z from "zod"
|
|
2
|
-
import { blockingCommandInputSchema } from "../../blockingCommandInputSchema.js"
|
|
3
|
-
import { trpc } from "../../connection/trpc.js"
|
|
4
|
-
import { serverTestDirectorySchema } from "../../types.js"
|
|
5
|
-
import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
|
|
6
|
-
import { tabIdSchema } from "../../utilities/tabId.js"
|
|
7
|
-
import * as terminal from "./api.js"
|
|
8
|
-
|
|
9
|
-
const startTerminalInputSchema = z.object({
|
|
10
|
-
tabId: tabIdSchema,
|
|
11
|
-
startTerminalArguments: z.object({
|
|
12
|
-
commandToRun: z.array(z.string()),
|
|
13
|
-
additionalEnvironmentVariables: z.record(z.string(), z.string()).optional(),
|
|
14
|
-
terminalDimensions: z.object({
|
|
15
|
-
cols: z.number(),
|
|
16
|
-
rows: z.number(),
|
|
17
|
-
}),
|
|
18
|
-
}),
|
|
19
|
-
})
|
|
20
|
-
export type StartTerminalInput = z.infer<typeof startTerminalInputSchema>
|
|
21
|
-
|
|
22
|
-
// let trpc infer the type as that is what it is designed to do
|
|
23
|
-
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
24
|
-
export function createTerminalRouter(config: TestServerConfig) {
|
|
25
|
-
const terminalRouter = trpc.router({
|
|
26
|
-
onStdout: trpc.procedure.input(z.object({ client: tabIdSchema })).subscription(options => {
|
|
27
|
-
return terminal.initializeStdout(options.input, options.signal)
|
|
28
|
-
}),
|
|
29
|
-
|
|
30
|
-
start: trpc.procedure
|
|
31
|
-
.input(startTerminalInputSchema)
|
|
32
|
-
.output(serverTestDirectorySchema)
|
|
33
|
-
.mutation(async options => {
|
|
34
|
-
return terminal.start(options.input, config)
|
|
35
|
-
}),
|
|
36
|
-
|
|
37
|
-
sendStdin: trpc.procedure.input(z.object({ tabId: tabIdSchema, data: z.string() })).mutation(options => {
|
|
38
|
-
return terminal.sendStdin(options.input)
|
|
39
|
-
}),
|
|
40
|
-
|
|
41
|
-
runBlockingShellCommand: trpc.procedure.input(blockingCommandInputSchema).mutation(async options => {
|
|
42
|
-
return terminal.runBlockingShellCommand(options.signal, options.input, options.input.allowFailure ?? false)
|
|
43
|
-
}),
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
return terminalRouter
|
|
47
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest"
|
|
2
|
-
import { blockingCommandInputSchema, type BlockingCommandInput } from "./blockingCommandInputSchema.js"
|
|
3
|
-
|
|
4
|
-
describe("blockingCommandInputSchema", () => {
|
|
5
|
-
it("allows either cwd or cwdRelative but not both", () => {
|
|
6
|
-
// It doesn't make sense to have two sources of truth for the cwd.
|
|
7
|
-
const fails = blockingCommandInputSchema.safeParse({
|
|
8
|
-
command: "command",
|
|
9
|
-
tabId: { tabId: "123" },
|
|
10
|
-
cwd: "cwd",
|
|
11
|
-
cwdRelative: "cwdRelative",
|
|
12
|
-
} satisfies Partial<BlockingCommandInput>)
|
|
13
|
-
|
|
14
|
-
expect(fails.error).toMatchInlineSnapshot(`
|
|
15
|
-
[ZodError: [
|
|
16
|
-
{
|
|
17
|
-
"code": "custom",
|
|
18
|
-
"path": [],
|
|
19
|
-
"message": "Both cwd and cwdRelative provided. Please provide either but not both at the same time."
|
|
20
|
-
}
|
|
21
|
-
]]
|
|
22
|
-
`)
|
|
23
|
-
})
|
|
24
|
-
})
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { Except } from "type-fest"
|
|
2
|
-
import * as z from "zod"
|
|
3
|
-
import { tabIdSchema } from "./utilities/tabId.js"
|
|
4
|
-
|
|
5
|
-
export const blockingCommandInputSchema = z
|
|
6
|
-
.object({
|
|
7
|
-
command: z.string(),
|
|
8
|
-
shell: z.string().optional(),
|
|
9
|
-
tabId: tabIdSchema,
|
|
10
|
-
allowFailure: z.boolean().optional(),
|
|
11
|
-
|
|
12
|
-
// absolute cwd
|
|
13
|
-
cwd: z.string().optional(),
|
|
14
|
-
cwdRelative: z.string().optional(),
|
|
15
|
-
envOverrides: z.record(z.string(), z.string()).optional(),
|
|
16
|
-
uid: z.number().optional(),
|
|
17
|
-
gid: z.number().optional(),
|
|
18
|
-
})
|
|
19
|
-
.refine(
|
|
20
|
-
data =>
|
|
21
|
-
// disallow both cwd and cwdRelative
|
|
22
|
-
!(data.cwd && data.cwdRelative),
|
|
23
|
-
{ message: "Both cwd and cwdRelative provided. Please provide either but not both at the same time." }
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
export type BlockingCommandClientInput = Except<BlockingCommandInput, "tabId">
|
|
27
|
-
export type BlockingCommandInput = z.infer<typeof blockingCommandInputSchema>
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { createDefaultConfig } from "./config.js"
|
|
2
|
-
import { testServerConfigSchema } from "./updateTestdirectorySchemaFile.js"
|
|
3
|
-
|
|
4
|
-
it("is possible not to customize anything", () => {
|
|
5
|
-
const defaultConfig = createDefaultConfig(process.cwd(), process.env)
|
|
6
|
-
testServerConfigSchema.parse(defaultConfig)
|
|
7
|
-
})
|
package/src/server/config.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import path from "path"
|
|
2
|
-
import type { TestServerConfig } from "./updateTestdirectorySchemaFile.js"
|
|
3
|
-
|
|
4
|
-
export const createDefaultConfig = (cwd: string, environment: NodeJS.ProcessEnv): TestServerConfig => {
|
|
5
|
-
return {
|
|
6
|
-
directories: {
|
|
7
|
-
testEnvironmentPath: path.join(cwd, "test-environment/"),
|
|
8
|
-
outputFilePath: path.join(cwd, "MyTestDirectory.ts"),
|
|
9
|
-
latestSymlinkName: "latest",
|
|
10
|
-
},
|
|
11
|
-
port: environment["PORT"] ? parseInt(environment["PORT"]) : 3000,
|
|
12
|
-
integrations: {
|
|
13
|
-
neovim: {
|
|
14
|
-
NVIM_APPNAMEs: ["nvim"],
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
}
|
|
18
|
-
}
|