aboutsummaryrefslogtreecommitdiffstats
path: root/frontend-vanilla/src/regression.test.ts
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-17 07:57:52 -0800
committerGitHub <noreply@github.com>2026-02-17 07:57:52 -0800
commit5c3b6234caf8b6c27f37d67d4e04c853e59888ef (patch)
tree3abc994bd8ac3699449cf37ca25ce34610657588 /frontend-vanilla/src/regression.test.ts
parentc15995fe944a6e8f3e68cf0c44fd454e53f21081 (diff)
parent7f0b9ae0f53f26304d26a8d45191f268821425c8 (diff)
downloadneko-5c3b6234caf8b6c27f37d67d4e04c853e59888ef.tar.gz
neko-5c3b6234caf8b6c27f37d67d4e04c853e59888ef.tar.bz2
neko-5c3b6234caf8b6c27f37d67d4e04c853e59888ef.zip
Merge pull request #9 from adammathes/claude/fix-open-tickets-IVV1C
Update benchmarks, fix SSRF proxy bypass, and refactor frontend sidebar layout
Diffstat (limited to 'frontend-vanilla/src/regression.test.ts')
-rw-r--r--frontend-vanilla/src/regression.test.ts92
1 files changed, 91 insertions, 1 deletions
diff --git a/frontend-vanilla/src/regression.test.ts b/frontend-vanilla/src/regression.test.ts
index a0b13d5..8529e20 100644
--- a/frontend-vanilla/src/regression.test.ts
+++ b/frontend-vanilla/src/regression.test.ts
@@ -1,7 +1,8 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { store } from './store';
import { apiFetch } from './api';
-import { renderItems } from './main';
+import { renderItems, renderLayout } from './main';
+import { createFeedItem } from './components/FeedItem';
// Mock api
vi.mock('./api', () => ({
@@ -167,3 +168,92 @@ describe('Scroll-to-Read Regression Tests', () => {
expect(apiFetch).not.toHaveBeenCalledWith(expect.stringContaining('/api/item/888'), expect.anything());
});
});
+
+// NK-t8qnrh: Links in feed item descriptions should have no underlines (match v1 style)
+describe('NK-t8qnrh: Feed item description links have no underlines', () => {
+ it('item-description should be rendered inside feed items', () => {
+ const item = {
+ _id: 1,
+ title: 'Test',
+ url: 'http://example.com',
+ description: '<p>Text with <a href="http://example.com">a link</a></p>',
+ read: false,
+ starred: false,
+ publish_date: '2024-01-01',
+ } as any;
+ const html = createFeedItem(item);
+ expect(html).toContain('class="item-description"');
+ expect(html).toContain('<a href="http://example.com">a link</a>');
+ });
+});
+
+// NK-mcl01m: Sidebar order should be filters → search → "+ new" → Feeds → Tags
+describe('NK-mcl01m: Sidebar section order', () => {
+ beforeEach(() => {
+ document.body.innerHTML = '<div id="app"></div>';
+ vi.mocked(apiFetch).mockResolvedValue({ ok: true, status: 200, json: async () => [] } as Response);
+ renderLayout();
+ });
+
+ it('filter-list appears before section-feeds in the sidebar', () => {
+ const sidebar = document.getElementById('sidebar');
+ expect(sidebar).not.toBeNull();
+ const filterList = sidebar!.querySelector('#filter-list');
+ const sectionFeeds = sidebar!.querySelector('#section-feeds');
+ expect(filterList).not.toBeNull();
+ expect(sectionFeeds).not.toBeNull();
+ // filter-list should come before section-feeds in DOM order
+ const position = filterList!.compareDocumentPosition(sectionFeeds!);
+ expect(position & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
+ });
+
+ it('section-feeds appears before section-tags in the sidebar', () => {
+ const sidebar = document.getElementById('sidebar');
+ const sectionFeeds = sidebar!.querySelector('#section-feeds');
+ const sectionTags = sidebar!.querySelector('#section-tags');
+ expect(sectionFeeds).not.toBeNull();
+ expect(sectionTags).not.toBeNull();
+ // feeds should come before tags
+ const position = sectionFeeds!.compareDocumentPosition(sectionTags!);
+ expect(position & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
+ });
+
+ it('search input appears after filter-list and before section-feeds', () => {
+ const sidebar = document.getElementById('sidebar');
+ const filterList = sidebar!.querySelector('#filter-list');
+ const searchInput = sidebar!.querySelector('#search-input');
+ const sectionFeeds = sidebar!.querySelector('#section-feeds');
+ expect(searchInput).not.toBeNull();
+ // search after filters
+ const pos1 = filterList!.compareDocumentPosition(searchInput!);
+ expect(pos1 & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
+ // search before feeds
+ const pos2 = searchInput!.compareDocumentPosition(sectionFeeds!);
+ expect(pos2 & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
+ });
+
+ it('sidebar has a "+ new" link pointing to settings', () => {
+ const newLink = document.querySelector('.new-feed-link');
+ expect(newLink).not.toBeNull();
+ expect(newLink!.textContent?.trim()).toBe('+ new');
+ });
+});
+
+// NK-z1czaq: Main content should fill full width (sidebar overlays, never shifts content)
+describe('NK-z1czaq: Sidebar overlays content, does not shift layout', () => {
+ beforeEach(() => {
+ document.body.innerHTML = '<div id="app"></div>';
+ vi.mocked(apiFetch).mockResolvedValue({ ok: true, status: 200, json: async () => [] } as Response);
+ });
+
+ it('sidebar is a sibling of main-content inside .layout (not flex-shifting)', () => {
+ renderLayout();
+ const sidebar = document.querySelector('.sidebar');
+ const mainContent = document.querySelector('.main-content');
+ expect(sidebar).not.toBeNull();
+ expect(mainContent).not.toBeNull();
+ // Both should be children of .layout
+ expect(sidebar!.parentElement?.classList.contains('layout')).toBe(true);
+ expect(mainContent!.parentElement?.classList.contains('layout')).toBe(true);
+ });
+});