UNPKG

bigpipe

Version:

Bigpipe a radical new web framework for Node.js that's inspired by Facebook's bigpipe concept.

1,182 lines (959 loc) 36 kB
describe('BigPipe', function () { 'use strict'; var All = require('./fixtures/pagelets/all') , Compiler = require('../lib/compiler') , Fittings = require('fittings') , common = require('./common') , Pagelet = require('pagelet') , assume = require('assume') , http = require('http') , Response = common.Response , Request = common.Request , BigPipe = common.BigPipe , server , app; this.timeout(30000); beforeEach(function (done) { server = http.createServer(function () { throw new Error('Unhandled request'); }); app = new BigPipe(server, { pagelets: __dirname +'/fixtures/pagelets', dist: '/tmp/dist' }).listen(common.port, done); }); afterEach(function (done) { server.close(done); }); it('has fallback if called as function without new', function () { assume(BigPipe()).to.be.instanceof(BigPipe); }); it('has defaults for options', function () { var bigpipe = new BigPipe(http.createServer()); assume(bigpipe).to.have.property('_options'); assume(bigpipe._options).to.have.property('merge'); }); it('is an EvenEmitter3', function () { assume(app).to.be.instanceOf(require('eventemitter3')); }); it('exposes the current version', function () { assume(app.version).to.equal(require(process.cwd() +'/package.json').version); }); it('correctly resolves `pagelets` as a string to an array', function () { assume(app._pagelets).to.be.a('array'); assume(app._pagelets).to.have.length(12); }); it('transforms pagelets', function () { var Pagelet = app._pagelets[0]; assume(Pagelet.method).to.be.a('array'); }); it('has supply middleware manager', function () { assume(app).to.have.property('middleware'); assume(app.middleware).to.be.an('object'); assume(app.middleware).to.be.instanceof(require('supply')); }); it('has zipline to handle gzip compression', function () { assume(app).to.have.property('_zipline'); assume(app._zipline).to.be.an('object'); assume(app._zipline).to.be.instanceof(require('zipline')); }); it('has compiler for asset management', function () { assume(app).to.have.property('_compiler'); assume(app._compiler).to.be.an('object'); assume(app._compiler).to.be.instanceof(Compiler); }); it('has temper instance for template rendering', function () { assume(app).to.have.property('_temper'); assume(app._temper).to.be.an('object'); assume(app._temper).to.be.instanceof(require('temper')); }); it('does not cache by default', function () { assume(app).to.have.property('_cache'); assume(app._cache).to.equal(false); }); it('can be initialized with a custom Framework', function () { var Framework = Fittings.extend({ name: 'moo' }); app = new BigPipe(server, { pagelets: __dirname +'/fixtures/pagelets', framework: Framework, dist: '/tmp/dist' }); assume(app._framework.name).equals('moo'); }); describe('.framework', function () { it('has a framework method', function () { assume(app.framework).is.a('function'); }); it('assigns a new internal Framework', function () { var Framework = Fittings.extend({ name: 'test' }); assume(app.framework(Framework)).equals(app); assume(app._framework).is.instanceOf(Framework); }); }); describe('.metrics', function () { it('has a metrics object', function () { assume(app.metrics).is.a('object'); }); it('emits `metrics:*` events when calling the methods', function (next) { var order = []; function receive(name) { order.push(name); if (order.length !== 5) return; assume(order[0]).equals('increment'); assume(order[1]).equals('decrement'); assume(order[2]).equals('timing'); assume(order[3]).equals('gauge'); assume(order[4]).equals('set'); next(); } app.on('metrics:increment', receive.bind(0, 'increment')) .on('metrics:decrement', receive.bind(0, 'decrement')) .on('metrics:timing', receive.bind(0, 'timing')) .on('metrics:gauge', receive.bind(0, 'gauge')) .on('metrics:set', receive.bind(0, 'set')); app.metrics.increment(); app.metrics.decrement(); app.metrics.timing(); app.metrics.gauge(); app.metrics.set(); }); it('receives the supplied arguments', function (next) { app.once('metrics:gauge', function (name, value) { assume(name).equals('http.concurrent'); assume(value).equals(100); next(); }); app.metrics.gauge('http.concurrent', 100); }); it('can be overriden using a statsd instance', function (next) { // // We're going to fake the client here as we implement the same API // interface. // var metrics = { increment: function increment() { next(); }}; var bigpipe = new BigPipe(http.createServer(), { metrics: metrics }); assume(bigpipe.metrics).equals(metrics); bigpipe.metrics.increment(); }); }); describe('_options', function () { it('has queryable options with defaults', function () { assume(app._options).to.be.a('function'); assume(app._options('host')).to.equal(undefined); assume(app._options('host', 'localhost')).to.equal('localhost'); var bigpipe = new BigPipe(http.createServer(), { pagelets: __dirname +'/fixtures/pagelets', dist: '/tmp/dist', host: '127.0.0.1' }); assume(bigpipe._options('host')).to.equal('127.0.0.1'); }); it('additional options can be merged, per example from a plugin', function () { assume(app._options.merge).to.be.a('function'); assume(app._options('test')).to.equal(undefined); var bigpipe = new BigPipe(http.createServer(), { pagelets: __dirname +'/fixtures/pagelets', dist: '/tmp/dist', host: '127.0.0.1' }); assume(bigpipe._options('host')).to.equal('127.0.0.1'); bigpipe._options.merge({ test: 'additional' }); assume(bigpipe._options('test')).to.equal('additional'); }); }); describe('.initialize', function () { it('is a function', function () { assume(app.initialize).is.a('function'); assume(app.initialize.length).to.equal(1); }); it('loads default middleware', function () { assume(app.middleware.layers[0]).to.have.property('name', 'defaults'); assume(app.middleware.layers[1]).to.have.property('name', 'zipline'); assume(app.middleware.layers[2]).to.have.property('name', 'compiler'); }); it('plugs in the provided plugins', function () { app.initialize(function optionStub(what, defaults) { if (what === 'framework') return Fittings.extend({ name: 'lol, stubbed' }); return [{ name: 'test', server: function noop() { } }]; }); assume(app._plugins).is.an('object'); assume(app._plugins.test).is.a('object'); assume(app._plugins.test).to.have.property('name', 'test'); assume(app._plugins.test.server).to.be.a('function'); }); }); describe('.router', function () { var bigpipeById = new BigPipe(http.createServer(), { dist: '/tmp/dist', pagelets: { tester: Pagelet.extend({ name: 'tester', view: __dirname +'/fixtures/view/all.html' }), index: Pagelet.extend({ path: '/', view: __dirname +'/fixtures/view/all.html' }) } }); it('finds the / pagelet', function (done) { app.once('bootstrap', function (pagelet, req, res) { assume(pagelet).to.be.instanceOf(Pagelet); assume(pagelet.statusCode).to.equal(200); done(); }); app.router(new Request('/'), new Response()); }); it('can route to specific pagelets by id', function (done) { bigpipeById.listen(common.port, function () { var local = bigpipeById._pagelets[0].prototype , id = local.id , name = local.name , view = local.view , path = local.path; bigpipeById.once('bootstrap', function (pagelet) { assume(pagelet).to.be.instanceOf(Pagelet); assume(pagelet.view).to.equal(view); assume(pagelet.name).to.equal(name); assume(pagelet.path).to.equal(path); bigpipeById._server.close(done); }); bigpipeById.router(new Request('/'), new Response(), id); }); }); it('will return 404 if the specified id cannot be found', function (done) { bigpipeById.listen(common.port, function () { var id = bigpipeById._pagelets[0].prototype.id; bigpipeById.once('bootstrap', function (pagelet) { assume(pagelet).to.be.instanceOf(require('404-pagelet')); assume(pagelet.name).to.equal('404'); assume(pagelet.path).to.equal('/404'); bigpipeById._server.close(done); }); bigpipeById.router(new Request('/'), new Response(), 'some random id'); }); }); it('doesnt find / for POST requests', function (done) { app.once('bootstrap', function (pagelet) { assume(pagelet).to.be.instanceOf(require('404-pagelet')); assume(pagelet.statusCode).to.equal(404); done(); }); app.router(new Request('/', 'POST'), new Response()); }); ['GET', 'POST', 'MOO'].forEach(function (method) { it('finds /all for '+ method, function (done) { app.once('bootstrap', function (pagelet) { assume(pagelet).to.be.instanceOf(Pagelet); assume(pagelet.statusCode).to.equal(200); done(); }); app.router(new Request('/all', method), new Response()); }); }); it('always returns a 404 page for unknown urls', function (done) { app.once('bootstrap', function (pagelet) { assume(pagelet).to.be.instanceOf(require('404-pagelet')); assume(pagelet.statusCode).to.equal(404); done(); }); app.router(new Request('/'+ Math.random(), 'POST'), new Response()); }); it('returns authorized conditional pagelet', function (done) { var notAllowedCalled = false , bigpipeIf = new BigPipe(http.createServer(), { dist: '/tmp/dist', pagelets: { notallowed: Pagelet.extend({ path: '/', view: __dirname +'/fixtures/view/all.html', if: function (req, fn) { assume(req.url).to.equal('/'); notAllowedCalled = true; fn(false); } }), allowed: Pagelet.extend({ path: '/', view: __dirname +'/fixtures/view/all.html', if: function (req, fn) { fn(true); } }) } }); bigpipeIf.listen(common.port, function () { bigpipeIf.once('bootstrap', function (pagelet) { assume(notAllowedCalled).to.equal(true); assume(pagelet).to.be.instanceOf(Pagelet); assume(pagelet.name).to.equal('allowed'); assume(pagelet.name).to.not.equal('notallowed'); bigpipeIf._server.close(done); }); bigpipeIf.router(new Request('/'), new Response()); }); }); it('adds and retrieves pagelets from a provided cache', function (done) { var cache = { get: function (url) { assume(url).to.equal('GET@/'); pattern.push('get'); return cache.pagelet; }, set: function (url, pagelet) { assume(url).to.equal('GET@/'); assume(pagelet).to.be.a('array'); pattern.push('set'); cache.pagelet = pagelet; } }; var pattern = []; var local = new BigPipe(server, { dist: '/tmp/dist', cache: cache }); local.define(__dirname +'/fixtures/pagelets', function define() { local.once('bootstrap', function (pagelet) { assume(pagelet).to.be.instanceOf(Pagelet); assume(pagelet.statusCode).to.equal(200); local.once('bootstrap', function (pagelet) { assume(pagelet).to.be.instanceOf(Pagelet); assume(pagelet.statusCode).to.equal(200); assume(pattern.join()).to.equal('get,set,get'); done(); }); local.router(new Request('/'), new Response()); }); local.router(new Request('/'), new Response()); }); }); }); describe('.define', function () { it('adds Pagelet to the pagelets collection', function (next) { var faq = require(__dirname + '/fixtures/pagelets/faq') , bigpipe = new BigPipe(server, { dist: '/tmp/dist' }); bigpipe.define(faq, function (err) { if (err) return next(err); assume(bigpipe._pagelets).to.have.length(1); assume(bigpipe._pagelets[0]).to.be.an('function'); next(); }); }); it('will resolve and add the pagelets if directory', function (next) { var bigpipe = new BigPipe(server, { dist: '/tmp/dist' }); bigpipe.define(__dirname + '/fixtures/pagelets', function (err) { if (err) return next(err); assume(bigpipe._pagelets).to.have.length(12); bigpipe._pagelets.forEach(function (pagelet) { assume(pagelet.prototype).to.have.property('id'); }); next(); }); }); }); describe('.discover', function () { it('is a function', function () { assume(app.discover).to.be.a('function'); assume(app.discover.length).to.equal(1); }); it('returns an error if the pagelets or middleware are invalid', function (done) { var bigpipe = new BigPipe(http.createServer(), { dist: '/tmp/dist' }); bigpipe.once('transform:pagelet:after', function (Pagelet, next) { return next(new Error('middleware failed')); }); bigpipe.discover(function (error) { assume(error).to.be.instanceof(Error); assume(error.message).to.include('middleware failed'); done(); }); }); it('provides default pagelets if no /404 or /500 is found', function () { assume(app._statusCodes[404]).to.equal(require('404-pagelet')); assume(app._statusCodes[500]).to.equal(require('500-pagelet')); }); it('uses default bootstrap Pagelet if none is provided', function () { assume(app._bootstrap).to.equal(require('bootstrap-pagelet')); }); it('uses provided 404 and 500 pagelets based on routes', function (done) { var custom = new BigPipe(server, { dist: '/tmp/dist' }).define(__dirname + '/fixtures/discover', function () { var Fourofour = require('404-pagelet') , Fivehundred = require('500-pagelet'); assume(custom._pagelets).to.have.length(0); assume(custom._statusCodes[404].prototype.view).to.not.equal(Fourofour.prototype.view); assume(custom._statusCodes[404].prototype.pagelets).to.not.equal(Fourofour.prototype.pagelets); assume(custom._statusCodes[500].prototype.view).to.not.equal(Fivehundred.prototype.view); assume(custom._statusCodes[500].prototype.pagelets).to.not.equal(Fivehundred.prototype.pagelets); done(); }); }); it('uses provided bootstrap pagelet based on name', function (done) { var custom = new BigPipe(server, { dist: '/tmp/dist' }).define(__dirname + '/fixtures/bootstrapper', function () { var Bootstrap = require('bootstrap-pagelet'); assume(custom._pagelets).to.have.length(0); assume(custom._bootstrap.prototype.view).to.not.equal(Bootstrap.prototype.view); assume(custom._bootstrap.prototype.title).to.not.equal(Bootstrap.prototype.title); done(); }); assume(custom._pagelets).to.have.length(0); assume(custom._bootstrap).to.not.equal(require('bootstrap-pagelet')); }); }); describe('.resolve', function () { it('omits any directories from the pagelets directory without an index.js', function () { var bigpipe = new BigPipe(server, { pagelets: __dirname + '/fixtures/discover', dist: '/tmp/dist' }); bigpipe._pagelets.forEach(function (pagelets) { assume(pagelets.id).to.not.match(/^dummy/); }); }); }); describe('.listen', function () { it('is a function', function () { assume(app.listen).to.be.a('function'); assume(app.listen.length).to.equal(2); }); it('returns an error if define fails', function (done) { var bigpipe = new BigPipe(http.createServer(), { pagelets: { failure: require('pagelet').extend({ view: undefined }) } }); bigpipe.listen(common.port, function (error) { assume(error).to.be.instanceof(Error); assume(error.message).to.include('should have a .view property'); done(); }); }); it('emits the error if no callback is provided', function (done) { var bigpipe = new BigPipe(http.createServer(), { pagelets: { failure: require('pagelet').extend({ view: undefined }) } }); bigpipe.once('error', function (error) { assume(error).to.be.instanceof(Error); assume(error.message).to.include('should have a .view property'); done(); }); bigpipe.listen(common.port); }); it('proxies event listeners', function (done) { // // Set a big timeout as we might need to lazy install dependencies // this.timeout(500E3); var bigpipe = new BigPipe(http.createServer(), { dist: '/tmp/dist' }); bigpipe.once('listening', function () { bigpipe._server.close(done); }); bigpipe.listen(common.port, function () { assume(bigpipe._server._events).to.have.property('listening'); assume(bigpipe._server._events.listening[0]).to.be.a('function'); assume(bigpipe._server._events.listening[0].toString()).to.equal(bigpipe.emits('listening').toString()); assume(bigpipe._server._events).to.have.property('request'); assume(bigpipe._server._events.request).to.be.a('function'); assume(bigpipe._server._events.request.toString()).to.equal(bigpipe.bind(bigpipe.dispatch).toString()); assume(bigpipe._server._events).to.have.property('error'); assume(bigpipe._server._events.error).to.be.a('function'); assume(bigpipe._server._events.error.toString()).to.equal(bigpipe.emits('error').toString()); }); }); it('will define and process the provided pagelets', function (done) { var bigpipe = new BigPipe(http.createServer(), { pagelets: __dirname +'/fixtures/pagelets', dist: '/tmp/dist' }); bigpipe.once('listening', function () { assume(bigpipe._pagelets.length).to.equal(12); bigpipe._server.close(done); }); assume(bigpipe._pagelets.length).to.equal(0); bigpipe.listen(common.port); }); }); describe('.createServer', function () { it('is a function', function () { assume(BigPipe.createServer).to.be.a('function'); assume(BigPipe.createServer.length).to.equal(2); }); it('has optional port argument that defaults to 8080', function (done) { var bigpipe = BigPipe.createServer({ dist: '/tmp/dist' }); assume(bigpipe._options('port')).to.equal(8080); bigpipe.once('listening', function () { bigpipe._server.close(done); }); }); it('accepts string as port number', function (done) { var port = common.port , bigpipe = BigPipe.createServer(port.toString(), { dist: '/tmp/dist' }); assume(bigpipe._options('port')).to.equal(port); bigpipe.once('listening', function () { bigpipe._server.close(done); }); }); it('will call .listen as soon as the server is completely initialized', function (done) { var bigpipe = BigPipe.createServer(common.port, { dist: '/tmp/dist' }); bigpipe.once('listening', function () { bigpipe._server.close(done); }); }); it('defaults options to empty object', function (done) { var bigpipe = BigPipe.createServer(common.port); bigpipe.once('listening', function () { bigpipe._server.close(done); }); }); it('returns bigpipe instance if listen is false', function () { assume(BigPipe.createServer(common.port, { listen: false })).to.be.instanceof(BigPipe); }); }); describe('.redirect', function () { it('redirects to specified location', function (done) { var property = Object.getOwnPropertyDescriptor(BigPipe.prototype, 'redirect') , pagelet = new Pagelet({res: {}, bigpipe: app }); assume(BigPipe.prototype).to.have.property('redirect'); assume(BigPipe.prototype.redirect).to.be.a('function'); assume(property.writable).to.equal(false); assume(property.enumerable).to.equal(false); assume(property.configurable).to.equal(false); pagelet._res.setHeader = function setHeader(header, value) { assume(header).to.equal('Location'); assume(value).to.equal('/redirected'); }; pagelet._res.end = function end() { assume(pagelet._res.statusCode).to.equal(301); done(); }; app.redirect(pagelet, '/redirected'); }); it('allows to set custom statusCode', function (done) { var pagelet = new Pagelet({res: {}, bigpipe: app }); pagelet._res.setHeader = function setHeader(header, value) { assume(header).to.equal('Location'); assume(value).to.equal('/redirected'); }; pagelet._res.end = function end() { assume(pagelet._res.statusCode).to.equal(400); done(); }; app.redirect(pagelet, '/redirected', 400); }); it('can add cache headers to prevent caching the redirect', function () { var resp = new Response , keys = ['Pragma', 'Expires', 'Cache-Control', 'Location'] , props = [ '/redirect', 'no-cache', 'Sat, 26 Jul 1997 05:00:00 GMT', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0' ]; resp.setHeader = function (key, prop) { assume(keys).to.include(key); assume(props).to.include(prop); }; app.redirect( new Pagelet({res: resp, bigpipe: app }), '/redirect', 302, { cache: false } ); }); it('emits end if the pagelet has a listener', function (done) { var pagelet = new Pagelet({res: new Response, bigpipe: app }); pagelet.once('end', function () { assume(arguments.length).to.equal(0); done(); }); app.redirect(pagelet, '/redirect', 302); }); }); describe('.status', function () { it('is a function', function () { assume(app.status).is.a('function'); assume(app.status.length).to.equal(4); }); it('emits an error on if the statusCode is unsupported', function (done) { app.once('error', function (error) { assume(error).to.be.instanceof(Error); assume(error.message).to.equal('Unsupported HTTP code: 303.'); done(); }); app.status(null, 303); }); it('bootstraps the request status pagelet', function () { app.discover(function () { var pagelet = app.status({ _req: new Request, _res: new Response }, 500, new Error('test message')); assume(pagelet).to.be.instanceof(require('500-pagelet')); assume(pagelet.data).to.be.instanceof(Error); assume(pagelet.data.message).to.equal('test message'); }); }); }); describe('.bind', function () { it('is a function', function () { assume(app.bind).is.a('function'); assume(app.bind.length).to.equal(1); }); it('binds the function to the BigPipe instance', function (done) { function test(one, two, three) { assume(this).to.be.instanceof(BigPipe); assume(one).to.equal('1st arg'); assume(two).to.equal('2nd arg'); assume(three).to.equal('3rd arg'); done(); } app.bind(test)('1st arg', '2nd arg', '3rd arg'); }); }); describe('.dispatch', function () { it('is a function', function () { assume(app.dispatch).is.a('function'); assume(app.dispatch.length).to.equal(2); }); it('returns early if middleware handles the response', function (done) { var bigpipe = new BigPipe(http.createServer(), { dist: '/tmp/dist' }); bigpipe.middleware.use('test', function (req, res, next) { assume(req.url).to.equal('/'); next(null, true); bigpipe._server.close(done); }); bigpipe.listen(common.port, function () { bigpipe.dispatch(new Request('/'), new Response); }); }); it('returns 500 Pagelet if middleware errors', function (done) { var bigpipe = new BigPipe(http.createServer(), { dist: '/tmp/dist' }); bigpipe.middleware.use('test', function (req, res, next) { assume(req.url).to.equal('/'); next(new Error('Testing message, fail!'), false); }); bigpipe.listen(common.port, function () { var response = new Response; response.write = function write(data, encoding, cb) { data = data.toString('utf-8'); assume(data).to.include('<title>BigPipe</title>'); assume(data).to.include('500, Internal server error'); assume(data).to.include('Error: Testing message, fail!'); bigpipe._server.close(done); }; bigpipe.dispatch(new Request('/'), response); }); }); it('delegates to router and bootstrap', function (done) { var response = new Response; response.write = function write(data, encoding, cb) { data = data.toString('utf-8'); assume(data).to.include('<body data-pagelet="faq">'); done(); }; app.dispatch(new Request('/faq'), response); }); }); describe('.capture', function () { it('is a function', function () { assume(app.capture).is.a('function'); assume(app.capture.length).to.equal(3); }); it('calls the status method with a 500', function (done) { var Mock = BigPipe.extend({ status: function status(pagelet, code, error, bootstrap) { assume(pagelet).to.be.instanceof(Pagelet); assume(code).to.equal(500); assume(error.message).to.equal('trigger status'); assume(bootstrap).to.equal(false); done(); } }); var mock = new Mock(http.createServer(), { dist: '/tmp/dist' }); mock.capture( new Error('trigger status'), new (Pagelet.extend({ name: 'mock' })) ); }); it('passes value of bootstrap flag', function (done) { var Mock = BigPipe.extend({ status: function status(pagelet, code, error, bootstrap) { assume(pagelet).to.be.instanceof(Pagelet); assume(code).to.equal(500); assume(error.message).to.equal('trigger status'); assume(bootstrap).to.equal(true); done(); } }); var mock = new Mock(http.createServer(), { dist: '/tmp/dist' }); mock.capture( new Error('trigger status'), new (Pagelet.extend({ name: 'mock' })), true ); }); }); describe('.sync', function () { it('is a function', function () { assume(app.sync).is.a('function'); assume(app.sync.length).to.equal(1); }); }); describe('.async', function () { it('is a function', function () { assume(app.async).is.a('function'); assume(app.async.length).to.equal(1); }); }); describe('.pipeline', function () { it('is a function', function () { assume(app.pipeline).is.a('function'); assume(app.pipeline.length).to.equal(1); }); }); describe('.pluggable', function () { it('is a function', function () { assume(app.pluggable).is.a('function'); assume(app.pluggable.length).to.equal(1); }); it('uses the provided plugins', function () { var plugins = [{ name: 'car', server: function noop() {} }, { name: 'bike', client: function noop() {} }]; app.pluggable(plugins); assume(app._plugins).to.be.an('object'); assume(app._plugins).to.have.property('car'); assume(app._plugins).to.have.property('bike'); assume(app._plugins.car).to.equal(plugins[0]); assume(app._plugins.bike).to.equal(plugins[1]); }); }); describe('.use', function () { it('is a function', function () { assume(app.use).is.a('function'); assume(app.use.length).to.equal(2); }); it('has optional name parameter', function () { app.use('nameless', { server: function noop() {} }); assume(app._plugins).to.have.property('nameless'); }); it('throws an error if the plugin has no name', function () { function throws() { app.use(void 0, { server: function noop() {} }); } assume(throws).to.throw(Error); assume(throws).to.throw('Plugin should be specified with a name.'); }); it('throws an error if the plugin name is not a string', function () { function throws() { app.use(12, { server: function noop() {} }); } assume(throws).to.throw(Error); assume(throws).to.throw('Plugin names should be a string.'); }); it('throws an error if the plugin is no object or string', function () { function throws() { app.use('test', 12); } assume(throws).to.throw(Error); assume(throws).to.throw('Plugin should be an object or function.'); }); it('throws an error if the plugin is redefined with the same name', function () { function throws() { app.use({ name: 'test', server: function noop() {}}); app.use({ name: 'test', server: function noop() {}}); } assume(throws).to.throw(Error); assume(throws).to.throw('The plugin name was already defined.'); }); it('throws an error if the plugin has no server or client functions', function () { function throws() { app.use({ name: 'test'}); } assume(throws).to.throw(Error); assume(throws).to.throw('The plugin is missing a client or server function.'); }); it('reads plugins from file', function () { app.use('fromfile', __dirname +'/fixtures/plugin'); assume(app._plugins).to.have.property('fromfile'); assume(app._plugins.fromfile).to.have.property('client'); assume(app._plugins.fromfile).to.have.property('server'); }); it('merges BigPipe and plugin options and calls the server function', function (done) { app.use('merge', { options: { test: 'value' }, server: function (bigpipe, options) { assume(bigpipe).to.be.instanceof(BigPipe); assume(options).to.be.an('function'); assume(options('test')).to.equal('value'); done(); } }); }); }); describe('.destroy', function () { it('is a function', function () { assume(app.destroy).is.a('function'); assume(app.destroy.length).to.equal(0); }); it('closes the server if required', function (done) { var bigpipe = new BigPipe(http.createServer(), { dist: '/tmp/dist' }); bigpipe.listen(common.port, function () { bigpipe._server.close = done; bigpipe.destroy(); }); }); it('removes listeners and cleans references', function (done) { var bigpipe = new BigPipe(http.createServer(), { pagelets: __dirname +'/fixtures/pagelets', dist: '/tmp/dist' }); bigpipe.listen(common.port, function () { bigpipe.destroy(); assume(bigpipe).to.not.have.property('_events'); assume(bigpipe._pagelets).to.equal(null); assume(bigpipe._temper).to.equal(null); assume(bigpipe._plugins).to.equal(null); assume(bigpipe.middleware).to.equal(null); done(); }); }); }); describe('.bootstrap', function () { var bigpipe = new BigPipe(http.createServer(), { pagelets: __dirname +'/fixtures/pagelets', dist: '/tmp/dist' }); it('is a function', function () { assume(app.bootstrap).is.a('function'); assume(app.bootstrap.length).to.equal(3); }); it('returns early if the response is finished', function () { var req = new Request , res = new Response , pagelet = new Pagelet , result; res.finished = true; result = app.bootstrap(pagelet, req, res); assume(pagelet._bootstrap).to.equal(undefined); assume(result).to.be.instanceof(BigPipe); }); it('forces sync mode if the JS is disabled or HTTP versions are missing', function () { var req = new Request , res = new Response , pagelet = new All({ req: req, res: res, bigpipe: bigpipe }); req.query.no_pagelet_js = '1'; pagelet.mode = 'async'; app.bootstrap(pagelet, req, res); assume(pagelet).to.have.property('mode', 'sync'); req.httpVersionMajor = '2'; pagelet.mode = 'async'; app.bootstrap(pagelet, req, res); assume(pagelet).to.have.property('mode', 'sync'); }); it('bootstraps the parent in async mode by default', function () { var req = new Request , res = new Response , pagelet = new All({ req: req, res: res, bigpipe: bigpipe }); req.httpVersionMajor = '2'; req.httpVersionMinor = '2'; app.bootstrap(pagelet, req, res); assume(pagelet.mode).to.equal('async'); }); it('adds the bootstrap pagelet to the parent', function () { var req = new Request , res = new Response , pagelet = new All({ req: req, res: res, bigpipe: bigpipe }); app.bootstrap(pagelet, req, res); assume(pagelet._bootstrap).to.be.an('object'); assume(pagelet._bootstrap).to.be.instanceof(require('bootstrap-pagelet')); }); it('calls .init on the parent pagelet', function (done) { var req = new Request , res = new Response , pagelet = new (All.extend({ init: function () { assume(arguments).to.have.length(0); done(); } }))({ req: req, res: res, bigpipe: bigpipe }); app.bootstrap(pagelet, req, res); }); it('calls .initialize on the parent pagelet', function (done) { var req = new Request , res = new Response , pagelet = new (All.extend({ initialize: function () { assume(arguments).to.have.length(0); done(); } }))({ req: req, res: res, bigpipe: bigpipe }); app.bootstrap(pagelet, req, res); }); it('calls .initialize (async) on the parent pagelet', function (done) { var req = new Request , res = new Response , pagelet = new (All.extend({ init: function () { done(); }, initialize: function (next) { assume(arguments).to.have.length(1); assume(next).to.be.a('function'); next(); } }))({ req: req, res: res, bigpipe: bigpipe }); app.bootstrap(pagelet, req, res); }); }); });