test/unit/server/web/test/index.js
const path = require('path');
const Lab = require('lab');
const sinon = require('sinon');
const Code = require('code');
const Hapi = require('hapi');
const cheerio = require('cheerio');
const defaults = require('../../../../../server/lib/defaults');
const TestPlugin = require('../../../../../server/web/test/index');
const YarPlugin = {
register: require('yar'),
options: { cookieOptions: { password: 'password-should-be-32-characters' } }
};
const AuthPlugin = {
register: require('hapi-auth-cookie'),
options: {}
};
const ScooterPlugin = {
register: require('scooter'),
options: {}
};
const MockCommentService = {
register: (server, options, next) => {
server.expose('create', function () {});
next();
}
};
MockCommentService.register.attributes = {
name: 'services/comments'
};
const MockPagesService = {
register: (server, options, next) => {
server.expose('updateHits', function () {});
server.expose('getBySlug', function () {});
server.expose('publish', function () {});
server.expose('getVisibleBySlugWithRevisions', function () {});
next();
}
};
MockPagesService.register.attributes = {
name: 'services/pages'
};
const lab = exports.lab = Lab.script();
let request, server, pagesServiceStub, commentsServiceStub;
const now = new Date();
lab.beforeEach(function (done) {
server = new Hapi.Server();
server.connection();
server.register([AuthPlugin], () => {
server.auth.strategy('session', 'cookie', {
password: 'testing',
cookie: 'sid-jsperf',
redirectTo: false,
isSecure: false
});
});
server.register(require('vision'), () => {
server.views({
engines: {
hbs: require('handlebars')
},
relativeTo: path.join(__dirname, '..', '..', '..', '..', '..'),
path: './server/web',
layout: true,
helpersPath: 'templates/helpers',
partialsPath: 'templates/partials',
context: (request) => ({
credentials: request.auth.credentials,
scheme: 'http',
domain: 'jsperf.test'
})
});
server.register([
YarPlugin,
ScooterPlugin,
MockPagesService,
MockCommentService,
TestPlugin
], (err) => {
if (err) return done(err);
pagesServiceStub = {
getBySlug: sinon.stub(server.plugins['services/pages'], 'getBySlug'),
updateHits: sinon.stub(server.plugins['services/pages'], 'updateHits').returns(Promise.resolve()),
getVisibleBySlugWithRevisions: sinon.stub(server.plugins['services/pages'], 'getVisibleBySlugWithRevisions')
};
commentsServiceStub = {
create: sinon.stub(server.plugins['services/comments'], 'create')
};
done();
});
});
});
const slug = 'oh-yea';
lab.experiment('web/test plugin', function () {
lab.beforeEach(function (done) {
request = {
method: 'GET',
url: '/' + slug
};
done();
});
lab.test('not found', function (done) {
pagesServiceStub.getBySlug.rejects(new Error('Not found'));
// adding revision to url here covers true case of rev ternary
request.url += '/999';
server.inject(request, function (response) {
Code.expect(response.statusCode).to.equal(404);
done();
});
});
lab.test('fail to get by slug', function (done) {
pagesServiceStub.getBySlug.rejects(new Error('real helpful'));
server.inject(request, function (response) {
Code.expect(response.statusCode).to.equal(500);
done();
});
});
lab.test('it responds with test page for slug', function (done) {
const now = new Date();
const page = {
id: 1,
slug: slug,
revision: 1,
browserscopeID: 'abc123',
title: 'Oh Yea',
info: 'Sample test',
setup: '',
teardown: '',
initHTML: '',
visible: 'y',
author: 'Max',
authorEmail: 'm@b.co',
authorURL: 'b.co',
hits: 0,
published: now,
updated: now,
maxRev: 2
};
const revisions = [{
published: now,
updated: now,
author: 'Max',
authorEmail: 'm@b.co',
authorURL: 'b.co',
revision: 1,
visible: 'y',
title: 'Oh Yea'
}, {
published: now,
updated: now,
author: 'Max',
authorEmail: 'm@b.co',
authorURL: 'b.co',
revision: 2,
visible: 'y',
title: 'Oh Yea'
}];
pagesServiceStub.getBySlug.returns(Promise.resolve([page, [], revisions, []]));
server.inject(request, function (response) {
Code.expect(response.statusCode).to.equal(200);
const $ = cheerio.load(response.payload);
Code.expect($('title').text()).to.equal('Oh Yea ยท jsPerf');
// make sure revision list is correct
const revLis = $('li', '#revisions');
const firstRevLi = revLis.first();
Code.expect(firstRevLi.hasClass('current')).to.be.true();
Code.expect(firstRevLi.children('a').first().attr('href')).to.equal(request.url);
const secondRevLi = revLis.eq(1);
Code.expect(secondRevLi.hasClass('current')).to.be.false();
Code.expect(secondRevLi.children('a').first().attr('href')).to.equal(request.url + '/' + revisions[1].revision);
done();
});
});
lab.test('it responds with highlighted test page for slug', function (done) {
pagesServiceStub.getBySlug.returns(Promise.resolve([{
id: 1,
slug: slug,
revision: 1,
browserscopeID: 'abc123',
title: 'Oh Yea',
info: 'Sample test',
setup: 'var a = 1',
teardown: 'delete a',
initHTML: "<div class='test'><script>var b = 2;</script></div>",
visible: 'n',
author: 'Max',
authorEmail: 'm@b.co',
authorURL: 'b.co',
hits: 0,
published: now,
updated: now
}, [], [], []]));
server.inject(request, function (response) {
Code.expect(response.statusCode).to.equal(200);
Code.expect(response.result).to.include('<pre><code class="js"><span class="pretty"> delete a</span></code></pre>');
done();
});
});
lab.experiment('Page Hits', function () {
lab.beforeEach(function (done) {
// Add a method of adding session data
server.route({
method: 'GET',
path: '/setsession',
config: {
handler: function (req, reply) {
var hits = {123: true};
req.yar.set('hits', hits);
return reply('session set');
}
}
});
done();
});
lab.test('updates unique page hits', function (done) {
pagesServiceStub.getBySlug.returns(Promise.resolve([{
id: 1,
slug: slug,
revision: 1,
browserscopeID: 'abc123',
title: 'Oh Yea',
info: 'Sample test',
setup: 'var a = 1',
teardown: 'delete a',
initHTML: "<div class='test'><script>var b = 2;</script></div>",
visible: 'n',
author: 'Max',
authorEmail: 'm@b.co',
authorURL: 'b.co',
hits: 0,
published: now,
updated: now
}, [], [], []]));
server.inject('/setsession', function (res) {
var header = res.headers['set-cookie'];
var cookie = header[0].match(/(?:[^\x00-\x20\(\)<>@\,;\:\\'\/\[\]\?\=\{\}\x7F]+)\s*=\s*(?:([^\x00-\x20\'\,\;\\\x7F]*))/); // eslint-disable-line no-control-regex, no-useless-escape
request.headers = {};
request.headers.cookie = 'session=' + cookie[1];
server.inject(request, function (response) {
var hits = response.request.yar.get('hits');
Code.expect(hits[1]).to.equal(true);
done();
});
});
});
lab.test('ignores duplicate page hits', function (done) {
pagesServiceStub.getBySlug.returns(Promise.resolve([{
id: 123,
slug: slug,
revision: 1,
browserscopeID: 'abc123',
title: 'Oh Yea',
info: 'Sample test',
setup: '',
teardown: '',
initHTML: '',
visible: 'y',
author: 'Max',
authorEmail: 'm@b.co',
authorURL: 'b.co',
hits: 0,
published: now,
updated: now
}, [], [], []]));
server.inject('/setsession', function (res) {
var header = res.headers['set-cookie'];
var cookie = header[0].match(/(?:[^\x00-\x20\(\)<>@\,;\:\\'\/\[\]\?\=\{\}\x7F]+)\s*=\s*(?:([^\x00-\x20\'\,\;\\\x7F]*))/); // eslint-disable-line no-control-regex, no-useless-escape
request.headers = {};
request.headers.cookie = 'session=' + cookie[1];
server.inject(request, function (response) {
var hits = response.request.yar.get('hits');
Code.expect(hits[123]).to.equal(true);
done();
});
});
});
lab.test('catches errors from page service', function (done) {
pagesServiceStub.getBySlug.returns(Promise.resolve([{
id: 999,
slug: slug,
revision: 1,
browserscopeID: 'abc123',
title: 'Oh Yea',
info: 'Sample test',
setup: '',
teardown: '',
initHTML: '',
visible: 'y',
author: 'Max',
authorEmail: 'm@b.co',
authorURL: 'b.co',
hits: 0,
published: now,
updated: now
}, [], [], []]));
const expectedError = new Error('catches-errors-from-page-service');
pagesServiceStub.updateHits.rejects(expectedError);
server.inject('/setsession', function (res) {
var header = res.headers['set-cookie'];
var cookie = header[0].match(/(?:[^\x00-\x20\(\)<>@\,;\:\\'\/\[\]\?\=\{\}\x7F]+)\s*=\s*(?:([^\x00-\x20\'\,\;\\\x7F]*))/); // eslint-disable-line no-control-regex, no-useless-escape
request.headers = {};
request.headers.cookie = 'session=' + cookie[1];
server.inject(request, function (response) {
Code.expect(response.statusCode).to.equal(200);
done();
});
});
});
});
lab.experiment('No Index Flag', function () {
lab.beforeEach(function (done) {
pagesServiceStub.getBySlug.returns(Promise.resolve([{
id: 1,
slug: slug,
revision: 1,
browserscopeID: 'abc123',
title: 'Oh Yea',
info: 'Sample test',
setup: '',
teardown: '',
initHTML: '',
visible: 'n',
author: 'Max',
authorEmail: 'm@b.co',
authorURL: 'b.co',
hits: 0,
published: now,
updated: now
}, [], [], []]));
done();
});
lab.test('sets noIndex to true if page is flaged as "owned" in the session', function (done) {
server.route({
method: 'GET',
path: '/setsession',
config: {
handler: function (req, reply) {
var owns = {1: true};
req.yar.set('own', owns);
return reply('session set');
}
}
});
server.inject('/setsession', function (res) {
var header = res.headers['set-cookie'];
var cookie = header[0].match(/(?:[^\x00-\x20\(\)<>@\,;\:\\'\/\[\]\?\=\{\}\x7F]+)\s*=\s*(?:([^\x00-\x20\'\,\;\\\x7F]*))/); // eslint-disable-line no-control-regex, no-useless-escape
request.headers = {};
request.headers.cookie = 'session=' + cookie[1];
server.inject(request, function (response) {
Code.expect(response.payload.indexOf('Not published yet!')).to.be.at.least(0);
done();
});
});
});
lab.test('sets noIndex to true if page is being viewed by an admin', function (done) {
server.route({
method: 'GET',
path: '/setsession',
config: {
handler: function (req, reply) {
var owns = {2: true};
req.yar.set('own', owns);
req.yar.set('admin', true);
return reply('session set');
}
}
});
server.inject('/setsession', function (res) {
var header = res.headers['set-cookie'];
var cookie = header[0].match(/(?:[^\x00-\x20\(\)<>@\,;\:\\'\/\[\]\?\=\{\}\x7F]+)\s*=\s*(?:([^\x00-\x20\'\,\;\\\x7F]*))/); // eslint-disable-line no-control-regex, no-useless-escape
request.headers = {};
request.headers.cookie = 'session=' + cookie[1];
server.inject(request, function (response) {
Code.expect(response.payload.indexOf('Not published yet!')).to.be.at.least(0);
done();
});
});
});
});
lab.experiment('comments form', function () {
lab.beforeEach(done => {
pagesServiceStub.getBySlug.returns(
Promise.resolve([{
id: 1,
slug: slug,
revision: 1,
browserscopeID: 'abc123',
title: 'Oh Yea',
info: 'Sample test',
setup: '',
teardown: '',
initHTML: '',
visible: 'y',
author: 'Max',
authorEmail: 'm@b.co',
authorURL: 'b.co',
hits: 0,
published: now,
updated: now
}, [], [], []])
);
done();
});
lab.test('is not displayed if user is anonymous', done => {
server.inject(request, response => {
Code.expect(response.statusCode).to.equal(200);
Code.expect(response.result).to.not.include('method="post" id="comment-form"');
Code.expect(response.result).to.include('<a class="login" href="/auth/github">');
done();
});
});
lab.test('is displayed if user is logged in', done => {
request.credentials = {'test': 'profile'};
server.inject(request, response => {
Code.expect(response.statusCode).to.equal(200);
Code.expect(response.result).to.include('method="post" id="comment-form"');
Code.expect(response.result).to.not.include('<a class="login" href="/auth/github">');
done();
});
});
});
lab.experiment('publish', () => {
lab.beforeEach((done) => {
request.url = `/${slug}/1/publish`;
done();
});
lab.test('handle error', (done) => {
pagesServiceStub.getBySlug.rejects(new Error('testing'));
server.inject(request, (res) => {
Code.expect(res.statusCode).to.equal(500);
done();
});
});
lab.test('404 when not owner or admin', (done) => {
pagesServiceStub.getBySlug.returns(Promise.resolve([{}]));
server.inject(request, (res) => {
Code.expect(res.statusCode).to.equal(404);
done();
});
});
lab.test('makes page visible', (done) => {
pagesServiceStub.getBySlug.returns(Promise.resolve([{
id: 1
}]));
server.route({
method: 'GET',
path: '/setsession',
config: {
handler: function (req, reply) {
var owns = {1: true};
req.yar.set('own', owns);
return reply('session set');
}
}
});
server.inject('/setsession', function (res) {
var header = res.headers['set-cookie'];
var cookie = header[0].match(/(?:[^\x00-\x20\(\)<>@\,;\:\\'\/\[\]\?\=\{\}\x7F]+)\s*=\s*(?:([^\x00-\x20\'\,\;\\\x7F]*))/); // eslint-disable-line no-control-regex, no-useless-escape
request.headers = {};
request.headers.cookie = 'session=' + cookie[1];
server.inject(request, (res) => {
Code.expect(res.statusCode).to.equal(302);
done();
});
});
});
});
});
lab.experiment('create comment', () => {
lab.beforeEach(done => {
request = {
method: 'POST',
url: `/${slug}/1`
};
done();
});
lab.test('not authorized', done => {
server.inject(request, res => {
Code.expect(res.statusCode).to.equal(401);
done();
});
});
lab.experiment('when authorized', () => {
lab.beforeEach(done => {
request.credentials = {'username': 'somerandomaccount', 'test': 'profile'};
done();
});
lab.test('it catches error', done => {
pagesServiceStub.getBySlug.rejects(new Error('testing'));
server.inject(request, res => {
Code.expect(res.statusCode).to.equal(500);
done();
});
});
lab.test('not found', function (done) {
pagesServiceStub.getBySlug.rejects(new Error('Not found'));
server.inject(request, function (response) {
Code.expect(response.statusCode).to.equal(404);
done();
});
});
lab.experiment('and page exists', () => {
lab.beforeEach(done => {
pagesServiceStub.getBySlug.returns(
Promise.resolve([{
id: 1,
slug: slug,
revision: 1,
browserscopeID: 'abc123',
title: 'Oh Yea',
info: 'Sample test',
setup: '',
teardown: '',
initHTML: '',
visible: 'y',
author: 'Max',
authorEmail: 'm@b.co',
authorURL: 'b.co',
hits: 0,
published: now,
updated: now
}, [], [], []])
);
request.headers = {};
done();
});
lab.test('should show errors if payload is not valid', done => {
request.payload = {
author: '',
authorEmail: '',
authorURL: 'bad url',
message: '',
unknown: ''
};
server.inject(request, response => {
Code.expect(response.statusCode).to.equal(200);
Code.expect(response.result).to.include(defaults.errors.comment.author);
Code.expect(response.result).to.include(defaults.errors.comment.authorEmail);
Code.expect(response.result).to.include(defaults.errors.comment.authorURL);
Code.expect(response.result).to.include(defaults.errors.comment.message);
done();
});
});
lab.experiment('it should respond 200 with ip from', () => {
lab.beforeEach(done => {
commentsServiceStub.create.returns(Promise.resolve({
id: 1,
pageID: 1,
author: 'Max',
authorEmail: 'mdd@test.com',
authorURL: 'http://b.co',
content: 'message',
ip: '127.0.0.1',
published: now
}));
request.payload = {
author: 'Max',
authorEmail: 'mdd@test.com',
authorURL: 'http://b.co',
message: 'message'
};
done();
});
lab.test('x-forwarded-for', done => {
request.headers = {'x-forwarded-for': '127.0.0.1'};
server.inject(request, response => {
Code.expect(response.statusCode).to.equal(200);
done();
});
});
lab.test('remoteAddress', done => {
request.remoteAddress = '127.0.0.1';
server.inject(request, response => {
Code.expect(response.statusCode).to.equal(200);
done();
});
});
});
});
});
});
lab.experiment('atom', () => {
lab.beforeEach(done => {
request = {
method: 'GET',
url: '/my-test.atom'
};
done();
});
lab.test('not found', done => {
pagesServiceStub.getVisibleBySlugWithRevisions.rejects(new Error('Not found'));
server.inject(request, response => {
Code.expect(response.statusCode).to.equal(404);
done();
});
});
lab.test('it responds with atom feed', done => {
const d = new Date();
pagesServiceStub.getVisibleBySlugWithRevisions.returns(Promise.resolve([
{
title: 'Unit Test',
slug: 'unit-test',
published: d,
updated: d
},
[
{
title: 'Unit',
author: 'Max',
revision: 1,
// info intentionally missing
published: d,
updated: d
}, {
title: 'Test',
author: 'Max',
revision: 2,
info: 'desc',
published: d,
updated: d
}
]
]));
server.inject(request, response => {
Code.expect(response.statusCode).to.equal(200);
Code.expect(response.headers['content-type']).to.equal('application/atom+xml;charset=UTF-8');
const dIso = d.toISOString();
const strip = (str) => str.replace(/\s/g, '');
Code.expect(strip(response.result)).to.equal(strip(`
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
<title>Unit Test</title>
<subtitle>The latest revisions for this jsPerf test case</subtitle>
<link rel="self" type="application/atom+xml" href="http://jsperf.test/unit-test.atom" />
<link rel="alternate" type="text/html" href="http://jsperf.test/unit-test" />
<updated>${d.toString()}</updated>
<id>tag:jsperf.test,2010:/unit-test</id>
<entry>
<title>Unit</title>
<author>
<name>Max</name>
</author>
<link rel="alternate" type="text/html" href="http://jsperf.test/unit-test" />
<summary>
No description entered
</summary>
<id>tag:jsperf.test,2010:/unit-test</id>
<published>${dIso}</published>
<updated>${dIso}</updated>
</entry>
<entry>
<title>Test</title>
<author>
<name>Max</name>
</author>
<link rel="alternate" type="text/html" href="http://jsperf.test/unit-test/2" />
<summary>
desc
</summary>
<id>tag:jsperf.test,2010:/unit-test/2</id>
<published>${dIso}</published>
<updated>${dIso}</updated>
</entry>
</feed>
`));
done();
});
});
lab.test('it responds with generic error', done => {
pagesServiceStub.getVisibleBySlugWithRevisions.rejects(new Error());
server.inject(request, response => {
Code.expect(response.statusCode).to.equal(500);
done();
});
});
});