├── .dockerignore ├── .gitignore ├── app ├── start.js ├── utils │ └── utils.js ├── app.js ├── package.json ├── controllers │ ├── reportCtrl.js │ └── lightouse.js └── __tests__ │ └── lighthouse.test.js ├── Makefile ├── Dockerfile └── README.md /.dockerignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | app/node_modules/ 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .build 2 | qemu-*-static 3 | *.yaml 4 | 5 | .DS_Store 6 | node_modules/ 7 | 8 | *.log 9 | 10 | app/*.log 11 | variables.env 12 | variables.env.now 13 | 14 | # Editor directories and files 15 | .idea 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | .sass-cache 21 | 22 | # Project specific 23 | 24 | report/* -------------------------------------------------------------------------------- /app/start.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config({ 2 | path: 'variables.env' 3 | }); 4 | 5 | const app = require('./app'); 6 | 7 | 8 | // let's run the server 9 | app.set('port', process.env.PORT || 3498); 10 | 11 | const server = app.listen(app.get('port'), () => { 12 | console.log(`Express running → PORT ${server.address().port}`); 13 | }); -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | docker build -t antoine/docker-lighthouse-express . --force-rm; 3 | 4 | build-no-cache: 5 | docker build --no-cache -t antoine/docker-lighthouse-express . --force-rm; 6 | 7 | run: 8 | docker run -d \ 9 | -p 3000:3498 -it \ 10 | -v $(shell pwd)/report:/home/chrome/reports \ 11 | --cap-add=SYS_ADMIN antoine/docker-lighthouse-express 12 | 13 | dev: 14 | docker run \ 15 | -p 3008:3498 -it \ 16 | -v $(shell pwd)/report:/home/chrome/reports \ 17 | -v $(shell pwd)/app:/app-dev \ 18 | --cap-add=SYS_ADMIN antoine/docker-lighthouse-express \ 19 | /bin/sh -------------------------------------------------------------------------------- /app/utils/utils.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | exports.now = () => new Date().toJSON().slice(0,19).replace(/-/g,'/').replace('T',' '); 4 | 5 | exports.dumpError = (err) => { 6 | if (typeof err === 'object') { 7 | if (err.message) { 8 | console.log(chalk.red('\nMessage: ' + err.message)); 9 | } 10 | if (err.stack) { 11 | console.log(chalk.blue('\nStacktrace:\n====================')); 12 | console.log(chalk.yellow(err.stack)); 13 | console.log(chalk.blue('====================')); 14 | } 15 | } else { 16 | console.log('dumpError :: argument is not an object'); 17 | } 18 | }; -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | const ligthHouseCtrl = require('./controllers/lightouse'); 4 | const reportCtrl = require('./controllers/reportCtrl') 5 | const cors = require('cors'); 6 | 7 | 8 | const app = express(); 9 | 10 | // enable cors 11 | app.use(cors()); 12 | 13 | //Takes the raw requests and turns them into usable properties on req.body 14 | app.use(bodyParser.json()) 15 | app.use(bodyParser.urlencoded({ 16 | extended: true 17 | })); 18 | 19 | 20 | // router 21 | app.post("/runtest", ligthHouseCtrl.validateParams, ligthHouseCtrl.mocking, ligthHouseCtrl.runTests); 22 | app.post("/runOriginalTest", ligthHouseCtrl.validateParams, ligthHouseCtrl.runOriginalTest); 23 | app.get('/report/', reportCtrl.getReport); 24 | app.get('/reports/', reportCtrl.getAllReports); 25 | 26 | 27 | module.exports = app; 28 | 29 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker-google-lighthouse-express-app", 3 | "version": "1.0.0", 4 | "main": "app.js", 5 | "scripts": { 6 | "test": "npx jest --forceExit", 7 | "dev" : "export REPORTS_PATH=/Users/abrossault/Documents/code/playground/docker-google-lighthouse-express/report/ && nodemon start.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.18.3", 14 | "chalk": "^2.4.2", 15 | "chrome-launcher": "^0.10.5", 16 | "cors": "^2.8.5", 17 | "dotenv": "^6.1.0", 18 | "express": "^4.16.4", 19 | "fast-glob": "^2.2.6", 20 | "lighthouse": "^5.2.0", 21 | "moment": "^2.22.2", 22 | "mysql2": "^1.6.4", 23 | "sequelize": "^4.41.2", 24 | "validator": "^10.9.0" 25 | }, 26 | "devDependencies": { 27 | "jest": "^24.3.0", 28 | "supertest": "^3.4.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/controllers/reportCtrl.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const fg = require('fast-glob'); 3 | 4 | 5 | exports.getReport = (req, res) => { 6 | 7 | try { 8 | 9 | const html = fs.readFileSync(`${process.env.REPORTS_PATH}${req.query.report}`); 10 | 11 | res.send(html.toString()); 12 | 13 | } catch (error) { 14 | 15 | return res.status(404).json({ 16 | error: "Nothing found" 17 | }); 18 | 19 | } 20 | } 21 | 22 | 23 | exports.getAllReports = async (req, res) => { 24 | 25 | try { 26 | 27 | let entries = await fg([`${process.env.REPORTS_PATH}*`]); 28 | 29 | entries = entries.map( e => e.replace(process.env.REPORTS_PATH,'')); 30 | 31 | res.json(entries) 32 | 33 | 34 | } catch ( e ) { 35 | 36 | return res.status(500).json({ 37 | error: e.toString() 38 | }); 39 | } 40 | 41 | 42 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Base docker image 2 | FROM debian:stretch-slim 3 | LABEL name="chrome-headless" \ 4 | maintainer="Justin Ribeiro " \ 5 | version="2.0" \ 6 | description="Google Chrome Headless in a container" 7 | 8 | # Install deps + add Chrome Stable + purge all the things 9 | RUN apt-get update && apt-get install -y \ 10 | apt-transport-https \ 11 | ca-certificates \ 12 | curl \ 13 | gnupg \ 14 | --no-install-recommends \ 15 | && curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \ 16 | && echo "deb https://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list \ 17 | && apt-get update && apt-get install -y \ 18 | google-chrome-stable \ 19 | fontconfig \ 20 | fonts-ipafont-gothic \ 21 | fonts-wqy-zenhei \ 22 | fonts-thai-tlwg \ 23 | fonts-kacst \ 24 | fonts-symbola \ 25 | fonts-noto \ 26 | ttf-freefont \ 27 | --no-install-recommends \ 28 | && curl -sL https://deb.nodesource.com/setup_10.x | bash - \ 29 | && apt-get install -y nodejs \ 30 | && apt-get purge --auto-remove -y curl gnupg \ 31 | && rm -rf /var/lib/apt/lists/* 32 | 33 | 34 | # Add Chrome as a user 35 | RUN groupadd -r chrome && useradd -r -g chrome -G audio,video chrome \ 36 | && mkdir -p /home/chrome && chown -R chrome:chrome /home/chrome \ 37 | && mkdir -p /opt/google/chrome && chown -R chrome:chrome /opt/google/chrome \ 38 | && mkdir -p /home/chrome/reports && chown -R chrome:chrome /home/chrome 39 | 40 | 41 | # Setup Express server 42 | WORKDIR /usr/src/app 43 | COPY ./app/ /usr/src/app/ 44 | RUN npm install --unsafe-perm 45 | 46 | WORKDIR /home/chrome/reports 47 | 48 | 49 | # Run Chrome non-privileged 50 | USER chrome 51 | 52 | VOLUME /home/chrome/reports 53 | 54 | ENV REPORTS_PATH="/home/chrome/reports/" 55 | 56 | # Drop to cli 57 | #ENTRYPOINT ["entrypoint"] 58 | #RUN node /usr/src/app/app.js & 59 | CMD [ "node", "/usr/src/app/start.js" ] 60 | #CMD ["bash"] 61 | 62 | -------------------------------------------------------------------------------- /app/__tests__/lighthouse.test.js: -------------------------------------------------------------------------------- 1 | //routes.test.js 2 | const request = require('supertest'); 3 | 4 | let app; 5 | 6 | /** 7 | * beforeAll 8 | * 9 | * Runs before running all the tests 10 | */ 11 | beforeAll(() => { 12 | 13 | app = require('../app'); 14 | 15 | process.env.PORT = 4300; 16 | process.env.REPORTS_PATH = "/Users/abrossault/Documents/code/playground/docker-LightHouse/docker-google-lighthouse-express/report/"; 17 | 18 | 19 | // let's run the server 20 | app.set('port', process.env.PORT); 21 | 22 | const server = app.listen(app.get('port'), () => { 23 | console.log(`Express running → PORT ${server.address().port}`); 24 | }); 25 | 26 | }); 27 | 28 | 29 | /** 30 | * afterAll 31 | * 32 | * Runs after running all the tests 33 | */ 34 | afterAll(() => { 35 | 36 | console.log('server closed!'); 37 | }); 38 | 39 | 40 | describe('Test the LightHouse endpoints', () => { 41 | 42 | beforeEach(() => { 43 | jest.setTimeout(200000); 44 | }); 45 | 46 | 47 | it('should return a test result', async () => { 48 | 49 | await request(app) 50 | .post('/runOriginalTest') 51 | .send({url: 'https://google.com/'}) 52 | .expect(200); 53 | 54 | }); 55 | 56 | 57 | it('should return a test result even with a slow page', async () => { 58 | 59 | await request(app) 60 | .post('/runOriginalTest') 61 | .send({url: 'https://www.landrover.ie/range-rover-velar'}) 62 | .expect(200); 63 | 64 | }); 65 | 66 | 67 | it('should send an error with a 500 status when we hit a 404', async () => { 68 | 69 | await request(app) 70 | .post('/runOriginalTest') 71 | .send({url: 'https://googdkk993ddssZle.com/'}) 72 | .expect(500); 73 | 74 | }); 75 | 76 | 77 | 78 | it('should run a test and block requests', async () => { 79 | 80 | await request(app) 81 | .post('/runtest') 82 | .send({ 83 | url: "https://monbraceletnato.fr/", 84 | blockedRequests: ["zopim.com","google-analytics.com","facebook.net"] 85 | }) 86 | .expect(200); 87 | 88 | }); 89 | 90 | 91 | 92 | it("should send an error with a 500 status when we hit a 404", async () => { 93 | 94 | await request(app) 95 | .post('/runtest') 96 | .send({ 97 | url: "https://monbracelfddffddffdfdddfdfetnato.fr/", 98 | blockedRequests: ["zopim.com","google-analytics.com","facebook.net"] 99 | }) 100 | .expect(500); 101 | 102 | }); 103 | 104 | 105 | 106 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker LightHouse Express 2 | 3 | An API to run LightHouse test and block requests ( WIP ) 4 | 5 | ## How to start 6 | 7 | ### Build the image 8 | 9 | ```Make build``` 10 | 11 | ### Make the report folder writable 12 | 13 | Make the sure the ```/report``` folder is writable. 14 | 15 | ```chmod 777 -R /report``` 16 | 17 | When LightHouse saves a test the result has to be saved on your machine. 18 | By default the folder used for saving tests is ```/report``` relative to where you clonned the project. 19 | 20 | ### Run the image 21 | 22 | ```Make run ``` 23 | 24 | The image should run on port ```3000``` 25 | 26 | 27 | ## API 28 | 29 | 30 | ### Run a test 31 | 32 | ```JSON 33 | curl -X POST \ 34 | http://localhost:3000/runOriginalTest \ 35 | -H 'Content-Type: application/json' \ 36 | -d '{ 37 | "url": "https://google.com/" 38 | }' 39 | ``` 40 | 41 | Returns : 42 | 43 | ```JSON 44 | { 45 | "originalTestResult": { 46 | "ok": true, 47 | "report": "monbraceletnato.fr_2018-11-25__17-45-52.original.report.html", 48 | "reportJSON": "monbraceletnato.fr_2018-11-25__17-45-52.original.report.json" 49 | } 50 | } 51 | ``` 52 | 53 | ### Run a test and block requests 54 | 55 | ```JSON 56 | curl -X POST \ 57 | http://localhost:3000/runtest \ 58 | -H 'Content-Type: application/json' \ 59 | -d '{ 60 | "url": "https://google.com/", 61 | "blockedRequests": [ 62 | "netmng.com", 63 | "googleadservices.com", 64 | "google-analytics.com", 65 | "googletagmanager.com", 66 | "facebook.net", 67 | "youtube.com" 68 | ] 69 | } 70 | ' 71 | ``` 72 | 73 | Returns : 74 | 75 | ```JSON 76 | { 77 | "originalTestResult": { 78 | "report": "www.google.com_2018-11-25__16-09-30.original.report.html", 79 | "reportJSON": "www.google.com_2018-11-25__16-09-30.original.report.json", 80 | "ok": true 81 | }, 82 | "blockedTestResult": { 83 | "ok": true, 84 | "report": "www.google.com_2018-11-25__16-13-47.blocked.report.html", 85 | "reportJSON": "www.google.com_2018-11-25__16-13-47.blocked.report.json" 86 | } 87 | } 88 | ``` 89 | 90 | 91 | ### Run a test and set a performance budget 92 | 93 | ```JSON 94 | curl -X POST \ 95 | http://localhost:3000/runOriginalTest \ 96 | -H 'Content-Type: application/json' \ 97 | -d '{ 98 | "url": "https://google.fr/", 99 | "budgets": [ 100 | { 101 | "resourceCounts": [ 102 | { 103 | "resourceType": "total", 104 | "budget": 8 105 | }, 106 | { 107 | "resourceType": "stylesheet", 108 | "budget": 1 109 | }, 110 | { 111 | "resourceType": "image", 112 | "budget": 1 113 | }, 114 | { 115 | "resourceType": "media", 116 | "budget": 0 117 | }, 118 | { 119 | "resourceType": "font", 120 | "budget": 2 121 | }, 122 | { 123 | "resourceType": "script", 124 | "budget": 1 125 | }, 126 | { 127 | "resourceType": "document", 128 | "budget": 0 129 | }, 130 | { 131 | "resourceType": "other", 132 | "budget": 1 133 | }, 134 | { 135 | "resourceType": "third-party", 136 | "budget": 0 137 | } 138 | ], 139 | "resourceSizes": [ 140 | { 141 | "resourceType": "total", 142 | "budget": 100 143 | }, 144 | { 145 | "resourceType": "stylesheet", 146 | "budget": 0 147 | }, 148 | { 149 | "resourceType": "image", 150 | "budget": 30 151 | }, 152 | { 153 | "resourceType": "media", 154 | "budget": 0 155 | }, 156 | { 157 | "resourceType": "font", 158 | "budget": 75 159 | }, 160 | { 161 | "resourceType": "script", 162 | "budget": 30 163 | }, 164 | { 165 | "resourceType": "document", 166 | "budget": 1 167 | }, 168 | { 169 | "resourceType": "other", 170 | "budget": 2 171 | }, 172 | { 173 | "resourceType": "third-party", 174 | "budget": 0 175 | } 176 | ], 177 | "timings": [ 178 | { 179 | "metric": "first-contentful-paint", 180 | "budget": 2000, 181 | "tolerance": 100 182 | }, 183 | { 184 | "metric": "first-cpu-idle", 185 | "budget": 2000, 186 | "tolerance": 100 187 | }, 188 | { 189 | "metric": "interactive", 190 | "budget": 2000, 191 | "tolerance": 100 192 | } 193 | ] 194 | } 195 | ] 196 | }' 197 | ``` 198 | 199 | Returns : 200 | 201 | ```json 202 | { 203 | "originalTestResult": { 204 | "ok": true, 205 | "report": "google.fr_2019-09-19__13-47-36.original.html", 206 | "reportJSON": "google.fr_2019-09-19__13-47-36.original.json" 207 | } 208 | } 209 | ``` 210 | 211 | 212 | ### Get the reports 213 | 214 | Get all reports : 215 | 216 | ``` 217 | curl -X GET 'http://localhost:300/reports/' 218 | ``` 219 | 220 | Get a specific report HTML format: 221 | 222 | ``` 223 | curl -X GET 'http://localhost:3000/report/?report=www.google.com_2018-11-25__16-13-47.blocked.report.html' 224 | ``` 225 | 226 | Returns an HTML file JSON format : 227 | 228 | ``` 229 | curl -X GET 'http://localhost:3000/report/?report=www.google.com_2018-11-25__16-13-47.blocked.report.json' 230 | ``` 231 | 232 | Returns a JSON 233 | 234 | 235 | ## Dev 236 | 237 | create a ```variables.env``` file in ```app``` 238 | 239 | set the ```REPORTS_PATH``` : 240 | 241 | ```` 242 | REPORTS_PATH=/Users/abrossault/Documents/code/playground/docker-LightHouse/docker-google-lighthouse-express/report/ 243 | ```` 244 | 245 | ### Run the tests 246 | 247 | cd ```app/``` ```npm run test``` 248 | 249 | ## Todo 250 | 251 | - [ ] Create more settings possibility with by using the [JSON config file](https://github.com/GoogleChrome/lighthouse/blob/master/docs/configuration.md) 252 | -------------------------------------------------------------------------------- /app/controllers/lightouse.js: -------------------------------------------------------------------------------- 1 | const lighthouse = require('lighthouse'); 2 | const chromeLauncher = require('chrome-launcher'); 3 | const ReportGenerator = require('lighthouse/lighthouse-core/report/report-generator'); 4 | 5 | const { 6 | URL 7 | } = require('url'); 8 | 9 | const validator = require('validator'); 10 | const utils = require('../utils/utils'); 11 | const fs = require('fs'); 12 | 13 | 14 | 15 | /** 16 | * validateParams 17 | * 18 | * Express middleware to validate the params 19 | */ 20 | exports.validateParams = (req, res, next) => { 21 | 22 | const { 23 | url 24 | } = req.body; 25 | 26 | // Validate params 27 | if (!url) { 28 | return res.status(400).send('400 : Missformed request ! Url param is missing'); 29 | } 30 | 31 | if (!validator.isURL(url)) { 32 | return res.status(400).send('400 : Missformed request ! url is not a valid'); 33 | } 34 | 35 | return next(); 36 | } 37 | 38 | /** 39 | * mocking 40 | * 41 | * Express middleware to mock the request and speedup dev time 42 | */ 43 | exports.mocking = (req, res, next) => { 44 | 45 | const { 46 | mocked 47 | } = req.body; 48 | 49 | if (mocked) { 50 | 51 | return res.json({ 52 | "originalTestResult": { 53 | "ok": true, 54 | "report": "www.random.net_2018-11-24__17-04-47.original.report.html", 55 | "reportJSON": "www.random.net_2018-11-24__17-04-47.original.report.json" 56 | }, 57 | "blockedTestResult": { 58 | "ok": true, 59 | "report": "www.random.net_2018-11-24__17-04-47.blocked.report.html", 60 | "reportJSON": "www.random.net_2018-11-24__17-04-47.blocked.report.json" 61 | } 62 | }); 63 | } 64 | 65 | return next(); 66 | } 67 | 68 | 69 | /** 70 | * createReportName 71 | * 72 | * @param {string} url 73 | * @param {string} suffix optional 74 | */ 75 | const createReportName = (url, suffix = "") => { 76 | 77 | const myURL = new URL(url); 78 | 79 | return `${myURL.host}_${new Date().toJSON().replace('T','__').replace(':','-').replace(':','-').slice(0,20)}${suffix}`; 80 | } 81 | 82 | 83 | /** 84 | * @name launchChromeAndRunLighthouse 85 | * @description start a Google Chrome insta,ce and run LightHouse 86 | * @param {string} url 87 | * @param {object} opts 88 | * @param {object} config 89 | */ 90 | const launchChromeAndRunLighthouse = async (url, opts, budgets = null) => { 91 | 92 | config = { 93 | extends: 'lighthouse:default', 94 | settings: { 95 | budgets, 96 | }, 97 | }; 98 | 99 | const chrome = await chromeLauncher.launch({ 100 | chromeFlags: opts.chromeFlags 101 | }); 102 | 103 | opts.port = chrome.port; 104 | 105 | const results = await lighthouse(url, opts, config); 106 | 107 | await chrome.kill(); 108 | 109 | 110 | // use results.lhr for the JS-consumeable output 111 | // https://github.com/GoogleChrome/lighthouse/blob/master/types/lhr.d.ts 112 | // use results.report for the HTML/JSON/CSV output as a string 113 | // use results.artifacts for the trace/screenshots/other specific case you need (rarer) 114 | 115 | return results; 116 | 117 | } 118 | 119 | 120 | /** 121 | * @name runLightHouseTest 122 | * @param {string} url 123 | * @param {array} blockedUrlPatterns 124 | */ 125 | const runLightHouseTest = async (url, blockedUrlPatterns = [], budget = null) => { 126 | 127 | // if blockedUrlPatterns contains items 128 | const suffix = blockedUrlPatterns.length === 0 ? ".original" : ".blocked"; 129 | 130 | const options = { 131 | blockedUrlPatterns, 132 | chromeFlags: ['--no-sandbox', '--headless', '--disable-gpu','--max-wait-for-load 20000'] 133 | }; 134 | 135 | 136 | const status = {}; 137 | 138 | try { 139 | 140 | const results = await launchChromeAndRunLighthouse(url, options,budget); 141 | 142 | // we create the HTML 143 | const html = ReportGenerator.generateReport(results.lhr, 'html') 144 | 145 | status.ok = true; 146 | 147 | // save the oupiut in process.env.REPORTS_PATH 148 | 149 | // we create the report names 150 | const htmlName = createReportName(url,`${suffix}.html`) 151 | const jsonName = createReportName(url,`${suffix}.json`); 152 | 153 | // Let's save the files 154 | fs.writeFileSync(`${process.env.REPORTS_PATH}${jsonName}`,results.report); 155 | fs.writeFileSync(`${process.env.REPORTS_PATH}${htmlName}`,html); 156 | 157 | status.report = htmlName; 158 | status.reportJSON = jsonName; 159 | 160 | } catch (error) { 161 | 162 | status.ok = false; 163 | status.message = error.toString(); 164 | 165 | utils.dumpError(error); 166 | 167 | } 168 | 169 | return status; 170 | 171 | }; 172 | 173 | 174 | 175 | /** 176 | * @name runOriginalTest 177 | * @description Runs a LightHouse test to be used as a reference 178 | */ 179 | exports.runOriginalTest = async (req, res) => { 180 | 181 | // get the url 182 | const { 183 | url, 184 | budgets 185 | } = req.body; 186 | 187 | 188 | console.log(`[LightHouse] ${utils.now()} starting the tests....`); 189 | 190 | try { 191 | 192 | const originalTestResult = await runLightHouseTest(url,[],budgets); 193 | 194 | console.log(`[LightHouse] ${utils.now()} Tests finished`) 195 | 196 | if( !originalTestResult.ok ){ 197 | return res.status(500).json(originalTestResult) 198 | } 199 | 200 | return res.json({ 201 | originalTestResult 202 | }); 203 | 204 | } catch (error) { 205 | 206 | console.log(`[LightHouse] ${utils.now()} Something wrong happpend ${error}`) 207 | 208 | return res.status(500).send(error.toString()); 209 | } 210 | 211 | 212 | } 213 | 214 | 215 | 216 | exports.runTests = async (req, res) => { 217 | 218 | const { 219 | url, 220 | blockedRequests, 221 | } = req.body; 222 | 223 | 224 | console.log(`[LightHouse] ${utils.now()} starting the tests....`); 225 | 226 | 227 | try { 228 | 229 | const originalTestResult = await runLightHouseTest(url); 230 | 231 | const blockedTestResult = await runLightHouseTest(url, blockedRequests); 232 | 233 | // even if originalTest is an object and not a Promise it will resolve 234 | //const [originalTestResult, blockedTestResult] = await Promise.all([originalTest, blockedTest]); 235 | 236 | if (!originalTestResult.ok) throw new Error(originalTestResult.message) 237 | 238 | if (!blockedTestResult.ok) throw new Error(blockedTestResult.message) 239 | 240 | 241 | console.log(`[LightHouse] ${utils.now()} Tests finished`) 242 | 243 | return res.json({ 244 | originalTestResult, 245 | blockedTestResult 246 | }); 247 | 248 | } catch (error) { 249 | 250 | console.log(`[LightHouse] ${utils.now()} Something wrong happpend ${error}`) 251 | 252 | utils.dumpError(error); 253 | 254 | return res.status(500).send(error.toString()); 255 | } 256 | 257 | 258 | 259 | } --------------------------------------------------------------------------------