@@ -256,8 +256,10 @@ test.describe('Join Us page', () => {
256256 await page . goto ( '/join-us' ) ;
257257
258258 // GitHub section
259- await expect ( page . getByRole ( 'heading' , { name : / ^ g i t h u b $ / i, level : 2 } ) ) . toBeVisible ( ) ;
260- await expect ( page . getByText ( / g i t h u b .c o m \/ p o d c o d a r / i) ) . toBeVisible ( ) ;
259+ await expect (
260+ page . getByRole ( 'heading' , { name : / c o n t r i b u a c o m c ó d i g o a b e r t o / i, level : 2 } )
261+ ) . toBeVisible ( ) ;
262+ await expect ( page . getByText ( / r e p o s i t ó r i o s d a o r g a n i z a ç ã o / i) ) . toBeVisible ( ) ;
261263
262264 // GitHub CTA button
263265 await expect ( page . getByRole ( 'link' , { name : / v e r r e p o s i t ó r i o s / i } ) ) . toHaveAttribute (
@@ -270,10 +272,12 @@ test.describe('Join Us page', () => {
270272 await page . goto ( '/join-us' ) ;
271273
272274 // Contact section
273- await expect ( page . getByRole ( 'heading' , { name : / ^ c o n t a t o $ / i, level : 2 } ) ) . toBeVisible ( ) ;
275+ await expect (
276+ page . getByRole ( 'heading' , { name : / p r o n t o p a r a f a z e r p a r t e / i, level : 2 } )
277+ ) . toBeVisible ( ) ;
274278
275279 // Link to contact page
276- await expect ( page . getByRole ( 'link' , { name : / e n v i e u m a m e n s a g e m / i } ) ) . toHaveAttribute (
280+ await expect ( page . getByRole ( 'link' , { name : / e n t r e e m c o n t a t o / i } ) ) . toHaveAttribute (
277281 'href' ,
278282 '/contact'
279283 ) ;
@@ -345,4 +349,291 @@ test.describe('Contact page', () => {
345349 const mailtoLink = page . locator ( 'a[href^="mailto:"]' ) . last ( ) ;
346350 await expect ( mailtoLink ) . toBeVisible ( ) ;
347351 } ) ;
352+
353+ test ( 'has FAQ section with multiple categories' , async ( { page } ) => {
354+ await page . goto ( '/contact' ) ;
355+
356+ // FAQ section heading
357+ await expect ( page . getByRole ( 'heading' , { name : / p e r g u n t a s f r e q u e n t e s / i } ) ) . toBeVisible ( ) ;
358+
359+ // FAQ categories
360+ await expect ( page . getByText ( 'Sobre a PodCodar e Nossa Missão' ) ) . toBeVisible ( ) ;
361+ await expect ( page . getByText ( 'Para Novos Membros' ) ) . toBeVisible ( ) ;
362+ await expect ( page . getByText ( 'Para Empresas e Parceiros' ) ) . toBeVisible ( ) ;
363+ await expect ( page . getByText ( 'Outras Dúvidas' ) ) . toBeVisible ( ) ;
364+
365+ // Sample questions from each category
366+ await expect ( page . getByText ( 'O que é a PodCodar?' ) ) . toBeVisible ( ) ;
367+ await expect ( page . getByText ( 'Preciso pagar para participar da comunidade?' ) ) . toBeVisible ( ) ;
368+ await expect ( page . getByText ( 'Quero participar! Qual é o primeiro passo?' ) ) . toBeVisible ( ) ;
369+ await expect (
370+ page . getByText ( 'Somos uma empresa. Como podemos ser parceiros da PodCodar?' )
371+ ) . toBeVisible ( ) ;
372+ await expect ( page . getByText ( 'Quais ferramentas de comunicação vocês usam?' ) ) . toBeVisible ( ) ;
373+ } ) ;
374+ } ) ;
375+
376+ // ──────────────────────────────────────────────────────────────────────────────
377+ // Transparency page (/transparency)
378+ // ──────────────────────────────────────────────────────────────────────────────
379+
380+ test . describe ( 'Transparency page' , ( ) => {
381+ test ( 'navigates to Transparency page via nav link' , async ( { page } ) => {
382+ await page . goto ( '/' ) ;
383+
384+ await page
385+ . getByRole ( 'navigation' , { name : 'Primary' } )
386+ . getByRole ( 'link' , { name : / t r a n s p a r ê n c i a / i } )
387+ . click ( ) ;
388+
389+ await expect ( page ) . toHaveURL ( / \/ t r a n s p a r e n c y / ) ;
390+ await expect ( page . getByRole ( 'heading' , { name : / t r a n s p a r ê n c i a / i, level : 1 } ) ) . toBeVisible ( ) ;
391+ } ) ;
392+
393+ test ( 'can navigate directly to /transparency' , async ( { page } ) => {
394+ await page . goto ( '/transparency' ) ;
395+
396+ await expect ( page ) . toHaveTitle ( / P o d C o d a r / i) ;
397+ await expect ( page . getByRole ( 'heading' , { name : / t r a n s p a r ê n c i a / i, level : 1 } ) ) . toBeVisible ( ) ;
398+ } ) ;
399+
400+ test ( 'has hero section with stats' , async ( { page } ) => {
401+ await page . goto ( '/transparency' ) ;
402+
403+ // Eyebrow badge
404+ await expect ( page . getByText ( 'Compromisso com a Transparência' ) ) . toBeVisible ( ) ;
405+
406+ // Hero heading
407+ await expect ( page . getByRole ( 'heading' , { name : / t r a n s p a r ê n c i a / i, level : 1 } ) ) . toBeVisible ( ) ;
408+
409+ // Stats are visible
410+ await expect ( page . locator ( 'text=Documentos' ) . first ( ) ) . toBeVisible ( ) ;
411+ await expect ( page . locator ( 'text=Membros' ) . first ( ) ) . toBeVisible ( ) ;
412+ } ) ;
413+
414+ test ( 'has documents section with categories' , async ( { page } ) => {
415+ await page . goto ( '/transparency' ) ;
416+
417+ // Documents section heading
418+ await expect ( page . getByRole ( 'heading' , { name : / d o c u m e n t o s / i, level : 2 } ) ) . toBeVisible ( ) ;
419+
420+ // CNPJ badge - scoped to main content to avoid footer match
421+ await expect (
422+ page . locator ( 'main, [role="main"]' ) . getByText ( '53.979.776/0001-61' ) . first ( )
423+ ) . toBeVisible ( ) ;
424+
425+ // Category headings (at least one should exist based on content)
426+ const hasInstitucional = await page
427+ . getByText ( 'Institucional' )
428+ . first ( )
429+ . isVisible ( )
430+ . catch ( ( ) => false ) ;
431+ const hasFinanceiro = await page
432+ . getByText ( 'Financeiro' )
433+ . first ( )
434+ . isVisible ( )
435+ . catch ( ( ) => false ) ;
436+ const hasFiscal = await page
437+ . getByText ( 'Fiscal' )
438+ . first ( )
439+ . isVisible ( )
440+ . catch ( ( ) => false ) ;
441+ expect ( hasInstitucional || hasFinanceiro || hasFiscal ) . toBe ( true ) ;
442+ } ) ;
443+
444+ test ( 'has board members section' , async ( { page } ) => {
445+ await page . goto ( '/transparency' ) ;
446+
447+ await expect (
448+ page . getByRole ( 'heading' , { name : / c o n s e l h o a d m i n i s t r a t i v o / i, level : 2 } )
449+ ) . toBeVisible ( ) ;
450+
451+ // Board members from data/transparency.ts
452+ await expect ( page . getByText ( 'Marco Antônio de Souza Júnior' ) ) . toBeVisible ( ) ;
453+ await expect ( page . getByText ( 'Pedro Henrique Ramos Costa' ) ) . toBeVisible ( ) ;
454+ await expect ( page . getByText ( 'Marina Andrade Fonseca' ) ) . toBeVisible ( ) ;
455+ await expect ( page . getByText ( 'Pedro Frattezi Silva' ) ) . toBeVisible ( ) ;
456+ } ) ;
457+
458+ test ( 'has metrics section with impact numbers' , async ( { page } ) => {
459+ await page . goto ( '/transparency' ) ;
460+
461+ await expect (
462+ page . getByRole ( 'heading' , { name : / i m p a c t o e r e s u l t a d o s / i, level : 2 } )
463+ ) . toBeVisible ( ) ;
464+
465+ // Metrics from data/transparency.ts
466+ await expect ( page . locator ( 'text=300+' ) . first ( ) ) . toBeVisible ( ) ;
467+ await expect ( page . locator ( 'text=30+' ) . first ( ) ) . toBeVisible ( ) ;
468+ await expect ( page . locator ( 'text=16+' ) . first ( ) ) . toBeVisible ( ) ;
469+ } ) ;
470+
471+ test ( 'has commitment section with CTA links' , async ( { page } ) => {
472+ await page . goto ( '/transparency' ) ;
473+
474+ await expect ( page . getByRole ( 'heading' , { name : / n o s s o c o m p r o m i s s o / i, level : 2 } ) ) . toBeVisible ( ) ;
475+
476+ // CTA links
477+ await expect ( page . getByRole ( 'link' , { name : / f a l e c o n o s c o / i } ) . first ( ) ) . toHaveAttribute (
478+ 'href' ,
479+ '/contact'
480+ ) ;
481+ await expect ( page . getByRole ( 'link' , { name : / c o m o a j u d a r / i } ) ) . toHaveAttribute (
482+ 'href' ,
483+ '/contributing'
484+ ) ;
485+ } ) ;
486+
487+ test ( 'has contact CTA with mailto link' , async ( { page } ) => {
488+ await page . goto ( '/transparency' ) ;
489+
490+ await expect ( page . getByRole ( 'heading' , { name : / f a l e c o n o s c o / i, level : 2 } ) ) . toBeVisible ( ) ;
491+
492+ const mailtoLink = page . locator ( 'a[href^="mailto:contato@podcodar.org"]' ) ;
493+ await expect ( mailtoLink ) . toBeVisible ( ) ;
494+ } ) ;
495+
496+ test ( 'can navigate to a document detail page' , async ( { page } ) => {
497+ await page . goto ( '/transparency' ) ;
498+
499+ // Click the first document card
500+ const firstDocLink = page . locator ( 'a[href^="/transparency/"]' ) . first ( ) ;
501+ await expect ( firstDocLink ) . toBeVisible ( ) ;
502+ await firstDocLink . click ( ) ;
503+
504+ // Should be on a detail page
505+ await expect ( page ) . toHaveURL ( / \/ t r a n s p a r e n c y \/ .+ / ) ;
506+
507+ // Detail page should have a breadcrumb back to transparency
508+ const backLink = page
509+ . getByRole ( 'navigation' , { name : 'Breadcrumb' } )
510+ . getByRole ( 'link' , { name : / t r a n s p a r ê n c i a / i } ) ;
511+ await expect ( backLink ) . toHaveAttribute ( 'href' , '/transparency' ) ;
512+ } ) ;
513+ } ) ;
514+
515+ // ──────────────────────────────────────────────────────────────────────────────
516+ // Transparency detail pages (/transparency/:slug)
517+ // ──────────────────────────────────────────────────────────────────────────────
518+
519+ test . describe ( 'Transparency detail pages' , ( ) => {
520+ test ( 'estatuto page has title, description and back link' , async ( { page } ) => {
521+ await page . goto ( '/transparency/estatuto' ) ;
522+
523+ // Page should have a heading (the page title h1, not the markdown content h1)
524+ await expect (
525+ page . locator ( 'h1.text-3xl' ) . filter ( { hasText : / a t a d e a s s e m b l e i a / i } )
526+ ) . toBeVisible ( ) ;
527+
528+ // Category badge
529+ await expect ( page . getByText ( 'Institucional' ) . first ( ) ) . toBeVisible ( ) ;
530+
531+ // Back link to transparency - use the breadcrumb nav, not the header nav
532+ const breadcrumbLink = page
533+ . getByRole ( 'navigation' , { name : 'Breadcrumb' } )
534+ . getByRole ( 'link' , { name : / t r a n s p a r ê n c i a / i } ) ;
535+ await expect ( breadcrumbLink ) . toHaveAttribute ( 'href' , / \/ t r a n s p a r e n c y / ) ;
536+
537+ // Back navigation at bottom
538+ await expect (
539+ page . getByRole ( 'link' , { name : / v o l t a r p a r a t o d o s o s d o c u m e n t o s / i } )
540+ ) . toHaveAttribute ( 'href' , '/transparency' ) ;
541+ } ) ;
542+
543+ test ( 'document page has external file link when available' , async ( { page } ) => {
544+ await page . goto ( '/transparency/estatuto' ) ;
545+
546+ // Should have a view/download link
547+ const viewLink = page . getByRole ( 'link' , { name : / v e r d o c u m e n t o / i } ) ;
548+ await expect ( viewLink ) . toBeVisible ( ) ;
549+ } ) ;
550+
551+ test ( 'renders 404 for non-existent transparency slug' , async ( { page } ) => {
552+ await page . goto ( '/transparency/non-existent-doc' ) ;
553+
554+ // Should show 404
555+ await expect ( page . getByText ( '404' ) ) . toBeVisible ( ) ;
556+ await expect ( page . getByRole ( 'heading' , { name : / o p s / i } ) ) . toBeVisible ( ) ;
557+ } ) ;
558+ } ) ;
559+
560+ // ──────────────────────────────────────────────────────────────────────────────
561+ // 404 page
562+ // ──────────────────────────────────────────────────────────────────────────────
563+
564+ test . describe ( '404 page' , ( ) => {
565+ test ( 'shows 404 for non-existent routes' , async ( { page } ) => {
566+ await page . goto ( '/this-page-does-not-exist' ) ;
567+
568+ await expect ( page . getByText ( '404' ) ) . toBeVisible ( ) ;
569+ await expect ( page . getByRole ( 'heading' , { name : / o p s / i } ) ) . toBeVisible ( ) ;
570+ await expect ( page . getByText ( / e s s a p á g i n a n ã o e x i s t e / i) ) . toBeVisible ( ) ;
571+ } ) ;
572+
573+ test ( 'has navigation links back to main pages' , async ( { page } ) => {
574+ await page . goto ( '/non-existent-page' ) ;
575+
576+ // Link back to home - scoped to 404 navigation
577+ const nav404 = page . getByRole ( 'navigation' , { name : '404 navigation' } ) ;
578+ await expect ( nav404 . getByRole ( 'link' , { name : / i n í c i o / i } ) ) . toHaveAttribute ( 'href' , '/' ) ;
579+
580+ // Link to join-us
581+ await expect ( nav404 . getByRole ( 'link' , { name : / f a ç a p a r t e / i } ) ) . toHaveAttribute (
582+ 'href' ,
583+ '/join-us/'
584+ ) ;
585+ } ) ;
586+
587+ test ( 'returns 404 status for non-existent page' , async ( { page, context } ) => {
588+ const response = await page . goto ( '/definitely-not-real' ) ;
589+ expect ( response ?. status ( ) ) . toBe ( 404 ) ;
590+ } ) ;
591+ } ) ;
592+
593+ // ──────────────────────────────────────────────────────────────────────────────
594+ // Cross-page navigation
595+ // ──────────────────────────────────────────────────────────────────────────────
596+
597+ test . describe ( 'Cross-page navigation' , ( ) => {
598+ test ( 'all main nav links are present in header' , async ( { page } ) => {
599+ await page . goto ( '/' ) ;
600+
601+ const nav = page . getByRole ( 'navigation' , { name : 'Primary' } ) ;
602+
603+ await expect ( nav . getByRole ( 'link' , { name : / i n í c i o / i } ) ) . toBeVisible ( ) ;
604+ await expect ( nav . getByRole ( 'link' , { name : / s o b r e / i } ) ) . toBeVisible ( ) ;
605+ await expect ( nav . getByRole ( 'link' , { name : / t r a n s p a r ê n c i a / i } ) ) . toBeVisible ( ) ;
606+ await expect ( nav . getByRole ( 'link' , { name : / c o n t a t o / i } ) ) . toBeVisible ( ) ;
607+ } ) ;
608+
609+ test ( 'can navigate from about to join-us via CTA' , async ( { page } ) => {
610+ await page . goto ( '/about' ) ;
611+
612+ const cta = page . getByRole ( 'link' , { name : / f a ç a p a r t e / i } ) . last ( ) ;
613+ await cta . click ( ) ;
614+
615+ await expect ( page ) . toHaveURL ( / \/ j o i n - u s / ) ;
616+ } ) ;
617+
618+ test ( 'footer social links are consistent across pages' , async ( { page } ) => {
619+ const pages = [ '/' , '/about' , '/contact' , '/transparency' , '/join-us' , '/contributing' ] ;
620+
621+ for ( const path of pages ) {
622+ await page . goto ( path ) ;
623+ const socialLinks = page . locator ( 'footer' ) . getByTestId ( 'social-links' ) ;
624+ await expect ( socialLinks . getByRole ( 'link' ) ) . toHaveCount ( 4 ) ;
625+ }
626+ } ) ;
627+
628+ test ( 'can navigate to transparency from footer or nav on any page' , async ( { page } ) => {
629+ await page . goto ( '/join-us' ) ;
630+
631+ await page
632+ . getByRole ( 'navigation' , { name : 'Primary' } )
633+ . getByRole ( 'link' , { name : / t r a n s p a r ê n c i a / i } )
634+ . click ( ) ;
635+
636+ await expect ( page ) . toHaveURL ( / \/ t r a n s p a r e n c y / ) ;
637+ await expect ( page . getByRole ( 'heading' , { name : / t r a n s p a r ê n c i a / i, level : 1 } ) ) . toBeVisible ( ) ;
638+ } ) ;
348639} ) ;
0 commit comments