@@ -25,6 +25,7 @@ import { createRoot, type Root } from "react-dom/client";
2525import type { CascadeResult } from "../../lib/heuristics/types.ts" ;
2626import type { AnonymousAtsScore } from "../../lib/score/score.ts" ;
2727import type { ContactOverrides } from "../../hooks/useEditableParse.ts" ;
28+ import type { RenderFinding } from "../../lib/pdf/render-findings.ts" ;
2829
2930( globalThis as { IS_REACT_ACT_ENVIRONMENT ?: boolean } ) . IS_REACT_ACT_ENVIRONMENT =
3031 true ;
@@ -33,6 +34,9 @@ const pdfDownload = vi.fn();
3334const markdownDownload = vi . fn ( ) ;
3435const reportDownload = vi . fn ( ( ) => Promise . resolve ( true ) ) ;
3536let pdfError : string | null = null ;
37+ /** #621 export findings the PDF row surfaces — empty for a clean export, which
38+ * is what every case here renders unless it says otherwise. */
39+ let pdfFindings : RenderFinding [ ] = [ ] ;
3640
3741/** What each hook was handed as its journey Download-stage mark site (#826).
3842 * The success point lives inside the hooks, so what this component owns — and
@@ -46,7 +50,12 @@ vi.mock("../../hooks/useDownloadPdf.ts", () => ({
4650 onDownloaded ?: ( ) => void ,
4751 ) => {
4852 captured . pdf = onDownloaded ;
49- return { download : pdfDownload , isGenerating : false , error : pdfError } ;
53+ return {
54+ download : pdfDownload ,
55+ isGenerating : false ,
56+ error : pdfError ,
57+ findings : pdfFindings ,
58+ } ;
5059 } ,
5160} ) ) ;
5261vi . mock ( "../../hooks/useDownloadMarkdown.ts" , ( ) => ( {
@@ -154,6 +163,7 @@ beforeEach(() => {
154163 markdownDownload . mockClear ( ) ;
155164 reportDownload . mockClear ( ) ;
156165 pdfError = null ;
166+ pdfFindings = [ ] ;
157167 // jsdom does not implement modal dialogs in every version, and the primitive
158168 // calls `showModal()` from an effect. Stubbed to a plain open so the tests
159169 // exercise the dialog's CONTENT rather than the UA's modality.
@@ -421,4 +431,46 @@ describe("ExportDialog", () => {
421431 }
422432 expect ( onExported ) . toHaveBeenCalledTimes ( 3 ) ;
423433 } ) ;
434+
435+ // #621 — the export reports what it could not render cleanly, on the row that
436+ // produced the file. Advisory: the user already has the PDF.
437+ describe ( "export findings" , ( ) => {
438+ it ( "renders NO warning chrome when the export was clean" , ( ) => {
439+ // The common case. A permanent "0 issues" strip on the download row would
440+ // train every user to stop reading it.
441+ const el = render ( exportable ( ) ) ;
442+ expect ( text ( el ) ) . not . toContain ( "Check the export" ) ;
443+ expect ( el . querySelector ( "[aria-live]:not([aria-live=\"off\"]) ul" ) ) . toBeNull ( ) ;
444+ } ) ;
445+
446+ it ( "names the field and states what happened, never colour alone" , ( ) => {
447+ pdfFindings = [
448+ {
449+ kind : "glyph-degraded" ,
450+ severity : "warning" ,
451+ sourceField : "Experience \u2192 Staff Engineer \u00b7 Acme \u2192 bullet 3" ,
452+ detail : 'The export font has no glyph for "\u2605", so it was drawn as "?".' ,
453+ } ,
454+ ] ;
455+ const el = render ( exportable ( ) ) ;
456+ // The badge carries a WORD, not just a tone.
457+ expect ( text ( el ) ) . toContain ( "Check the export" ) ;
458+ expect ( text ( el ) ) . toContain ( "Experience \u2192 Staff Engineer \u00b7 Acme \u2192 bullet 3" ) ;
459+ expect ( text ( el ) ) . toContain ( "\u2605" ) ;
460+ // And it says the file arrived — a finding is not a refusal.
461+ expect ( text ( el ) ) . toContain ( "Your PDF downloaded" ) ;
462+ } ) ;
463+
464+ it ( "counts the overflow instead of listing forty rows" , ( ) => {
465+ pdfFindings = Array . from ( { length : 8 } , ( _ , i ) => ( {
466+ kind : "glyph-degraded" as const ,
467+ severity : "info" as const ,
468+ sourceField : `Experience \u2192 Role ${ i + 1 } ` ,
469+ detail : 'The export font has no glyph for "\u2192", so it was drawn as "->".' ,
470+ } ) ) ;
471+ const el = render ( exportable ( ) ) ;
472+ expect ( el . querySelectorAll ( "li" ) ) . toHaveLength ( 5 ) ;
473+ expect ( text ( el ) ) . toContain ( "and 3 more" ) ;
474+ } ) ;
475+ } ) ;
424476} ) ;
0 commit comments