fix(web): /machine terminal visible prop, stop/restart confirmation, accurate waiting label
This commit is contained in:
@@ -42,6 +42,40 @@ const stoppedStatus = {
|
||||
containerName: 'spoon-home-gib',
|
||||
};
|
||||
|
||||
const runningStatus = {
|
||||
...stoppedStatus,
|
||||
running: true,
|
||||
startedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// Point the status endpoint at a running box so the Stop/Restart controls render.
|
||||
const useRunningStatus = () => {
|
||||
fetchSpy.mockImplementation((input: RequestInfo | URL) => {
|
||||
const url =
|
||||
typeof input === 'string'
|
||||
? input
|
||||
: input instanceof URL
|
||||
? input.href
|
||||
: input.url;
|
||||
if (url.startsWith('/api/box/status')) {
|
||||
return Promise.resolve(jsonResponse({ status: runningStatus }));
|
||||
}
|
||||
if (url.startsWith('/api/box/tree')) {
|
||||
return Promise.resolve(jsonResponse({ tree }));
|
||||
}
|
||||
if (url.startsWith('/api/box/lifecycle')) {
|
||||
return Promise.resolve(jsonResponse({ status: stoppedStatus }));
|
||||
}
|
||||
return Promise.resolve(jsonResponse({}));
|
||||
});
|
||||
};
|
||||
|
||||
const lifecycleCalls = () =>
|
||||
fetchSpy.mock.calls.filter(([input]) => {
|
||||
const url = typeof input === 'string' ? input : '';
|
||||
return url.startsWith('/api/box/lifecycle');
|
||||
});
|
||||
|
||||
const tree = {
|
||||
name: '',
|
||||
path: '',
|
||||
@@ -127,4 +161,51 @@ describe('MachineShell', () => {
|
||||
render(<MachineShell />);
|
||||
expect(await screen.findByText('terminal')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not stop the box until the confirmation is accepted', async () => {
|
||||
useRunningStatus();
|
||||
render(<MachineShell />);
|
||||
const stopButton = await screen.findByRole('button', { name: 'Stop' });
|
||||
fireEvent.click(stopButton);
|
||||
|
||||
// The confirmation dialog intervenes: no lifecycle POST fires on the click.
|
||||
const confirm = await screen.findByRole('button', { name: 'Stop machine' });
|
||||
expect(lifecycleCalls()).toHaveLength(0);
|
||||
|
||||
fireEvent.click(confirm);
|
||||
await waitFor(() =>
|
||||
expect(fetchSpy).toHaveBeenCalledWith(
|
||||
'/api/box/lifecycle',
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ action: 'stop' }),
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('does not restart the box until the confirmation is accepted', async () => {
|
||||
useRunningStatus();
|
||||
render(<MachineShell />);
|
||||
const restartButton = await screen.findByRole('button', {
|
||||
name: 'Restart',
|
||||
});
|
||||
fireEvent.click(restartButton);
|
||||
|
||||
const confirm = await screen.findByRole('button', {
|
||||
name: 'Restart machine',
|
||||
});
|
||||
expect(lifecycleCalls()).toHaveLength(0);
|
||||
|
||||
fireEvent.click(confirm);
|
||||
await waitFor(() =>
|
||||
expect(fetchSpy).toHaveBeenCalledWith(
|
||||
'/api/box/lifecycle',
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ action: 'restart' }),
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user