From f712f4bea6aa4e2b15c7a42f8bb893c26220b39f Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Sat, 3 Jun 2017 11:48:35 +0200 Subject: [PATCH 1/6] #150 Group the tests by features, refactor tailor instance creation into function --- tests/tailor.js | 1373 +++++++++++++++++++++++------------------------ 1 file changed, 682 insertions(+), 691 deletions(-) diff --git a/tests/tailor.js b/tests/tailor.js index d4b50ca..2a3079a 100644 --- a/tests/tailor.js +++ b/tests/tailor.js @@ -28,31 +28,34 @@ describe('Tailor', () => { }); } - beforeEach((done) => { - const tailor = new Tailor({ - fetchContext: mockContext, - pipeDefinition: () => new Buffer(''), - fetchTemplate: (request, parseTemplate) => { - const template = mockTemplate(request); - const childTemplate = mockChildTemplate(request); - if (template) { - return parseTemplate(template, childTemplate).then((parsedTemplate) => { - cacheTemplate(template); - return parsedTemplate; - }); - } else { - const error = new Error(); - error.presentable = '
error template
'; - return Promise.reject(error); - } - }, - pipeInstanceName: 'p', - pipeAttributes: (attributes) => { - return { - id: attributes.id - }; + const createTailorInstance = ({ maxAssetLinks = 1 } = {}) => new Tailor({ + fetchContext: mockContext, + maxAssetLinks, + pipeDefinition: () => new Buffer(''), + fetchTemplate: (request, parseTemplate) => { + const template = mockTemplate(request); + const childTemplate = mockChildTemplate(request); + if (template) { + return parseTemplate(template, childTemplate).then((parsedTemplate) => { + cacheTemplate(template); + return parsedTemplate; + }); + } else { + const error = new Error(); + error.presentable = '
error template
'; + return Promise.reject(error); } - }); + }, + pipeInstanceName: 'p', + pipeAttributes: (attributes) => { + return { + id: attributes.id + }; + } + }); + + beforeEach((done) => { + const tailor = createTailorInstance(); mockContext.returns(Promise.resolve({})); server = http.createServer(tailor.requestHandler); server.listen(8080, 'localhost', done); @@ -66,380 +69,352 @@ describe('Tailor', () => { server.close(done); }); - it('should return 500 if the layout wasn\'t found', (done) => { - mockTemplate.returns(false); - getResponse('http://localhost:8080/missing-template').then((response) => { - assert.equal(response.statusCode, 500); - }).then(done, done); - }); - - it('should render with presentable error template content', (done) => { - mockTemplate.returns(false); - getResponse('http://localhost:8080/missing-template').then((response) => { - assert.equal(response.statusCode, 500); - assert.equal(response.body, '
error template
'); - }).then(done, done); - }); + describe('Basic Features::Tailor', () => { + it('should return 500 with presentable error if the layout wasn\'t found', (done) => { + mockTemplate.returns(false); + getResponse('http://localhost:8080/missing-template').then((response) => { + assert.equal(response.statusCode, 500); + assert.equal(response.body, '
error template
'); + }).then(done, done); + }); + it('should stream content from http and https fragments', (done) => { - it('should stream content from http and https fragments', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'hello'); - nock('https://fragment') - .get('/1').reply(200, 'hello'); + nock('http://fragment:9000') + .get('/2').reply(200, 'world'); - nock('http://fragment:9000') - .get('/2').reply(200, 'world'); + mockTemplate + .returns( + '' + + '' + ); - mockTemplate - .returns( - '' + - '' - ); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.statusCode, 200); + assert.equal( + response.body, + '' + + '' + + '' + + 'hello' + + 'world' + + '' + + '' + ); + }).then(done, done); - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.statusCode, 200); - assert.equal( - response.body, - '' + - '' + - '' + - 'hello' + - 'world' + - '' + - '' - ); - }).then(done, done); + }); - }); + it('should support async fragments', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'hello'); - it('should return response code and location header ' + - 'of the 1st primary fragment', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'hello') - .get('/2').reply(300, 'world', { 'Location': 'https://redirect' }) - .get('/3').reply(500, '!'); - - mockTemplate - .returns( - '' + - '' + - '' - ); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.statusCode, 300); - assert.equal(response.headers.location, 'https://redirect'); - }).then(done, done); - }); + mockTemplate + .returns(''); - it('should forward headers to fragment', (done) => { - - const headers = { - 'X-Zalando-Custom': 'test', - 'Referer': 'https://google.com', - 'Accept-Language': 'en-gb', - 'User-Agent': 'MSIE6', - 'X-Wrong-Header': 'should not be forwarded', - 'Cookie': 'value' - }; - - const expectedHeaders = { - 'X-Zalando-Custom': 'test', - 'Referer': 'https://google.com', - 'Accept-Language': 'en-gb', - 'User-Agent': 'MSIE6' - }; - - nock('https://fragment', { - reqheaders: expectedHeaders, - badheaders: ['X-Wrong-Header', 'Cookie'] - }).get('/').reply(200); - - mockTemplate - .returns(''); - - http.get({ - hostname: 'localhost', - path: '/test', - port: 8080, - headers: headers - }, (response) => { - response.resume(); - done(); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '' + + '' + + 'hello' + + '' + + '' + + '' + ); + }).then(done, done); }); + it('should support script based fragments for inserting in head', (done) => { + nock('https://fragment') + .get('/yes').reply(200, 'yes'); + + mockTemplate + .returns(''); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + 'yes' + + '' + + '' + + ''+ + '' + ); + }).then(done, done); + }); }); - it('should disable browser cache', (done) => { - nock('https://fragment').get('/1').reply(200, 'hello'); + describe('Headers::Tailor ', () => { + it('should return response code and location header of the 1st primary fragment', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'hello') + .get('/2').reply(300, 'world', { 'Location': 'https://redirect' }) + .get('/3').reply(500, '!'); - mockTemplate - .returns(''); + mockTemplate + .returns( + '' + + '' + + '' + ); - getResponse('http://localhost:8080/test').then((response) => { - const headers = response.headers; - assert.equal('no-cache, no-store, must-revalidate', headers['cache-control']); - assert.equal('no-cache', headers['pragma']); - }).then(done, done); - }); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.statusCode, 300); + assert.equal(response.headers.location, 'https://redirect'); + }).then(done, done); + }); - it('should set timeout for a fragment request', (done) => { - nock('https://fragment') - .get('/1').socketDelay(101).reply(200, 'hello') - .get('/2').socketDelay(3001).reply(200, 'world'); + it('should forward headers to fragment', (done) => { + + const headers = { + 'X-Zalando-Custom': 'test', + 'Referer': 'https://google.com', + 'Accept-Language': 'en-gb', + 'User-Agent': 'MSIE6', + 'X-Wrong-Header': 'should not be forwarded', + 'Cookie': 'value' + }; + + const expectedHeaders = { + 'X-Zalando-Custom': 'test', + 'Referer': 'https://google.com', + 'Accept-Language': 'en-gb', + 'User-Agent': 'MSIE6' + }; + + nock('https://fragment', { + reqheaders: expectedHeaders, + badheaders: ['X-Wrong-Header', 'Cookie'] + }).get('/').reply(200); - mockTemplate - .returns( - '' + - '' - ); + mockTemplate + .returns(''); + + http.get({ + hostname: 'localhost', + path: '/test', + port: 8080, + headers: headers + }, (response) => { + response.resume(); + done(); + }); - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, ''); - }).then(done, done); - }); + }); - it('should return 500 in case of primary timeout', (done) => { - nock('https://fragment') - .get('/1').socketDelay(101).reply(200, 'hello'); + it('should disable browser cache', (done) => { + nock('https://fragment').get('/1').reply(200, 'hello'); - mockTemplate - .returns( - '' - ); + mockTemplate + .returns(''); - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.statusCode, 500); - }).then(done, done); + getResponse('http://localhost:8080/test').then((response) => { + const headers = response.headers; + assert.equal('no-cache, no-store, must-revalidate', headers['cache-control']); + assert.equal('no-cache', headers['pragma']); + }).then(done, done); + }); }); + + describe('Timeout::Tailor ', () => { + it('should set timeout for a fragment request', (done) => { + nock('https://fragment') + .get('/1').socketDelay(101).reply(200, 'hello') + .get('/2').socketDelay(3001).reply(200, 'world'); - it('should return 500 in case of primary error if fallback is not specified', (done) => { - nock('https://fragment') - .get('/1').replyWithError('panic!'); + mockTemplate + .returns( + '' + + '' + ); - mockTemplate - .returns( - '' - ); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, ''); + }).then(done, done); + }); - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.statusCode, 500); - }).then(done, done); - }); + it('should return 500 in case of primary timeout', (done) => { + nock('https://fragment') + .get('/1').socketDelay(101).reply(200, 'hello'); - it('should fetch the fallback fragment when specified', (done) => { - nock('https://fragment'). - get('/1').reply(500, 'Internal Server Error'); - nock('https://fragment'). - get('/fallback').reply(200, 'Fallback fragment'); - - mockTemplate - .returns( - '' + - '' - ); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.statusCode, 200); - }).then(done, done); - }); + mockTemplate + .returns( + '' + ); - it('should return 500 if both primary and fallback fragment is not reachable', (done) => { - nock('https://fragment'). - get('/1').replyWithError('panic!'); - nock('https://fragment'). - get('/fallback').reply(500, 'Internal Server Error'); - - mockTemplate - .returns( - ' ' + - '' - ); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.statusCode, 500); - }).then(done, done); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.statusCode, 500); + }).then(done, done); + }); }); + describe('Fallback::Tailor ', () => { + it('should return 500 in case of primary error if fallback is not specified', (done) => { + nock('https://fragment') + .get('/1').replyWithError('panic!'); - it('should insert link to css from fragment link header', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'hello', { - 'Link': '; rel="stylesheet",; rel="fragment-script"' - }); + mockTemplate + .returns( + '' + ); - mockTemplate - .returns(''); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '' + - '' + - 'hello' + - '' + - '' + - '' - ); - }).then(done, done); - }); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.statusCode, 500); + }).then(done, done); + }); - it('should call the pipe start and end with custom pipe attributes', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'hello', { - 'Link': '; rel="fragment-script"' - }); + it('should fetch the fallback fragment when specified', (done) => { + nock('https://fragment'). + get('/1').reply(500, 'Internal Server Error'); + nock('https://fragment'). + get('/fallback').reply(200, 'Fallback fragment'); - mockTemplate - .returns(''); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - 'hello' + - '' + - '' - ); - }).then(done, done); - }); + mockTemplate + .returns( + '' + + '' + ); - it('should use loadCSS for async fragments', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'hello', { - 'Link': '; rel="stylesheet",; rel="fragment-script"' - }); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.statusCode, 200); + }).then(done, done); + }); - mockTemplate - .returns(''); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '' + - 'hello' + - '' + - '' - ); - }).then(done, done); - }); + it('should return 500 if both primary and fallback fragment is not reachable', (done) => { + nock('https://fragment'). + get('/1').replyWithError('panic!'); + nock('https://fragment'). + get('/fallback').reply(500, 'Internal Server Error'); - it('should insert link to css and require js from fragment x-amz-meta-link header', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'hello', { - 'X-AMZ-META-LINK': '; rel="stylesheet",; rel="fragment-script"' - }); - - mockTemplate - .returns(''); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '' + - '' + - 'hello' + - '' + - '' + - '' - ); - }).then(done, done); - }); + mockTemplate + .returns( + ' ' + + '' + ); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.statusCode, 500); + }).then(done, done); + }); - it('should support async fragments', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'hello'); - - mockTemplate - .returns(''); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '' + - '' + - 'hello' + - '' + - '' + - '' - ); - }).then(done, done); }); - it('should replace fragment attributes with the one from context', (done) => { - nock('https://fragment') - .get('/yes').reply(200, 'yes'); + describe('Link::Tailor: ', () => { + it('should insert link to css from fragment link header', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'hello', { + 'Link': '; rel="stylesheet",; rel="fragment-script"' + }); - mockTemplate - .returns(''); + mockTemplate + .returns(''); - const contextObj = { - 'f-1' : { - src : 'https://fragment/yes', - primary: false, - async: true - } - }; - mockContext.returns(Promise.resolve(contextObj)); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.statusCode, 200); - assert.equal(response.body, - '' + - '' + - '' + - '' + - '' + - 'yes' + - '' + - '' + - '' - ); - }).then(done, done); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '' + + '' + + 'hello' + + '' + + '' + + '' + ); + }).then(done, done); + }); + + it('should use loadCSS for async fragments', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'hello', { + 'Link': '; rel="stylesheet",; rel="fragment-script"' + }); + + mockTemplate + .returns(''); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '' + + 'hello' + + '' + + '' + ); + }).then(done, done); + }); + + it('should insert link to css and require js from fragment x-amz-meta-link header', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'hello', { + 'X-AMZ-META-LINK': '; rel="stylesheet",; rel="fragment-script"' + }); + + mockTemplate + .returns(''); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '' + + '' + + 'hello' + + '' + + '' + + '' + ); + }).then(done, done); + }); }); + + describe('Attributes and Context::Tailor', () => { + it('should call the pipe start and end with custom pipe attributes', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'hello', { + 'Link': '; rel="fragment-script"' + }); + + mockTemplate + .returns(''); - it('should not mutate the template with the context', (done) => { - nock('https://fragment') - .get('/yes').reply(200, 'yes') - .get('/no').reply(200, 'no'); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + 'hello' + + '' + + '' + ); + }).then(done, done); + }); - mockTemplate - .returns(''); + it('should replace fragment attributes with the one from context', (done) => { + nock('https://fragment') + .get('/yes').reply(200, 'yes'); - const contextObj = { - 'f-1' : { - src : 'https://fragment/yes', - primary: false, - async: true - } - }; - mockContext.returns(Promise.resolve(contextObj)); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.statusCode, 200); - assert.equal(response.body, - '' + - '' + - '' + - '' + - '' + - 'yes' + - '' + - '' + - '' - ); - - // Second request - mockContext.returns(Promise.resolve({})); - mockTemplate.returns(cacheTemplate.args[0][0]); + mockTemplate + .returns(''); + + const contextObj = { + 'f-1' : { + src : 'https://fragment/yes', + primary: false, + async: true + } + }; + mockContext.returns(Promise.resolve(contextObj)); getResponse('http://localhost:8080/test').then((response) => { assert.equal(response.statusCode, 200); @@ -449,356 +424,396 @@ describe('Tailor', () => { '' + '' + '' + - 'no' + + 'yes' + '' + '' + '' ); }).then(done, done); }); - }); - it('should support script based fragments for inserting in head', (done) => { - nock('https://fragment') - .get('/yes').reply(200, 'yes'); - - mockTemplate - .returns(''); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - 'yes' + - '' + - '' + - '' + - '' - ); - }).then(done, done); - }); + it('should not mutate the template with the context', (done) => { + nock('https://fragment') + .get('/yes').reply(200, 'yes') + .get('/no').reply(200, 'no'); - it('should support base templates using slots', (done) => { - mockTemplate - .returns( - '' + - '' + - '' - ); - - mockChildTemplate - .returns( - '' - ); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '' + - '' + - '' - ); - }).then(done, done); - }); + mockTemplate + .returns(''); - it('should support custom slots for shuffling the nodes', (done) => { - mockTemplate - .returns( - '' + - '' + - '' + - '' + - '' + - '' - ); - - mockChildTemplate - .returns( - '' + - '' + - '

Last

' - ); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '' + - '' + - '' + - '

Last

' + - '' + - '' - ); - }).then(done, done); - }); + const contextObj = { + 'f-1' : { + src : 'https://fragment/yes', + primary: false, + async: true + } + }; + mockContext.returns(Promise.resolve(contextObj)); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.statusCode, 200); + assert.equal(response.body, + '' + + '' + + '' + + '' + + '' + + 'yes' + + '' + + '' + + '' + ); - it('should insert default slots if unnamed slot is present in parent template', (done) => { - mockTemplate - .returns( - '' + - '' + - '' + - '' + - '

blah

' + - '' - ); - - mockChildTemplate.returns('

hello

'); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '

hello

' + - '

blah

' + - '' + - '' - ); - }).then(done, done); + // Second request + mockContext.returns(Promise.resolve({})); + mockTemplate.returns(cacheTemplate.args[0][0]); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.statusCode, 200); + assert.equal(response.body, + '' + + '' + + '' + + '' + + '' + + 'no' + + '' + + '' + + '' + ); + }).then(done, done); + }); + }); }); - it('should warn if there are duplicate unnamed slots', (done) => { - sinon.stub(console, 'warn'); - mockTemplate.returns(''); + describe('Slots::Tailor ', () => { + it('should support base templates using slots', (done) => { + mockTemplate + .returns( + '' + + '' + + '' + ); + + mockChildTemplate + .returns( + '' + ); - http.get('http://localhost:8080/test', () => { - assert.equal(console.warn.callCount, 1); - console.warn.restore(); - done(); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '' + + '' + + '' + ); + }).then(done, done); }); - }); - it('should use the fallback slot nodes if present in the template', (done) => { - mockTemplate - .returns( - '' + - '

hello

' + - '
' - ); - - mockChildTemplate.returns(''); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '' + - '

hello

' + - '' + - '' - ); - }).then(done, done); - }); + it('should support custom slots for shuffling the nodes', (done) => { + mockTemplate + .returns( + '' + + '' + + '' + + '' + + '' + + '' + ); - it('should override the fallback slot nodes with slotted nodes from child template', (done) => { - mockTemplate - .returns( - '' + - '

hello

' + - '
' - ); - - mockChildTemplate.returns('

child

'); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '' + - '

child

' + - '' + - '' - ); - }).then(done, done); - }); + mockChildTemplate + .returns( + '' + + '' + + '

Last

' + ); - it('should include the child templates after the lastchild of body', (done) => { - mockTemplate.returns('

'); - - mockChildTemplate - .returns( - '
' + - '

' + - '
' - ); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '

' + - '

' + - '' + - '' - ); - }).then(done, done); - }); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '' + + '' + + '' + + '

Last

' + + '' + + '' + ); + }).then(done, done); + }); - it('should flatten nested fragments', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'hello') - .get('/2').reply(200, 'world'); - - mockTemplate - .returns( - '' + - '' + - '' + - '' - ); - mockChildTemplate.returns(''); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '' + - 'hello' + - '' + - '' + - 'world' + - '' + - '' + - '' - ); - }).then(done, done); - }); + it('should insert default slots if unnamed slot is present in parent template', (done) => { + mockTemplate + .returns( + '' + + '' + + '' + + '' + + '

blah

' + + '' + ); + + mockChildTemplate.returns('

hello

'); - it('should return 500 even if primary fragment is nested and timed out', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'hello') - .get('/2').socketDelay(101).reply(200, 'world'); - - mockTemplate - .returns( - '' + - '' + - '' + - '' - ); - - http.get('http://localhost:8080/test', (response) => { - assert.equal(response.statusCode, 500); - done(); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '

hello

' + + '

blah

' + + '' + + '' + ); + }).then(done, done); }); - }); - it('should unzip the fragment response if it is compressed', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'hello') - .defaultReplyHeaders({ - 'content-encoding': 'gzip', - }) - .get('/2') - .reply(200, ()=> { - return zlib.gzipSync('GZIPPED'); + it('should warn if there are duplicate unnamed slots', (done) => { + sinon.stub(console, 'warn'); + mockTemplate.returns(''); + + http.get('http://localhost:8080/test', () => { + assert.equal(console.warn.callCount, 1); + console.warn.restore(); + done(); }); + }); - mockTemplate - .returns( - '' + - '' - ); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '' + - 'hello' + - '' + - '' + - 'GZIPPED' + - '' + - '' + - '' - ); - }).then(done, done); - }); + it('should use the fallback slot nodes if present in the template', (done) => { + mockTemplate + .returns( + '' + + '

hello

' + + '
' + ); - it('should close the streams properly during unzping error', (done) => { - nock('https://fragment') - .defaultReplyHeaders({ - 'content-encoding': 'gzip', - }) - .get('/2') - .reply(200, ()=> { - return new Error('GZIP Error'); - }); + mockChildTemplate.returns(''); - mockTemplate - .returns( - '' - ); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.body, - '' + - '' + - '' + - '' + - '' + - '' + - '' - ); - }).then(done, done); - }); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '' + + '

hello

' + + '' + + '' + ); + }).then(done, done); + }); - it('should send Link headers for preloading if primary fragment exists', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'non-primary', { - 'Link': '; rel="stylesheet",; rel="fragment-script"' - }) - .get('/2').reply(200, 'primary', { - 'Link': '; rel="stylesheet",; rel="fragment-script"' - }); + it('should override the fallback slot nodes with slotted nodes from child template', (done) => { + mockTemplate + .returns( + '' + + '

hello

' + + '
' + ); + + mockChildTemplate.returns('

child

'); - mockTemplate - .returns( - '' + - '' - ); + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '' + + '

child

' + + '' + + '' + ); + }).then(done, done); + }); - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.headers.link, '; rel="preload"; as="style"; nopush,; rel="preload"; as="script"; nopush; crossorigin'); - }).then(done, done); }); - it('should not send crossorigin in Link headers for same origin scripts', (done) => { - nock('http://fragment') - .get('/').reply(200, 'primary', { - 'Link': '; rel="stylesheet",; rel="fragment-script"' + describe('Nested Fragments::Tailor ', () => { + it('should include the child templates after the lastchild of body', (done) => { + mockTemplate.returns('

'); + + mockChildTemplate + .returns( + '
' + + '

' + + '
' + ); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '

' + + '

' + + '' + + '' + ); + }).then(done, done); + }); + + it('should flatten nested fragments', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'hello') + .get('/2').reply(200, 'world'); + + mockTemplate + .returns( + '' + + '' + + '' + + '' + ); + mockChildTemplate.returns(''); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '' + + 'hello' + + '' + + '' + + 'world' + + '' + + '' + + '' + ); + }).then(done, done); + }); + + it('should return 500 even if primary fragment is nested and timed out', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'hello') + .get('/2').socketDelay(101).reply(200, 'world'); + + mockTemplate + .returns( + '' + + '' + + '' + + '' + ); + + http.get('http://localhost:8080/test', (response) => { + assert.equal(response.statusCode, 500); + done(); }); + }); + }); + + describe('Zip::Tailor ', () => { + it('should unzip the fragment response if it is compressed', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'hello') + .defaultReplyHeaders({ + 'content-encoding': 'gzip', + }) + .get('/2') + .reply(200, ()=> { + return zlib.gzipSync('GZIPPED'); + }); - mockTemplate.returns(''); + mockTemplate + .returns( + '' + + '' + ); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '' + + 'hello' + + '' + + '' + + 'GZIPPED' + + '' + + '' + + '' + ); + }).then(done, done); + }); - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.headers.link, '; rel="preload"; as="style"; nopush,; rel="preload"; as="script"; nopush; '); - }).then(done, done); + it('should close the streams properly during unzping error', (done) => { + nock('https://fragment') + .defaultReplyHeaders({ + 'content-encoding': 'gzip', + }) + .get('/2') + .reply(200, ()=> { + return new Error('GZIP Error'); + }); + + mockTemplate + .returns( + '' + ); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.body, + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + }).then(done, done); + }); }); - it('shouldn\'t send preloading headers if primary fragment doesn\'t exist', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'hello', { - 'Link': '; rel="stylesheet",; rel="fragment-script"' - }); + describe('Headers::Tailor', () => { + it('should send Link headers for preloading if primary fragment exists', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'non-primary', { + 'Link': '; rel="stylesheet",; rel="fragment-script"' + }) + .get('/2').reply(200, 'primary', { + 'Link': '; rel="stylesheet",; rel="fragment-script"' + }); - mockTemplate.returns(''); + mockTemplate + .returns( + '' + + '' + ); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.headers.link, '; rel="preload"; as="style"; nopush,; rel="preload"; as="script"; nopush; crossorigin'); + }).then(done, done); + }); + + it('should not send crossorigin in Link headers for same origin scripts', (done) => { + nock('http://fragment') + .get('/').reply(200, 'primary', { + 'Link': '; rel="stylesheet",; rel="fragment-script"' + }); - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.headers.link, undefined); - }).then(done, done); + mockTemplate.returns(''); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.headers.link, '; rel="preload"; as="style"; nopush,; rel="preload"; as="script"; nopush; '); + }).then(done, done); + }); + + it('shouldn\'t send preloading headers if primary fragment doesn\'t exist', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'hello', { + 'Link': '; rel="stylesheet",; rel="fragment-script"' + }); + + mockTemplate.returns(''); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.headers.link, undefined); + }).then(done, done); + }); }); describe('without option `maxAssetLinks` provided', () => { @@ -848,31 +863,7 @@ describe('Tailor', () => { describe('with `maxAssetLinks` set to `3`', () => { beforeEach((done) => { - const tailor2 = new Tailor({ - maxAssetLinks: 3, - fetchContext: mockContext, - pipeDefinition: () => new Buffer(''), - fetchTemplate: (request, parseTemplate) => { - const template = mockTemplate(request); - const childTemplate = mockChildTemplate(request); - if (template) { - return parseTemplate(template, childTemplate).then((parsedTemplate) => { - cacheTemplate(template); - return parsedTemplate; - }); - } else { - const error = new Error(); - error.presentable = '
error template
'; - return Promise.reject(error); - } - }, - pipeInstanceName: 'p', - pipeAttributes: (attributes) => { - return { - id: attributes.id - }; - } - }); + const tailor2 = createTailorInstance({ maxAssetLinks: 3 }); mockContext.returns(Promise.resolve({})); serverCustomOptions = http.createServer(tailor2.requestHandler); serverCustomOptions.listen(8081, 'localhost', done); From 20f794d0d68b13be48142b2979e44d1b02c02b2d Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Mon, 5 Jun 2017 10:24:43 +0200 Subject: [PATCH 2/6] #150 Remove duplicate test and suites --- lib/fragment.js | 4 +- tests/tailor.js | 128 ++++++++++++++++++------------------------------ 2 files changed, 50 insertions(+), 82 deletions(-) diff --git a/lib/fragment.js b/lib/fragment.js index a569ad4..01526d7 100644 --- a/lib/fragment.js +++ b/lib/fragment.js @@ -171,7 +171,9 @@ module.exports = class Fragment extends EventEmitter { insertStart () { this.styleRefs.forEach(uri => { this.stream.write( - this.attributes.async ? `` : `` + this.attributes.async + ? `` + : `` ); }); diff --git a/tests/tailor.js b/tests/tailor.js index 2a3079a..c316a03 100644 --- a/tests/tailor.js +++ b/tests/tailor.js @@ -222,6 +222,52 @@ describe('Tailor', () => { assert.equal('no-cache', headers['pragma']); }).then(done, done); }); + + it('should send Link headers for preloading if primary fragment exists', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'non-primary', { + 'Link': '; rel="stylesheet",; rel="fragment-script"' + }) + .get('/2').reply(200, 'primary', { + 'Link': '; rel="stylesheet",; rel="fragment-script"' + }); + + mockTemplate + .returns( + '' + + '' + ); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.headers.link, '; rel="preload"; as="style"; nopush,; rel="preload"; as="script"; nopush; crossorigin'); + }).then(done, done); + }); + + it('should not send crossorigin in Link headers for same origin scripts', (done) => { + nock('http://fragment') + .get('/').reply(200, 'primary', { + 'Link': '; rel="stylesheet",; rel="fragment-script"' + }); + + mockTemplate.returns(''); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.headers.link, '; rel="preload"; as="style"; nopush,; rel="preload"; as="script"; nopush; '); + }).then(done, done); + }); + + it('shouldn\'t send preloading headers if primary fragment doesn\'t exist', (done) => { + nock('https://fragment') + .get('/1').reply(200, 'hello', { + 'Link': '; rel="stylesheet",; rel="fragment-script"' + }); + + mockTemplate.returns(''); + + getResponse('http://localhost:8080/test').then((response) => { + assert.equal(response.headers.link, undefined); + }).then(done, done); + }); }); describe('Timeout::Tailor ', () => { @@ -400,39 +446,7 @@ describe('Tailor', () => { }).then(done, done); }); - it('should replace fragment attributes with the one from context', (done) => { - nock('https://fragment') - .get('/yes').reply(200, 'yes'); - - mockTemplate - .returns(''); - - const contextObj = { - 'f-1' : { - src : 'https://fragment/yes', - primary: false, - async: true - } - }; - mockContext.returns(Promise.resolve(contextObj)); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.statusCode, 200); - assert.equal(response.body, - '' + - '' + - '' + - '' + - '' + - 'yes' + - '' + - '' + - '' - ); - }).then(done, done); - }); - - it('should not mutate the template with the context', (done) => { + it('should get attributes from context and not mutate the template with the context', (done) => { nock('https://fragment') .get('/yes').reply(200, 'yes') .get('/no').reply(200, 'no'); @@ -768,54 +782,6 @@ describe('Tailor', () => { }); }); - describe('Headers::Tailor', () => { - it('should send Link headers for preloading if primary fragment exists', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'non-primary', { - 'Link': '; rel="stylesheet",; rel="fragment-script"' - }) - .get('/2').reply(200, 'primary', { - 'Link': '; rel="stylesheet",; rel="fragment-script"' - }); - - mockTemplate - .returns( - '' + - '' - ); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.headers.link, '; rel="preload"; as="style"; nopush,; rel="preload"; as="script"; nopush; crossorigin'); - }).then(done, done); - }); - - it('should not send crossorigin in Link headers for same origin scripts', (done) => { - nock('http://fragment') - .get('/').reply(200, 'primary', { - 'Link': '; rel="stylesheet",; rel="fragment-script"' - }); - - mockTemplate.returns(''); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.headers.link, '; rel="preload"; as="style"; nopush,; rel="preload"; as="script"; nopush; '); - }).then(done, done); - }); - - it('shouldn\'t send preloading headers if primary fragment doesn\'t exist', (done) => { - nock('https://fragment') - .get('/1').reply(200, 'hello', { - 'Link': '; rel="stylesheet",; rel="fragment-script"' - }); - - mockTemplate.returns(''); - - getResponse('http://localhost:8080/test').then((response) => { - assert.equal(response.headers.link, undefined); - }).then(done, done); - }); - }); - describe('without option `maxAssetLinks` provided', () => { it('should handle the first fragment-script Header Link only', (done) => { nock('https://fragment') From b1adb4808006caf8aa96bb55c2c1c2a653f5e684 Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Tue, 6 Jun 2017 17:39:17 +0200 Subject: [PATCH 3/6] Update Buffer to use Node v8 syntax (#154) * #149 Update Buffer to use Node v8 syntax --- .travis.yml | 1 + index.js | 2 +- lib/fragment.js | 2 +- lib/serializer.js | 2 +- perf/fragment-server.js | 2 +- tests/handle-tag.js | 2 +- tests/streams/buffer-concat-stream.js | 4 ++-- tests/streams/content-length-stream.js | 6 +++--- tests/tailor.events.js | 2 +- tests/tailor.js | 2 +- 10 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index ddb0b82..b7aa58e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: node_js node_js: - 6.1.0 - 7.1.0 + - 8.0.0 cache: yarn: true directories: diff --git a/index.js b/index.js index ad15259..385e5be 100644 --- a/index.js +++ b/index.js @@ -45,7 +45,7 @@ module.exports = class Tailor extends EventEmitter { memoizedDefinition = `\n\n`); + return Buffer.from(`${memoizedDefinition}var ${pipeInstanceName}=${PIPE_DEFINITION}\n`); }; const requestOptions = Object.assign({ diff --git a/lib/fragment.js b/lib/fragment.js index 01526d7..82b1f70 100644 --- a/lib/fragment.js +++ b/lib/fragment.js @@ -105,7 +105,7 @@ module.exports = class Fragment extends EventEmitter { ); // Async fragments are piped later on the page if (this.attributes.async) { - return new Buffer(``); + return Buffer.from(``); } return this.stream; } diff --git a/lib/serializer.js b/lib/serializer.js index ce1c402..c716f7f 100644 --- a/lib/serializer.js +++ b/lib/serializer.js @@ -32,7 +32,7 @@ module.exports = class CustomSerializer extends Serializer { */ pushBuffer() { if (this.html !== '') { - this.serializedList.push(new Buffer(this.html)); + this.serializedList.push(Buffer.from(this.html)); this.html = ''; } } diff --git a/perf/fragment-server.js b/perf/fragment-server.js index 60cd3a8..77febd3 100644 --- a/perf/fragment-server.js +++ b/perf/fragment-server.js @@ -3,7 +3,7 @@ const http = require('http'); const SIZE = 1024; const CHARS = '0123456789abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY'; -const buffer = new Buffer(SIZE); +const buffer = Buffer.alloc(SIZE); for (let i = 0; i < SIZE; i++ ) { buffer.write(CHARS.charAt(Math.round(Math.random() * (CHARS.length - 1))), i); } diff --git a/tests/handle-tag.js b/tests/handle-tag.js index 46a174e..23485e7 100644 --- a/tests/handle-tag.js +++ b/tests/handle-tag.js @@ -23,7 +23,7 @@ describe('Handle tag', () => { return Promise.reject('Error fetching template'); } }, - pipeDefinition: () => new Buffer(''), + pipeDefinition: () => Buffer.from(''), handledTags: ['x-tag'], handleTag: mockHandleTag }); diff --git a/tests/streams/buffer-concat-stream.js b/tests/streams/buffer-concat-stream.js index 0a427c9..853f08e 100644 --- a/tests/streams/buffer-concat-stream.js +++ b/tests/streams/buffer-concat-stream.js @@ -9,8 +9,8 @@ describe('BufferConcatStream', () => { assert(result.toString(), 'foobar'); done(); }); - st.write(new Buffer('foo')); - st.end(new Buffer('bar')); + st.write(Buffer.from('foo')); + st.end(Buffer.from('bar')); }); }); diff --git a/tests/streams/content-length-stream.js b/tests/streams/content-length-stream.js index 0d68d97..d6ea8be 100644 --- a/tests/streams/content-length-stream.js +++ b/tests/streams/content-length-stream.js @@ -10,8 +10,8 @@ describe('ContentLengthStream', () => { assert(contentLength, 'foobar'.length); done(); }); - st.write(new Buffer('foo')); - st.end(new Buffer('bar')); + st.write(Buffer.from('foo')); + st.end(Buffer.from('bar')); }); it('is a Transform stream', () => { @@ -20,7 +20,7 @@ describe('ContentLengthStream', () => { }); it('passes through data chunks', (done) => { - const chunk = new Buffer('foo'); + const chunk = Buffer.from('foo'); const st = new ContentLengthStream(() => {}); st.on('data', (data) => { assert.equal(data, chunk); diff --git a/tests/tailor.events.js b/tests/tailor.events.js index ac0c153..d1f9323 100644 --- a/tests/tailor.events.js +++ b/tests/tailor.events.js @@ -16,7 +16,7 @@ describe('Tailor events', () => { beforeEach((done) => { tailor = new Tailor({ fetchContext: mockContext, - pipeDefinition: () => new Buffer(''), + pipeDefinition: () => Buffer.from(''), fetchTemplate: (request, parseTemplate) => { const template = mockTemplate(request); if (template) { diff --git a/tests/tailor.js b/tests/tailor.js index c316a03..d939ec5 100644 --- a/tests/tailor.js +++ b/tests/tailor.js @@ -31,7 +31,7 @@ describe('Tailor', () => { const createTailorInstance = ({ maxAssetLinks = 1 } = {}) => new Tailor({ fetchContext: mockContext, maxAssetLinks, - pipeDefinition: () => new Buffer(''), + pipeDefinition: () => Buffer.from(''), fetchTemplate: (request, parseTemplate) => { const template = mockTemplate(request); const childTemplate = mockChildTemplate(request); From 5e646d89027b6ddd7e8efe266a9aeccc14bd0ad4 Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Thu, 8 Jun 2017 00:24:56 +0200 Subject: [PATCH 4/6] #158 Add unit test for fetch template.js --- mock-templates/base-template.html | 1 + mock-templates/test.html | 1 + tests/fetch-template.js | 50 +++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 mock-templates/base-template.html create mode 100644 mock-templates/test.html create mode 100644 tests/fetch-template.js diff --git a/mock-templates/base-template.html b/mock-templates/base-template.html new file mode 100644 index 0000000..4b56630 --- /dev/null +++ b/mock-templates/base-template.html @@ -0,0 +1 @@ +
base-template
\ No newline at end of file diff --git a/mock-templates/test.html b/mock-templates/test.html new file mode 100644 index 0000000..ed56825 --- /dev/null +++ b/mock-templates/test.html @@ -0,0 +1 @@ +
test
\ No newline at end of file diff --git a/tests/fetch-template.js b/tests/fetch-template.js new file mode 100644 index 0000000..81a720f --- /dev/null +++ b/tests/fetch-template.js @@ -0,0 +1,50 @@ +'use strict'; +const fetchTemplate = require('../lib/fetch-template'); +const assert = require('assert'); +const sinon = require('sinon'); + +describe('fetch-template', () => { + const mockParseTemplate = sinon.spy(); + let mockRequest; + + it('should be able to fetch the static template with absolute path', () => { + mockRequest = { + url: 'http://localhost:8080/test' + }; + + fetchTemplate('mock-templates')(mockRequest, mockParseTemplate) + .then(() => { + assert(mockParseTemplate.calledOnce); + return assert(mockParseTemplate.calledWith( + Buffer.from('
test
') + )); + }); + }); + + it('should be able to fetch template with relative path and baseTemplateFn', () => { + mockRequest = { + url: 'http://localhost:8080/base-template' + }; + const baseTemplateFn = () => 'base-template'; + + fetchTemplate('mock-templates', baseTemplateFn)(mockRequest, mockParseTemplate) + .then(() => { + assert(mockParseTemplate.calledOnce); + return assert(mockParseTemplate.calledWith( + Buffer.from('
base-template
') + )); + }); + }); + + it('should throw TEMPLATE_NOT_FOUND error for not present template', () => { + mockRequest = { + url: 'http://localhost:8080/test' + }; + + fetchTemplate('templates')(mockRequest, mockParseTemplate) + .catch(err => { + assert(err.code, 1); + assert(err.presentable, 'template not found'); + }); + }); +}); \ No newline at end of file From de6dbe492926c1bd86c97beeb3dab854c70c6717 Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Thu, 8 Jun 2017 22:06:23 +0200 Subject: [PATCH 5/6] #158 Add in-memory fragment templates for fetch template.js tests --- mock-templates/base-template.html | 1 - mock-templates/test.html | 1 - tests/fetch-template.js | 35 ++++++++++++++++++++++++------- 3 files changed, 27 insertions(+), 10 deletions(-) delete mode 100644 mock-templates/base-template.html delete mode 100644 mock-templates/test.html diff --git a/mock-templates/base-template.html b/mock-templates/base-template.html deleted file mode 100644 index 4b56630..0000000 --- a/mock-templates/base-template.html +++ /dev/null @@ -1 +0,0 @@ -
base-template
\ No newline at end of file diff --git a/mock-templates/test.html b/mock-templates/test.html deleted file mode 100644 index ed56825..0000000 --- a/mock-templates/test.html +++ /dev/null @@ -1 +0,0 @@ -
test
\ No newline at end of file diff --git a/tests/fetch-template.js b/tests/fetch-template.js index 81a720f..c481394 100644 --- a/tests/fetch-template.js +++ b/tests/fetch-template.js @@ -2,36 +2,55 @@ const fetchTemplate = require('../lib/fetch-template'); const assert = require('assert'); const sinon = require('sinon'); +const path = require('path'); +const fs = require('fs'); describe('fetch-template', () => { const mockParseTemplate = sinon.spy(); let mockRequest; + const templatePath = path.join(__dirname); + const testTemplatePath = `${templatePath}/test.html`; + const baseTemplatePath = `${templatePath}/base-template.html`; + + beforeEach(() => { + fs.writeFileSync(testTemplatePath, '
test
'); + fs.writeFileSync(baseTemplatePath, '
base-template
'); + }); + + afterEach(() => { + if (fs.existsSync(testTemplatePath)) { + fs.unlinkSync(testTemplatePath); + } + + if (fs.existsSync(baseTemplatePath)) { + fs.unlinkSync(baseTemplatePath); + } + }); it('should be able to fetch the static template with absolute path', () => { mockRequest = { url: 'http://localhost:8080/test' }; - fetchTemplate('mock-templates')(mockRequest, mockParseTemplate) + return fetchTemplate(templatePath)(mockRequest, mockParseTemplate) .then(() => { assert(mockParseTemplate.calledOnce); - return assert(mockParseTemplate.calledWith( - Buffer.from('
test
') - )); + return assert(mockParseTemplate.calledWith('
test
')); }); }); it('should be able to fetch template with relative path and baseTemplateFn', () => { mockRequest = { - url: 'http://localhost:8080/base-template' + url: 'http://localhost:8080/test' }; const baseTemplateFn = () => 'base-template'; - fetchTemplate('mock-templates', baseTemplateFn)(mockRequest, mockParseTemplate) + return fetchTemplate(templatePath, baseTemplateFn)(mockRequest, mockParseTemplate) .then(() => { - assert(mockParseTemplate.calledOnce); + assert(mockParseTemplate.called); return assert(mockParseTemplate.calledWith( - Buffer.from('
base-template
') + '
base-template
', + '
test
' )); }); }); From 80f2571af6ace6bcaceeadf04b63dabbf969c212 Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Fri, 9 Jun 2017 12:38:22 +0200 Subject: [PATCH 6/6] #158 Return the promise for async test --- tests/fetch-template.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fetch-template.js b/tests/fetch-template.js index c481394..b130a8c 100644 --- a/tests/fetch-template.js +++ b/tests/fetch-template.js @@ -60,10 +60,10 @@ describe('fetch-template', () => { url: 'http://localhost:8080/test' }; - fetchTemplate('templates')(mockRequest, mockParseTemplate) + return fetchTemplate('templates')(mockRequest, mockParseTemplate) .catch(err => { assert(err.code, 1); assert(err.presentable, 'template not found'); }); }); -}); \ No newline at end of file +});