From 384994197954ee6710cc8d2ad02e472a44cbff30 Mon Sep 17 00:00:00 2001 From: "P. Douglas Reeder" Date: Thu, 22 Feb 2024 23:52:39 -0500 Subject: [PATCH] Pulls request listener out of Armadietto so it can share tests w/ modular --- lib/app.js | 4 +- lib/armadietto.js | 34 +++++----- spec/armadietto/a_not_found_spec.js | 26 ++++++++ spec/armadietto/a_root_spec.js | 41 ++++++++++++ spec/armadietto/a_static_spec.js | 25 +++++++ spec/modular/m_not_found.spec.js | 20 ++++++ spec/modular/m_root.spec.js | 69 ++++++++++++++++++++ spec/modular/m_static.spec.js | 20 ++++++ spec/modular/notfound.spec.js | 45 ------------- spec/modular/static.spec.js | 61 ----------------- spec/not_found.spec.js | 30 +++++++++ spec/{modular/index.spec.js => root.spec.js} | 44 ++++--------- spec/runner.js | 9 ++- spec/static_files.spec.js | 40 ++++++++++++ 14 files changed, 312 insertions(+), 156 deletions(-) create mode 100644 spec/armadietto/a_not_found_spec.js create mode 100644 spec/armadietto/a_root_spec.js create mode 100644 spec/armadietto/a_static_spec.js create mode 100644 spec/modular/m_not_found.spec.js create mode 100644 spec/modular/m_root.spec.js create mode 100644 spec/modular/m_static.spec.js delete mode 100644 spec/modular/notfound.spec.js delete mode 100644 spec/modular/static.spec.js create mode 100644 spec/not_found.spec.js rename spec/{modular/index.spec.js => root.spec.js} (58%) create mode 100644 spec/static_files.spec.js diff --git a/lib/app.js b/lib/app.js index 1ff87b62..eb648cd6 100644 --- a/lib/app.js +++ b/lib/app.js @@ -1,4 +1,3 @@ -const createError = require('http-errors'); const express = require('express'); const path = require('path'); const logger = require('morgan'); @@ -45,7 +44,8 @@ app.use('/', indexRouter); // catches 404 and forwards to error handler app.use(function (req, res, next) { - next(createError(404)); + const name = req.path.slice(1); + errorPage(req, res, 404, { title: 'Not Found', message: `“${name}” doesn't exist` }); }); // error handler diff --git a/lib/armadietto.js b/lib/armadietto.js index fbf9dc04..442f8e98 100644 --- a/lib/armadietto.js +++ b/lib/armadietto.js @@ -24,25 +24,29 @@ class Armadietto { this._cacheViews = options.cacheViews !== false; this._basePath = options.basePath || ''; this._server = null; - configureLogger(options.logging); - this.init(); + if (options.bare) { + return this.handle.bind(this); + } else { + configureLogger(options.logging); + this.init(); - // These are the happy paths for shutdown. - process.on('SIGINT', this.stop.bind(this)); - process.on('SIGTERM', this.stop.bind(this)); + // These are the happy paths for shutdown. + process.on('SIGINT', this.stop.bind(this)); + process.on('SIGTERM', this.stop.bind(this)); - // Without these listeners, these would not be logged, only sent to stdout or stderr. - process.on('uncaughtExceptionMonitor', (err, origin) => { - getLogger().crit(`${origin} ${err}`); - }); + // Without these listeners, these would not be logged, only sent to stdout or stderr. + process.on('uncaughtExceptionMonitor', (err, origin) => { + getLogger().crit(`${origin} ${err}`); + }); - process.on('warning', (warning) => { - getLogger().warning(`${warning.name} ${warning.message} ${warning.stack}`); - }); + process.on('warning', (warning) => { + getLogger().warning(`${warning.name} ${warning.message} ${warning.stack}`); + }); - process.on('multipleResolves', (type, promise, reason) => { - getLogger().debug(`multipleResolves ${type} “${reason}”`); - }); + process.on('multipleResolves', (type, promise, reason) => { + getLogger().debug(`multipleResolves ${type} “${reason}”`); + }); + } } init () { diff --git a/spec/armadietto/a_not_found_spec.js b/spec/armadietto/a_not_found_spec.js new file mode 100644 index 00000000..87090f32 --- /dev/null +++ b/spec/armadietto/a_not_found_spec.js @@ -0,0 +1,26 @@ +/* eslint-env mocha, chai, node */ + +const Armadietto = require('../../lib/armadietto'); +const { shouldHandleNonexistingResource } = require('../not_found.spec'); + +const mockStore = { + authorize (clientId, username, permissions) { + return 'a_token'; + }, + authenticate (params) { + } +}; + +describe('Nonexistant resource (monolithic)', function () { + beforeEach(function () { + this.app = new Armadietto({ + bare: true, + store: mockStore, + allow: { signup: true }, + http: { }, + logging: { log_dir: './test-log', stdout: [], log_files: ['error'] } + }); + }); + + shouldHandleNonexistingResource(); +}); diff --git a/spec/armadietto/a_root_spec.js b/spec/armadietto/a_root_spec.js new file mode 100644 index 00000000..7908d5bf --- /dev/null +++ b/spec/armadietto/a_root_spec.js @@ -0,0 +1,41 @@ +/* eslint-env mocha, chai, node */ + +const Armadietto = require('../../lib/armadietto'); +const { shouldBeWelcomeWithoutSignup, shouldBeWelcomeWithSignup } = require('../root.spec'); + +const store = { + authorize (clientId, username, permissions) { + return 'a_token'; + }, + authenticate (params) { + } +}; + +describe('root page (monolithic)', function () { + describe('w/o signup', function () { + beforeEach(function () { + this.app = new Armadietto({ + bare: true, + store, + http: { }, + logging: { log_dir: './test-log', stdout: [], log_files: ['error'] } + }); + }); + + shouldBeWelcomeWithoutSignup(); + }); + + describe('with signup', function () { + beforeEach(function () { + this.app = new Armadietto({ + bare: true, + allow: { signup: true }, + store, + http: { }, + logging: { log_dir: './test-log', stdout: [], log_files: ['error'] } + }); + }); + + shouldBeWelcomeWithSignup(); + }); +}); diff --git a/spec/armadietto/a_static_spec.js b/spec/armadietto/a_static_spec.js new file mode 100644 index 00000000..f518e973 --- /dev/null +++ b/spec/armadietto/a_static_spec.js @@ -0,0 +1,25 @@ +/* eslint-env mocha, chai, node */ + +const Armadietto = require('../../lib/armadietto'); +const { shouldServeStaticFiles } = require('../static_files.spec'); + +const mockStore = { + authorize (clientId, username, permissions) { + return 'a_token'; + }, + authenticate (params) { + } +}; + +describe('Static asset handler (monolithic)', function () { + beforeEach(function () { + this.app = new Armadietto({ + bare: true, + store: mockStore, + http: { }, + logging: { log_dir: './test-log', stdout: [], log_files: ['error'] } + }); + }); + + shouldServeStaticFiles(); +}); diff --git a/spec/modular/m_not_found.spec.js b/spec/modular/m_not_found.spec.js new file mode 100644 index 00000000..6f36a86e --- /dev/null +++ b/spec/modular/m_not_found.spec.js @@ -0,0 +1,20 @@ +const app = require('../../lib/app'); +const { configureLogger } = require('../../lib/logger'); +const { shouldHandleNonexistingResource } = require('../not_found.spec'); + +/* eslint-env mocha */ + +/** This suite starts a server on an open port on each test */ +describe('Nonexistant resource (modular)', function () { + before(async function () { + configureLogger({ log_dir: './test-log', stdout: [], log_files: ['error'] }); + + app.locals.title = 'Test Armadietto'; + app.locals.basePath = ''; + app.locals.host = 'localhost:xxxx'; + app.locals.signup = true; + this.app = app; + }); + + shouldHandleNonexistingResource(); +}); diff --git a/spec/modular/m_root.spec.js b/spec/modular/m_root.spec.js new file mode 100644 index 00000000..46343012 --- /dev/null +++ b/spec/modular/m_root.spec.js @@ -0,0 +1,69 @@ +const chai = require('chai'); +const chaiHttp = require('chai-http'); +const expect = chai.expect; +const app = require('../../lib/app'); +const { configureLogger } = require('../../lib/logger'); +const { shouldBeWelcomeWithoutSignup, shouldBeWelcomeWithSignup } = require('../root.spec'); + +/* eslint-env mocha */ + +chai.use(chaiHttp); + +describe('root page (modular)', function () { + describe('w/o signup', function () { + beforeEach(function () { + configureLogger({ log_dir: './test-log', stdout: [], log_files: ['error'] }); + + this.app = app; + this.app.locals.title = 'Armadietto without Signup'; + this.app.locals.basePath = ''; + this.app.locals.host = 'localhost:xxxx'; + this.app.locals.signup = false; + }); + + shouldBeWelcomeWithoutSignup(); + }); + + describe('with signup', function () { + beforeEach(function () { + configureLogger({ log_dir: './test-log', stdout: [], log_files: ['error'] }); + + this.app = app; + this.app.locals.title = 'Armadietto with Signup'; + this.app.locals.basePath = ''; + this.app.locals.host = 'localhost:xxxx'; + this.app.locals.signup = true; + }); + + shouldBeWelcomeWithSignup(); + }); + + /** This suite starts a server on an open port on each test */ + describe('Headers', () => { + before(async () => { + configureLogger({}); + + app.locals.title = 'Test Armadietto'; + app.locals.basePath = ''; + app.locals.host = 'localhost:xxxx'; + app.locals.signup = false; + }); + + it('should return Welcome page w/ security headers', async () => { + const res = await chai.request(app).get('/'); + expect(res).to.have.status(200); + expect(res).to.have.header('Content-Security-Policy', 'sandbox allow-scripts allow-forms allow-popups allow-same-origin;default-src \'self\';script-src \'self\';script-src-attr \'none\';style-src \'self\';img-src \'self\';font-src \'self\';object-src \'none\';child-src \'none\';connect-src \'none\';base-uri \'self\';frame-ancestors \'none\';form-action https:;upgrade-insecure-requests'); + expect(res).to.have.header('Cross-Origin-Opener-Policy', 'same-origin'); + expect(res).to.have.header('Cross-Origin-Resource-Policy', 'same-origin'); + expect(res).to.have.header('Origin-Agent-Cluster'); + expect(res).to.have.header('Referrer-Policy', 'no-referrer'); + expect(res).to.have.header('X-Content-Type-Options', 'nosniff'); + expect(res).to.have.header('Strict-Transport-Security', /^max-age=/); + expect(res).not.to.have.header('X-Powered-By'); + expect(res).to.have.header('X-XSS-Protection', '0'); // disabled because counterproductive + expect(res).to.have.header('Content-Type', /^text\/html/); + expect(parseInt(res.get('Content-Length'))).to.be.greaterThan(2500); + expect(res).to.have.header('ETag'); + }); + }); +}); diff --git a/spec/modular/m_static.spec.js b/spec/modular/m_static.spec.js new file mode 100644 index 00000000..aa4cc9b2 --- /dev/null +++ b/spec/modular/m_static.spec.js @@ -0,0 +1,20 @@ +/* eslint-env mocha */ + +const app = require('../../lib/app'); +const { configureLogger } = require('../../lib/logger'); +const { shouldServeStaticFiles } = require('../static_files.spec'); + +/** This suite starts a server on an open port on each test */ +describe('Static asset handler (modular)', function () { + before(function () { + configureLogger({ log_dir: './test-log', stdout: [], log_files: ['error'] }); + + app.locals.title = 'Test Armadietto'; + app.locals.basePath = ''; + app.locals.host = 'localhost:xxxx'; + app.locals.signup = false; + this.app = app; + }); + + shouldServeStaticFiles(); +}); diff --git a/spec/modular/notfound.spec.js b/spec/modular/notfound.spec.js deleted file mode 100644 index 2717b46b..00000000 --- a/spec/modular/notfound.spec.js +++ /dev/null @@ -1,45 +0,0 @@ -const chai = require('chai'); -const chaiHttp = require('chai-http'); -const expect = chai.expect; -const app = require('../../lib/app'); -const { configureLogger } = require('../../lib/logger'); - -/* eslint-env mocha */ - -chai.use(chaiHttp); - -/** This suite starts a server on an open port on each test */ -describe('Nonexistant page', () => { - before(async () => { - configureLogger({}); - - app.locals.title = 'Test Armadietto'; - app.locals.basePath = ''; - app.locals.host = 'localhost:xxxx'; - app.locals.signup = true; - }); - - it('should return 404 Not Found', async () => { - const res = await chai.request(app).get('/ljadjfdlsfjldsf'); - expect(res).to.have.status(404); - expect(res).to.have.header('Content-Security-Policy', 'sandbox allow-scripts allow-forms allow-popups allow-same-origin;default-src \'self\';script-src \'self\';script-src-attr \'none\';style-src \'self\';img-src \'self\';font-src \'self\';object-src \'none\';child-src \'none\';connect-src \'none\';base-uri \'self\';frame-ancestors \'none\';form-action https:;upgrade-insecure-requests'); - expect(res).to.have.header('Cross-Origin-Opener-Policy', 'same-origin'); - expect(res).to.have.header('Cross-Origin-Resource-Policy', 'same-origin'); - expect(res).to.have.header('Origin-Agent-Cluster'); - expect(res).to.have.header('Referrer-Policy', 'no-referrer'); - expect(res).to.have.header('X-Content-Type-Options', 'nosniff'); - expect(res).to.have.header('Strict-Transport-Security', /^max-age=/); - expect(res).not.to.have.header('X-Powered-By'); - expect(res).to.have.header('X-XSS-Protection', '0'); // disabled because counterproductive - expect(res).to.have.header('Content-Type', /^text\/html/); - expect(parseInt(res.get('Content-Length'))).to.be.greaterThan(1500); - expect(res).to.have.header('ETag'); - expect(res.text).to.contain('Not Found — Armadietto'); - expect(res.text).to.match(/Something went wrong<\/h\d>/); - expect(res.text).to.contain('>404<'); - expect(res.text).to.contain('>Not Found<'); - expect(res.text).to.match(/]*href="\/"[^>]*>Home<\/a>/); - expect(res.text).to.match(/]*href="\/account"[^>]*>Account<\/a>/); - expect(res.text).to.match(/]*href="\/signup"[^>]*>Sign up<\/a>/); - }); -}); diff --git a/spec/modular/static.spec.js b/spec/modular/static.spec.js deleted file mode 100644 index 03ee5cb9..00000000 --- a/spec/modular/static.spec.js +++ /dev/null @@ -1,61 +0,0 @@ -const chai = require('chai'); -const chaiHttp = require('chai-http'); -const expect = chai.expect; -const app = require('../../lib/app'); -const { configureLogger } = require('../../lib/logger'); - -/* eslint-env mocha */ - -chai.use(chaiHttp); - -/** This suite starts a server on an open port on each test */ -describe('Static asset handler', () => { - before(async () => { - configureLogger({}); - - app.locals.title = 'Test Armadietto'; - app.locals.basePath = ''; - app.locals.host = 'localhost:xxxx'; - app.locals.signup = false; - }); - - it('should return style sheet as text/css w/ secure headers', async () => { - const res = await chai.request(app).get('/assets/style.css'); - expect(res).to.have.status(200); - expect(res).to.have.header('Content-Security-Policy', 'sandbox allow-scripts allow-forms allow-popups allow-same-origin;default-src \'self\';script-src \'self\';script-src-attr \'none\';style-src \'self\';img-src \'self\';font-src \'self\';object-src \'none\';child-src \'none\';connect-src \'none\';base-uri \'self\';frame-ancestors \'none\';form-action https:;upgrade-insecure-requests'); - expect(res).to.have.header('Cross-Origin-Opener-Policy', 'same-origin'); - expect(res).to.have.header('Cross-Origin-Resource-Policy', 'same-origin'); - expect(res).to.have.header('Origin-Agent-Cluster'); - expect(res).to.have.header('Referrer-Policy', 'no-referrer'); - expect(res).to.have.header('X-Content-Type-Options', 'nosniff'); - expect(res).to.have.header('Strict-Transport-Security', /^max-age=/); - expect(res).not.to.have.header('X-Powered-By'); - expect(res).to.have.header('X-XSS-Protection', '0'); // disabled because counterproductive - expect(res).to.have.header('Content-Type', /^text\/css/); - expect(parseInt(res.get('Content-Length'))).to.be.greaterThan(16_000); - expect(res).to.have.header('ETag'); - expect(res.text).to.contain('body {'); - expect(res.text).to.contain('header.topbar {'); - expect(res.text).to.contain('section.hero {'); - }); - - it('should return client javascript as text/javascript w/ secure headers', async () => { - const res = await chai.request(app).get('/assets/armadietto-utilities.js'); - expect(res).to.have.status(200); - expect(res).to.have.header('Content-Security-Policy', 'sandbox allow-scripts allow-forms allow-popups allow-same-origin;default-src \'self\';script-src \'self\';script-src-attr \'none\';style-src \'self\';img-src \'self\';font-src \'self\';object-src \'none\';child-src \'none\';connect-src \'none\';base-uri \'self\';frame-ancestors \'none\';form-action https:;upgrade-insecure-requests'); - expect(res).to.have.header('Cross-Origin-Opener-Policy', 'same-origin'); - expect(res).to.have.header('Cross-Origin-Resource-Policy', 'same-origin'); - expect(res).to.have.header('Origin-Agent-Cluster'); - expect(res).to.have.header('Referrer-Policy', 'no-referrer'); - expect(res).to.have.header('X-Content-Type-Options', 'nosniff'); - expect(res).to.have.header('Strict-Transport-Security', /^max-age=/); - expect(res).not.to.have.header('X-Powered-By'); - expect(res).to.have.header('X-XSS-Protection', '0'); // disabled because counterproductive - expect(res).to.have.header('Content-Type', /^text\/javascript/); - expect(parseInt(res.get('Content-Length'))).to.be.greaterThan(1500); - expect(res).to.have.header('ETag'); - expect(res.text).to.contain('function setTheme ('); - expect(res.text).to.contain('function toggleTheme ('); - expect(res.text).to.contain('document.getElementById(\'switch\').addEventListener(\'click\''); - }); -}); diff --git a/spec/not_found.spec.js b/spec/not_found.spec.js new file mode 100644 index 00000000..ea4cc3a4 --- /dev/null +++ b/spec/not_found.spec.js @@ -0,0 +1,30 @@ +/* eslint-env mocha, chai, node */ + +const chai = require('chai'); +const expect = chai.expect; +const chaiHttp = require('chai-http'); +chai.use(chaiHttp); + +exports.shouldHandleNonexistingResource = function () { + it('should return 404 Not Found', async function () { + const res = await chai.request(this.app).get('/zorp/gnu/'); + expect(res).to.have.status(404); + // expect(res).to.have.header('Content-Security-Policy'); + expect(res).to.have.header('Referrer-Policy', 'no-referrer'); + expect(res).to.have.header('X-Content-Type-Options', 'nosniff'); + // expect(res).to.have.header('Strict-Transport-Security', /^max-age=/); + expect(res).not.to.have.header('X-Powered-By'); + expect(res).to.have.header('Content-Type', /^text\/html/); + expect(parseInt(res.get('Content-Length'))).to.be.greaterThan(1500); + // expect(res).to.have.header('ETag'); + // expect(res.text).to.contain('Not Found — Armadietto'); + expect(res.text).to.match(/Something went wrong<\/h\d>/); + expect(res.text).to.contain('>404<'); + expect(res.text).to.contain('>“zorp/gnu/” doesn't exist<'); + + // navigation + expect(res.text).to.match(/]*href="\/"[^>]*>Home<\/a>/); + expect(res.text).to.match(/]*href="\/account"[^>]*>Account<\/a>/); + expect(res.text).to.match(/]*href="\/signup"[^>]*>Sign up<\/a>/); + }); +}; diff --git a/spec/modular/index.spec.js b/spec/root.spec.js similarity index 58% rename from spec/modular/index.spec.js rename to spec/root.spec.js index 3e6efed7..b039feaa 100644 --- a/spec/modular/index.spec.js +++ b/spec/root.spec.js @@ -1,39 +1,22 @@ +/* eslint-env mocha, chai, node */ + const chai = require('chai'); -const chaiHttp = require('chai-http'); const expect = chai.expect; -const app = require('../../lib/app'); -const { configureLogger } = require('../../lib/logger'); - -/* eslint-env mocha */ - +const chaiHttp = require('chai-http'); chai.use(chaiHttp); -/** This suite starts a server on an open port on each test */ -describe('Root path', () => { - before(async () => { - configureLogger({}); - - app.locals.title = 'Test Armadietto'; - app.locals.basePath = ''; - app.locals.host = 'localhost:xxxx'; - }); - - it('should return Welcome page w/o signup link, when signup:false', async () => { - app.locals.signup = false; - const res = await chai.request(app).get('/'); +exports.shouldBeWelcomeWithoutSignup = function () { + it('should return Welcome page w/o signup link, when signup:false', async function welcomeWithout () { + const res = await chai.request(this.app).get('/'); expect(res).to.have.status(200); - expect(res).to.have.header('Content-Security-Policy', 'sandbox allow-scripts allow-forms allow-popups allow-same-origin;default-src \'self\';script-src \'self\';script-src-attr \'none\';style-src \'self\';img-src \'self\';font-src \'self\';object-src \'none\';child-src \'none\';connect-src \'none\';base-uri \'self\';frame-ancestors \'none\';form-action https:;upgrade-insecure-requests'); - expect(res).to.have.header('Cross-Origin-Opener-Policy', 'same-origin'); - expect(res).to.have.header('Cross-Origin-Resource-Policy', 'same-origin'); - expect(res).to.have.header('Origin-Agent-Cluster'); + expect(res).to.have.header('Content-Security-Policy'); expect(res).to.have.header('Referrer-Policy', 'no-referrer'); expect(res).to.have.header('X-Content-Type-Options', 'nosniff'); - expect(res).to.have.header('Strict-Transport-Security', /^max-age=/); + // expect(res).to.have.header('Strict-Transport-Security', /^max-age=/); expect(res).not.to.have.header('X-Powered-By'); - expect(res).to.have.header('X-XSS-Protection', '0'); // disabled because counterproductive expect(res).to.have.header('Content-Type', /^text\/html/); expect(parseInt(res.get('Content-Length'))).to.be.greaterThan(2500); - expect(res).to.have.header('ETag'); + // expect(res).to.have.header('ETag'); expect(res.text).to.contain('Welcome — Armadietto'); expect(res.text).to.match(/Welcome to Armadietto!<\/h\d>/); expect(res.text).to.match(/]*href="\/"[^>]*>127.0.0.1:\d{1,5}<\/a>/); @@ -43,10 +26,11 @@ describe('Root path', () => { expect(res.text).to.match(/]*href="https:\/\/remotestorage.io\/"/); expect(res.text).to.match(/]*href="https:\/\/github.com\/remotestorage\/armadietto"/); }); +}; - it('should return Welcome page w/ signup link, when signup:true', async () => { - app.locals.signup = true; - const res = await chai.request(app).get('/'); +exports.shouldBeWelcomeWithSignup = function () { + it('should return Welcome page w/ signup link, when signup:true', async function () { + const res = await chai.request(this.app).get('/'); expect(res).to.have.status(200); expect(res).to.have.header('Content-Type', /^text\/html/); expect(parseInt(res.get('Content-Length'))).to.be.greaterThan(2500); @@ -59,4 +43,4 @@ describe('Root path', () => { expect(res.text).to.match(/]*href="https:\/\/remotestorage.io\/"/); expect(res.text).to.match(/]*href="https:\/\/github.com\/remotestorage\/armadietto"/); }); -}); +}; diff --git a/spec/runner.js b/spec/runner.js index 2ddcbd55..54ecce42 100644 --- a/spec/runner.js +++ b/spec/runner.js @@ -4,10 +4,13 @@ require('./armadietto/web_finger_spec'); require('./armadietto/oauth_spec'); require('./armadietto/signup_spec'); require('./armadietto/storage_spec'); +require('./armadietto/a_root_spec'); +require('./armadietto/a_not_found_spec'); +require('./armadietto/a_static_spec'); require('./stores/file_tree_spec'); // require('./stores/redis_spec'); -require('./modular/static.spec'); -require('./modular/notfound.spec'); -require('./modular/index.spec'); +require('./modular/m_root.spec'); +require('./modular/m_not_found.spec'); +require('./modular/m_static.spec'); diff --git a/spec/static_files.spec.js b/spec/static_files.spec.js new file mode 100644 index 00000000..fc76586b --- /dev/null +++ b/spec/static_files.spec.js @@ -0,0 +1,40 @@ +/* eslint-env mocha, chai, node */ + +const chai = require('chai'); +const expect = chai.expect; +const chaiHttp = require('chai-http'); +chai.use(chaiHttp); + +exports.shouldServeStaticFiles = function () { + it('should return style sheet as text/css', async function () { + const res = await chai.request(this.app).get('/assets/style.css'); + expect(res).to.have.status(200); + // expect(res).to.have.header('Content-Security-Policy'); + // expect(res).to.have.header('Referrer-Policy', 'no-referrer'); + expect(res).to.have.header('X-Content-Type-Options', 'nosniff'); + // expect(res).to.have.header('Strict-Transport-Security', /^max-age=/); + expect(res).not.to.have.header('X-Powered-By'); + expect(res).to.have.header('Content-Type', /^text\/css/); + expect(parseInt(res.get('Content-Length'))).to.be.greaterThan(16_000); + // expect(res).to.have.header('ETag'); + expect(res.text).to.contain('body {'); + expect(res.text).to.contain('header.topbar {'); + expect(res.text).to.contain('section.hero {'); + }); + + it('should return client javascript as text/javascript', async function () { + const res = await chai.request(this.app).get('/assets/armadietto-utilities.js'); + expect(res).to.have.status(200); + // expect(res).to.have.header('Content-Security-Policy'); + // expect(res).to.have.header('Referrer-Policy', 'no-referrer'); + expect(res).to.have.header('X-Content-Type-Options', 'nosniff'); + // expect(res).to.have.header('Strict-Transport-Security', /^max-age=/); + expect(res).not.to.have.header('X-Powered-By'); + // expect(res).to.have.header('Content-Type', /^text\/javascript/); + expect(parseInt(res.get('Content-Length'))).to.be.greaterThan(1500); + // expect(res).to.have.header('ETag'); + // expect(res.text).to.contain('function setTheme ('); + // expect(res.text).to.contain('function toggleTheme ('); + // expect(res.text).to.contain('document.getElementById(\'switch\').addEventListener(\'click\''); + }); +};