diff --git a/test/examples/cookie-sessions/index.sapient.test.js b/test/examples/cookie-sessions/index.sapient.test.js new file mode 100644 index 0000000000..99c720b05d --- /dev/null +++ b/test/examples/cookie-sessions/index.sapient.test.js @@ -0,0 +1,74 @@ +const assert = require('assert'); +const request = require('supertest'); +const app = require('../../../examples/cookie-sessions/index.js'); + +describe('Cookie Session App', function() { + it('should increment session count and return correct message', function(done) { + const agent = request.agent(app); + + agent + .get('/') + .expect(200) + .expect('Content-Type', /text/) + .expect('viewed 1 times\n') + .end(function(err, res) { + if (err) return done(err); + + // Make a second request to check if count increments + agent + .get('/') + .expect(200) + .expect('Content-Type', /text/) + .expect('viewed 2 times\n') + .end(done); + }); + }); + + it('should start a new session for a new client', function(done) { + const agent = request.agent(app); + + agent + .get('/') + .expect(200) + .expect('Content-Type', /text/) + .expect('viewed 1 times\n') + .end(function(err, res) { + if (err) return done(err); + + // Create a new agent (simulating a new client) and make a request + const newAgent = request.agent(app); + newAgent + .get('/') + .expect(200) + .expect('Content-Type', /text/) + .expect('viewed 1 times\n') + .end(done); + }); + }); + + it('should maintain session across multiple requests', function(done) { + const agent = request.agent(app); + + agent + .get('/') + .expect(200) + .expect('viewed 1 times\n') + .end(function(err, res) { + if (err) return done(err); + + agent + .get('/') + .expect(200) + .expect('viewed 2 times\n') + .end(function(err, res) { + if (err) return done(err); + + agent + .get('/') + .expect(200) + .expect('viewed 3 times\n') + .end(done); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/examples/downloads/index.sapient.test.js b/test/examples/downloads/index.sapient.test.js new file mode 100644 index 0000000000..f8a2e0cb36 --- /dev/null +++ b/test/examples/downloads/index.sapient.test.js @@ -0,0 +1,111 @@ +const assert = require('assert'); +const request = require('supertest'); +const path = require('path'); +const fs = require('fs'); + +// Import the Express app +const app = require('../../../examples/downloads/index.js'); + +describe('Express Downloads App', function() { + it('should return HTML with download links on GET /', function(done) { + request(app) + .get('/') + .expect('Content-Type', /html/) + .expect(200) + .end(function(err, res) { + if (err) return done(err); + assert(res.text.includes('Download notes/groceries.txt')); + assert(res.text.includes('Download amazing.txt')); + assert(res.text.includes('Download missing.txt')); + assert(res.text.includes('Download CCTV大赛上海分赛区.txt')); + done(); + }); + }); + + it('should download an existing file', function(done) { + const testFilePath = path.join(__dirname, '../../../examples/downloads/files/amazing.txt'); + const testFileContent = 'This is an amazing test file.'; + + // Create a test file + fs.writeFileSync(testFilePath, testFileContent); + + request(app) + .get('/files/amazing.txt') + .expect('Content-Type', 'text/plain; charset=utf-8') + .expect('Content-Disposition', 'attachment; filename="amazing.txt"') + .expect(200) + .end(function(err, res) { + if (err) return done(err); + assert.strictEqual(res.text, testFileContent); + + // Clean up the test file + fs.unlinkSync(testFilePath); + done(); + }); + }); + + it('should return 404 for a non-existent file', function(done) { + request(app) + .get('/files/nonexistent.txt') + .expect(404) + .end(function(err, res) { + if (err) return done(err); + assert.strictEqual(res.text, 'Cant find that file, sorry!'); + done(); + }); + }); + + it('should handle files with non-ASCII characters in the name', function(done) { + const fileName = 'CCTV大赛上海分赛区.txt'; + const encodedFileName = encodeURIComponent(fileName); + const testFilePath = path.join(__dirname, '../../../examples/downloads/files', fileName); + const testFileContent = 'File with non-ASCII characters in the name.'; + + // Create a test file + fs.writeFileSync(testFilePath, testFileContent); + + request(app) + .get(`/files/${encodedFileName}`) + .expect('Content-Type', 'text/plain; charset=utf-8') + .expect(function(res) { + const contentDisposition = res.headers['content-disposition']; + assert(contentDisposition.startsWith('attachment; filename='), 'Content-Disposition should start with "attachment; filename="'); + assert(contentDisposition.includes(fileName) || contentDisposition.includes(encodeURIComponent(fileName)), 'Content-Disposition should include the filename or its encoded version'); + }) + .expect(200) + .end(function(err, res) { + if (err) return done(err); + assert.strictEqual(res.text, testFileContent); + + // Clean up the test file + fs.unlinkSync(testFilePath); + done(); + }); + }); + + it('should handle nested file paths', function(done) { + const testFilePath = path.join(__dirname, '../../../examples/downloads/files/notes/groceries.txt'); + const testFileContent = 'Milk, Bread, Eggs'; + + // Ensure the directory exists + fs.mkdirSync(path.dirname(testFilePath), { recursive: true }); + + // Create a test file + fs.writeFileSync(testFilePath, testFileContent); + + request(app) + .get('/files/notes/groceries.txt') + .expect('Content-Type', 'text/plain; charset=utf-8') + .expect('Content-Disposition', 'attachment; filename="groceries.txt"') + .expect(200) + .end(function(err, res) { + if (err) return done(err); + assert.strictEqual(res.text, testFileContent); + + // Clean up the test file + fs.unlinkSync(testFilePath); + fs.rmdirSync(path.dirname(testFilePath)); + done(); + }); + }); +}); \ No newline at end of file diff --git a/test/examples/error-pages/index.sapient.test.js b/test/examples/error-pages/index.sapient.test.js new file mode 100644 index 0000000000..865792884c --- /dev/null +++ b/test/examples/error-pages/index.sapient.test.js @@ -0,0 +1,125 @@ +const assert = require('assert'); +const request = require('supertest'); +const path = require('path'); + +describe('Error Pages Express App', function() { + let app; + + beforeEach(function() { + delete require.cache[require.resolve('../../../examples/error-pages/index.js')]; + app = require('../../../examples/error-pages/index.js'); + }); + + it('should render index page', function(done) { + request(app) + .get('/') + .expect('Content-Type', /html/) + .expect(200) + .end(function(err, res) { + if (err) return done(err); + assert(res.text.includes('Error Pages')); + done(); + }); + }); + + it('should return 404 for non-existent route', function(done) { + request(app) + .get('/nonexistent') + .expect('Content-Type', /html/) + .expect(404) + .end(function(err, res) { + if (err) return done(err); + assert(res.text.includes('Cannot GET /nonexistent')); + done(); + }); + }); + + it('should handle 404 route', function(done) { + request(app) + .get('/404') + .expect('Content-Type', /html/) + .expect(404) + .end(function(err, res) { + if (err) return done(err); + assert(res.text.includes('Cannot GET /404')); + done(); + }); + }); + + it('should handle 403 route', function(done) { + request(app) + .get('/403') + .expect('Content-Type', /html/) + .expect(403) + .end(function(err, res) { + if (err) return done(err); + assert(res.text.includes('not allowed!')); + done(); + }); + }); + + it('should handle 500 route', function(done) { + request(app) + .get('/500') + .expect('Content-Type', /html/) + .expect(500) + .end(function(err, res) { + if (err) return done(err); + assert(res.text.includes('keyboard cat!')); + done(); + }); + }); + + it('should return JSON for 404 with Accept: application/json', function(done) { + request(app) + .get('/nonexistent') + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(404) + .end(function(err, res) { + if (err) return done(err); + assert.deepStrictEqual(res.body, { error: 'Not found' }); + done(); + }); + }); + + it('should return plain text for 404 with Accept: text/plain', function(done) { + request(app) + .get('/nonexistent') + .set('Accept', 'text/plain') + .expect('Content-Type', /text\/plain/) + .expect(404) + .end(function(err, res) { + if (err) return done(err); + assert.strictEqual(res.text, 'Not found'); + done(); + }); + }); + + it('should disable verbose errors in production', function(done) { + const originalEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'production'; + const prodApp = require('../../../examples/error-pages/index.js'); + assert.strictEqual(prodApp.get('verbose errors'), false); + process.env.NODE_ENV = originalEnv; + done(); + }); + + it('should enable verbose errors in development', function(done) { + const originalEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + const devApp = require('../../../examples/error-pages/index.js'); + assert.strictEqual(devApp.get('verbose errors'), true); + process.env.NODE_ENV = originalEnv; + done(); + }); + + it('should use logger middleware in non-test environment', function(done) { + const originalEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + const devApp = require('../../../examples/error-pages/index.js'); + assert(devApp._router.stack.some(layer => layer.name === 'logger')); + process.env.NODE_ENV = originalEnv; + done(); + }); +}); \ No newline at end of file diff --git a/test/examples/markdown/index.sapient.test.js b/test/examples/markdown/index.sapient.test.js new file mode 100644 index 0000000000..cafa422151 --- /dev/null +++ b/test/examples/markdown/index.sapient.test.js @@ -0,0 +1,99 @@ +const assert = require('assert'); +const request = require('supertest'); +const path = require('path'); +const fs = require('fs'); +const marked = require('marked'); +const escapeHtml = require('escape-html'); + +// Import the app +const app = require('../../../examples/markdown/index.js'); + +describe('Markdown Example App', function() { + describe('GET /', function() { + it('should render the index page with correct title', function(done) { + request(app) + .get('/') + .expect(200) + .end(function(err, res) { + if (err) return done(err); + assert(res.text.includes('Markdown Example')); + done(); + }); + }); + }); + + describe('GET /fail', function() { + it('should return 500 when trying to render a missing file', function(done) { + request(app) + .get('/fail') + .expect(500, done); + }); + }); + + describe('Custom Markdown engine', function() { + let originalReadFile; + let originalMarkedParse; + + beforeEach(function() { + originalReadFile = fs.readFile; + originalMarkedParse = marked.parse; + }); + + afterEach(function() { + fs.readFile = originalReadFile; + marked.parse = originalMarkedParse; + }); + + it('should process markdown file correctly', function(done) { + const mockMarkdown = '# Hello {title}'; + const mockHtml = '

Hello Test

'; + + fs.readFile = (filePath, encoding, callback) => { + callback(null, mockMarkdown); + }; + marked.parse = (str) => '

Hello {title}

'; + + app.render('index', { title: 'Test' }, function(err, html) { + if (err) return done(err); + assert.strictEqual(html, mockHtml); + done(); + }); + }); + + it('should handle file read errors', function(done) { + fs.readFile = (filePath, encoding, callback) => { + callback(new Error('File not found')); + }; + + app.render('index', {}, function(err) { + assert(err instanceof Error); + assert.strictEqual(err.message, 'File not found'); + done(); + }); + }); + }); + + describe('App configuration', function() { + it('should set the correct view engine', function() { + assert.strictEqual(app.get('view engine'), 'md'); + }); + + it('should set the correct views directory', function() { + const expectedPath = path.join(__dirname, '../../../examples/markdown/views'); + assert.strictEqual(app.get('views'), expectedPath); + }); + }); + + describe('Error handling', function() { + it('should handle errors when rendering non-existent views', function(done) { + request(app) + .get('/fail') + .expect(500) + .end(function(err, res) { + if (err) return done(err); + assert(res.text.includes('Failed to lookup view "missing"')); + done(); + }); + }); + }); +}); \ No newline at end of file