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/2] #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/2] #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')