Skip to content

Commit

Permalink
Added 10 integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
leo committed May 24, 2018
1 parent 91b7270 commit 6522921
Show file tree
Hide file tree
Showing 4 changed files with 2,756 additions and 48 deletions.
13 changes: 10 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
"description": "The routing foundation of `serve` and static deployments on Now",
"main": "src/index.js",
"scripts": {
"test": "yarn run lint",
"lint": "zeit-eslint --ext .jsx,.js .",
"test": "yarn run test-lint && yarn run test-integration",
"test-lint": "zeit-eslint --ext .jsx,.js .",
"test-integration": "ava test/integration.js --fail-fast",
"lint-staged": "git diff --diff-filter=ACMRT --cached --name-only '*.js' '*.jsx' | xargs zeit-eslint",
"build-views": "dottojs -s ./src -d ./src",
"prepublish": "yarn run build-views"
Expand All @@ -24,9 +25,15 @@
"devDependencies": {
"@zeit/eslint-config-node": "0.2.13",
"@zeit/git-hooks": "0.1.4",
"ava": "0.25.0",
"commander": "2.15.1",
"dot": "1.1.2",
"eslint": "4.19.1"
"eslint": "4.19.1",
"micro": "9.3.1",
"node-fetch": "2.1.2",
"request": "2.87.0",
"request-promise": "4.2.2",
"test-listen": "1.1.0"
},
"eslintConfig": {
"extends": [
Expand Down
19 changes: 15 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ const applyRewrites = (requestPath, rewrites = []) => {
};

const shouldRedirect = (decodedPath, {redirects = [], trailingSlash}, cleanUrl) => {
if (redirects.length === 0) {
const slashing = typeof trailingSlash === 'boolean';

if (redirects.length === 0 && !slashing) {
return null;
}

Expand All @@ -104,7 +106,7 @@ const shouldRedirect = (decodedPath, {redirects = [], trailingSlash}, cleanUrl)
cleanedUrl = true;
}

if (typeof trailingSlash === 'boolean') {
if (slashing) {
const {ext, name} = path.parse(decodedPath);
const isTrailed = decodedPath.endsWith('/');
const isDotfile = name.startsWith('.');
Expand Down Expand Up @@ -430,7 +432,11 @@ module.exports = async (request, response, config = {}, methods = {}) => {
}
}

const acceptsJSON = request.headers.accept.includes('application/json');
let acceptsJSON = null;

if (request.headers.accept) {
acceptsJSON = request.headers.accept.includes('application/json');
}

if (((stats && stats.isDirectory()) || !stats) && acceptsJSON) {
response.setHeader('Content-Type', 'application/json');
Expand All @@ -453,8 +459,13 @@ module.exports = async (request, response, config = {}, methods = {}) => {

if (directory) {
response.statusCode = 200;
response.end(directory);

// When JSON is accepted, we already set the header before
if (!response.getHeader('Content-Type')) {
response.setHeader('Content-Type', 'text/html; charset=utf-8');
}

response.end(directory);
return;
}

Expand Down
218 changes: 218 additions & 0 deletions test/integration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// Native
const path = require('path');

// Packages
const test = require('ava');
const listen = require('test-listen');
const micro = require('micro');
const fetch = require('node-fetch');
const fs = require('fs-extra');

// Utilities
const handler = require('../');

const getUrl = config => {
const server = micro(async (request, response) => {
await handler(request, response, config);
});

return listen(server);
};

const getDirectoryContents = async (location = process.cwd(), sub) => {
const excluded = [
'.DS_Store',
'.git'
];

const content = await fs.readdir(location);

if (sub) {
content.unshift('..');
}

return content.filter(item => !excluded.includes(item));
};

test('render html directory listing', async t => {
const contents = await getDirectoryContents();

const url = await getUrl();
const response = await fetch(url);
const text = await response.text();

const type = response.headers.get('content-type');

t.is(type, 'text/html; charset=utf-8');
t.true(contents.every(item => text.includes(item)));
});

test('render json directory listing', async t => {
const contents = await getDirectoryContents();
const url = await getUrl();

const response = await fetch(url, {
headers: {
Accept: 'application/json'
}
});

const type = response.headers.get('content-type');
t.is(type, 'application/json');

const {files} = await response.json();

const existing = files.every(file => {
const full = file.base.replace('/', '');
return contents.includes(full);
});

t.true(existing);
});

test('render html sub directory listing', async t => {
const name = '.circleci';

const sub = path.join(process.cwd(), name);
const contents = await getDirectoryContents(sub, true);
const url = await getUrl();
const response = await fetch(`${url}/${name}`);
const text = await response.text();

const type = response.headers.get('content-type');
t.is(type, 'text/html; charset=utf-8');

t.true(contents.every(item => text.includes(item)));
});

test('render json sub directory listing', async t => {
const name = 'src';

const sub = path.join(process.cwd(), name);
const contents = await getDirectoryContents(sub, true);
const url = await getUrl();

const response = await fetch(`${url}/${name}`, {
headers: {
Accept: 'application/json'
}
});

const type = response.headers.get('content-type');
t.is(type, 'application/json');

const {files} = await response.json();

const existing = files.every(file => {
const full = file.base.replace('/', '');
return contents.includes(full);
});

t.true(existing);
});

test('render dotfile', async t => {
const name = '.yarnrc';
const related = path.join(process.cwd(), name);

const content = await fs.readFile(related, 'utf8');
const url = await getUrl();
const response = await fetch(`${url}/${name}`);
const text = await response.text();

t.deepEqual(content, text);
});

test('render json file', async t => {
const name = 'package.json';
const related = path.join(process.cwd(), name);

const content = await fs.readJSON(related);
const url = await getUrl();
const response = await fetch(`${url}/${name}`);

const type = response.headers.get('content-type');
t.is(type, 'application/json');

const text = await response.text();
const spec = JSON.parse(text);

t.deepEqual(spec, content);
});

test('use `public` config property', async t => {
const name = 'src';
const url = await getUrl({'public': name});

const response = await fetch(url, {
headers: {
Accept: 'application/json'
}
});

const {files, directory} = await response.json();
t.is(directory, 'src/');

const type = response.headers.get('content-type');
t.is(type, 'application/json');

const related = path.join(process.cwd(), name);
const contents = await getDirectoryContents(related);

const existing = files.every(file => {
const full = file.base.replace('/', '');
return contents.includes(full);
});

t.true(existing);
});

test('set `trailingSlash` config property to `true`', async t => {
const url = await getUrl({
trailingSlash: true
});

const target = `${url}/test`;

const response = await fetch(target, {
redirect: 'manual',
follow: 0
});

const location = response.headers.get('location');
t.is(location, `${target}/`);
});

test('set `trailingSlash` config property to `false`', async t => {
const url = await getUrl({
trailingSlash: false
});

const target = `${url}/test`;

const response = await fetch(`${target}/`, {
redirect: 'manual',
follow: 0
});

const location = response.headers.get('location');
t.is(location, target);
});

test('set `rewrites` config property to wildcard paths', async t => {
const destination = '.yarnrc';
const related = path.join(process.cwd(), destination);
const content = await fs.readFile(related, 'utf8');

const url = await getUrl({
rewrites: [{
source: 'face/**',
destination
}]
});

const response = await fetch(`${url}/face/delete`);
const text = await response.text();

t.is(text, content);
});
Loading

0 comments on commit 6522921

Please sign in to comment.