fix(web): P4-1 restore graceful degradation for missions/tasks fetch on project detail
ci/woodpecker/pr/ci Pipeline was successful

This commit is contained in:
shaggy (mosaic-dev box)
2026-08-10 22:43:47 -05:00
parent a6085eea37
commit 29db24210c
2 changed files with 23 additions and 4 deletions
+21 -2
View File
@@ -134,17 +134,36 @@ describe('ProjectDetailPage', () => {
expect(container.querySelector('[role="dialog"]')).toBeNull(); expect(container.querySelector('[role="dialog"]')).toBeNull();
}); });
it('renders a visible alert when any project detail request fails and lets the user navigate back', async () => { it('renders the project with an empty missions tab when the missions request fails', async () => {
apiMock apiMock
.mockResolvedValueOnce(projectFixtures[0]) .mockResolvedValueOnce(projectFixtures[0])
.mockRejectedValueOnce(new Error('Missions request failed')) .mockRejectedValueOnce(new Error('Missions request failed'))
.mockResolvedValueOnce(taskFixtures.filter((task) => task.projectId === 'project-1')); .mockResolvedValueOnce(taskFixtures.filter((task) => task.projectId === 'project-1'));
await renderProjectDetailPage();
expect(container.textContent).toContain('Mosaic Stack');
expect(container.querySelector('[role="alert"]')).toBeNull();
await act(async () => {
clickButtonByText('Missions (0)');
});
expect(container.textContent).toContain('No missions for this project');
});
it('renders a visible alert when the project request fails and lets the user navigate back', async () => {
apiMock
.mockRejectedValueOnce(new Error('Project request failed'))
.mockResolvedValueOnce(missionFixtures)
.mockResolvedValueOnce(taskFixtures.filter((task) => task.projectId === 'project-1'));
const router = await renderProjectDetailPage(); const router = await renderProjectDetailPage();
const alert = container.querySelector('[role="alert"]'); const alert = container.querySelector('[role="alert"]');
expect(alert).toBeTruthy(); expect(alert).toBeTruthy();
expect(alert?.textContent).toContain('Missions request failed'); expect(alert?.textContent).toContain('Project request failed');
expect(container.textContent).not.toContain('Mosaic Stack');
await act(async () => { await act(async () => {
clickButtonByText('Back to projects'); clickButtonByText('Back to projects');
+2 -2
View File
@@ -76,8 +76,8 @@ export function ProjectDetailPage(): ReactElement {
void Promise.all([ void Promise.all([
api<Project>('/api/projects/' + id), api<Project>('/api/projects/' + id),
api<Mission[]>('/api/missions'), api<Mission[]>('/api/missions').catch(() => [] as Mission[]),
api<Task[]>('/api/tasks?projectId=' + id), api<Task[]>('/api/tasks?projectId=' + id).catch(() => [] as Task[]),
]) ])
.then(([loadedProject, allMissions, loadedTasks]) => { .then(([loadedProject, allMissions, loadedTasks]) => {
if (cancelled) return; if (cancelled) return;