/**
* @vitest-environment happy-dom
*/
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { render as rtlRender, act } from '@testing-library/react';
import React from 'react';
import { BranchCreatorScreen } from '../../../components/screens/BranchCreatorScreen.js';
import { Window } from 'happy-dom';
import { render as inkRender } from 'ink-testing-library';
describe('BranchCreatorScreen', () => {
beforeEach(() => {
vi.useFakeTimers();
// Setup happy-dom
const window = new Window();
globalThis.window = window as any;
globalThis.document = window.document as any;
});
afterEach(() => {
vi.useRealTimers();
});
it('should render header with title', () => {
const onBack = vi.fn();
const onCreate = vi.fn().mockResolvedValue(undefined);
const { getByText } = rtlRender(
);
expect(getByText(/New Branch/i)).toBeDefined();
});
it('should render branch type selection initially', () => {
const onBack = vi.fn();
const onCreate = vi.fn().mockResolvedValue(undefined);
const { getByText } = rtlRender(
);
expect(getByText(/Select branch type/i)).toBeDefined();
expect(getByText(/feature/i)).toBeDefined();
expect(getByText(/hotfix/i)).toBeDefined();
expect(getByText(/release/i)).toBeDefined();
});
it('should render footer with actions', () => {
const onBack = vi.fn();
const onCreate = vi.fn().mockResolvedValue(undefined);
const { getAllByText } = rtlRender(
);
expect(getAllByText(/enter/i).length).toBeGreaterThan(0);
expect(getAllByText(/esc/i).length).toBeGreaterThan(0);
});
it('should show branch name input after type selection', () => {
const onBack = vi.fn();
const onCreate = vi.fn().mockResolvedValue(undefined);
const { container } = rtlRender(
);
// Test will verify the screen transitions from type selection to name input
expect(container).toBeDefined();
});
it('should handle branch creation', () => {
const onBack = vi.fn();
const onCreate = vi.fn().mockResolvedValue(undefined);
const { container } = rtlRender(
);
// Test will verify onCreate is called with correct branch name
expect(container).toBeDefined();
});
it('should use terminal height for layout calculation', () => {
const originalRows = process.stdout.rows;
process.stdout.rows = 30;
const onBack = vi.fn();
const onCreate = vi.fn().mockResolvedValue(undefined);
const { container } = rtlRender(
);
expect(container).toBeDefined();
process.stdout.rows = originalRows;
});
it('should handle back navigation with ESC key', () => {
const onBack = vi.fn();
const onCreate = vi.fn().mockResolvedValue(undefined);
const { container } = rtlRender(
);
// Test will verify onBack is called when ESC is pressed
expect(container).toBeDefined();
});
it('should display creating state while waiting for branch creation', async () => {
expect.assertions(3);
const onBack = vi.fn();
let resolveCreate: (() => void) | null = null;
const onCreate = vi.fn(
() =>
new Promise((resolve) => {
resolveCreate = resolve;
})
);
const { stdin, lastFrame } = inkRender(
);
// Select default branch type (feature)
await act(async () => {
stdin.write('\r');
});
await act(async () => {
await Promise.resolve();
});
const branchName = 'new-branch';
for (const char of branchName) {
await act(async () => {
stdin.write(char);
});
}
await act(async () => {
await Promise.resolve();
});
// Submit branch name
await act(async () => {
stdin.write('\r');
});
await act(async () => {
await Promise.resolve();
});
expect(onCreate).toHaveBeenCalledWith(`feature/${branchName}`);
expect(lastFrame()).toContain('Creating branch');
expect(lastFrame()).toContain(`feature/${branchName}`);
await act(async () => {
resolveCreate?.();
});
await act(async () => {
await Promise.resolve();
});
});
it('should ignore ESC input while branch creation is in progress', async () => {
expect.assertions(2);
const onBack = vi.fn();
let resolveCreate: (() => void) | null = null;
const onCreate = vi.fn(
() =>
new Promise((resolve) => {
resolveCreate = resolve;
})
);
const { stdin, lastFrame } = inkRender(
);
// Move to name input
await act(async () => {
stdin.write('\r');
});
await act(async () => {
await Promise.resolve();
});
const branchName = 'blocking-branch';
for (const char of branchName) {
await act(async () => {
stdin.write(char);
});
}
await act(async () => {
await Promise.resolve();
});
await act(async () => {
stdin.write('\r');
});
await act(async () => {
await Promise.resolve();
});
// Attempt to cancel with ESC during creation
await act(async () => {
stdin.write('\u001B');
});
await act(async () => {
await Promise.resolve();
});
expect(onBack).not.toHaveBeenCalled();
expect(lastFrame()).toContain('Creating branch');
await act(async () => {
resolveCreate?.();
});
await act(async () => {
await Promise.resolve();
});
});
});