package html_test import ( "bytes" "strings" "testing" "time" htmlwriter "github.com/nmoniz/any2anexoj/internal/writing/html" "github.com/nmoniz/any2anexoj/internal/writing/table" "github.com/nmoniz/any2anexoj/internal/writing" "github.com/nmoniz/any2anexoj/internal" "github.com/shopspring/decimal" ) // buildTestAggregator returns an AggregatorWriter seeded with two // deterministic ReportItems so every test can assert on a stable document // shape. Mirrors the fixture pattern used in // internal/writing/table/printer_test.go. func buildTestAggregator(t *testing.T) *internal.AggregatorWriter { t.Helper() aw := internal.NewAggregatorWriter() ctx := t.Context() err := aw.Write(ctx, internal.ReportItem{ Symbol: "AAPL", Nature: internal.NatureG01, BrokerCountry: 826, AssetCountry: 840, BuyValue: decimal.NewFromFloat(100.50), BuyTimestamp: time.Date(2023, 1, 15, 0, 0, 0, 0, time.UTC), SellValue: decimal.NewFromFloat(150.75), SellTimestamp: time.Date(2023, 6, 20, 0, 0, 0, 0, time.UTC), Fees: decimal.NewFromFloat(2.50), Taxes: decimal.NewFromFloat(5.00), }) if err != nil { t.Fatalf("failed to write first report item: %v", err) } err = aw.Write(ctx, internal.ReportItem{ Symbol: "GOOGL", Nature: internal.NatureG20, BrokerCountry: 826, AssetCountry: 840, BuyValue: decimal.NewFromFloat(200.00), BuyTimestamp: time.Date(2023, 3, 10, 0, 0, 0, 0, time.UTC), SellValue: decimal.NewFromFloat(225.50), SellTimestamp: time.Date(2023, 9, 5, 0, 0, 0, 0, time.UTC), Fees: decimal.NewFromFloat(3.00), Taxes: decimal.NewFromFloat(7.50), }) if err != nil { t.Fatalf("failed to write second report item: %v", err) } return aw } func TestWriter_Render(t *testing.T) { loc, err := writing.NewLocalizer("en") if err != nil { t.Fatalf("create localizer: %v", err) } var buf bytes.Buffer // RenderHTML never writes to the printer's output, so Discard is safe. pp := table.NewPrinter(&buf, loc) hw := htmlwriter.NewWriter(&buf, pp, loc, "en") if err := hw.Render(buildTestAggregator(t)); err != nil { t.Fatalf("Render returned an error: %v", err) } got := buf.String() wantSubstrings := []string{ "", ``, ``, ``, `Tax report`, `https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css`, `integrity="`, `crossorigin="anonymous"`, `
`, `

Tax report

`, `
`, ``, `
`, ``, ``, ``, `

Generated at `, } for _, s := range wantSubstrings { if !strings.Contains(got, s) { t.Errorf("rendered HTML missing expected substring %q\nGot:\n%s", s, got) } } // The table fragment must be embedded as raw HTML, not escaped. if strings.Contains(got, "<table") { t.Errorf("table fragment appears to be HTML-escaped; expected raw markup\nGot:\n%s", got) } if !strings.Contains(got, " tag in output\nGot:\n%s", got) } // Generated timestamp must be valid RFC3339 UTC. startIdx := strings.Index(got, "Generated at ") if startIdx == -1 { t.Fatalf("missing 'Generated at' prefix in output") } timestampLine := got[startIdx:] endIdx := strings.Index(timestampLine, "

") if endIdx == -1 { t.Fatalf("missing

closing tag after 'Generated at' line") } timestampStr := strings.TrimPrefix(strings.SplitN(timestampLine, "

", 2)[0], "Generated at ") if _, err := time.Parse(time.RFC3339, timestampStr); err != nil { t.Errorf("generation timestamp is not valid RFC3339: %q (err: %v)", timestampStr, err) } } // TestWriter_NoRowDuplication verifies that calling Render twice on the // same writer produces documents with the same number of
rows, just // like table.Printer's no-duplication guarantee. func TestWriter_NoRowDuplication(t *testing.T) { loc, err := writing.NewLocalizer("en") if err != nil { t.Fatalf("create localizer: %v", err) } var buf bytes.Buffer pp := table.NewPrinter(&buf, loc) hw := htmlwriter.NewWriter(&buf, pp, loc, "en") aw := buildTestAggregator(t) if err := hw.Render(aw); err != nil { t.Fatalf("first Render returned an error: %v", err) } first := buf.String() buf.Reset() if err := hw.Render(aw); err != nil { t.Fatalf("second Render returned an error: %v", err) } second := buf.String() firstRows := strings.Count(first, " row in the first render; got:\n%s", first) } if firstRows != secondRows { t.Errorf("expected second Render to produce the same number of rows as the first (no duplication)\nfirst rows=%d, second rows=%d\nFirst:\n%s\nSecond:\n%s", firstRows, secondRows, first, second) } } func TestWriter_LangAttribute(t *testing.T) { loc, err := writing.NewLocalizer("pt") if err != nil { t.Fatalf("create localizer: %v", err) } var buf bytes.Buffer pp := table.NewPrinter(&buf, loc) hw := htmlwriter.NewWriter(&buf, pp, loc, "pt") if err := hw.Render(buildTestAggregator(t)); err != nil { t.Fatalf("Render returned an error: %v", err) } got := buf.String() if !strings.Contains(got, ``) { t.Errorf("expected attribute; got:\n%s", got) } if !strings.Contains(got, `

Relatório fiscal

`) { t.Errorf("expected Portuguese title in

; got:\n%s", got) } }