197 lines
6.1 KiB
TypeScript
197 lines
6.1 KiB
TypeScript
/* @vitest-environment jsdom */
|
|
|
|
import {
|
|
cleanup,
|
|
fireEvent,
|
|
render,
|
|
screen,
|
|
waitFor,
|
|
} from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
|
|
|
|
import * as api from './api';
|
|
import { PreviewDeployerApp } from './PreviewDeployerApp';
|
|
|
|
vi.mock('./api');
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
beforeEach(() => {
|
|
vi.mocked(api.getSession).mockResolvedValue({ authenticated: true });
|
|
vi.mocked(api.createSession).mockResolvedValue({ authenticated: true });
|
|
vi.mocked(api.deleteSession).mockResolvedValue(undefined);
|
|
vi.mocked(api.listDeployments).mockResolvedValue([]);
|
|
vi.mocked(api.searchBranches).mockResolvedValue([]);
|
|
vi.mocked(api.searchCommits).mockResolvedValue([]);
|
|
vi.mocked(api.createDeployment).mockResolvedValue({
|
|
id: null,
|
|
branch: 'master',
|
|
status: 'queued',
|
|
health: 'pending',
|
|
createdAt: '2026-08-15T00:00:00Z',
|
|
updatedAt: '2026-08-15T00:00:00Z',
|
|
});
|
|
vi.mocked(api.uninstallDeployment).mockResolvedValue({
|
|
id: '1',
|
|
branch: 'master',
|
|
status: 'uninstalling',
|
|
health: 'pending',
|
|
createdAt: '2026-08-15T00:00:00Z',
|
|
updatedAt: '2026-08-15T00:02:00Z',
|
|
});
|
|
});
|
|
|
|
test('requires an access token before showing deployments', async () => {
|
|
const user = userEvent.setup();
|
|
vi.mocked(api.getSession).mockResolvedValue({ authenticated: false });
|
|
render(<PreviewDeployerApp />);
|
|
|
|
expect(await screen.findByText('访问口令')).toBeTruthy();
|
|
await user.type(screen.getByPlaceholderText('请输入访问口令'), 'team-token');
|
|
await user.click(screen.getByRole('button', { name: '进入发布面板' }));
|
|
|
|
await waitFor(() => {
|
|
expect(api.createSession).toHaveBeenCalledWith('team-token');
|
|
});
|
|
expect(await screen.findByText('构建并发布一个分支')).toBeTruthy();
|
|
});
|
|
|
|
test('submits a branch with an optional commit hash', async () => {
|
|
const user = userEvent.setup();
|
|
render(<PreviewDeployerApp />);
|
|
|
|
const branchInput = await screen.findByPlaceholderText('master');
|
|
await user.clear(branchInput);
|
|
await user.type(branchInput, 'feature/preview');
|
|
await user.type(
|
|
screen.getByPlaceholderText('留空则构建分支最新提交'),
|
|
'aa5221abc',
|
|
);
|
|
await user.click(screen.getByRole('button', { name: '开始构建' }));
|
|
|
|
await waitFor(() => {
|
|
expect(api.createDeployment).toHaveBeenCalledWith({
|
|
branch: 'feature/preview',
|
|
commitHash: 'aa5221abc',
|
|
});
|
|
});
|
|
});
|
|
|
|
test('shows health and web url, then confirms uninstall', async () => {
|
|
vi.mocked(api.listDeployments).mockResolvedValue([
|
|
{
|
|
id: '42',
|
|
branch: 'feature/demo',
|
|
resolvedCommit: '1234567890abcdef',
|
|
status: 'running',
|
|
health: 'healthy',
|
|
webPort: 8400,
|
|
webUrl: 'http://192.168.35.82:8400',
|
|
createdAt: 1_787_270_400,
|
|
updatedAt: 1_787_270_460,
|
|
},
|
|
]);
|
|
render(<PreviewDeployerApp />);
|
|
|
|
expect(await screen.findByText('健康')).toBeTruthy();
|
|
expect(screen.getByText('端口 8400')).toBeTruthy();
|
|
expect(screen.getByText('构建 #42')).toBeTruthy();
|
|
expect(
|
|
screen.getByRole('link', { name: /打开 Web/u }).getAttribute('href'),
|
|
).toBe('http://192.168.35.82:8400');
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: '卸载' }));
|
|
expect(screen.getByRole('dialog')).toBeTruthy();
|
|
fireEvent.click(screen.getByRole('button', { name: '确认卸载' }));
|
|
|
|
await waitFor(() => {
|
|
expect(api.uninstallDeployment).toHaveBeenCalledWith('42');
|
|
});
|
|
});
|
|
|
|
test('shows debounced branch results and selects one with the keyboard', async () => {
|
|
const user = userEvent.setup();
|
|
vi.mocked(api.searchBranches).mockResolvedValue([
|
|
{ name: 'feature/search-one', commitHash: '111111111111' },
|
|
{ name: 'feature/search-two', commitHash: '222222222222' },
|
|
]);
|
|
render(<PreviewDeployerApp />);
|
|
|
|
const branchInput = await screen.findByRole('combobox', { name: '分支名' });
|
|
await user.clear(branchInput);
|
|
await user.type(branchInput, 'feature/search');
|
|
|
|
expect(
|
|
await screen.findByRole('option', { name: /feature\/search-one/u }),
|
|
).toBeTruthy();
|
|
expect(api.searchBranches).toHaveBeenLastCalledWith(
|
|
'feature/search',
|
|
expect.any(AbortSignal),
|
|
);
|
|
await user.keyboard('{ArrowDown}{ArrowDown}{Enter}');
|
|
expect((branchInput as HTMLInputElement).value).toBe('feature/search-two');
|
|
});
|
|
|
|
test('searches commits in the current branch and selects a result', async () => {
|
|
const user = userEvent.setup();
|
|
vi.mocked(api.searchCommits).mockResolvedValue([
|
|
{
|
|
commitHash: 'aa5221abcdef0123456789',
|
|
shortHash: 'aa5221a',
|
|
subject: '补充预览搜索',
|
|
},
|
|
]);
|
|
render(<PreviewDeployerApp />);
|
|
|
|
const commitInput = await screen.findByRole('combobox', {
|
|
name: 'Commit Hash',
|
|
});
|
|
await user.type(commitInput, 'aa5221a');
|
|
|
|
const option = await screen.findByRole('option', {
|
|
name: /aa5221a.*补充预览搜索/u,
|
|
});
|
|
expect(api.searchCommits).toHaveBeenCalledWith(
|
|
'master',
|
|
'aa5221a',
|
|
expect.any(AbortSignal),
|
|
);
|
|
await user.click(option);
|
|
expect((commitInput as HTMLInputElement).value).toBe(
|
|
'aa5221abcdef0123456789',
|
|
);
|
|
});
|
|
|
|
test('clears commit and ignores stale commit responses when branch changes', async () => {
|
|
const user = userEvent.setup();
|
|
let resolveOldSearch: ((items: api.CommitRef[]) => void) | undefined;
|
|
vi.mocked(api.searchCommits).mockImplementation(
|
|
() =>
|
|
new Promise<api.CommitRef[]>((resolve) => {
|
|
resolveOldSearch = resolve;
|
|
}),
|
|
);
|
|
render(<PreviewDeployerApp />);
|
|
|
|
const branchInput = await screen.findByRole('combobox', { name: '分支名' });
|
|
const commitInput = screen.getByRole('combobox', { name: 'Commit Hash' });
|
|
await user.type(commitInput, 'abcdef1');
|
|
await waitFor(() => expect(api.searchCommits).toHaveBeenCalled());
|
|
await user.clear(branchInput);
|
|
await user.type(branchInput, 'feature/new');
|
|
|
|
expect((commitInput as HTMLInputElement).value).toBe('');
|
|
resolveOldSearch?.([
|
|
{
|
|
commitHash: 'abcdef1234567890',
|
|
shortHash: 'abcdef1',
|
|
subject: '旧分支提交',
|
|
},
|
|
]);
|
|
await Promise.resolve();
|
|
expect(screen.queryByText('旧分支提交')).toBeNull();
|
|
});
|