diff --git a/frontend/src/lib/person/genealogy/StammbaumSidePanel.svelte.test.ts b/frontend/src/lib/person/genealogy/StammbaumSidePanel.svelte.test.ts index c200a6ce..7e33e3d8 100644 --- a/frontend/src/lib/person/genealogy/StammbaumSidePanel.svelte.test.ts +++ b/frontend/src/lib/person/genealogy/StammbaumSidePanel.svelte.test.ts @@ -79,7 +79,6 @@ describe('StammbaumSidePanel', () => { props: { node: baseNode(), onClose: () => {} } }); - // no years line visible — should not see the question-mark fallback expect(document.body.textContent).not.toMatch(/\?–\?/); }); @@ -128,20 +127,19 @@ describe('StammbaumSidePanel', () => { props: { node: baseNode(), onClose: () => {} } }); - await new Promise((r) => setTimeout(r, 100)); await expect.element(page.getByText(/keine beziehungen bekannt/i)).toBeVisible(); }); it('shows the error banner when both fetch calls fail', async () => { - fetchSpy.mockResolvedValue(new Response('error', { status: 500 })); + fetchSpy.mockImplementation(async () => new Response('error', { status: 500 })); render(StammbaumSidePanel, { props: { node: baseNode(), onClose: () => {} } }); - await new Promise((r) => setTimeout(r, 100)); - const alert = document.querySelector('[role="alert"]'); - expect(alert).not.toBeNull(); + await vi.waitFor(() => { + expect(document.querySelector('[role="alert"]')).not.toBeNull(); + }); }); it('shows the AddRelationshipForm only when canWrite is true', async () => { @@ -149,12 +147,12 @@ describe('StammbaumSidePanel', () => { props: { node: baseNode(), onClose: () => {}, canWrite: true } }); - await new Promise((r) => setTimeout(r, 100)); - // AddRelationshipForm exposes a "Beziehung hinzufügen" toggle button - const addButtons = Array.from(document.querySelectorAll('button')).filter((b) => - b.textContent?.toLowerCase().includes('hinzufügen') - ); - expect(addButtons.length).toBeGreaterThan(0); + await vi.waitFor(() => { + const addButtons = Array.from(document.querySelectorAll('button')).filter((b) => + b.textContent?.toLowerCase().includes('hinzufügen') + ); + expect(addButtons.length).toBeGreaterThan(0); + }); }); it('hides the AddRelationshipForm when canWrite is false', async () => { @@ -162,7 +160,7 @@ describe('StammbaumSidePanel', () => { props: { node: baseNode(), onClose: () => {}, canWrite: false } }); - await new Promise((r) => setTimeout(r, 100)); + // canWrite=false hides the form — assert the toggle button is absent in the rendered DOM. const addButtons = Array.from(document.querySelectorAll('button')).filter((b) => b.textContent?.toLowerCase().includes('hinzufügen') ); diff --git a/frontend/src/lib/person/relationship/AddRelationshipForm.svelte.test.ts b/frontend/src/lib/person/relationship/AddRelationshipForm.svelte.test.ts index 705fee7b..4e530518 100644 --- a/frontend/src/lib/person/relationship/AddRelationshipForm.svelte.test.ts +++ b/frontend/src/lib/person/relationship/AddRelationshipForm.svelte.test.ts @@ -75,17 +75,16 @@ describe('AddRelationshipForm', () => { expect(cancelBtn).toBeDefined(); cancelBtn?.click(); - // After cancel, the form fields should be gone - await new Promise((r) => setTimeout(r, 50)); - expect(document.querySelector('select[name="relationType"]')).toBeNull(); + await vi.waitFor(() => { + expect(document.querySelector('select[name="relationType"]')).toBeNull(); + }); }); - it('calls onSubmit with the form data when the form is submitted via callback', async () => { + it('does not invoke onSubmit before user submission', async () => { const onSubmit = vi.fn().mockResolvedValue(undefined); render(AddRelationshipForm, { props: { personId: 'p-1', onSubmit } }); - // We can't easily trigger the submit without a person typeahead, - // but at least verify the prop is wired (no crash). + // Without a person selected, the form cannot be submitted by the user. expect(onSubmit).not.toHaveBeenCalled(); }); @@ -93,36 +92,35 @@ describe('AddRelationshipForm', () => { render(AddRelationshipForm, { props: { personId: 'p-1' } }); await page.getByRole('button', { name: /hinzufügen/i }).click(); - await new Promise((r) => setTimeout(r, 30)); - const form = document.querySelector('form[action="?/addRelationship"]'); - expect(form).not.toBeNull(); + await vi.waitFor(() => { + expect(document.querySelector('form[action="?/addRelationship"]')).not.toBeNull(); + }); }); it('renders the callback-based form when onSubmit is provided', async () => { render(AddRelationshipForm, { props: { personId: 'p-1', onSubmit: async () => {} } }); await page.getByRole('button', { name: /hinzufügen/i }).click(); - await new Promise((r) => setTimeout(r, 30)); - // callback form has no action attribute (just onsubmit handler) - const enhancedForm = document.querySelector('form[action="?/addRelationship"]'); - expect(enhancedForm).toBeNull(); - const fallbackForm = document.querySelector('form'); - expect(fallbackForm).not.toBeNull(); + await vi.waitFor(() => { + // callback form has no action attribute (just onsubmit handler) + expect(document.querySelector('form[action="?/addRelationship"]')).toBeNull(); + expect(document.querySelector('form')).not.toBeNull(); + }); }); it('shows the self-error when the related person id equals personId', async () => { render(AddRelationshipForm, { props: { personId: 'p-self' } }); await page.getByRole('button', { name: /hinzufügen/i }).click(); - await new Promise((r) => setTimeout(r, 30)); - - // Set the relatedPersonId hidden input to match personId - const relInput = document.querySelector('input[name="relatedPersonId"]') as HTMLInputElement; + const relInput = (await vi.waitFor(() => { + const el = document.querySelector('input[name="relatedPersonId"]') as HTMLInputElement; + expect(el).not.toBeNull(); + return el; + })) as HTMLInputElement; relInput.value = 'p-self'; relInput.dispatchEvent(new Event('input', { bubbles: true })); - await new Promise((r) => setTimeout(r, 30)); await expect.element(page.getByText(/selbst|self/i)).toBeVisible(); }); @@ -131,13 +129,12 @@ describe('AddRelationshipForm', () => { render(AddRelationshipForm, { props: { personId: 'p-1' } }); await page.getByRole('button', { name: /hinzufügen/i }).click(); - await new Promise((r) => setTimeout(r, 30)); - // The "Hinzufügen" button changed from toggle button → submit button. Find the submit one. - const submitBtn = Array.from( - document.querySelectorAll('button[type="submit"]') - )[0] as HTMLButtonElement; - expect(submitBtn.disabled).toBe(true); + await vi.waitFor(() => { + const submitBtn = document.querySelector('button[type="submit"]') as HTMLButtonElement | null; + expect(submitBtn).not.toBeNull(); + expect(submitBtn!.disabled).toBe(true); + }); }); it('keeps submit disabled when there is a yearError', async () => { @@ -155,10 +152,9 @@ describe('AddRelationshipForm', () => { relInput.value = 'p-other'; relInput.dispatchEvent(new Event('input', { bubbles: true })); - await new Promise((r) => setTimeout(r, 30)); - const submitBtn = Array.from( - document.querySelectorAll('button[type="submit"]') - )[0] as HTMLButtonElement; - expect(submitBtn.disabled).toBe(true); + await vi.waitFor(() => { + const submitBtn = document.querySelector('button[type="submit"]') as HTMLButtonElement; + expect(submitBtn.disabled).toBe(true); + }); }); });