diff --git a/frontend/src/components/Catalog.test.jsx b/frontend/src/components/Catalog.test.jsx new file mode 100644 index 0000000..71b91cf --- /dev/null +++ b/frontend/src/components/Catalog.test.jsx @@ -0,0 +1,63 @@ +import React from 'react' +import { describe, it, expect, vi, beforeEach } from 'vitest' +import { render, screen, fireEvent, waitFor } from '@testing-library/react' +import { MemoryRouter } from 'react-router-dom' + +// §22.4a SLICE-3 — the faceted catalog pane (PUC-3). Mock the api so +// getCollection drives the field schema and listRFCs returns { items, facets }. +const listRFCs = vi.fn() +const getCollection = vi.fn() +vi.mock('../api', () => ({ + listRFCs: (...a) => listRFCs(...a), + listProposals: () => Promise.resolve({ items: [] }), + getCollection: (...a) => getCollection(...a), +})) +vi.mock('../lib/entryPaths', () => ({ + entryPath: () => '/x', proposalPath: () => '/p', + useProjectId: () => 'ohm', useCollectionId: () => 'default', +})) +import Catalog from './Catalog.jsx' + +const renderCatalog = () => + render( {}} version={0} />) + +describe('Catalog faceted pane', () => { + beforeEach(() => { listRFCs.mockReset(); getCollection.mockReset() }) + + it('renders facet groups with counts when the collection declares fields', async () => { + getCollection.mockResolvedValue({ + fields: { priority: { type: 'enum', values: ['P0', 'P1'] } }, + viewer: { can_contribute: false }, entry_noun: 'RFC', + }) + listRFCs.mockResolvedValue({ + items: [{ slug: 'a', title: 'A', state: 'active', tags: [], starred_by_me: false }], + facets: { priority: { P0: 3, P1: 1 }, state: { active: 4 } }, + }) + renderCatalog() + expect(await screen.findByText('P0')).toBeInTheDocument() + expect(screen.getByText('3')).toBeInTheDocument() + }) + + it('re-fetches with the selection when a facet checkbox is toggled', async () => { + getCollection.mockResolvedValue({ + fields: { priority: { type: 'enum', values: ['P0'] } }, + viewer: {}, entry_noun: 'RFC', + }) + listRFCs.mockResolvedValue({ items: [], facets: { priority: { P0: 2 }, state: {} } }) + renderCatalog() + const box = await screen.findByText('P0') + fireEvent.click(box.closest('label').querySelector('input')) + await waitFor(() => { + const last = listRFCs.mock.calls.at(-1) + expect(last[2].selections.priority).toContain('P0') + }) + }) + + it('renders legacy state chips when the collection declares no fields', async () => { + getCollection.mockResolvedValue({ fields: null, viewer: {}, entry_noun: 'RFC' }) + listRFCs.mockResolvedValue({ items: [], facets: {} }) + renderCatalog() + expect(await screen.findByText('Super-draft')).toBeInTheDocument() + expect(screen.getByText('Active')).toBeInTheDocument() + }) +})