opencode-miniterm 1.0.4 ā 1.0.6
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/bun.lock +3 -3
- package/package.json +3 -2
- package/src/ansi.ts +2 -0
- package/src/commands/quit.ts +10 -1
- package/src/index.ts +304 -285
- package/test/README.md +164 -0
- package/test/render.test.ts +0 -1
- package/test/tmux-menu-examples.ts +119 -0
- package/test/tmux-readline.test.ts +518 -0
package/test/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Tmux Readline Tests
|
|
2
|
+
|
|
3
|
+
This directory contains tmux-based integration tests for the readline functionality of opencode-miniterm.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `tmux-readline.test.ts` script uses tmux to simulate real terminal interactions with the application, testing:
|
|
8
|
+
|
|
9
|
+
- **Input Editing**: Character insertion, backspace, delete key
|
|
10
|
+
- **Cursor Navigation**: Left/right arrow keys, word boundaries (Meta+left/right)
|
|
11
|
+
- **History Navigation**: Up/down arrow through command history
|
|
12
|
+
- **Tab Completion**: Slash commands (`/`) and file references (`@`)
|
|
13
|
+
- **Smart Features**: Double-space to period conversion
|
|
14
|
+
- **Escape Key**: Clearing input and canceling requests
|
|
15
|
+
|
|
16
|
+
## Running the Tests
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Run all tmux readline tests
|
|
20
|
+
bun run test:tmux
|
|
21
|
+
|
|
22
|
+
# Or run directly
|
|
23
|
+
bun test/tmux-readline.test.ts
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## How It Works
|
|
27
|
+
|
|
28
|
+
The test script:
|
|
29
|
+
|
|
30
|
+
1. **Sets up a tmux session** - Creates a detached tmux session for testing
|
|
31
|
+
2. **Starts the application** - Launches opencode-miniterm in the tmux session
|
|
32
|
+
3. **Sends keystrokes** - Simulates user input using `tmux send-keys`
|
|
33
|
+
4. **Captures output** - Uses `tmux capture-pane` to read terminal state
|
|
34
|
+
5. **Verifies behavior** - Checks that the output matches expected results
|
|
35
|
+
6. **Cleans up** - Kills the tmux session after tests complete
|
|
36
|
+
|
|
37
|
+
## Test Cases
|
|
38
|
+
|
|
39
|
+
### Input Editing
|
|
40
|
+
|
|
41
|
+
- **Character insertion**: Typing "hello" should insert characters correctly
|
|
42
|
+
- **Backspace**: Deleting characters before the cursor
|
|
43
|
+
- **Delete key**: Deleting character at the cursor position
|
|
44
|
+
|
|
45
|
+
### Cursor Navigation
|
|
46
|
+
|
|
47
|
+
- **Left arrow**: Moving cursor left one character
|
|
48
|
+
- **Right arrow**: Moving cursor right one character
|
|
49
|
+
- **Meta+Left**: Jumping to previous word boundary
|
|
50
|
+
- **Meta+Right**: Jumping to next word boundary
|
|
51
|
+
|
|
52
|
+
### History Navigation
|
|
53
|
+
|
|
54
|
+
- **Up arrow**: Navigating backward through command history
|
|
55
|
+
- **Down arrow**: Navigating forward through command history
|
|
56
|
+
|
|
57
|
+
### Tab Completion
|
|
58
|
+
|
|
59
|
+
- **Slash commands**: Tab after `/` shows available commands
|
|
60
|
+
- **File references**: Tab after `@` completes file paths
|
|
61
|
+
|
|
62
|
+
### Smart Features
|
|
63
|
+
|
|
64
|
+
- **Double space**: Two spaces within 500ms converts to period + space
|
|
65
|
+
|
|
66
|
+
### Escape Key
|
|
67
|
+
|
|
68
|
+
- **Clear input**: Escape clears the current input buffer
|
|
69
|
+
|
|
70
|
+
## Requirements
|
|
71
|
+
|
|
72
|
+
- **tmux**: Must be installed on your system (`brew install tmux` on macOS)
|
|
73
|
+
- **Bun**: Must have bun installed to run the tests
|
|
74
|
+
- **opencode-miniterm**: Must be able to run the application
|
|
75
|
+
|
|
76
|
+
## Troubleshooting
|
|
77
|
+
|
|
78
|
+
### Tests fail with "Expected prompt to contain X"
|
|
79
|
+
|
|
80
|
+
The test might be running too fast. Increase `WAIT_MS` at the top of the test file.
|
|
81
|
+
|
|
82
|
+
### Tests hang or timeout
|
|
83
|
+
|
|
84
|
+
The application might be stuck in a command menu. Press `Escape` or `C-c` in the tmux session to recover.
|
|
85
|
+
|
|
86
|
+
### tmux session already exists
|
|
87
|
+
|
|
88
|
+
The script automatically kills existing test sessions. If you see errors, manually run:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
tmux kill-session -t opencode-test
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Tests don't start
|
|
95
|
+
|
|
96
|
+
Ensure tmux is installed and working:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
tmux -V
|
|
100
|
+
tmux new-session -d -s test
|
|
101
|
+
tmux kill-session -t test
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Adding New Tests
|
|
105
|
+
|
|
106
|
+
To add a new test:
|
|
107
|
+
|
|
108
|
+
1. Create an async test function that:
|
|
109
|
+
- Clears input with `await clearInput()`
|
|
110
|
+
- Sends keystrokes with `await sendKeys("key")`
|
|
111
|
+
- Waits for processing with `await sleep(ms)`
|
|
112
|
+
- Captures output with `await capturePane()`
|
|
113
|
+
- Asserts behavior with `assertPromptContains()` or `assertContains()`
|
|
114
|
+
- Throws errors with descriptive messages
|
|
115
|
+
|
|
116
|
+
2. Register the test in `runAllTests()`:
|
|
117
|
+
```typescript
|
|
118
|
+
await runTest("Your test name", testYourFunction);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Example Test
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
async function testYourFeature(): Promise<void> {
|
|
125
|
+
await clearInput();
|
|
126
|
+
await sendKeys("test");
|
|
127
|
+
await sendKeys("Enter");
|
|
128
|
+
await sleep(1000);
|
|
129
|
+
|
|
130
|
+
const output = await capturePane();
|
|
131
|
+
if (!assertContains(output, "expected text")) {
|
|
132
|
+
throw new Error(`Expected output to contain 'expected text'`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Command Menu Tests
|
|
138
|
+
|
|
139
|
+
For examples of testing the interactive command menus (agents, models, sessions), see `tmux-menu-examples.ts`. This file demonstrates:
|
|
140
|
+
|
|
141
|
+
- How to enter command menus (`/agents`, `/models`, `/sessions`)
|
|
142
|
+
- How to navigate menu options (Up, Down)
|
|
143
|
+
- How to filter/search within menus
|
|
144
|
+
- How to select items (Enter)
|
|
145
|
+
- How to cancel menus (Escape)
|
|
146
|
+
- How to verify the results
|
|
147
|
+
|
|
148
|
+
## Key Constants
|
|
149
|
+
|
|
150
|
+
- `TMUX_SESSION`: "opencode-test" - The tmux session name
|
|
151
|
+
- `TMUX_WINDOW`: "readline-test" - The tmux window name
|
|
152
|
+
- `WAIT_MS`: 100 - Default wait time between keystrokes
|
|
153
|
+
- `APP_PATH`: Path to the application entry point
|
|
154
|
+
|
|
155
|
+
## Helper Functions
|
|
156
|
+
|
|
157
|
+
- `setupTmux()` / `teardownTmux()`: Create/destroy test environment
|
|
158
|
+
- `startApp()` / `stopApp()`: Start/stop the application
|
|
159
|
+
- `sendKeys(keys)`: Send keystrokes to tmux (e.g., "C-c", "Enter", "M-Left")
|
|
160
|
+
- `capturePane()`: Get current terminal output
|
|
161
|
+
- `clearInput()`: Clear the current input line
|
|
162
|
+
- `assertContains(output, expected)`: Check if output contains text
|
|
163
|
+
- `assertPromptContains(text, expected)`: Check if prompt contains text
|
|
164
|
+
- `stripAnsi(text)`: Remove ANSI escape codes from text
|
package/test/render.test.ts
CHANGED
|
@@ -193,7 +193,6 @@ describe("render", () => {
|
|
|
193
193
|
render(state);
|
|
194
194
|
|
|
195
195
|
const output = stripANSI(write.mock.calls.map((c) => c[0]).join("")).replaceAll(" ", "");
|
|
196
|
-
console.log(output);
|
|
197
196
|
expect(output).toContain("foo\nbar\n\nš ok");
|
|
198
197
|
expect(output).toContain("stuff\n\nbaz");
|
|
199
198
|
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Example: How to add command menu tests
|
|
2
|
+
// This file contains examples for testing the interactive command menus
|
|
3
|
+
import { $ } from "bun";
|
|
4
|
+
|
|
5
|
+
const TMUX_SESSION = "opencode-test-menu";
|
|
6
|
+
|
|
7
|
+
async function testAgentMenu(): Promise<void> {
|
|
8
|
+
console.log("Testing agent menu navigation");
|
|
9
|
+
|
|
10
|
+
// Start with /agents command
|
|
11
|
+
await $`tmux send-keys -t ${TMUX_SESSION} "/agents" Enter`;
|
|
12
|
+
await sleep(500);
|
|
13
|
+
|
|
14
|
+
// Send some filter text
|
|
15
|
+
await $`tmux send-keys -t ${TMUX_SESSION} "code"`;
|
|
16
|
+
await sleep(100);
|
|
17
|
+
|
|
18
|
+
// Navigate up
|
|
19
|
+
await $`tmux send-keys -t ${TMUX_SESSION} Up`;
|
|
20
|
+
await sleep(100);
|
|
21
|
+
|
|
22
|
+
// Navigate down
|
|
23
|
+
await $`tmux send-keys -t ${TMUX_SESSION} Down`;
|
|
24
|
+
await sleep(100);
|
|
25
|
+
|
|
26
|
+
// Clear filter
|
|
27
|
+
await $`tmux send-keys -t ${TMUX_SESSION} C-u`;
|
|
28
|
+
await sleep(100);
|
|
29
|
+
|
|
30
|
+
// Cancel with escape
|
|
31
|
+
await $`tmux send-keys -t ${TMUX_SESSION} Escape`;
|
|
32
|
+
await sleep(100);
|
|
33
|
+
|
|
34
|
+
// Verify we're back at prompt
|
|
35
|
+
const output = await capturePane();
|
|
36
|
+
if (!assertPromptContains(output, "#")) {
|
|
37
|
+
throw new Error("Should be back at prompt");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function testModelMenu(): Promise<void> {
|
|
42
|
+
console.log("Testing model menu navigation");
|
|
43
|
+
|
|
44
|
+
await $`tmux send-keys -t ${TMUX_SESSION} "/models" Enter`;
|
|
45
|
+
await sleep(500);
|
|
46
|
+
|
|
47
|
+
// Navigate through options
|
|
48
|
+
await $`tmux send-keys -t ${TMUX_SESSION} Down`;
|
|
49
|
+
await sleep(100);
|
|
50
|
+
await $`tmux send-keys -t ${TMUX_SESSION} Down`;
|
|
51
|
+
await sleep(100);
|
|
52
|
+
|
|
53
|
+
// Select a model
|
|
54
|
+
await $`tmux send-keys -t ${TMUX_SESSION} Enter`;
|
|
55
|
+
await sleep(200);
|
|
56
|
+
|
|
57
|
+
// Verify selection worked
|
|
58
|
+
const output = await capturePane();
|
|
59
|
+
if (!assertContains(output, "Model changed")) {
|
|
60
|
+
throw new Error("Model should be changed");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function testSessionMenu(): Promise<void> {
|
|
65
|
+
console.log("Testing session menu navigation");
|
|
66
|
+
|
|
67
|
+
await $`tmux send-keys -t ${TMUX_SESSION} "/sessions" Enter`;
|
|
68
|
+
await sleep(500);
|
|
69
|
+
|
|
70
|
+
// Test pagination
|
|
71
|
+
await $`tmux send-keys -t ${TMUX_SESSION} Down`;
|
|
72
|
+
await sleep(100);
|
|
73
|
+
await $`tmux send-keys -t ${TMUX_SESSION} Down`;
|
|
74
|
+
await sleep(100);
|
|
75
|
+
|
|
76
|
+
// Select a session
|
|
77
|
+
await $`tmux send-keys -t ${TMUX_SESSION} Enter`;
|
|
78
|
+
await sleep(200);
|
|
79
|
+
|
|
80
|
+
// Verify we're using the selected session
|
|
81
|
+
const output = await capturePane();
|
|
82
|
+
if (!assertContains(output, "Session loaded")) {
|
|
83
|
+
throw new Error("Session should be loaded");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Note: These are example tests that would need to be integrated
|
|
88
|
+
// into the main tmux-readline.test.ts file. They demonstrate:
|
|
89
|
+
// 1. How to enter command menus (/agents, /models, /sessions)
|
|
90
|
+
// 2. How to navigate menu options (Up, Down)
|
|
91
|
+
// 3. How to filter/search within menus
|
|
92
|
+
// 4. How to select items (Enter)
|
|
93
|
+
// 5. How to cancel menus (Escape)
|
|
94
|
+
// 6. How to verify the results
|
|
95
|
+
|
|
96
|
+
function sleep(ms: number): Promise<void> {
|
|
97
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function capturePane(): Promise<string> {
|
|
101
|
+
const result = await $`tmux capture-pane -t ${TMUX_SESSION} -p`.quiet();
|
|
102
|
+
return result.stdout.toString();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function assertPromptContains(text: string, expected: string): boolean {
|
|
106
|
+
const lines = text.split("\n");
|
|
107
|
+
for (const line of lines) {
|
|
108
|
+
if (line.includes("#")) {
|
|
109
|
+
return line.includes(expected);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function assertContains(output: string, expected: string): boolean {
|
|
116
|
+
return output.includes(expected);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export {};
|