test(ideas): voeg component-tests toe voor activeProductId-voorvulling

AC1: "Nieuw idee"-select voorgevuld met activeProductId
AC2: "Nieuw idee"-select leeg bij activeProductId=null
AC3: "Snel idee" stuurt product_id=activeProductId mee bij createIdeaAction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scrum4Me Agent 2026-05-15 04:57:12 +02:00
parent e6c1b13f46
commit edd647b3d1

View file

@ -1,6 +1,6 @@
// @vitest-environment jsdom
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import '@testing-library/jest-dom'
import React from 'react'
@ -74,8 +74,15 @@ vi.mock('@/components/ui/popover', () => {
// Import after mocks
import { useUserSettingsStore } from '@/stores/user-settings/store'
import { IdeaList } from '@/components/ideas/idea-list'
import { createIdeaAction } from '@/actions/ideas'
import type { IdeaDto } from '@/lib/idea-dto'
const PRODUCTS = [
{ id: 'prod-1', name: 'Product A', repo_url: null },
// repo_url ingesteld zodat de optietekst gewoon "Product B" is (zonder "(geen repo)" suffix)
{ id: 'prod-2', name: 'Product B', repo_url: 'https://github.com/org/prod-b' },
]
// Minimal IdeaDto factory
function makeIdea(overrides: Partial<IdeaDto> = {}): IdeaDto {
return {
@ -199,3 +206,72 @@ describe('IdeaList — filterpopover', () => {
expect(stored).toEqual([])
})
})
describe('IdeaList — activeProductId voorvullen', () => {
// Hulpfunctie: vind een knop op basis van gedeeltelijke tekstinhoud.
// getByText() werkt hier betrouwbaarder dan getByRole({name}) voor knoppen
// met SVG-icoon omdat de accessible-name-berekening van Base UI knoppen in
// jsdom soms afwijkt van wat we verwachten.
function clickButton(label: string) {
const btn = Array.from(document.querySelectorAll('button')).find(
(b) => b.textContent?.trim().includes(label)
)
if (!btn) throw new Error(`Knop met tekst "${label}" niet gevonden`)
fireEvent.click(btn)
}
it('AC1: "Nieuw idee"-select is voorgevuld met het actieve product', async () => {
render(
<IdeaList ideas={[]} products={PRODUCTS} isDemo={false} activeProductId="prod-2" />
)
clickButton('Nieuw idee')
// Wacht tot het formulier verschijnt; create-form-select toont "Product B" (waarde 'prod-2').
// De toolbar-select toont "Alle producten" (waarde 'all'), zodat displayValue uniek is.
const createFormSelect = await waitFor(() => screen.getByDisplayValue('Product B'))
expect(createFormSelect).toHaveValue('prod-2')
})
it('AC2: "Nieuw idee"-select staat op leeg wanneer activeProductId null is', async () => {
render(
<IdeaList ideas={[]} products={PRODUCTS} isDemo={false} activeProductId={null} />
)
clickButton('Nieuw idee')
// Toolbar-select toont "Alle producten"; create-form-select toont de placeholder (waarde '').
const createFormSelect = await waitFor(() =>
screen.getByDisplayValue('Geen product (kan later worden gekoppeld)')
)
expect(createFormSelect).toHaveValue('')
})
it('AC3: "Snel idee" stuurt product_id gelijk aan activeProductId mee', async () => {
vi.mocked(createIdeaAction).mockResolvedValue({ data: { code: 'ID-99', id: 'idea-99' } } as never)
render(
<IdeaList ideas={[]} products={PRODUCTS} isDemo={false} activeProductId="prod-2" />
)
// Open "Snel idee"-formulier en wacht tot het verschijnt
clickButton('Snel idee')
await waitFor(() => screen.getByPlaceholderText('Titel *'))
// Vul de verplichte titel in
fireEvent.change(screen.getByPlaceholderText('Titel *'), {
target: { value: 'Mijn snel idee' },
})
// Klik Opslaan — startTransition roept createIdeaAction synchroon aan
clickButton('Opslaan')
await waitFor(() => {
expect(createIdeaAction).toHaveBeenCalledWith({
title: 'Mijn snel idee',
description: null,
product_id: 'prod-2',
})
})
})
})