|
|
@@ -32,6 +32,21 @@ function createSpawnResult(
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+function createPaneListJson(parentTabId = 0): string {
|
|
|
+ return JSON.stringify([
|
|
|
+ {
|
|
|
+ id: 0,
|
|
|
+ is_plugin: false,
|
|
|
+ tab_id: parentTabId,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 4,
|
|
|
+ is_plugin: false,
|
|
|
+ tab_id: 1,
|
|
|
+ },
|
|
|
+ ]);
|
|
|
+}
|
|
|
+
|
|
|
async function importFreshZellij() {
|
|
|
return import(`./index?test=${importCounter++}`);
|
|
|
}
|
|
|
@@ -42,15 +57,20 @@ function commands(): string[][] {
|
|
|
|
|
|
describe('ZellijMultiplexer', () => {
|
|
|
const originalZellij = process.env.ZELLIJ;
|
|
|
+ const originalZellijPaneId = process.env.ZELLIJ_PANE_ID;
|
|
|
|
|
|
beforeEach(() => {
|
|
|
process.env.ZELLIJ = '1';
|
|
|
+ process.env.ZELLIJ_PANE_ID = '0';
|
|
|
|
|
|
crossSpawnMock.mockReset();
|
|
|
crossSpawnMock.mockImplementation((command: string[]) => {
|
|
|
if (command[0] === 'which') {
|
|
|
return createSpawnResult(0, '/usr/bin/zellij\n');
|
|
|
}
|
|
|
+ if (command.includes('list-panes')) {
|
|
|
+ return createSpawnResult(0, createPaneListJson());
|
|
|
+ }
|
|
|
if (command.includes('new-pane')) {
|
|
|
return createSpawnResult(0, 'terminal_2\n');
|
|
|
}
|
|
|
@@ -60,9 +80,10 @@ describe('ZellijMultiplexer', () => {
|
|
|
|
|
|
afterEach(() => {
|
|
|
process.env.ZELLIJ = originalZellij;
|
|
|
+ process.env.ZELLIJ_PANE_ID = originalZellijPaneId;
|
|
|
});
|
|
|
|
|
|
- test('current-tab mode spawns a pane in the active tab', async () => {
|
|
|
+ test('current-tab mode spawns a pane in the parent OpenCode tab', async () => {
|
|
|
const { ZellijMultiplexer } = await importFreshZellij();
|
|
|
const zellij = new ZellijMultiplexer('main-vertical', 60, 'current-tab');
|
|
|
|
|
|
@@ -84,6 +105,8 @@ describe('ZellijMultiplexer', () => {
|
|
|
'/usr/bin/zellij',
|
|
|
'action',
|
|
|
'new-pane',
|
|
|
+ '--tab-id',
|
|
|
+ '0',
|
|
|
'--name',
|
|
|
'Current tab worker',
|
|
|
'--close-on-exit',
|
|
|
@@ -108,6 +131,9 @@ describe('ZellijMultiplexer', () => {
|
|
|
if (command[0] === 'which') {
|
|
|
return createSpawnResult(0, '/usr/bin/zellij\n');
|
|
|
}
|
|
|
+ if (command.includes('list-panes')) {
|
|
|
+ return createSpawnResult(0, createPaneListJson());
|
|
|
+ }
|
|
|
if (command.includes('new-pane')) {
|
|
|
return createSpawnResult(0, 'plugin_2\n');
|
|
|
}
|
|
|
@@ -123,4 +149,99 @@ describe('ZellijMultiplexer', () => {
|
|
|
|
|
|
expect(result).toEqual({ success: false });
|
|
|
});
|
|
|
+
|
|
|
+ test('current-tab mode targets the parent OpenCode tab even when another tab is focused', async () => {
|
|
|
+ const { ZellijMultiplexer } = await importFreshZellij();
|
|
|
+ const zellij = new ZellijMultiplexer('main-vertical', 60, 'current-tab');
|
|
|
+
|
|
|
+ crossSpawnMock.mockImplementation((command: string[]) => {
|
|
|
+ if (command[0] === 'which') {
|
|
|
+ return createSpawnResult(0, '/usr/bin/zellij\n');
|
|
|
+ }
|
|
|
+ if (command.includes('list-panes')) {
|
|
|
+ return createSpawnResult(0, createPaneListJson(0));
|
|
|
+ }
|
|
|
+ if (command.includes('current-tab-info')) {
|
|
|
+ return createSpawnResult(0, JSON.stringify({ tab_id: 1 }));
|
|
|
+ }
|
|
|
+ if (command.includes('new-pane')) {
|
|
|
+ return createSpawnResult(0, 'terminal_2\n');
|
|
|
+ }
|
|
|
+ return createSpawnResult();
|
|
|
+ });
|
|
|
+
|
|
|
+ const result = await zellij.spawnPane(
|
|
|
+ 'session-1',
|
|
|
+ 'Current tab worker',
|
|
|
+ 'http://localhost:4096',
|
|
|
+ '/repo',
|
|
|
+ );
|
|
|
+
|
|
|
+ const newPaneCommand = commands().find((command) =>
|
|
|
+ command.includes('new-pane'),
|
|
|
+ );
|
|
|
+
|
|
|
+ const tabIdArgIndex = newPaneCommand?.indexOf('--tab-id') ?? -1;
|
|
|
+ expect(result).toEqual({ success: true, paneId: 'terminal_2' });
|
|
|
+ expect(tabIdArgIndex).toBeGreaterThanOrEqual(0);
|
|
|
+ expect(newPaneCommand?.[tabIdArgIndex + 1]).toBe('0');
|
|
|
+ });
|
|
|
+
|
|
|
+ test('current-tab mode accepts terminal-prefixed parent pane ids', async () => {
|
|
|
+ process.env.ZELLIJ_PANE_ID = 'terminal_0';
|
|
|
+
|
|
|
+ const { ZellijMultiplexer } = await importFreshZellij();
|
|
|
+ const zellij = new ZellijMultiplexer('main-vertical', 60, 'current-tab');
|
|
|
+
|
|
|
+ await zellij.spawnPane(
|
|
|
+ 'session-1',
|
|
|
+ 'Current tab worker',
|
|
|
+ 'http://localhost:4096',
|
|
|
+ '/repo',
|
|
|
+ );
|
|
|
+
|
|
|
+ const newPaneCommand = commands().find((command) =>
|
|
|
+ command.includes('new-pane'),
|
|
|
+ );
|
|
|
+ const tabIdArgIndex = newPaneCommand?.indexOf('--tab-id') ?? -1;
|
|
|
+
|
|
|
+ expect(tabIdArgIndex).toBeGreaterThanOrEqual(0);
|
|
|
+ expect(newPaneCommand?.[tabIdArgIndex + 1]).toBe('0');
|
|
|
+ });
|
|
|
+
|
|
|
+ test('current-tab mode falls back to the focused tab if parent tab lookup fails', async () => {
|
|
|
+ const { ZellijMultiplexer } = await importFreshZellij();
|
|
|
+ const zellij = new ZellijMultiplexer('main-vertical', 60, 'current-tab');
|
|
|
+
|
|
|
+ crossSpawnMock.mockImplementation((command: string[]) => {
|
|
|
+ if (command[0] === 'which') {
|
|
|
+ return createSpawnResult(0, '/usr/bin/zellij\n');
|
|
|
+ }
|
|
|
+ if (command.includes('list-panes')) {
|
|
|
+ return createSpawnResult(1, '', 'list failed');
|
|
|
+ }
|
|
|
+ if (command.includes('current-tab-info')) {
|
|
|
+ return createSpawnResult(0, JSON.stringify({ tab_id: 1 }));
|
|
|
+ }
|
|
|
+ if (command.includes('new-pane')) {
|
|
|
+ return createSpawnResult(0, 'terminal_2\n');
|
|
|
+ }
|
|
|
+ return createSpawnResult();
|
|
|
+ });
|
|
|
+
|
|
|
+ await zellij.spawnPane(
|
|
|
+ 'session-1',
|
|
|
+ 'Current tab worker',
|
|
|
+ 'http://localhost:4096',
|
|
|
+ '/repo',
|
|
|
+ );
|
|
|
+
|
|
|
+ const newPaneCommand = commands().find((command) =>
|
|
|
+ command.includes('new-pane'),
|
|
|
+ );
|
|
|
+ const tabIdArgIndex = newPaneCommand?.indexOf('--tab-id') ?? -1;
|
|
|
+
|
|
|
+ expect(tabIdArgIndex).toBeGreaterThanOrEqual(0);
|
|
|
+ expect(newPaneCommand?.[tabIdArgIndex + 1]).toBe('1');
|
|
|
+ });
|
|
|
});
|