2026-01-30 09:53:09 -08:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright 2025 Google LLC
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
|
|
|
import {
|
|
|
|
|
shellReducer,
|
|
|
|
|
initialState,
|
|
|
|
|
type ShellState,
|
|
|
|
|
type ShellAction,
|
|
|
|
|
} from './shellReducer.js';
|
|
|
|
|
|
|
|
|
|
describe('shellReducer', () => {
|
|
|
|
|
it('should return the initial state', () => {
|
|
|
|
|
// @ts-expect-error - testing default case
|
|
|
|
|
expect(shellReducer(initialState, { type: 'UNKNOWN' })).toEqual(
|
|
|
|
|
initialState,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle SET_ACTIVE_PTY', () => {
|
|
|
|
|
const action: ShellAction = { type: 'SET_ACTIVE_PTY', pid: 12345 };
|
|
|
|
|
const state = shellReducer(initialState, action);
|
|
|
|
|
expect(state.activeShellPtyId).toBe(12345);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle SET_OUTPUT_TIME', () => {
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
const action: ShellAction = { type: 'SET_OUTPUT_TIME', time: now };
|
|
|
|
|
const state = shellReducer(initialState, action);
|
|
|
|
|
expect(state.lastShellOutputTime).toBe(now);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle SET_VISIBILITY', () => {
|
|
|
|
|
const action: ShellAction = { type: 'SET_VISIBILITY', visible: true };
|
|
|
|
|
const state = shellReducer(initialState, action);
|
2026-03-28 17:27:51 -04:00
|
|
|
expect(state.isBackgroundTaskVisible).toBe(true);
|
2026-01-30 09:53:09 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle TOGGLE_VISIBILITY', () => {
|
|
|
|
|
const action: ShellAction = { type: 'TOGGLE_VISIBILITY' };
|
|
|
|
|
let state = shellReducer(initialState, action);
|
2026-03-28 17:27:51 -04:00
|
|
|
expect(state.isBackgroundTaskVisible).toBe(true);
|
2026-01-30 09:53:09 -08:00
|
|
|
state = shellReducer(state, action);
|
2026-03-28 17:27:51 -04:00
|
|
|
expect(state.isBackgroundTaskVisible).toBe(false);
|
2026-01-30 09:53:09 -08:00
|
|
|
});
|
|
|
|
|
|
2026-03-28 17:27:51 -04:00
|
|
|
it('should handle REGISTER_TASK', () => {
|
2026-01-30 09:53:09 -08:00
|
|
|
const action: ShellAction = {
|
2026-03-28 17:27:51 -04:00
|
|
|
type: 'REGISTER_TASK',
|
2026-01-30 09:53:09 -08:00
|
|
|
pid: 1001,
|
|
|
|
|
command: 'ls',
|
|
|
|
|
initialOutput: 'init',
|
|
|
|
|
};
|
|
|
|
|
const state = shellReducer(initialState, action);
|
2026-03-28 17:27:51 -04:00
|
|
|
expect(state.backgroundTasks.has(1001)).toBe(true);
|
|
|
|
|
expect(state.backgroundTasks.get(1001)).toEqual({
|
2026-01-30 09:53:09 -08:00
|
|
|
pid: 1001,
|
|
|
|
|
command: 'ls',
|
|
|
|
|
output: 'init',
|
|
|
|
|
isBinary: false,
|
|
|
|
|
binaryBytesReceived: 0,
|
|
|
|
|
status: 'running',
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-28 17:27:51 -04:00
|
|
|
it('should not REGISTER_TASK if PID already exists', () => {
|
2026-01-30 09:53:09 -08:00
|
|
|
const action: ShellAction = {
|
2026-03-28 17:27:51 -04:00
|
|
|
type: 'REGISTER_TASK',
|
2026-01-30 09:53:09 -08:00
|
|
|
pid: 1001,
|
|
|
|
|
command: 'ls',
|
|
|
|
|
initialOutput: 'init',
|
|
|
|
|
};
|
|
|
|
|
const state = shellReducer(initialState, action);
|
|
|
|
|
const state2 = shellReducer(state, { ...action, command: 'other' });
|
|
|
|
|
expect(state2).toBe(state);
|
2026-03-28 17:27:51 -04:00
|
|
|
expect(state2.backgroundTasks.get(1001)?.command).toBe('ls');
|
2026-01-30 09:53:09 -08:00
|
|
|
});
|
|
|
|
|
|
2026-03-28 17:27:51 -04:00
|
|
|
it('should handle UPDATE_TASK', () => {
|
2026-01-30 09:53:09 -08:00
|
|
|
const registeredState = shellReducer(initialState, {
|
2026-03-28 17:27:51 -04:00
|
|
|
type: 'REGISTER_TASK',
|
2026-01-30 09:53:09 -08:00
|
|
|
pid: 1001,
|
|
|
|
|
command: 'ls',
|
|
|
|
|
initialOutput: 'init',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const action: ShellAction = {
|
2026-03-28 17:27:51 -04:00
|
|
|
type: 'UPDATE_TASK',
|
2026-01-30 09:53:09 -08:00
|
|
|
pid: 1001,
|
|
|
|
|
update: { status: 'exited', exitCode: 0 },
|
|
|
|
|
};
|
|
|
|
|
const state = shellReducer(registeredState, action);
|
2026-03-28 17:27:51 -04:00
|
|
|
const shell = state.backgroundTasks.get(1001);
|
2026-01-30 09:53:09 -08:00
|
|
|
expect(shell?.status).toBe('exited');
|
|
|
|
|
expect(shell?.exitCode).toBe(0);
|
|
|
|
|
// Map should be new
|
2026-03-28 17:27:51 -04:00
|
|
|
expect(state.backgroundTasks).not.toBe(registeredState.backgroundTasks);
|
2026-01-30 09:53:09 -08:00
|
|
|
});
|
|
|
|
|
|
2026-03-28 17:27:51 -04:00
|
|
|
it('should handle APPEND_TASK_OUTPUT when visible (triggers re-render)', () => {
|
2026-01-30 09:53:09 -08:00
|
|
|
const visibleState: ShellState = {
|
|
|
|
|
...initialState,
|
2026-03-28 17:27:51 -04:00
|
|
|
isBackgroundTaskVisible: true,
|
|
|
|
|
backgroundTasks: new Map([
|
2026-01-30 09:53:09 -08:00
|
|
|
[
|
|
|
|
|
1001,
|
|
|
|
|
{
|
|
|
|
|
pid: 1001,
|
|
|
|
|
command: 'ls',
|
|
|
|
|
output: 'init',
|
|
|
|
|
isBinary: false,
|
|
|
|
|
binaryBytesReceived: 0,
|
|
|
|
|
status: 'running',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
]),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const action: ShellAction = {
|
2026-03-28 17:27:51 -04:00
|
|
|
type: 'APPEND_TASK_OUTPUT',
|
2026-01-30 09:53:09 -08:00
|
|
|
pid: 1001,
|
|
|
|
|
chunk: ' + more',
|
|
|
|
|
};
|
|
|
|
|
const state = shellReducer(visibleState, action);
|
2026-03-28 17:27:51 -04:00
|
|
|
expect(state.backgroundTasks.get(1001)?.output).toBe('init + more');
|
2026-01-30 09:53:09 -08:00
|
|
|
// Drawer is visible, so we expect a NEW map object to trigger React re-render
|
2026-03-28 17:27:51 -04:00
|
|
|
expect(state.backgroundTasks).not.toBe(visibleState.backgroundTasks);
|
2026-01-30 09:53:09 -08:00
|
|
|
});
|
|
|
|
|
|
2026-03-28 17:27:51 -04:00
|
|
|
it('should handle APPEND_TASK_OUTPUT when hidden (no re-render optimization)', () => {
|
2026-01-30 09:53:09 -08:00
|
|
|
const hiddenState: ShellState = {
|
|
|
|
|
...initialState,
|
2026-03-28 17:27:51 -04:00
|
|
|
isBackgroundTaskVisible: false,
|
|
|
|
|
backgroundTasks: new Map([
|
2026-01-30 09:53:09 -08:00
|
|
|
[
|
|
|
|
|
1001,
|
|
|
|
|
{
|
|
|
|
|
pid: 1001,
|
|
|
|
|
command: 'ls',
|
|
|
|
|
output: 'init',
|
|
|
|
|
isBinary: false,
|
|
|
|
|
binaryBytesReceived: 0,
|
|
|
|
|
status: 'running',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
]),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const action: ShellAction = {
|
2026-03-28 17:27:51 -04:00
|
|
|
type: 'APPEND_TASK_OUTPUT',
|
2026-01-30 09:53:09 -08:00
|
|
|
pid: 1001,
|
|
|
|
|
chunk: ' + more',
|
|
|
|
|
};
|
|
|
|
|
const state = shellReducer(hiddenState, action);
|
2026-03-28 17:27:51 -04:00
|
|
|
expect(state.backgroundTasks.get(1001)?.output).toBe('init + more');
|
2026-01-30 09:53:09 -08:00
|
|
|
// Drawer is hidden, so we expect the SAME map object (mutation optimization)
|
2026-03-28 17:27:51 -04:00
|
|
|
expect(state.backgroundTasks).toBe(hiddenState.backgroundTasks);
|
2026-01-30 09:53:09 -08:00
|
|
|
});
|
|
|
|
|
|
2026-03-28 17:27:51 -04:00
|
|
|
it('should handle SYNC_BACKGROUND_TASKS', () => {
|
|
|
|
|
const action: ShellAction = { type: 'SYNC_BACKGROUND_TASKS' };
|
2026-01-30 09:53:09 -08:00
|
|
|
const state = shellReducer(initialState, action);
|
2026-03-28 17:27:51 -04:00
|
|
|
expect(state.backgroundTasks).not.toBe(initialState.backgroundTasks);
|
2026-01-30 09:53:09 -08:00
|
|
|
});
|
|
|
|
|
|
2026-03-28 17:27:51 -04:00
|
|
|
it('should handle DISMISS_TASK', () => {
|
2026-01-30 09:53:09 -08:00
|
|
|
const registeredState: ShellState = {
|
|
|
|
|
...initialState,
|
2026-03-28 17:27:51 -04:00
|
|
|
isBackgroundTaskVisible: true,
|
|
|
|
|
backgroundTasks: new Map([
|
2026-01-30 09:53:09 -08:00
|
|
|
[
|
|
|
|
|
1001,
|
|
|
|
|
{
|
|
|
|
|
pid: 1001,
|
|
|
|
|
command: 'ls',
|
|
|
|
|
output: 'init',
|
|
|
|
|
isBinary: false,
|
|
|
|
|
binaryBytesReceived: 0,
|
|
|
|
|
status: 'running',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
]),
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-28 17:27:51 -04:00
|
|
|
const action: ShellAction = { type: 'DISMISS_TASK', pid: 1001 };
|
2026-01-30 09:53:09 -08:00
|
|
|
const state = shellReducer(registeredState, action);
|
2026-03-28 17:27:51 -04:00
|
|
|
expect(state.backgroundTasks.has(1001)).toBe(false);
|
|
|
|
|
expect(state.isBackgroundTaskVisible).toBe(false); // Auto-hide if last shell
|
2026-01-30 09:53:09 -08:00
|
|
|
});
|
|
|
|
|
});
|