Skip to content

Commit

Permalink
♻️ Core server improvements (#781)
Browse files Browse the repository at this point in the history
* ✨ Utilize inheritance to create more robust custom server

* ✅ Add and improve core tests

* ✅ Adjust test helpers for better compat

* 🐛 Fix support for Node < 15.7.0
  • Loading branch information
wwilsman authored Feb 16, 2022
1 parent 4d429d7 commit a0c0c9a
Show file tree
Hide file tree
Showing 15 changed files with 1,114 additions and 477 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"memfs": "^3.4.0",
"nock": "^13.1.1",
"nyc": "^15.1.0",
"quibble": "^0.6.8",
"rollup": "^2.53.2",
"tsd": "^0.19.0"
}
Expand Down
3 changes: 3 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@
"@percy/config": "1.0.0-beta.75",
"@percy/dom": "1.0.0-beta.75",
"@percy/logger": "1.0.0-beta.75",
"content-disposition": "^0.5.4",
"cross-spawn": "^7.0.3",
"extract-zip": "^2.0.1",
"mime-types": "^2.1.34",
"path-to-regexp": "^6.2.0",
"rimraf": "^3.0.2",
"ws": "^8.0.0"
}
Expand Down
68 changes: 68 additions & 0 deletions packages/core/src/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import fs from 'fs';
import logger from '@percy/logger';
import Server from './server';
import pkg from '../package.json';

export function createPercyServer(percy, port) {
return new Server({ port })
// facilitate logger websocket connections
.websocket(ws => logger.connect(ws))
// general middleware
.route((req, res, next) => {
// treat all request bodies as json
if (req.body) try { req.body = JSON.parse(req.body); } catch {}

// add version header
res.setHeader('Access-Control-Expose-Headers', '*, X-Percy-Core-Version');
res.setHeader('X-Percy-Core-Version', pkg.version);

// return json errors
return next().catch(e => res.json(e.status ?? 500, {
error: e.message,
success: false
}));
})
// healthcheck returns basic information
.route('get', '/percy/healthcheck', (req, res) => res.json(200, {
loglevel: percy.loglevel(),
config: percy.config,
build: percy.build,
success: true
}))
// get or set config options
.route(['get', 'post'], '/percy/config', async (req, res) => res.json(200, {
config: req.body ? await percy.setConfig(req.body) : percy.config,
success: true
}))
// responds once idle (may take a long time)
.route('get', '/percy/idle', async (req, res) => res.json(200, {
success: await percy.idle().then(() => true)
}))
// convenient @percy/dom bundle
.route('get', '/percy/dom.js', (req, res) => {
return res.file(200, require.resolve('@percy/dom'));
})
// legacy agent wrapper for @percy/dom
.route('get', '/percy-agent.js', async (req, res) => {
logger('core:server').deprecated([
'It looks like you’re using @percy/cli with an older SDK.',
'Please upgrade to the latest version to fix this warning.',
'See these docs for more info: https:docs.percy.io/docs/migrating-to-percy-cli'
].join(' '));

let content = await fs.promises.readFile(require.resolve('@percy/dom'), 'utf-8');
let wrapper = '(window.PercyAgent = class { snapshot(n, o) { return PercyDOM.serialize(o); } });';
return res.send(200, 'applicaton/javascript', content.concat(wrapper));
})
// post one or more snapshots
.route('post', '/percy/snapshot', async (req, res) => {
let snapshot = percy.snapshot(req.body);
if (!req.url.searchParams.has('async')) await snapshot;
return res.json(200, { success: true });
})
// stops percy at the end of the current event loop
.route('/percy/stop', (req, res) => {
setImmediate(() => percy.stop());
return res.json(200, { success: true });
});
}
9 changes: 4 additions & 5 deletions packages/core/src/percy.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { merge } from '@percy/config/dist/utils';
import logger from '@percy/logger';
import Queue from './queue';
import Browser from './browser';
import createPercyServer from './server';
import { createPercyServer } from './api';

import {
getSnapshotConfig,
Expand Down Expand Up @@ -85,8 +85,7 @@ export class Percy {
});

if (server) {
this.server = createPercyServer(this);
this.port = port;
this.server = createPercyServer(this, port);
}
}

Expand All @@ -97,7 +96,7 @@ export class Percy {

// Snapshot server API address
address() {
return `http://localhost:${this.port}`;
return this.server?.address();
}

// Set client & environment info, and override loaded config options
Expand Down Expand Up @@ -185,7 +184,7 @@ export class Percy {
}

// start the server after everything else is ready
yield this.server?.listen(this.port);
yield this.server?.listen();

// mark instance as started
this.log.info('Percy has started!');
Expand Down
Loading

0 comments on commit a0c0c9a

Please sign in to comment.