Skip to content

Commit 086e08c

Browse files
Throw on malformed createProject response instead of silently skipping onProjectCreated
Previously, if the backend returned a JSON object that failed isInterlinearProjectSummary, the modal would call onClose() without invoking onProjectCreated, silently dropping the contract. Now it throws, routing through the existing error handler. Also fixes an incomplete fixture in the success test that was missing required summary fields.
1 parent 8259994 commit 086e08c

2 files changed

Lines changed: 34 additions & 6 deletions

File tree

src/__tests__/components/CreateProjectModal.test.tsx

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,14 @@ describe('CreateProjectModal', () => {
100100
});
101101

102102
it('calls onClose after submitting when sendCommand returns a project JSON', async () => {
103-
jest
104-
.mocked(papi.commands.sendCommand)
105-
.mockResolvedValue(
106-
JSON.stringify({ id: 'new-project-id', createdAt: '2026-01-01T00:00:00.000Z' }),
107-
);
103+
jest.mocked(papi.commands.sendCommand).mockResolvedValue(
104+
JSON.stringify({
105+
id: 'new-project-id',
106+
createdAt: '2026-01-01T00:00:00.000Z',
107+
sourceProjectId: testProjectId,
108+
analysisWritingSystem: 'en',
109+
}),
110+
);
108111
const onClose = jest.fn();
109112
render(<CreateProjectModal projectId={testProjectId} onClose={onClose} />);
110113

@@ -145,6 +148,29 @@ describe('CreateProjectModal', () => {
145148
);
146149
});
147150

151+
it('does not call onProjectCreated or onClose and sends an error notification when sendCommand returns malformed JSON', async () => {
152+
jest.mocked(papi.commands.sendCommand).mockResolvedValue(JSON.stringify({ bad: 'shape' }));
153+
const onProjectCreated = jest.fn();
154+
const onClose = jest.fn();
155+
render(
156+
<CreateProjectModal
157+
projectId={testProjectId}
158+
onClose={onClose}
159+
onProjectCreated={onProjectCreated}
160+
/>,
161+
);
162+
163+
await userEvent.click(screen.getByRole('button', { name: /^create$/i }));
164+
165+
await waitFor(() =>
166+
expect(papi.notifications.send).toHaveBeenCalledWith(
167+
expect.objectContaining({ severity: 'error' }),
168+
),
169+
);
170+
expect(onProjectCreated).not.toHaveBeenCalled();
171+
expect(onClose).not.toHaveBeenCalled();
172+
});
173+
148174
it('does not call onProjectCreated or onClose when sendCommand returns undefined', async () => {
149175
jest.mocked(papi.commands.sendCommand).mockResolvedValue(undefined);
150176
const onProjectCreated = jest.fn();

src/components/CreateProjectModal.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ export function CreateProjectModal({
7171
);
7272
if (!projectJson) return;
7373
const parsed: unknown = JSON.parse(projectJson);
74-
if (isInterlinearProjectSummary(parsed)) onProjectCreated?.(parsed);
74+
if (!isInterlinearProjectSummary(parsed))
75+
throw new Error('Created project has unexpected shape');
76+
onProjectCreated?.(parsed);
7577
onClose();
7678
} catch (e) {
7779
logger.error('Interlinearizer: failed to create project', e);

0 commit comments

Comments
 (0)