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/9] #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/9] #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/9] 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/9] #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/9] #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/9] #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 +}); From f4876914be175073e6791df3c0b69655a2ac78c7 Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Sat, 2 Sep 2017 12:48:30 +0200 Subject: [PATCH 7/9] #172 #153 Use the node v8 promisify API to remove unnecessary code --- lib/fetch-template.js | 21 ++++++++++----------- package.json | 3 ++- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/fetch-template.js b/lib/fetch-template.js index 5b62aa3..892b8d5 100644 --- a/lib/fetch-template.js +++ b/lib/fetch-template.js @@ -3,6 +3,7 @@ const path = require('path'); const url = require('url'); const fs = require('fs'); +const promisify = require('util.promisify'); const TEMPLATE_ERROR = 0; const TEMPLATE_NOT_FOUND = 1; @@ -21,21 +22,19 @@ class TemplateError extends Error { } } + +/** + * Promisify the fs.readFile function + */ +const readFileP = promisify(fs.readFile); + /** * Read the file from File System * * @param {string} path */ -const readFile = (path) => - new Promise((resolve, reject) => { - fs.readFile(path, 'utf-8', (err, data) => { - if (err) { - reject(new TemplateError(err)); - return; - } - resolve(data); - }); - }); +const readFile = (path) => readFileP(path, 'utf-8') + .catch(err => new TemplateError(err)); /** * Returns the template path validating a exactly file or a directory @@ -106,7 +105,7 @@ module.exports = (templatesPath, baseTemplateFn) => if (templateStat.isFile || typeof baseTemplateFn !== 'function') { return parseTemplate(baseTemplate); } - + const templateName = baseTemplateFn(pathname); if (!templateName) { return parseTemplate(baseTemplate); diff --git a/package.json b/package.json index b601311..4d592e5 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "license": "MIT", "dependencies": { "http-link-header": "^0.8.0", - "parse5": "^3.0.2" + "parse5": "^3.0.2", + "util.promisify": "^1.0.0" }, "devDependencies": { "codecov": "^2.2.0", From f7c614209be9f07ed13504d3e85ac419b5e53d09 Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Sat, 2 Sep 2017 12:49:42 +0200 Subject: [PATCH 8/9] #172 Remove unnecessary space --- lib/fetch-template.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fetch-template.js b/lib/fetch-template.js index 892b8d5..b08846d 100644 --- a/lib/fetch-template.js +++ b/lib/fetch-template.js @@ -105,7 +105,7 @@ module.exports = (templatesPath, baseTemplateFn) => if (templateStat.isFile || typeof baseTemplateFn !== 'function') { return parseTemplate(baseTemplate); } - + const templateName = baseTemplateFn(pathname); if (!templateName) { return parseTemplate(baseTemplate); From a85b418d4f89487a5603404910f02e860c6f933b Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Mon, 4 Sep 2017 12:21:01 +0200 Subject: [PATCH 9/9] #172 #153 Add yarn lock file --- yarn.lock | 115 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 35 deletions(-) diff --git a/yarn.lock b/yarn.lock index 947547b..ce93403 100644 --- a/yarn.lock +++ b/yarn.lock @@ -158,10 +158,6 @@ babel-code-frame@^6.22.0: esutils "^2.0.2" js-tokens "^3.0.0" -balanced-match@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -184,13 +180,6 @@ boom@2.x.x: dependencies: hoek "2.x.x" -brace-expansion@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" - dependencies: - balanced-match "^0.4.1" - concat-map "0.0.1" - brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" @@ -361,13 +350,7 @@ debug@2.6.0: dependencies: ms "0.7.2" -debug@^2.2.0: - version "2.6.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.3.tgz#0f7eb8c30965ec08c72accfa0130c8b79984141d" - dependencies: - ms "0.7.2" - -debug@^2.6.8: +debug@^2.2.0, debug@^2.6.8: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: @@ -391,6 +374,13 @@ deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" +define-properties@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" + dependencies: + foreach "^2.0.5" + object-keys "^1.0.8" + del@^2.0.2: version "2.2.2" resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" @@ -434,6 +424,24 @@ end-of-stream@^1.0.0: dependencies: once "^1.4.0" +es-abstract@^1.5.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.8.1.tgz#fd85a3bdfa67786ce7be7e1584678e119cd70c04" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.1" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + +es-to-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" + dependencies: + is-callable "^1.1.1" + is-date-object "^1.0.1" + is-symbol "^1.0.1" + escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -586,6 +594,10 @@ flat-cache@^1.2.1: graceful-fs "^4.1.2" write "^0.2.1" +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -608,6 +620,10 @@ fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" +function-bind@^1.0.2, function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + generate-function@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" @@ -624,7 +640,7 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -glob@7.1.1, glob@^7.0.0: +glob@7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" dependencies: @@ -645,7 +661,7 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -712,6 +728,12 @@ has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" +has@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + dependencies: + function-bind "^1.0.2" + hawk@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" @@ -787,6 +809,14 @@ is-buffer@^1.0.2: version "1.1.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" +is-callable@^1.1.1, is-callable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -828,12 +858,22 @@ is-property@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + dependencies: + has "^1.0.1" + is-resolvable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" dependencies: tryit "^1.0.1" +is-symbol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" + is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -883,14 +923,7 @@ js-tokens@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" -js-yaml@3.x: - version "3.8.2" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.2.tgz#02d3e2c0f6beab20248d412c352203827d786721" - dependencies: - argparse "^1.0.7" - esprima "^3.1.1" - -js-yaml@^3.8.4: +js-yaml@3.x, js-yaml@^3.8.4: version "3.8.4" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6" dependencies: @@ -1076,13 +1109,7 @@ mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" -"minimatch@2 || 3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" - dependencies: - brace-expansion "^1.0.0" - -minimatch@^3.0.2, minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -1173,6 +1200,17 @@ object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" +object-keys@^1.0.8: + version "1.0.11" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" + +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + once@1.x, once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -1652,6 +1690,13 @@ util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" +util.promisify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + uuid@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"