Files
pubcli/internal/display/formatter_test.go
teernisse 73d55bc30e Add terminal display layer with lipgloss styling and JSON output
Rendering layer for deals, stores, and categories with two output
modes: styled terminal text using lipgloss (color-coded BOGO tags,
price highlights, dim metadata, word-wrapped descriptions) and
compact JSON for programmatic consumption.

JSON output types (DealJSON, StoreJSON) normalize raw API fields —
cleaning HTML entities, dereferencing nullable pointers, and
computing derived fields like isBogo. Terminal output includes
contextual headers with item counts and date ranges. Tests verify
both rendering modes including HTML entity handling and nil safety.
2026-02-22 21:41:53 -05:00

153 lines
4.3 KiB
Go

package display_test
import (
"bytes"
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tayloree/publix-deals/internal/api"
"github.com/tayloree/publix-deals/internal/display"
)
func ptr(s string) *string { return &s }
func sampleDeals() []api.SavingItem {
return []api.SavingItem{
{
ID: "1",
Title: ptr("Chicken Breasts"),
Savings: ptr("$3.99 lb"),
Description: ptr("USDA Grade A, 97% Fat Free"),
Department: ptr("Meat"),
Brand: ptr("Publix"),
Categories: []string{"meat"},
AdditionalDealInfo: ptr("SAVE UP TO $1.00 LB"),
StartFormatted: "2/18",
EndFormatted: "2/24",
},
{
ID: "2",
Title: ptr("Nutella & More"),
Savings: ptr("Buy 1 Get 1 FREE"),
Department: ptr("Grocery"),
Categories: []string{"bogo", "grocery"},
StartFormatted: "2/18",
EndFormatted: "2/24",
},
}
}
func TestPrintDeals_ContainsExpectedContent(t *testing.T) {
var buf bytes.Buffer
display.PrintDeals(&buf, sampleDeals())
output := buf.String()
assert.Contains(t, output, "Publix Weekly Deals")
assert.Contains(t, output, "2 items")
assert.Contains(t, output, "Chicken Breasts")
assert.Contains(t, output, "$3.99 lb")
assert.Contains(t, output, "SAVE UP TO $1.00 LB")
assert.Contains(t, output, "BOGO") // Nutella is bogo
// HTML entities should be unescaped
assert.Contains(t, output, "Nutella & More")
assert.NotContains(t, output, "&")
}
func TestPrintDealsJSON(t *testing.T) {
var buf bytes.Buffer
err := display.PrintDealsJSON(&buf, sampleDeals())
require.NoError(t, err)
assert.NotContains(t, buf.String(), "\n ")
var deals []display.DealJSON
err = json.Unmarshal(buf.Bytes(), &deals)
require.NoError(t, err)
assert.Len(t, deals, 2)
assert.Equal(t, "Chicken Breasts", deals[0].Title)
assert.Equal(t, "$3.99 lb", deals[0].Savings)
assert.Equal(t, "Meat", deals[0].Department)
assert.False(t, deals[0].IsBogo)
// HTML entities should be clean in JSON too
assert.Equal(t, "Nutella & More", deals[1].Title)
assert.True(t, deals[1].IsBogo)
}
func TestPrintDealsJSON_NilFields(t *testing.T) {
items := []api.SavingItem{{ID: "nil-test"}}
var buf bytes.Buffer
err := display.PrintDealsJSON(&buf, items)
require.NoError(t, err)
var deals []display.DealJSON
err = json.Unmarshal(buf.Bytes(), &deals)
require.NoError(t, err)
assert.Len(t, deals, 1)
assert.Equal(t, "", deals[0].Title)
assert.NotNil(t, deals[0].Categories)
}
func TestPrintStores(t *testing.T) {
stores := []api.Store{
{Key: "01425", Name: "Peachers Mill", Addr: "1490 Tiny Town Rd", City: "Clarksville", State: "TN", Zip: "37042", Distance: "5"},
}
var buf bytes.Buffer
display.PrintStores(&buf, stores, "37042")
output := buf.String()
assert.Contains(t, output, "37042")
assert.Contains(t, output, "#1425")
assert.Contains(t, output, "Peachers Mill")
assert.Contains(t, output, "5 miles")
}
func TestPrintStoresJSON(t *testing.T) {
stores := []api.Store{
{Key: "01425", Name: "Peachers Mill", Addr: "1490 Tiny Town Rd", City: "Clarksville", State: "TN", Zip: "37042", Distance: "5"},
}
var buf bytes.Buffer
err := display.PrintStoresJSON(&buf, stores)
require.NoError(t, err)
assert.NotContains(t, buf.String(), "\n ")
var out []display.StoreJSON
err = json.Unmarshal(buf.Bytes(), &out)
require.NoError(t, err)
assert.Len(t, out, 1)
assert.Equal(t, "1425", out[0].Number)
assert.Equal(t, "Peachers Mill", out[0].Name)
assert.Contains(t, out[0].Address, "Clarksville")
}
func TestPrintCategories(t *testing.T) {
cats := map[string]int{"bogo": 10, "meat": 5, "produce": 3}
var buf bytes.Buffer
display.PrintCategories(&buf, cats, "1425")
output := buf.String()
assert.Contains(t, output, "1425")
assert.Contains(t, output, "bogo")
assert.Contains(t, output, "10 deals")
assert.Contains(t, output, "meat")
assert.Contains(t, output, "produce")
}
func TestPrintCategoriesJSON(t *testing.T) {
cats := map[string]int{"bogo": 10, "meat": 5}
var buf bytes.Buffer
err := display.PrintCategoriesJSON(&buf, cats)
require.NoError(t, err)
assert.NotContains(t, buf.String(), "\n ")
var out map[string]int
err = json.Unmarshal(buf.Bytes(), &out)
require.NoError(t, err)
assert.Equal(t, 10, out["bogo"])
assert.Equal(t, 5, out["meat"])
}