Skip to content

Commit fa2d987

Browse files
committed
Enhance PDF generation tests and add CLI command for Mocha
- Introduced a new Mocha CLI command in package.json for easier test execution. - Expanded PDF generation tests to cover various scenarios, including handling of empty markdown and special characters. - Improved cleanup process for temporary files created during tests. - Verified module structure and dependencies in tests to ensure proper setup. Co-authored-by: Yukaii <[email protected]> Signed-off-by: Yukai Huang <[email protected]>
1 parent 3630af4 commit fa2d987

File tree

4 files changed

+116
-90
lines changed

4 files changed

+116
-90
lines changed

lib/note/noteActions.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,17 @@ async function actionPDF (req, res, note) {
7878
}
7979
const pdfPath = config.tmpPath + '/' + Date.now() + '.pdf'
8080
content = content.replace(/\]\(\//g, '](' + url + '/')
81-
81+
8282
try {
8383
await convertMarkdownToPDF(content, pdfPath, {
8484
highlightCssPath: highlightCssPath
8585
})
86-
86+
8787
if (!fs.existsSync(pdfPath)) {
8888
logger.error('PDF seems to not be generated as expected. File doesn\'t exist: ' + pdfPath)
8989
return errorInternalError(req, res)
9090
}
91-
91+
9292
const stream = fs.createReadStream(pdfPath)
9393
let filename = title
9494
// Be careful of special characters
@@ -98,7 +98,7 @@ async function actionPDF (req, res, note) {
9898
res.setHeader('Cache-Control', 'private')
9999
res.setHeader('Content-Type', 'application/pdf; charset=UTF-8')
100100
res.setHeader('X-Robots-Tag', 'noindex, nofollow') // prevent crawling
101-
101+
102102
// Cleanup file after streaming
103103
const cleanup = () => {
104104
try {
@@ -109,7 +109,7 @@ async function actionPDF (req, res, note) {
109109
logger.error('Failed to cleanup PDF file:', err)
110110
}
111111
}
112-
112+
113113
stream.on('end', cleanup)
114114
stream.on('error', cleanup)
115115
stream.pipe(res)

lib/utils/markdown-to-pdf.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ function createMarkdownRenderer () {
4646

4747
async function convertMarkdownToPDF (markdown, outputPath, options = {}) {
4848
const md = createMarkdownRenderer()
49-
49+
5050
// Convert markdown to HTML
5151
const htmlContent = md.render(markdown)
52-
52+
5353
// Read highlight.js CSS
5454
const highlightCssPath = options.highlightCssPath || path.join(__dirname, '../../node_modules/highlight.js/styles/github-gist.css')
5555
let highlightCss = ''
56-
56+
5757
if (fs.existsSync(highlightCssPath)) {
5858
highlightCss = fs.readFileSync(highlightCssPath, 'utf8')
5959
}
@@ -138,16 +138,16 @@ async function convertMarkdownToPDF (markdown, outputPath, options = {}) {
138138
headless: true,
139139
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
140140
})
141-
141+
142142
const page = await browser.newPage()
143-
143+
144144
// Set a timeout for page operations
145145
page.setDefaultTimeout(30000)
146-
147-
await page.setContent(fullHtml, {
146+
147+
await page.setContent(fullHtml, {
148148
waitUntil: 'networkidle'
149149
})
150-
150+
151151
await page.pdf({
152152
path: outputPath,
153153
format: 'A4',
@@ -159,7 +159,7 @@ async function convertMarkdownToPDF (markdown, outputPath, options = {}) {
159159
},
160160
printBackground: true
161161
})
162-
162+
163163
return true
164164
} catch (error) {
165165
throw new Error(`PDF generation failed: ${error.message}`)
@@ -176,4 +176,4 @@ async function convertMarkdownToPDF (markdown, outputPath, options = {}) {
176176

177177
module.exports = {
178178
convertMarkdownToPDF
179-
}
179+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"jsonlint": "find . -type f -not -ipath \"./.devcontainer/*\" -not -ipath \"./node_modules/*\" -not -ipath \"./.vscode/*\" \\( -name \"*.json\" -o -name \"*.json.*\" \\) | xargs -n 1 -I{} -- bash -c 'echo {}; jq . {} > /dev/null;'",
2323
"start": "sequelize db:migrate && node app.js",
2424
"mocha": "mocha --require intelli-espower-loader --exit ./test --recursive",
25+
"mocha:cli": "mocha --require intelli-espower-loader --exit",
2526
"mocha:ci": "mocha --no-color -R dot --require intelli-espower-loader --exit ./test --recursive",
2627
"coverage": "nyc mocha --require intelli-espower-loader --exit --recursive ./test",
2728
"coverage:ci": "nyc mocha --no-color -R dot --require intelli-espower-loader --exit --recursive ./test",

test/pdf-generation.test.js

Lines changed: 100 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,36 @@
1+
/* eslint-env node, mocha */
12
'use strict'
23

34
const assert = require('assert')
45
const fs = require('fs')
56
const path = require('path')
7+
const os = require('os')
68

7-
// Test the PDF generation functionality
8-
// This test mocks dependencies to verify the logic without requiring full installation
9+
describe('PDF Generation', function () {
10+
let convertMarkdownToPDF
911

10-
describe('PDF Generation Tests', function () {
11-
const testMarkdown = `# Test Document
12+
before(function () {
13+
// Import the PDF conversion function
14+
const markdownToPdf = require('../lib/utils/markdown-to-pdf')
15+
convertMarkdownToPDF = markdownToPdf.convertMarkdownToPDF
16+
})
17+
18+
describe('Module Structure', function () {
19+
it('should export convertMarkdownToPDF function', function () {
20+
assert(typeof convertMarkdownToPDF === 'function', 'convertMarkdownToPDF should be a function')
21+
})
22+
23+
it('should have required dependencies', function () {
24+
const packagePath = path.join(__dirname, '../package.json')
25+
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'))
26+
27+
assert(packageJson.dependencies['playwright-chromium'], 'playwright-chromium should be in dependencies')
28+
assert(!packageJson.dependencies['markdown-pdf'], 'markdown-pdf should not be in dependencies')
29+
})
30+
})
31+
32+
describe('PDF Conversion', function () {
33+
const testMarkdown = `# Test Document
1234
1335
This is a **test** document with some content.
1436
@@ -19,82 +41,85 @@ console.log('Hello World');
1941
2042
- List item 1
2143
- List item 2
44+
45+
> This is a blockquote
46+
47+
| Column 1 | Column 2 |
48+
|----------|----------|
49+
| Value 1 | Value 2 |
2250
`
2351

24-
it('should have the markdown-to-pdf utility', function () {
25-
const filePath = path.join(__dirname, '../lib/utils/markdown-to-pdf.js')
26-
assert(fs.existsSync(filePath), 'markdown-to-pdf.js should exist')
27-
})
52+
let tempDir
53+
let outputPath
2854

29-
it('should have updated actionPDF function', function () {
30-
const filePath = path.join(__dirname, '../lib/note/noteActions.js')
31-
const content = fs.readFileSync(filePath, 'utf8')
32-
33-
// Should not contain markdown-pdf references
34-
assert(!content.includes("require('markdown-pdf')"), 'Should not import markdown-pdf')
35-
assert(!content.includes('markdownpdf('), 'Should not use markdownpdf function')
36-
37-
// Should contain puppeteer-based implementation
38-
assert(content.includes('convertMarkdownToPDF'), 'Should use convertMarkdownToPDF')
39-
assert(content.includes('async function actionPDF'), 'actionPDF should be async')
40-
assert(content.includes('await convertMarkdownToPDF'), 'Should await PDF conversion')
41-
})
55+
beforeEach(function () {
56+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pdf-test-'))
57+
outputPath = path.join(tempDir, 'test-output.pdf')
58+
})
4259

43-
it('should export convertMarkdownToPDF function', function () {
44-
const filePath = path.join(__dirname, '../lib/utils/markdown-to-pdf.js')
45-
const content = fs.readFileSync(filePath, 'utf8')
46-
47-
assert(content.includes('convertMarkdownToPDF'), 'Should define convertMarkdownToPDF function')
48-
assert(content.includes('module.exports'), 'Should export the function')
49-
assert(content.includes('puppeteer'), 'Should use puppeteer')
50-
assert(content.includes('markdownit'), 'Should use markdown-it')
51-
})
60+
afterEach(function () {
61+
// Clean up temp files
62+
if (fs.existsSync(outputPath)) {
63+
fs.unlinkSync(outputPath)
64+
}
65+
if (fs.existsSync(tempDir)) {
66+
fs.rmdirSync(tempDir)
67+
}
68+
})
69+
70+
it('should convert markdown to PDF successfully', async function () {
71+
this.timeout(30000) // Increase timeout for PDF generation
5272

53-
it('should have puppeteer in package.json dependencies', function () {
54-
const packagePath = path.join(__dirname, '../package.json')
55-
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'))
56-
57-
assert(packageJson.dependencies.puppeteer, 'puppeteer should be in dependencies')
58-
assert(!packageJson.dependencies['markdown-pdf'], 'markdown-pdf should be removed')
73+
const result = await convertMarkdownToPDF(testMarkdown, outputPath)
74+
75+
assert(result === true, 'convertMarkdownToPDF should return true on success')
76+
assert(fs.existsSync(outputPath), 'PDF file should be created')
77+
78+
const stats = fs.statSync(outputPath)
79+
assert(stats.size > 0, 'PDF file should not be empty')
80+
})
81+
82+
it('should handle empty markdown', async function () {
83+
this.timeout(30000)
84+
85+
const result = await convertMarkdownToPDF('', outputPath)
86+
87+
assert(result === true, 'Should handle empty markdown')
88+
assert(fs.existsSync(outputPath), 'PDF file should be created even for empty content')
89+
})
90+
91+
it('should handle markdown with special characters', async function () {
92+
this.timeout(30000)
93+
94+
const specialMarkdown = `# Special Characters
95+
96+
This has **special** characters: & < > " '
97+
98+
\`\`\`html
99+
<div class="test">Hello & Goodbye</div>
100+
\`\`\`
101+
`
102+
103+
const result = await convertMarkdownToPDF(specialMarkdown, outputPath)
104+
105+
assert(result === true, 'Should handle special characters')
106+
assert(fs.existsSync(outputPath), 'PDF file should be created')
107+
})
108+
109+
it('should throw error for invalid output path', async function () {
110+
this.timeout(30000)
111+
112+
const invalidPath = '/nonexistent/directory/test.pdf'
113+
114+
try {
115+
await convertMarkdownToPDF(testMarkdown, invalidPath)
116+
assert.fail('Should throw error for invalid path')
117+
} catch (error) {
118+
assert(error instanceof Error, 'Should throw an Error')
119+
assert(error.message.includes('PDF generation failed'), 'Error should mention PDF generation failure')
120+
}
121+
})
59122
})
60123
})
61124

62-
// If running this file directly, run a simple test
63-
if (require.main === module) {
64-
console.log('Running PDF generation tests...')
65-
66-
try {
67-
const testDir = path.dirname(__filename)
68-
69-
// Test 1: Check files exist
70-
const markdownToPdfPath = path.join(testDir, '../lib/utils/markdown-to-pdf.js')
71-
const noteActionsPath = path.join(testDir, '../lib/note/noteActions.js')
72-
73-
console.log('✅ Checking file existence...')
74-
assert(fs.existsSync(markdownToPdfPath), 'markdown-to-pdf.js should exist')
75-
assert(fs.existsSync(noteActionsPath), 'noteActions.js should exist')
76-
77-
// Test 2: Check content
78-
console.log('✅ Checking file content...')
79-
const noteActionsContent = fs.readFileSync(noteActionsPath, 'utf8')
80-
assert(noteActionsContent.includes('convertMarkdownToPDF'), 'Should use convertMarkdownToPDF')
81-
assert(!noteActionsContent.includes("require('markdown-pdf')"), 'Should not import markdown-pdf')
82-
83-
const markdownToPdfContent = fs.readFileSync(markdownToPdfPath, 'utf8')
84-
assert(markdownToPdfContent.includes('puppeteer'), 'Should use puppeteer')
85-
assert(markdownToPdfContent.includes('module.exports'), 'Should export functions')
86-
87-
// Test 3: Check package.json
88-
console.log('✅ Checking package.json...')
89-
const packagePath = path.join(testDir, '../package.json')
90-
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'))
91-
assert(packageJson.dependencies.puppeteer, 'puppeteer should be in dependencies')
92-
assert(!packageJson.dependencies['markdown-pdf'], 'markdown-pdf should be removed')
93-
94-
console.log('✅ All tests passed!')
95-
96-
} catch (error) {
97-
console.error('❌ Test failed:', error.message)
98-
process.exit(1)
99-
}
100-
}
125+

0 commit comments

Comments
 (0)