Skip to content
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: add tests for check markdown script #3378

Merged
merged 60 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
5a90f49
initial setup
vishvamsinh28 Nov 9, 2024
5373c9f
tests updated
vishvamsinh28 Nov 9, 2024
a30cef3
test updated again
vishvamsinh28 Nov 9, 2024
a9da240
fwqfwef
vishvamsinh28 Nov 9, 2024
1079101
Merge branch 'master' into markdownTest
vishvamsinh28 Nov 10, 2024
86201ed
use async await
vishvamsinh28 Nov 10, 2024
7c44f4a
tests updated
vishvamsinh28 Nov 10, 2024
c98ecdd
wqegeg
vishvamsinh28 Nov 10, 2024
6e34205
test updated
vishvamsinh28 Nov 10, 2024
4445ce5
Merge branch 'master' into markdownTest
vishvamsinh28 Nov 11, 2024
333e0b2
feg
vishvamsinh28 Nov 11, 2024
8849351
Fixed variable shadowing
vishvamsinh28 Nov 11, 2024
1fbac7c
Merge branch 'master' into markdownTest
vishvamsinh28 Nov 13, 2024
fada5ab
added tests for continue
vishvamsinh28 Nov 13, 2024
68cd3ee
workflow re-run
vishvamsinh28 Nov 14, 2024
998af5a
workflow update
vishvamsinh28 Nov 14, 2024
f5c8964
ff
vishvamsinh28 Nov 14, 2024
d92b519
Merge branch 'master' into markdownTest
vishvamsinh28 Nov 15, 2024
d0fe33a
Merge branch 'master' into markdownTest
vishvamsinh28 Nov 18, 2024
216a511
Merge branch 'master' into markdownTest
vishvamsinh28 Nov 22, 2024
8a5235b
Merge branch 'master' into markdownTest
vishvamsinh28 Nov 23, 2024
a93b45d
suggetion applied
vishvamsinh28 Nov 23, 2024
2732f0f
suggetion applied
vishvamsinh28 Nov 23, 2024
4fcdea8
suggetion applied 2
vishvamsinh28 Nov 23, 2024
e1a16c1
test added for err
vishvamsinh28 Nov 23, 2024
7401e7f
Merge branch 'master' into markdownTest
vishvamsinh28 Nov 28, 2024
0fbdc06
main function added
vishvamsinh28 Nov 28, 2024
02e221b
added test for main function
vishvamsinh28 Nov 28, 2024
b17b83e
tests updated
vishvamsinh28 Nov 28, 2024
35d2e84
jgk
vishvamsinh28 Nov 28, 2024
7921604
test updated
vishvamsinh28 Nov 28, 2024
9d75691
Merge branch 'master' into markdownTest
vishvamsinh28 Nov 29, 2024
b36972d
Merge branch 'markdownTest' of https://github.com/vishvamsinh28/websi…
vishvamsinh28 Nov 29, 2024
dff6ccc
main function test added
vishvamsinh28 Nov 29, 2024
53c86fd
test updated
vishvamsinh28 Nov 29, 2024
92cc885
test updates
vishvamsinh28 Nov 29, 2024
871e305
Merge branch 'master' into markdownTest
vishvamsinh28 Nov 29, 2024
c0afa97
script updated
vishvamsinh28 Nov 29, 2024
10c1ba5
add tests for main function
vishvamsinh28 Nov 29, 2024
2fa09e6
test updated
vishvamsinh28 Nov 29, 2024
aa0e241
status code upudated
vishvamsinh28 Nov 29, 2024
746dc88
test updated
vishvamsinh28 Nov 29, 2024
4558748
Merge branch 'markdownTest' of https://github.com/vishvamsinh28/websi…
vishvamsinh28 Nov 29, 2024
29cd78c
script updated
vishvamsinh28 Nov 29, 2024
e50392c
Merge branch 'master' into markdownTest
vishvamsinh28 Nov 30, 2024
7a4b871
Merge branch 'master' into markdownTest
vishvamsinh28 Dec 2, 2024
d993379
Merge branch 'master' into markdownTest
vishvamsinh28 Dec 4, 2024
8d9221b
apply nitpicks
vishvamsinh28 Dec 4, 2024
f595339
coverage udpated
vishvamsinh28 Dec 4, 2024
1cb57f0
Merge branch 'master' into markdownTest
vishvamsinh28 Dec 5, 2024
4ee6c48
Merge branch 'master' into markdownTest
vishvamsinh28 Dec 7, 2024
04127eb
Merge branch 'master' into markdownTest
vishvamsinh28 Dec 7, 2024
bcaf621
remove nested try/catch
vishvamsinh28 Dec 8, 2024
dd802c4
Merge branch 'master' into markdownTest
vishvamsinh28 Dec 9, 2024
1a46eda
Merge branch 'master' into markdownTest
vishvamsinh28 Dec 9, 2024
3a9916c
Merge branch 'master' into markdownTest
vishvamsinh28 Dec 10, 2024
cc23800
apply nitpicks
vishvamsinh28 Dec 10, 2024
486b4af
Merge branch 'master' into markdownTest
vishvamsinh28 Dec 12, 2024
0a91465
Merge branch 'master' into markdownTest
vishvamsinh28 Dec 13, 2024
17dab5c
Merge branch 'master' into markdownTest
vishvamsinh28 Dec 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 37 additions & 20 deletions scripts/markdown/check-markdown.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const fs = require('fs');
const fs = require('fs').promises;
const matter = require('gray-matter');
akshatnema marked this conversation as resolved.
Show resolved Hide resolved
const path = require('path');

Expand Down Expand Up @@ -98,14 +98,10 @@ function validateDocs(frontmatter) {
* @param {Function} validateFunction - The function used to validate the frontmatter.
* @param {string} [relativePath=''] - The relative path of the folder for logging purposes.
*/
function checkMarkdownFiles(folderPath, validateFunction, relativePath = '') {
fs.readdir(folderPath, (err, files) => {
if (err) {
console.error('Error reading directory:', err);
return;
}

files.forEach(file => {
async function checkMarkdownFiles(folderPath, validateFunction, relativePath = '') {
try {
const files = await fs.readdir(folderPath);
const filePromises = files.map(async (file) => {
const filePath = path.join(folderPath, file);
const relativeFilePath = path.join(relativePath, file);

Expand All @@ -114,17 +110,14 @@ function checkMarkdownFiles(folderPath, validateFunction, relativePath = '') {
return;
}

fs.stat(filePath, (err, stats) => {
if (err) {
console.error('Error reading file stats:', err);
return;
}
try {
const stats = await fs.stat(filePath);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why there are nesting try/catch in this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the outer block manages directory-level issues like reading or iterating through folders, while the inner block handles file-specific errors such as reading markdown files

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But a single try catch can handle multiple errors inside that block, so you should have a single try catch block only.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UPDATED


// Recurse if directory, otherwise validate markdown file
if (stats.isDirectory()) {
checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
await checkMarkdownFiles(filePath, validateFunction, relativeFilePath);
} else if (path.extname(file) === '.md') {
const fileContent = fs.readFileSync(filePath, 'utf-8');
const fileContent = await fs.readFile(filePath, 'utf-8');
const { data: frontmatter } = matter(fileContent);

const errors = validateFunction(frontmatter);
Expand All @@ -134,13 +127,37 @@ function checkMarkdownFiles(folderPath, validateFunction, relativePath = '') {
process.exitCode = 1;
}
}
});
} catch (err) {
console.error(`Error processing file ${relativeFilePath}:`, err);
throw err;
}
});
});

await Promise.all(filePromises);
} catch (err) {
console.error(`Error in directory ${folderPath}:`, err);
throw err;
}
}

const docsFolderPath = path.resolve(__dirname, '../../markdown/docs');
const blogsFolderPath = path.resolve(__dirname, '../../markdown/blog');

checkMarkdownFiles(docsFolderPath, validateDocs);
checkMarkdownFiles(blogsFolderPath, validateBlogs);
async function main() {
try {
await Promise.all([
checkMarkdownFiles(docsFolderPath, validateDocs),
checkMarkdownFiles(blogsFolderPath, validateBlogs)
]);
} catch (error) {
console.error('Failed to validate markdown files:', error);
process.exit(1);
}
}

/* istanbul ignore next */
if (require.main === module) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this comment is added?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is added to exclude the if block beneath it from coverage reporting. This block ensures that the script runs its main() function only when executed directly (and not when imported as a module). Without this check, the main() function would execute automatically when the script is imported during testing.

main();
}

module.exports = { validateBlogs, validateDocs, checkMarkdownFiles, main, isValidURL }
150 changes: 150 additions & 0 deletions tests/markdown/check-markdown.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
const fs = require('fs').promises;
const path = require('path');
const os = require('os');
const {
isValidURL,
main,
validateBlogs,
validateDocs,
checkMarkdownFiles
} = require('../../scripts/markdown/check-markdown');

describe('Frontmatter Validator', () => {
let tempDir;
let mockConsoleError;
let mockProcessExit;

beforeEach(async () => {
mockConsoleError = jest.spyOn(console, 'error').mockImplementation();
mockProcessExit = jest.spyOn(process, 'exit').mockImplementation();
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'test-config'));
});

afterEach(async () => {
mockConsoleError.mockRestore();
mockProcessExit.mockRestore();
await fs.rm(tempDir, { recursive: true, force: true });
});

it('validates authors array and returns specific errors', async () => {
const frontmatter = {
title: 'Test Blog',
date: '2024-01-01',
type: 'blog',
tags: ['test'],
cover: 'cover.jpg',
authors: [{ name: 'John' }, { photo: 'jane.jpg' }, { name: 'Bob', photo: 'bob.jpg', link: 'not-a-url' }]
};

const errors = validateBlogs(frontmatter);
expect(errors).toEqual(expect.arrayContaining([
'Author at index 0 is missing a photo',
'Author at index 1 is missing a name',
'Invalid URL for author at index 2: not-a-url'
]));
});

it('validates docs frontmatter for required fields', async () => {
const frontmatter = { title: 123, weight: 'not-a-number' };
const errors = validateDocs(frontmatter);
expect(errors).toEqual(expect.arrayContaining([
'Title is missing or not a string',
'Weight is missing or not a number'
]));
});

it('checks for errors in markdown files in a directory', async () => {
await fs.writeFile(path.join(tempDir, 'invalid.md'), `---\ntitle: Invalid Blog\n---`);
const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation();

await checkMarkdownFiles(tempDir, validateBlogs);

expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('Errors in file invalid.md:'));
mockConsoleLog.mockRestore();
});

it('returns multiple validation errors for invalid blog frontmatter', async () => {
const frontmatter = {
title: 123,
date: 'invalid-date',
type: 'blog',
tags: 'not-an-array',
cover: ['not-a-string'],
authors: { name: 'John Doe' }
};
const errors = validateBlogs(frontmatter);

expect(errors).toEqual([
"Invalid date format: invalid-date",
"Tags should be an array",
"Cover must be a string",
"Authors should be an array"]);
});

it('logs error to console when an error occurs in checkMarkdownFiles', async () => {
const invalidFolderPath = path.join(tempDir, 'non-existent-folder');

await expect(checkMarkdownFiles(invalidFolderPath, validateBlogs))
.rejects.toThrow('ENOENT');

expect(mockConsoleError.mock.calls[0][0]).toContain('Error in directory');
});

it('skips the "reference/specification" folder during validation', async () => {
const referenceSpecDir = path.join(tempDir, 'reference', 'specification');
await fs.mkdir(referenceSpecDir, { recursive: true });
await fs.writeFile(path.join(referenceSpecDir, 'skipped.md'), `---\ntitle: Skipped File\n---`);

const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation();

await checkMarkdownFiles(tempDir, validateDocs);

expect(mockConsoleLog).not.toHaveBeenCalledWith(expect.stringContaining('Errors in file reference/specification/skipped.md'));
mockConsoleLog.mockRestore();
});

it('logs and rejects when an exception occurs while processing a file', async () => {
const filePath = path.join(tempDir, 'invalid.md');
await fs.writeFile(filePath, `---\ntitle: Valid Title\n---`);

const mockReadFile = jest.spyOn(fs, 'readFile').mockRejectedValue(new Error('Test readFile error'));

await expect(checkMarkdownFiles(tempDir, validateBlogs)).rejects.toThrow('Test readFile error');
expect(mockConsoleError).toHaveBeenCalledWith(
expect.stringContaining(`Error processing file invalid.md:`),
expect.any(Error)
);

mockReadFile.mockRestore();
});

it('should handle main function errors and exit with status 1', async () => {
jest.spyOn(fs, 'readdir').mockRejectedValue(new Error('Test error'));

await main();

expect(mockProcessExit).toHaveBeenCalledWith(1);

expect(mockConsoleError).toHaveBeenCalledWith(
'Failed to validate markdown files:',
expect.any(Error)
);
});

it('should handle successful main function execution', async () => {

await main();

expect(mockConsoleError).not.toHaveBeenCalledWith()
});
akshatnema marked this conversation as resolved.
Show resolved Hide resolved

it('should return true or false for URLs', () => {
expect(isValidURL('http://example.com')).toBe(true);
expect(isValidURL('https://www.example.com')).toBe(true);
expect(isValidURL('ftp://ftp.example.com')).toBe(true);
expect(isValidURL('invalid-url')).toBe(false);
expect(isValidURL('/path/to/file')).toBe(false);
expect(isValidURL('www.example.com')).toBe(false);
});

});
Loading