From a4997a5fbc65913b55f2215eb3b868693bd76c51 Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Sat, 14 Feb 2026 10:03:35 -0800 Subject: test: increase frontend coverage for Settings and improve FeedItem css --- frontend/coverage/src/components/FeedItem.tsx.html | 380 +++++++++++++-------- 1 file changed, 238 insertions(+), 142 deletions(-) (limited to 'frontend/coverage/src/components/FeedItem.tsx.html') diff --git a/frontend/coverage/src/components/FeedItem.tsx.html b/frontend/coverage/src/components/FeedItem.tsx.html index 5512b78..6e76131 100644 --- a/frontend/coverage/src/components/FeedItem.tsx.html +++ b/frontend/coverage/src/components/FeedItem.tsx.html @@ -1,64 +1,68 @@ + - + + Code coverage report for src/components/FeedItem.tsx - - - - -
-
-

- All files / - src/components FeedItem.tsx -

-
-
- 78.94% - Statements - 15/19 -
- -
- 88.88% - Branches - 16/18 -
- -
- 85.71% - Functions - 6/7 -
- -
- 78.94% - Lines - 15/19 -
+ + + +
+
+

All files / src/components FeedItem.tsx

+
+ +
+ 78.12% + Statements + 25/32 +
+ + +
+ 86.95% + Branches + 20/23 +
+ + +
+ 83.33% + Functions + 10/12 +
+ + +
+ 80.64% + Lines + 25/31 +
+ +

- Press n or j to go to the next uncovered block, b, - p or k for the previous block. + Press n or j to go to the next uncovered block, b, p or k for the previous block.

-
-
-

+    
+    
+
1 2 3 @@ -143,7 +147,41 @@ 82 83 84 -85  +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116  +  +  +        @@ -151,16 +189,18 @@       +33x +33x   -21x -21x +33x +16x     -21x +33x 1x     -21x +33x 1x   1x @@ -196,7 +236,28 @@       -21x +33x +1x +1x +1x +  +1x +1x +  +  +1x +1x +  +  +  +  +  +  +  +33x +  +  +        @@ -227,108 +288,143 @@       - 
import { useState } from 'react';
+ 
+ 
+ 
+ 
+ 
+ 
import { useState, useEffect } from 'react';
 import type { Item } from '../types';
 import './FeedItem.css';
  
+import { apiFetch } from '../utils';
+ 
 interface FeedItemProps {
-    item: Item;
+  item: Item;
 }
  
 export default function FeedItem({ item: initialItem }: FeedItemProps) {
-    const [item, setItem] = useState(initialItem);
-    const [loading, setLoading] = useState(false);
+  const [item, setItem] = useState(initialItem);
+  const [loading, setLoading] = useState(false);
+ 
+  useEffect(() => {
+    setItem(initialItem);
+  }, [initialItem]);
  
+  const toggleStar = () => {
+    updateItem({ ...item, starred: !item.starred });
+  };
  
-    const toggleStar = () => {
-        updateItem({ ...item, starred: !item.starred });
-    };
+  const updateItem = (newItem: Item) => {
+    setLoading(true);
+    // Optimistic update
+    const previousItem = item;
+    setItem(newItem);
  
-    const updateItem = (newItem: Item) => {
-        setLoading(true);
-        // Optimistic update
-        const previousItem = item;
-        setItem(newItem);
+    apiFetch(`/api/item/${newItem._id}`, {
+      method: 'PUT',
+      headers: {
+        'Content-Type': 'application/json',
+      },
+      body: JSON.stringify({
+        _id: newItem._id,
+        read: newItem.read,
+        starred: newItem.starred,
+      }),
+    })
+      .then((res) => {
+        Iif (!res.ok) {
+          throw new Error('Failed to update item');
+        }
+        return res.json();
+      })
+      .then(() => {
+        // Confirm with server response if needed, but for now we trust the optimistic update
+        // or we could setItem(updated) if the server returns the full object
+        setLoading(false);
+      })
+      .catch((err) => {
+        console.error('Error updating item:', err);
+        // Revert on error
+        setItem(previousItem);
+        setLoading(false);
+      });
+  };
  
-        fetch(`/api/item/${newItem._id}`, {
-            method: 'PUT',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                _id: newItem._id,
-                read: newItem.read,
-                starred: newItem.starred,
-            }),
-        })
-            .then((res) => {
-                Iif (!res.ok) {
-                    throw new Error('Failed to update item');
-                }
-                return res.json();
-            })
-            .then(() => {
-                // Confirm with server response if needed, but for now we trust the optimistic update
-                // or we could setItem(updated) if the server returns the full object
-                setLoading(false);
-            })
-            .catch((err) => {
-                console.error('Error updating item:', err);
-                // Revert on error
-                setItem(previousItem);
-                setLoading(false);
-            });
-    };
+  const loadFullContent = (e: React.MouseEvent) => {
+    e.stopPropagation();
+    setLoading(true);
+    apiFetch(`/api/item/${item._id}`)
+      .then((res) => {
+        Iif (!res.ok) throw new Error('Failed to fetch full content');
+        return res.json();
+      })
+      .then((data) => {
+        setItem({ ...item, ...data });
+        setLoading(false);
+      })
+      .catch((err) => {
+        console.error('Error fetching full content:', err);
+        setLoading(false);
+      });
+  };
  
-    return (
-        <li className={`feed-item ${item.read ? 'read' : 'unread'} ${loading ? 'loading' : ''}`}>
-            <div className="item-header">
-                <button
-                    onClick={(e) => {
-                        e.stopPropagation();
-                        toggleStar();
-                    }}
-                    className={`star-btn ${item.starred ? 'is-starred' : 'is-unstarred'}`}
-                    title={item.starred ? "Unstar" : "Star"}
-                >
-                    {item.starred ? '★' : '☆'}
-                </button>
-                <a href={item.url} target="_blank" rel="noopener noreferrer" className="item-title">
-                    {item.title || '(No Title)'}
-                </a>
-            </div>
-            <div className="dateline">
-                <a href={item.url} target="_blank" rel="noopener noreferrer">
-                    {new Date(item.publish_date).toLocaleDateString()}
-                    {item.feed_title && ` - ${item.feed_title}`}
-                </a>
-                <div className="item-actions" style={{ display: 'inline-block', float: 'right' }}>
-                </div>
-            </div>
-            {item.description && (
-                <div className="item-description" dangerouslySetInnerHTML={{ __html: item.description }} />
-            )}
-        </li>
-    );
+  return (
+    <li className={`feed-item ${item.read ? 'read' : 'unread'} ${loading ? 'loading' : ''}`}>
+      <div className="item-header">
+        <a href={item.url} target="_blank" rel="noopener noreferrer" className="item-title">
+          {item.title || '(No Title)'}
+        </a>
+        <button
+          onClick={(e) => {
+            e.stopPropagation();
+            toggleStar();
+          }}
+          className={`star-btn ${item.starred ? 'is-starred' : 'is-unstarred'}`}
+          title={item.starred ? 'Unstar' : 'Star'}
+        >
+          ★
+        </button>
+      </div>
+      <div className="dateline">
+        <a href={item.url} target="_blank" rel="noopener noreferrer">
+          {new Date(item.publish_date).toLocaleDateString()}
+          {item.feed_title && ` - ${item.feed_title}`}
+        </a>
+        <div className="item-actions" style={{ display: 'inline-block', float: 'right' }}>
+          {!item.full_content && (
+            <button onClick={loadFullContent} className="scrape-btn" title="Load Full Content">
+              text
+            </button>
+          )}
+        </div>
+      </div>
+      {(item.full_content || item.description) && (
+        <div
+          className="item-description"
+          dangerouslySetInnerHTML={{ __html: item.full_content || item.description }}
+        />
+      )}
+    </li>
+  );
 }
  
-
- -
- - - - - - - +
+
+ + + + + + + \ No newline at end of file -- cgit v1.2.3