feat: implement newsletter browser UI and enhance link filtering functionality
This commit is contained in:
+88
-2
@@ -33,12 +33,22 @@ function fixtureDatabase(): string {
|
||||
const db = new CatalogDatabase(path);
|
||||
db.saveCatalogRun({
|
||||
mode: 'test',
|
||||
newslettersProcessed: 1,
|
||||
linksExtracted: 1,
|
||||
newslettersProcessed: 2,
|
||||
linksExtracted: 3,
|
||||
sponsorCount: 1,
|
||||
deadLinkCount: 1,
|
||||
errors: 0,
|
||||
rows: [
|
||||
{
|
||||
'Issue Date': '2026-05-10',
|
||||
Category: 'SQLite',
|
||||
'Link URL': 'https://sqlite.example/old',
|
||||
Title: 'Older SQLite Post',
|
||||
Description: 'An older database post',
|
||||
'Page Title + Meta': '',
|
||||
'Source Newsletter': 'DB Weekly',
|
||||
'Also In': ''
|
||||
},
|
||||
{
|
||||
'Issue Date': '2026-05-17',
|
||||
Category: 'SQLite',
|
||||
@@ -48,6 +58,16 @@ function fixtureDatabase(): string {
|
||||
'Page Title + Meta': '',
|
||||
'Source Newsletter': 'DB Weekly',
|
||||
'Also In': ''
|
||||
},
|
||||
{
|
||||
'Issue Date': '2026-05-16',
|
||||
Category: 'JavaScript',
|
||||
'Link URL': 'https://js.example',
|
||||
Title: 'JS Post',
|
||||
Description: 'A JavaScript post',
|
||||
'Page Title + Meta': '',
|
||||
'Source Newsletter': 'JS Weekly',
|
||||
'Also In': ''
|
||||
}
|
||||
],
|
||||
sponsors: [
|
||||
@@ -67,13 +87,79 @@ function fixtureDatabase(): string {
|
||||
}
|
||||
|
||||
describe('web app', () => {
|
||||
it('provides newsletter summaries and latest/all issue link scopes', () => {
|
||||
const path = fixtureDatabase();
|
||||
const db = new CatalogDatabase(path);
|
||||
try {
|
||||
const summaries = db.newsletterSummaries();
|
||||
expect(
|
||||
summaries.map(({ name, latestIssueDate, issueCount, linkCount, sponsorCount }) => ({
|
||||
name,
|
||||
latestIssueDate,
|
||||
issueCount,
|
||||
linkCount,
|
||||
sponsorCount
|
||||
}))
|
||||
).toEqual([
|
||||
{
|
||||
name: 'DB Weekly',
|
||||
latestIssueDate: '2026-05-17',
|
||||
issueCount: 2,
|
||||
linkCount: 2,
|
||||
sponsorCount: 1
|
||||
},
|
||||
{
|
||||
name: 'JS Weekly',
|
||||
latestIssueDate: '2026-05-16',
|
||||
issueCount: 1,
|
||||
linkCount: 1,
|
||||
sponsorCount: 0
|
||||
}
|
||||
]);
|
||||
|
||||
const dbWeekly = summaries.find((newsletter) => newsletter.name === 'DB Weekly');
|
||||
expect(
|
||||
db.newsletterLinks(dbWeekly?.id ?? 0, { scope: 'latest' }).map((link) => link.title)
|
||||
).toEqual(['SQLite Post']);
|
||||
expect(
|
||||
db.newsletterLinks(dbWeekly?.id ?? 0, { scope: 'all' }).map((link) => link.title)
|
||||
).toEqual(['SQLite Post', 'Older SQLite Post']);
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('renders dashboard and catalog pages from SQLite', async () => {
|
||||
const path = fixtureDatabase();
|
||||
|
||||
await expect(withServer(path, '/')).resolves.toContain('Newsletter Link Catalog');
|
||||
await expect(withServer(path, '/links')).resolves.toContain('SQLite Post');
|
||||
await expect(withServer(path, '/links?q=JavaScript')).resolves.toContain('JS Post');
|
||||
await expect(withServer(path, '/links?q=JavaScript')).resolves.not.toContain('SQLite Post');
|
||||
await expect(withServer(path, '/sponsors')).resolves.toContain('Acme');
|
||||
await expect(withServer(path, '/dead-links')).resolves.toContain('https://dead.example');
|
||||
await expect(withServer(path, '/runs')).resolves.toContain('test');
|
||||
});
|
||||
|
||||
it('renders the two-pane newsletter browser with latest issue by default and all issue mode', async () => {
|
||||
const path = fixtureDatabase();
|
||||
|
||||
const latest = await withServer(path, '/newsletters');
|
||||
expect(latest).toContain('class="app-shell"');
|
||||
expect(latest).toContain('Search newsletters');
|
||||
expect(latest).toContain('aria-current="true"');
|
||||
expect(latest).toContain('Latest Issue');
|
||||
expect(latest).toContain('All Issues');
|
||||
expect(latest).toContain('SQLite Post');
|
||||
expect(latest).not.toContain('Older SQLite Post');
|
||||
expect(latest).toContain('target="_blank"');
|
||||
expect(latest).toContain('rel="noopener noreferrer"');
|
||||
|
||||
const allIssues = await withServer(path, '/newsletters?scope=all');
|
||||
expect(allIssues).toContain('SQLite Post');
|
||||
expect(allIssues).toContain('Older SQLite Post');
|
||||
expect(allIssues).toContain('2026-05-10');
|
||||
expect(allIssues).toContain('name="category"');
|
||||
expect(allIssues).toContain('name="q"');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user