Skip to content

Commit

Permalink
Merges --routes and -- flags then adds tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lukejacksonn committed May 18, 2020
1 parent 8bb7357 commit 4feda85
Show file tree
Hide file tree
Showing 9 changed files with 499 additions and 38 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
*.pem
*.crt

.DS_Store
.DS_Store
node_modules
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
],
"scripts": {
"cleanup": "rm -f servor.key servor.crt",
"test": "node cli test --browse --reload",
"test:secure": "yarn test --secure"
"test": "npm run cleanup && node test.js"
},
"author": "Luke Jackson <[email protected]>",
"license": "MIT"
"license": "MIT",
"devDependencies": {
"puppeteer": "^3.0.4"
}
}
60 changes: 32 additions & 28 deletions servor.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ module.exports = async ({
const utf8 = (file) => Buffer.from(file, 'binary').toString('utf8');

const sendError = (res, status) => {
res.writeHead(status);
res.writeHead(status, { 'Access-Control-Allow-Origin': '*' });
res.write(`${status}`);
res.end();
};

Expand Down Expand Up @@ -121,44 +122,47 @@ module.exports = async ({

const isRouteRequest = (pathname) => !~pathname.split('/').pop().indexOf('.');

const registerClient = (res) => {
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Access-Control-Allow-Origin': '*',
});
sendMessage(res, 'connected', 'ready');
setInterval(sendMessage, 60000, res, 'ping', 'waiting');
clients.push(res);
};

// Start the server on the desired port

server((req, res) => {
const pathname = url.parse(req.url).pathname;
if (reload && pathname === '/livereload') {
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Access-Control-Allow-Origin': '*',
});
sendMessage(res, 'connected', 'ready');
setInterval(sendMessage, 60000, res, 'ping', 'waiting');
clients.push(res);
} else {
if (reload && pathname === '/livereload') registerClient(res);
else {
const isRoute = isRouteRequest(pathname);
const hasRoute = isRoute && routes && indexFileExists(pathname);
const status = isRoute && pathname !== '/' ? 301 : 200;
const hasIndex = isRoute && routes && indexFileExists(pathname);
const status = !isRoute || hasIndex || pathname === '/' ? 200 : 301;
console.log(status, pathname, !isRoute || hasIndex || pathname === '/');

const resource = isRoute
? hasRoute
? hasIndex
? `/${decodeURI(pathname)}/${fallback}`
: `/${fallback}`
: decodeURI(pathname);
const uri = path.join(root, resource);
let ext = uri.replace(/^.*[\.\/\\]/, '').toLowerCase();
fs.stat(uri, (err) => {
if (err) return sendError(res, 404);
fs.readFile(uri, 'binary', (err, file) => {
if (err) return sendError(res, 500);
if (isRoute) {
const base = path.join('/', pathname, '/');
const doc = `<!doctype html><meta charset="utf-8"/><base href="${base}"/>`;
if (module) file = `<script type='module'>${file}</script>`;
file = doc + file + inject + livereload;
ext = 'html';
}
sendFile(res, status, file, ext);
});
if (!fs.existsSync(uri)) return sendError(res, 404);
fs.readFile(uri, 'binary', (err, file) => {
if (err) return sendError(res, 500);
if (isRoute) {
const base = path.join('/', pathname, '/');
const doc = `<!doctype html><meta charset="utf-8"/><base href="${base}"/>`;
if (module) file = `<script type='module'>${file}</script>`;
file = doc + file + inject + livereload;
ext = 'html';
}
sendFile(res, status, file, ext);
});
}
}).listen(parseInt(port, 10));
Expand Down
139 changes: 139 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
const fs = require('fs');
const puppeteer = require('puppeteer');
const cp = require('child_process');

const matches = (obj, source) =>
Object.keys(source).every(
(key) =>
obj.hasOwnProperty(key) &&
JSON.stringify(obj[key]) === JSON.stringify(source[key])
);

const test = (cmd) => (url) => (expect) => async () => {
// Make sure nothing is running on port 8080
cp.execSync(
"lsof -n -i4TCP:8080 | grep LISTEN | awk '{ print $2 }' | xargs kill"
);

// Run the command and wait for the server to start
const [c, ...a] = cmd.split(' ');
const servor = cp.spawn(c, a);
await new Promise((resolve) => servor.stdout.on('data', resolve));

const browser = await puppeteer.launch({
ignoreHTTPSErrors: true,
headless: true,
slowMo: 0,
});

// Load new page and go to url
const page = await browser.newPage();
await page.setCacheEnabled(false);

const res = await page.goto(url);

// Collect data from response and page
const status = res.status();
const headers = res.headers();
const content = await page.content();

// Change a file to trigger reload
let reload = false;
if (cmd.includes('--reload')) {
fs.readFile('test/index.html', 'utf-8', (_, data) => {
fs.writeFileSync('test/index.html', data, 'utf-8');
});
reload = await page.waitForNavigation({ timeout: 1000 }).catch(() => false);
}

const result = {
status,
reload: !!reload,
gzip: headers['content-encoding'] === 'gzip',
cors: headers['access-control-allow-origin'] === '*',
includes: [
'SERVOR_TEST_INDEX',
'SERVOR_TEST_NESTED_INDEX',
'SERVOR_TEST_MODULE_INDEX',
'SERVOR_TEST_NESTED_MODULE_INDEX',
].filter((x) => content.includes(x)),
};

const passed = matches(result, expect);
console.log(
passed
? { ['PASSED']: { cmd, url, out: JSON.stringify(result) } }
: { ['FAILED']: { cmd, url, result, expect } }
);

servor.kill();
await browser.close();
};

(async () => {
const base = { status: 200, gzip: true, cors: true, reload: false };

await test('node cli test')('http://localhost:8080')({
...base,
includes: ['SERVOR_TEST_INDEX'],
})();

await test('node cli test')('http://localhost:8080/nested')({
...base,
status: 301,
includes: ['SERVOR_TEST_INDEX'],
})();

await test('node cli test')('http://localhost:8080/assets/exists.png')({
...base,
gzip: false,
})();

await test('node cli test')('http://localhost:8080/assets/no-exists.png')({
...base,
status: 404,
gzip: false,
})();

await test('node cli test --reload')('http://localhost:8080')({
...base,
reload: true,
includes: ['SERVOR_TEST_INDEX'],
})();

await test('node cli test --routes')('http://localhost:8080')({
...base,
includes: ['SERVOR_TEST_INDEX'],
})();

await test('node cli test --routes')('http://localhost:8080/nested')({
...base,
includes: ['SERVOR_TEST_NESTED_INDEX'],
})();

await test('node cli test --module')('http://localhost:8080')({
...base,
includes: ['SERVOR_TEST_MODULE_INDEX'],
})();

await test('node cli test --secure')('https://localhost:8080')({
...base,
includes: ['SERVOR_TEST_INDEX'],
})();

await test('node cli test --secure --reload --routes --module')(
'https://localhost:8080'
)({
...base,
reload: true,
includes: ['SERVOR_TEST_MODULE_INDEX'],
})();

await test('node cli test --secure --reload --routes --module')(
'https://localhost:8080/nested'
)({
...base,
reload: true,
includes: ['SERVOR_TEST_NESTED_MODULE_INDEX'],
})();
})();
2 changes: 1 addition & 1 deletion test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8" />
<title>Web Application</title>
<title>SERVOR_TEST_INDEX</title>
<link rel="stylesheet" href="/assets/index.css" />
<script defer src="/assets/index.js"></script>
</head>
Expand Down
2 changes: 2 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* SERVOR_TEST_MODULE_INDEX */

import { react, html, css } from 'https://unpkg.com/rplus';

const style = css`
Expand Down
6 changes: 1 addition & 5 deletions test/nested/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Web Application</title>
<title>SERVOR_TEST_NESTED_INDEX</title>
<link rel="stylesheet" href="./assets/index.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="An example web app" />
<script defer src="./assets/index.js"></script>
<script type="module">
import x from './app.js';
console.log(x);
</script>
</head>
<body>
<img src="./assets/exists.png" alt="File that exists" />
Expand Down
2 changes: 2 additions & 0 deletions test/nested/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* SERVOR_TEST_NESTED_MODULE_INDEX */

import { react, html, css } from 'https://unpkg.com/rplus';

const style = css`
Expand Down
Loading

0 comments on commit 4feda85

Please sign in to comment.