diff --git a/tests/build-rss.test.js b/tests/build-rss.test.js index 67e042252f00..c2d43a5b7df1 100644 --- a/tests/build-rss.test.js +++ b/tests/build-rss.test.js @@ -3,28 +3,20 @@ const path = require('path'); const rssFeed = require('../scripts/build-rss'); const { XMLParser } = require('fast-xml-parser'); const parser = new XMLParser({ ignoreAttributes: false }); -const { mockRssData, title, type, desc, missingDateMockData, incompletePostMockData } = require('./fixtures/rssData'); +const { createMockData } = require('./fixtures/rssData'); describe('rssFeed', () => { const testOutputDir = path.join(__dirname, '..', 'public', 'test-output'); const outputPath = 'test-output/rss.xml'; beforeAll(async () => { - try { - await fs.promises.mkdir(testOutputDir, { recursive: true }); - } catch (err) { - throw new Error(`Error while creating temp dir: ${err.message}`); - } + await fs.promises.mkdir(testOutputDir, { recursive: true }); }); afterAll(async () => { - try { - const files = await fs.promises.readdir(testOutputDir); - await Promise.all(files.map(file => fs.promises.unlink(path.join(testOutputDir, file)))); - await fs.promises.rmdir(testOutputDir); - } catch (err) { - throw new Error(`Error while deleting temp dir: ${err.message}`); - } + const files = await fs.promises.readdir(testOutputDir); + await Promise.all(files.map(file => fs.promises.unlink(path.join(testOutputDir, file)))); + await fs.promises.rmdir(testOutputDir); }); afterEach(() => { @@ -32,9 +24,16 @@ describe('rssFeed', () => { }); it('should generate RSS feed and write to file', async () => { - jest.doMock('../config/posts.json', () => mockRssData, { virtual: true }); + const mockData = createMockData({ + blogPosts: [ + { title: 'Featured Post 1', slug: '/blog/featured-post-1', date: '2023-07-05', featured: true, cover: '/img/cover1.jpg' }, + { title: 'Non-Featured Post 1', slug: '/blog/non-featured-post-1', date: '2023-07-04', featured: false }, + ], + }); - await expect(rssFeed(type, title, desc, outputPath)).resolves.toBeUndefined() + jest.doMock('../config/posts.json', () => mockData, { virtual: true }); + + await expect(rssFeed('blog', 'Test Blog RSS', 'Test blog RSS feed', outputPath)).resolves.toBeUndefined(); const filePath = path.join(__dirname, '..', 'public', outputPath); expect(fs.existsSync(filePath)).toBe(true); @@ -43,83 +42,51 @@ describe('rssFeed', () => { }); it('should prioritize featured posts over non-featured ones', async () => { - jest.doMock('../config/posts.json', () => mockRssData, { virtual: true }); - - await expect(rssFeed(type, title, desc, outputPath)).resolves.toBeUndefined(); - - const filePath = path.join(__dirname, '..', 'public', outputPath); - const fileContent = fs.readFileSync(filePath, 'utf8'); - - const parsedContent = parser.parse(fileContent); - const itemTitles = parsedContent.rss.channel.item.map(item => item.title); - - expect(itemTitles[0]).toBe('Test Post 1'); - expect(itemTitles[1]).toBe('Another Featured Post'); - - expect(itemTitles[2]).toBe('Post with Special Characters: & < > "'); - expect(itemTitles[3]).toBe('Post with UTC Date Format'); - expect(itemTitles[4]).toBe('Non-Featured Post 1'); - expect(itemTitles[5]).toBe('Non-Featured Post 3'); - }); - - it('should sort posts by date in descending order', async () => { - jest.doMock('../config/posts.json', () => mockRssData, { virtual: true }); - - await expect(rssFeed(type, title, desc, outputPath)).resolves.toBeUndefined(); - - const filePath = path.join(__dirname, '..', 'public', outputPath); - const fileContent = fs.readFileSync(filePath, 'utf8'); - - const parsedContent = parser.parse(fileContent); - const itemTitles = parsedContent.rss.channel.item.map(item => item.title); - - expect(itemTitles[0]).toBe('Test Post 1'); - expect(itemTitles[1]).toBe('Another Featured Post') - expect(itemTitles[2]).toBe('Post with Special Characters: & < > "'); - expect(itemTitles[3]).toBe('Post with UTC Date Format'); - expect(itemTitles[4]).toBe('Non-Featured Post 1'); - expect(itemTitles[5]).toBe('Non-Featured Post 3'); - }); + const mockData = createMockData({ + blogPosts: [ + { title: 'Non-Featured Post 1', slug: '/blog/non-featured-post-1', date: '2023-07-04', featured: false }, + { title: 'Featured Post 1', slug: '/blog/featured-post-1', date: '2023-07-05', featured: true }, + ], + }); - it('should set correct enclosure type based on image extension', async () => { - jest.doMock('../config/posts.json', () => mockRssData, { virtual: true }); + jest.doMock('../config/posts.json', () => mockData, { virtual: true }); - await expect(rssFeed(type, title, desc, outputPath)).resolves.toBeUndefined() + await expect(rssFeed('blog', 'Test Blog RSS', 'Test blog RSS feed', outputPath)).resolves.toBeUndefined(); const filePath = path.join(__dirname, '..', 'public', outputPath); const fileContent = fs.readFileSync(filePath, 'utf8'); + const parsedContent = parser.parse(fileContent); + const itemTitles = parsedContent.rss.channel.item.map(item => item.title); - expect(fileContent).toContain(' { - jest.doMock('../config/posts.json', () => mockRssData, { virtual: true }); - - const invalidOutputPath = "invalid/path"; - - await expect(rssFeed(type, title, desc, invalidOutputPath)).rejects.toThrow(/ENOENT|EACCES/); + it('should sort posts by date in descending order', async () => { + const mockData = createMockData({ + blogPosts: [ + { title: 'Post with Newer Date', slug: '/blog/newer-date', date: '2023-07-06', featured: false }, + { title: 'Post with Older Date', slug: '/blog/older-date', date: '2023-07-04', featured: false }, + ], + }); - }); + jest.doMock('../config/posts.json', () => mockData, { virtual: true }); - it('should throw an error when posts.json is malformed', async () => { - jest.doMock('../config/posts.json', () => { - return { invalidKey: [] }; - }, { virtual: true }); + await expect(rssFeed('blog', 'Test Blog RSS', 'Test blog RSS feed', outputPath)).resolves.toBeUndefined(); - await expect(rssFeed(type, title, desc, outputPath)).rejects.toThrow('Failed to generate RSS feed'); + const filePath = path.join(__dirname, '..', 'public', outputPath); + const fileContent = fs.readFileSync(filePath, 'utf8'); + const parsedContent = parser.parse(fileContent); + const itemTitles = parsedContent.rss.channel.item.map(item => item.title); + expect(itemTitles[0]).toBe('Post with Newer Date'); + expect(itemTitles[1]).toBe('Post with Older Date'); }); it('should handle empty posts array', async () => { - const emptyMockData = { blog: [] }; + const emptyMockData = createMockData({ blogPosts: [] }); jest.doMock('../config/posts.json', () => emptyMockData, { virtual: true }); - await expect(rssFeed(type, title, desc, outputPath)).resolves.toBeUndefined() + await expect(rssFeed('blog', 'Test Blog RSS', 'Test blog RSS feed', outputPath)).resolves.toBeUndefined(); const filePath = path.join(__dirname, '..', 'public', outputPath); const fileContent = fs.readFileSync(filePath, 'utf8'); @@ -128,18 +95,14 @@ describe('rssFeed', () => { }); it('should throw an error when post is missing required fields', async () => { + const mockData = createMockData({ + blogPosts: [ + { slug: '/blog/incomplete-post', date: '2024-07-05', featured: false }, + ], + }, false); - jest.doMock('../config/posts.json', () => incompletePostMockData, { virtual: true }); - - await expect(rssFeed(type, title, desc, outputPath)).rejects.toThrow('Missing required fields'); - - }); - - it('should throw an error when a post is missing a date field during sorting', async () => { - - jest.doMock('../config/posts.json', () => missingDateMockData, { virtual: true }); - - await expect(rssFeed(type, title, desc, outputPath)).rejects.toThrow('Failed to generate RSS feed: Missing date in posts: Post without Date'); + jest.doMock('../config/posts.json', () => mockData, { virtual: true }); + await expect(rssFeed('blog', 'Test Blog RSS', 'Test blog RSS feed', outputPath)).rejects.toThrow('Missing required fields'); }); }); diff --git a/tests/fixtures/rssData.js b/tests/fixtures/rssData.js index 89717784e51b..7f4fb5f70516 100644 --- a/tests/fixtures/rssData.js +++ b/tests/fixtures/rssData.js @@ -1,93 +1,40 @@ -const mockRssData = { - blog: [ - { - title: 'Non-Featured Post 1', - slug: '/blog/non-featured-post-1', - excerpt: 'This is a non-featured post', - date: '2023-07-05', - featured: false, - }, - { - title: 'Test Post 1', - slug: '/blog/test-post-1', - excerpt: 'This is a featured test post', - date: '2023-07-07', - featured: true, - cover: '/img/test-cover.jpg', - }, - { - title: 'Another Featured Post', - slug: '/blog/another-featured-post', - excerpt: 'This is another featured post', - date: '2023-07-06', - featured: true, - cover: '/img/test-cover.svg', - }, - { - title: 'Non-Featured Post 2', - slug: '/blog/non-featured-post-2', - excerpt: 'This is another non-featured post', - date: '2023-07-03', - featured: false, - cover: '/img/test-cover.webp', - }, - { - title: 'Non-Featured Post 3', - slug: '/blog/non-featured-post-3', - excerpt: 'This is yet another non-featured post', - date: '2023-07-04', - featured: false, - cover: '/img/test-cover.png', - }, - { - title: 'Post with Special Characters: & < > "', - slug: '/blog/special-chars', - excerpt: 'Testing HTML entities & encoding', - date: '2023-07-06T12:00:00Z', - featured: false, - }, - { - title: 'Post with UTC Date Format', - slug: '/blog/utc-date-format', - excerpt: 'This post uses a UTC date format', - date: 'Wed, 05 Jul 2023 12:00:00 GMT', - featured: false, - }, - ], -}; +function createMockPost({ + title, + slug, + excerpt, + date, + featured, + cover, +} = {}, useDefaults = true) { + const post = {}; -const missingDateMockData = { - blog: [ - { - title: 'Post without Date', - slug: '/blog/no-date-post', - excerpt: 'This post is missing a date', - featured: false, - }, - { - title: 'Valid Post', - slug: '/blog/valid-post', - excerpt: 'This post has a valid date', - date: '2024-07-05', - featured: true, - }, - ], -}; + if (useDefaults) { + post.title = title ?? 'Default Post Title'; + post.slug = slug ?? '/blog/default-post'; + post.excerpt = excerpt ?? 'This is a default excerpt'; + post.date = date ?? new Date().toISOString(); + post.featured = featured ?? false; + } else { + if (title !== undefined) post.title = title; + if (slug !== undefined) post.slug = slug; + if (excerpt !== undefined) post.excerpt = excerpt; + if (date !== undefined) post.date = date; + if (featured !== undefined) post.featured = featured; + } -const incompletePostMockData = { - blog: [ - { - slug: '/blog/incomplete-post', - excerpt: 'This post is incomplete', - date: '2024-07-05', - featured: false, - }, - ], -}; + if (cover !== undefined) post.cover = cover; + return post; +} -const type = 'blog'; -const title = 'Test Blog RSS'; -const desc = 'Test blog RSS feed'; -const outputPath = 'test-output/blog.xml'; +function createMockData({ + blogPosts = [], +} = {}, useDefaults = true) { + return { + blog: blogPosts.map(post => createMockPost(post, useDefaults)), + }; +} -module.exports = { mockRssData, title, type, desc, outputPath, missingDateMockData, incompletePostMockData }; +module.exports = { + createMockPost, + createMockData, +};