summaryrefslogtreecommitdiff
path: root/internal/api/plantoeat_test.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-01-26 16:49:44 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-01-26 16:49:44 -1000
commit42a4e32daca13b518e64e5821080ff3d6adf0e39 (patch)
tree639c790e25b961ecf51ab6ea75206bc3432f1548 /internal/api/plantoeat_test.go
parent8de1b5cb8915ed9a6e32566431d05fafafeb338d (diff)
Use configured timezone throughout codebase
- Add config/timezone.go with timezone utilities: - SetDisplayTimezone(), GetDisplayTimezone() - Now(), Today() - current time/date in display TZ - ParseDateInDisplayTZ(), ToDisplayTZ() - parsing helpers - Initialize timezone at startup in main.go - Update all datetime logic to use configured timezone: - handlers/handlers.go - all time.Now() calls - handlers/timeline.go - date parsing - handlers/timeline_logic.go - now calculation - models/atom.go - ComputeUIFields() - models/timeline.go - ComputeDaySection() - api/plantoeat.go - meal date parsing - api/todoist.go - due date parsing - api/trello.go - due date parsing This ensures all dates/times display correctly regardless of server timezone setting. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'internal/api/plantoeat_test.go')
-rw-r--r--internal/api/plantoeat_test.go126
1 files changed, 126 insertions, 0 deletions
diff --git a/internal/api/plantoeat_test.go b/internal/api/plantoeat_test.go
new file mode 100644
index 0000000..50ad4ed
--- /dev/null
+++ b/internal/api/plantoeat_test.go
@@ -0,0 +1,126 @@
+package api
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestParseShoppingListHTML(t *testing.T) {
+ // Sample HTML structure matching PlanToEat's shopping list
+ html := `
+ <div id="shopping_items" data-total-items="3">
+ <div id="store-0" class="store" data-store-id="0">
+ <div class="store_heading">
+ <h4 class="store_name">Costco (3)</h4>
+ </div>
+ <table>
+ <tr>
+ <td class="column">
+ <div class="category-box" id="cat01_350">
+ <p class="category-title"><span>Baking</span></p>
+ <ul class="ingredients">
+ <li class="sli noselect i493745871" data-store-id="0">
+ <label class="checkbox-label">
+ <input type="checkbox" name="items[493745871]" value="493745871">
+ <strong>Olive oil</strong> <span class="quan">2 tablespoons</span>
+ </label>
+ </li>
+ <li class="sli noselect i493745872 checked" data-store-id="0">
+ <label class="checkbox-label">
+ <input type="checkbox" name="items[493745872]" value="493745872" checked>
+ <strong>Flour</strong> <span class="quan">1 cup</span>
+ </label>
+ </li>
+ </ul>
+ </div>
+ <div class="category-box" id="cat01_347">
+ <p class="category-title"><span>Meat</span></p>
+ <ul class="ingredients">
+ <li class="sli noselect i493745889" data-store-id="0">
+ <label class="checkbox-label">
+ <input type="checkbox" name="items[493745889]" value="493745889">
+ <strong>Ground beef</strong> <span class="quan">1 pound</span>
+ </label>
+ </li>
+ </ul>
+ </div>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>`
+
+ items, err := parseShoppingListHTML(strings.NewReader(html))
+ if err != nil {
+ t.Fatalf("parseShoppingListHTML failed: %v", err)
+ }
+
+ if len(items) != 3 {
+ t.Errorf("Expected 3 items, got %d", len(items))
+ }
+
+ // Check first item
+ if items[0].Name != "Olive oil" {
+ t.Errorf("Expected first item name 'Olive oil', got '%s'", items[0].Name)
+ }
+ if items[0].Quantity != "2 tablespoons" {
+ t.Errorf("Expected quantity '2 tablespoons', got '%s'", items[0].Quantity)
+ }
+ if items[0].Category != "Baking" {
+ t.Errorf("Expected category 'Baking', got '%s'", items[0].Category)
+ }
+ if items[0].Store != "Costco" {
+ t.Errorf("Expected store 'Costco', got '%s'", items[0].Store)
+ }
+ if items[0].ID != "493745871" {
+ t.Errorf("Expected ID '493745871', got '%s'", items[0].ID)
+ }
+ if items[0].Checked {
+ t.Error("Expected first item to not be checked")
+ }
+
+ // Check checked item
+ if !items[1].Checked {
+ t.Error("Expected second item to be checked")
+ }
+
+ // Check item from different category
+ if items[2].Name != "Ground beef" {
+ t.Errorf("Expected 'Ground beef', got '%s'", items[2].Name)
+ }
+ if items[2].Category != "Meat" {
+ t.Errorf("Expected category 'Meat', got '%s'", items[2].Category)
+ }
+}
+
+func TestParseShoppingListHTML_EmptyList(t *testing.T) {
+ html := `<div id="shopping_items" data-total-items="0"></div>`
+
+ items, err := parseShoppingListHTML(strings.NewReader(html))
+ if err != nil {
+ t.Fatalf("parseShoppingListHTML failed: %v", err)
+ }
+
+ if len(items) != 0 {
+ t.Errorf("Expected 0 items for empty list, got %d", len(items))
+ }
+}
+
+func TestCleanQuantity(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {"1 cup", "1 cup"},
+ {"2 tablespoons", "2 tablespoons"},
+ {"1\u00a0pound", "1 pound"},
+ {" 3 oz ", "3 oz"},
+ }
+
+ for _, tt := range tests {
+ result := cleanQuantity(tt.input)
+ if result != tt.expected {
+ t.Errorf("cleanQuantity(%q) = %q, want %q", tt.input, result, tt.expected)
+ }
+ }
+}