-
-
Notifications
You must be signed in to change notification settings - Fork 632
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: added test for build-rss.js #3101
base: master
Are you sure you want to change the base?
Changes from 13 commits
63e8bf5
c2570b9
30f1c7f
aad7bf2
312d4c5
8d7269a
cfd547f
5859c79
44c3b63
de5edec
bf10b10
1bacff6
3ce90ca
5d8fb67
198c2a4
5f4d05b
5ab37aa
81b9845
92e86a3
0d0cb30
d7e0ac1
fc57c2a
1a8aa47
5ddb184
5e8c0c7
1eb6790
b03191d
239e65b
5e98d50
45f6ef6
e6258c5
7c9730f
6e9f039
aac6e13
0f61abe
e06fa47
bc67c28
8c93997
6e0b4ce
2631dc6
a66f2a2
a51c362
4eb10e3
4e75b0a
5892f1e
e04cfba
07b2344
d55f553
d9585fe
d41b851
d5b6882
7980458
6a93919
a5c083b
49f308a
a4903ef
1ab85ee
36e7511
1d39693
8a55fa2
2ee9f6f
6804d6e
fd0bb07
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,60 +16,63 @@ function clean(s) { | |
} | ||
|
||
module.exports = function rssFeed(type, title, desc, outputPath) { | ||
try { | ||
const posts = getAllPosts()[`${type}`] | ||
.sort((i1, i2) => { | ||
const i1Date = new Date(i1.date) | ||
const i2Date = new Date(i2.date) | ||
|
||
const posts = getAllPosts()[`${type}`] | ||
.sort((i1, i2) => { | ||
const i1Date = new Date(i1.date) | ||
const i2Date = new Date(i2.date) | ||
if (i1.featured && !i2.featured) return -1 | ||
if (!i1.featured && i2.featured) return 1 | ||
return i2Date - i1Date | ||
}) | ||
|
||
if (i1.featured && !i2.featured) return -1 | ||
if (!i1.featured && i2.featured) return 1 | ||
return i2Date - i1Date | ||
}) | ||
const base = 'https://www.asyncapi.com' | ||
const tracking = '?utm_source=rss'; | ||
|
||
const base = 'https://www.asyncapi.com' | ||
const tracking = '?utm_source=rss'; | ||
const feed = {} | ||
const rss = {} | ||
rss['@version'] = '2.0' | ||
rss["@xmlns:atom"] = 'http://www.w3.org/2005/Atom' | ||
rss.channel = {} | ||
rss.channel.title = title | ||
rss.channel.link = `${base}/${outputPath}` | ||
rss.channel["atom:link"] = {} | ||
rss.channel["atom:link"]["@rel"] = 'self' | ||
rss.channel["atom:link"]["@href"] = rss.channel.link | ||
rss.channel["atom:link"]["@type"] = 'application/rss+xml' | ||
rss.channel.description = desc | ||
rss.channel.language = 'en-gb'; | ||
rss.channel.copyright = 'Made with :love: by the AsyncAPI Initiative.'; | ||
rss.channel.webMaster = '[email protected] (AsyncAPI Initiative)' | ||
rss.channel.pubDate = new Date().toUTCString() | ||
rss.channel.generator = 'next.js' | ||
rss.channel.item = [] | ||
|
||
const feed = {} | ||
const rss = {} | ||
rss['@version'] = '2.0' | ||
rss["@xmlns:atom"] = 'http://www.w3.org/2005/Atom' | ||
rss.channel = {} | ||
rss.channel.title = title | ||
rss.channel.link = `${base}/${outputPath}` | ||
rss.channel["atom:link"] = {} | ||
rss.channel["atom:link"]["@rel"] = 'self' | ||
rss.channel["atom:link"]["@href"] = rss.channel.link | ||
rss.channel["atom:link"]["@type"] = 'application/rss+xml' | ||
rss.channel.description = desc | ||
rss.channel.language = 'en-gb'; | ||
rss.channel.copyright = 'Made with :love: by the AsyncAPI Initiative.'; | ||
rss.channel.webMaster = '[email protected] (AsyncAPI Initiative)' | ||
rss.channel.pubDate = new Date().toUTCString() | ||
rss.channel.generator = 'next.js' | ||
rss.channel.item = [] | ||
|
||
for (let post of posts) { | ||
const link = `${base}${post.slug}${tracking}`; | ||
const item = { title: post.title, description: clean(post.excerpt), link, category: type, guid: { '@isPermaLink': true, '': link }, pubDate: new Date(post.date).toUTCString() } | ||
if (post.cover) { | ||
const enclosure = {}; | ||
enclosure["@url"] = base+post.cover; | ||
enclosure["@length"] = 15026; // dummy value, anything works | ||
enclosure["@type"] = 'image/jpeg'; | ||
if (typeof enclosure["@url"] === 'string') { | ||
let tmp = enclosure["@url"].toLowerCase(); | ||
if (tmp.indexOf('.png')>=0) enclosure["@type"] = 'image/png'; | ||
if (tmp.indexOf('.svg')>=0) enclosure["@type"] = 'image/svg+xml'; | ||
if (tmp.indexOf('.webp')>=0) enclosure["@type"] = 'image/webp'; | ||
for (let post of posts) { | ||
const link = `${base}${post.slug}${tracking}`; | ||
const item = { title: post.title, description: clean(post.excerpt), link, category: type, guid: { '@isPermaLink': true, '': link }, pubDate: new Date(post.date).toUTCString() } | ||
if (post.cover) { | ||
const enclosure = {}; | ||
enclosure["@url"] = base + post.cover; | ||
enclosure["@length"] = 15026; // dummy value, anything works | ||
enclosure["@type"] = 'image/jpeg'; | ||
if (typeof enclosure["@url"] === 'string') { | ||
let tmp = enclosure["@url"].toLowerCase(); | ||
if (tmp.indexOf('.png') >= 0) enclosure["@type"] = 'image/png'; | ||
if (tmp.indexOf('.svg') >= 0) enclosure["@type"] = 'image/svg+xml'; | ||
if (tmp.indexOf('.webp') >= 0) enclosure["@type"] = 'image/webp'; | ||
} | ||
item.enclosure = enclosure; | ||
} | ||
item.enclosure = enclosure; | ||
rss.channel.item.push(item) | ||
} | ||
rss.channel.item.push(item) | ||
} | ||
|
||
feed.rss = rss | ||
feed.rss = rss | ||
|
||
const xml = json2xml.getXml(feed,'@','',2) | ||
fs.writeFileSync(`./public/${outputPath}`, xml, 'utf8') | ||
}; | ||
const xml = json2xml.getXml(feed, '@', '', 2) | ||
fs.writeFileSync(`./public/${outputPath}`, xml, 'utf8') | ||
} catch (err) { | ||
throw new Error(`Failed to generate RSS feed: ${err.message}`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instead of throwing error, can we return error as promise? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are processing the script synchronously, so returning a promise wouldn't be appropriate here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Catching of error doesn't depend on whether the function is synchronous or asynchronous. It will still return the error, based on the function return type. |
||
} | ||
}; |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, add the scenarios where the function should actually lead to an error, instead of executing it perfectly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vishvamsinh28 @anshgoyalevil Comment not addressed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll add more tests for it. 👍 I can add a test to check if the user doesn't have a posts.json file, but for that, I'll have to temporarily rename the file. Should I add that test? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, consider a scenario if data inside posts.json is in wrong manner. Then it should return error, right? Make the tests for those cases as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @akshatnema updated the test |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const rssFeed = require('../scripts/build-rss'); | ||
const { mockRssData, title, type, desc, outputPath } = require('./fixtures/rssData'); | ||
|
||
jest.mock('../config/posts.json', () => mockRssData, { virtual: true }); | ||
|
||
describe('rssFeed', () => { | ||
const testOutputDir = path.join(__dirname, '..', 'public', 'test-output'); | ||
|
||
beforeEach(() => { | ||
fs.mkdirSync(testOutputDir, { recursive: true }); | ||
}); | ||
|
||
afterEach(() => { | ||
if (fs.existsSync(testOutputDir)) { | ||
fs.readdirSync(testOutputDir).forEach(file => { | ||
fs.unlinkSync(path.join(testOutputDir, file)); | ||
}); | ||
fs.rmdirSync(testOutputDir); | ||
} | ||
}); | ||
|
||
it('should generate RSS feed and write to file', () => { | ||
rssFeed(type, title, desc, outputPath); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
const filePath = path.join(__dirname, '..', 'public', outputPath); | ||
expect(fs.existsSync(filePath)).toBe(true); | ||
const fileContent = fs.readFileSync(filePath, 'utf8'); | ||
expect(fileContent).toContain('<rss version="2.0"'); | ||
expect(fileContent).toContain('<title>Test Blog RSS</title>'); | ||
}); | ||
|
||
it('should sort posts by date and featured status', () => { | ||
rssFeed(type, title, desc, outputPath); | ||
|
||
const filePath = path.join(__dirname, '..', 'public', outputPath); | ||
const fileContent = fs.readFileSync(filePath, 'utf8'); | ||
|
||
const itemTitles = fileContent.match(/<title>(.*?)<\/title>/g); | ||
expect(itemTitles[1]).toContain('Test Post 1'); | ||
expect(itemTitles[2]).toContain('Test Post 2'); | ||
expect(itemTitles[3]).toContain('PNG Post'); | ||
expect(itemTitles[4]).toContain('SVG Post'); | ||
expect(itemTitles[5]).toContain('WebP Post'); | ||
}); | ||
|
||
it('should add enclosure for posts with cover image', () => { | ||
rssFeed(type, title, desc, outputPath); | ||
|
||
const filePath = path.join(__dirname, '..', 'public', outputPath); | ||
const fileContent = fs.readFileSync(filePath, 'utf8'); | ||
|
||
expect(fileContent).toContain('<enclosure url="https://www.asyncapi.com/img/test-cover.jpg"'); | ||
expect(fileContent).toContain('type="image/jpeg"'); | ||
}); | ||
|
||
it('should set correct enclosure type based on image extension', () => { | ||
rssFeed(type, title, desc, outputPath); | ||
|
||
const filePath = path.join(__dirname, '..', 'public', outputPath); | ||
const fileContent = fs.readFileSync(filePath, 'utf8'); | ||
|
||
expect(fileContent).toContain('<enclosure url="https://www.asyncapi.com/img/test-cover.png"'); | ||
expect(fileContent).toContain('type="image/png"'); | ||
expect(fileContent).toContain('<enclosure url="https://www.asyncapi.com/img/test-cover.svg"'); | ||
expect(fileContent).toContain('type="image/svg+xml"'); | ||
expect(fileContent).toContain('<enclosure url="https://www.asyncapi.com/img/test-cover.webp"'); | ||
expect(fileContent).toContain('type="image/webp"'); | ||
}); | ||
|
||
it('should throw error when write operation fails', () => { | ||
const outputPath = "invalid/path" | ||
try{ | ||
rssFeed(type, title, desc, outputPath) | ||
}catch(err){ | ||
expect(err.message).toMatch(/ENOENT|EACCES/); | ||
} | ||
}); | ||
|
||
}); |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we not following eslint rules inside this file? I see lot of indentation spaces here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not getting any lint errors locally, and I have removed unnecessary indentation in previous commits. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
const mockRssData = { | ||
blog: [ | ||
{ | ||
title: 'Test Post 1', | ||
slug: '/blog/test-post-1', | ||
excerpt: 'This is a test post', | ||
date: '2024-07-07', | ||
featured: true, | ||
cover: '/img/test-cover.jpg' | ||
}, | ||
{ | ||
title: 'Test Post 2', | ||
slug: '/blog/test-post-2', | ||
excerpt: 'This is another test post', | ||
date: '2024-07-06', | ||
featured: false | ||
}, | ||
{ | ||
title: 'PNG Post', | ||
slug: '/blog/png-post', | ||
excerpt: 'This is a post with PNG image', | ||
date: '2024-07-05', | ||
featured: false, | ||
cover: '/img/test-cover.png' | ||
}, | ||
{ | ||
title: 'SVG Post', | ||
slug: '/blog/svg-post', | ||
excerpt: 'This is a post with SVG image', | ||
date: '2024-07-04', | ||
featured: false, | ||
cover: '/img/test-cover.svg' | ||
}, | ||
{ | ||
title: 'WebP Post', | ||
slug: '/blog/webp-post', | ||
excerpt: 'This is a post with WebP image', | ||
date: '2024-07-03', | ||
featured: false, | ||
cover: '/img/test-cover.webp' | ||
} | ||
] | ||
}; | ||
|
||
const type = 'blog'; | ||
const title = 'Test Blog RSS'; | ||
const desc = 'Test blog RSS feed'; | ||
const outputPath = 'test-output/blog.xml'; | ||
|
||
module.exports = { mockRssData, title, type, desc, outputPath }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use mime-types package for better image type detection
Replace manual image type detection with the
mime-types
package: