diff --git a/Gruntfile.js b/Gruntfile.js index 561405ad4e..6299d99b65 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -633,12 +633,12 @@ var _ = require('lodash'), // Custom test runner for our Casper.js functional tests // This really ought to be refactored into a separate grunt task module grunt.registerTask('spawnCasperJS', function (target) { - target = _.contains(['client', 'frontend', 'setup'], target) ? target + '/' : undefined; + target = _.contains(['client', 'setup'], target) ? target + '/' : undefined; var done = this.async(), options = ['host', 'noPort', 'port', 'email', 'password'], args = ['test'] - .concat(grunt.option('target') || target || ['client/', 'frontend/']) + .concat(grunt.option('target') || target || ['client/']) .concat(['--includes=base.js', '--log-level=debug', '--port=2369']); // Forward parameters from grunt to casperjs diff --git a/core/test/functional/frontend/error_test.js b/core/test/functional/frontend/error_test.js deleted file mode 100644 index 34e632b1e1..0000000000 --- a/core/test/functional/frontend/error_test.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Tests if RSS exists and is working - */ -/*globals CasperTest, casper, url */ -CasperTest.begin('Check post not found (404)', 2, function suite(test) { - casper.thenOpen(url + 'asdf/', function (response) { - test.assertEqual(response.status, 404, 'Response status should be 404.'); - test.assertSelectorHasText('.error-code', '404'); - }); -}, true); - -CasperTest.begin('Check frontend route not found (404)', 2, function suite(test) { - casper.thenOpen(url + 'asdf/asdf/', function (response) { - test.assertEqual(response.status, 404, 'Response status should be 404.'); - test.assertSelectorHasText('.error-code', '404'); - }); -}, true); - -CasperTest.begin('Check frontend tag route not found (404)', 2, function suite(test) { - casper.thenOpen(url + 'tag/asdf/', function (response) { - test.assertEqual(response.status, 404, 'Response status should be 404.'); - test.assertSelectorHasText('.error-code', '404'); - }); -}, true); diff --git a/core/test/functional/frontend/feed_test.js b/core/test/functional/frontend/feed_test.js deleted file mode 100644 index d3ad3f8f32..0000000000 --- a/core/test/functional/frontend/feed_test.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Tests if RSS exists and is working - */ -/*globals url, CasperTest, casper */ -CasperTest.begin('Ensure that RSS is available', 11, function suite(test) { - CasperTest.Routines.togglePermalinks.run('off'); - casper.thenOpen(url + 'rss/', function (response) { - var content = this.getHTML(), - siteTitle = '<![CDATA[Test Blog]]>', - siteDescription = '', - siteUrl = 'http://127.0.0.1:2369/', - postTitle = '', - postStart = 'You\'re live!', - postEnd = 'you think :)

]]>
', - postLink = 'http://127.0.0.1:2369/welcome-to-ghost/', - postCreator = ''; - - test.assertEqual(response.status, 200, 'Response status should be 200.'); - test.assert(content.indexOf('= 0, 'Feed should contain = 0, 'Feed should contain blog title.'); - test.assert(content.indexOf(siteDescription) >= 0, 'Feed should contain blog description.'); - test.assert(content.indexOf(siteUrl) >= 0, 'Feed should contain link to blog.'); - test.assert(content.indexOf(postTitle) >= 0, 'Feed should contain welcome post title.'); - test.assert(content.indexOf(postStart) >= 0, 'Feed should contain start of welcome post content.'); - test.assert(content.indexOf(postEnd) >= 0, 'Feed should contain end of welcome post content.'); - test.assert(content.indexOf(postLink) >= 0, 'Feed should have link to the welcome post.'); - test.assert(content.indexOf(postCreator) >= 0, 'Welcome post should have Test User as the creator.'); - test.assert(content.indexOf('') >= 0, 'Feed should contain '); - }); -}, false); - -CasperTest.begin('Ensure that author element is not included. Only dc:creator', 3, function suite(test) { - CasperTest.Routines.togglePermalinks.run('off'); - casper.thenOpen(url + 'rss/', function (response) { - var content = this.getHTML(), - author = '', - postCreator = ''; - - test.assertEqual(response.status, 200, 'Response status should be 200.'); - test.assert(content.indexOf(author) < 0, 'Author element should not be included'); - test.assert(content.indexOf(postCreator) >= 0, 'Welcome post should have Test User as the creator.'); - }); -}, false); - -CasperTest.begin('Ensures dated permalinks works with RSS', 2, function suite(test) { - CasperTest.Routines.togglePermalinks.run('on'); - casper.thenOpen(url + 'rss/', function (response) { - var content = this.getHTML(), - today = new Date(), - dd = ('0' + today.getDate()).slice(-2), - mm = ('0' + (today.getMonth() + 1)).slice(-2), - yyyy = today.getFullYear(), - postLink = '/' + yyyy + '/' + mm + '/' + dd + '/welcome-to-ghost/'; - - test.assertEqual(response.status, 200, 'Response status should be 200.'); - test.assert(content.indexOf(postLink) >= 0, 'Feed should have dated permalink.'); - }); - CasperTest.Routines.togglePermalinks.run('off'); -}, false); - -CasperTest.begin('Ensure that character set is UTF-8 for RSS feed', 1, function suite(test) { - CasperTest.Routines.togglePermalinks.run('off'); - casper.thenOpen(url + 'rss/', function (response) { - test.assertEqual(response.headers.get('Content-Type'), 'text/xml; charset=utf-8', 'Content type should include UTF-8 character set encoding.'); - }); -}, false); diff --git a/core/test/functional/frontend/home_test.js b/core/test/functional/frontend/home_test.js deleted file mode 100644 index 3cfebd3c75..0000000000 --- a/core/test/functional/frontend/home_test.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Tests the homepage - */ - -/*globals CasperTest, casper, url */ -CasperTest.begin('Home page loads', 3, function suite(test) { - casper.start(url, function then() { - test.assertTitle('Test Blog', 'The homepage should have a title and it should be "Test Blog"'); - test.assertExists('.content .post', 'There is at least one post on this page'); - test.assertSelectorHasText('.poweredby', 'Proudly published with Ghost'); - }); -}, true); - -CasperTest.begin('Test helpers on homepage', 3, function suite(test) { - casper.start(url, function then() { - // body class - test.assertExists('body.home-template', 'body_class outputs correct home-template class'); - // post class - test.assertExists('article.post', 'post_class outputs correct post class'); - test.assertExists('article.tag-getting-started', 'post_class outputs correct tag class'); - }); -}, true); - -CasperTest.begin('Test navigating to Post', 4, function suite(test) { - casper.thenOpen(url, function then() { - var lastPost = '.content article:last-of-type', - lastPostLink = lastPost + ' .post-title a'; - - test.assertExists(lastPost, 'there is a last child on the page'); - test.assertSelectorHasText(lastPostLink, 'Welcome to Ghost', 'Is correct post'); - - casper.then(function testLink() { - var link = this.evaluate(function (lastPostLink) { - return document.querySelector(lastPostLink).getAttribute('href'); - }, lastPostLink); - - test.assert(link === '/welcome-to-ghost/', 'Has correct link'); - }); - - casper.thenClick(lastPostLink); - - casper.waitForResource(/welcome-to-ghost/).then(function (resource) { - test.assert(resource.status === 200, 'resource got 200'); - }); - }); -}, true); - -CasperTest.begin('Test navigating to Post with date permalink', 4, function suite(test) { - CasperTest.Routines.togglePermalinks.run('on'); - casper.thenOpen(url, function then() { - var lastPost = '.content article:last-of-type', - lastPostLink = lastPost + ' .post-title a', - today = new Date(), - dd = ('0' + today.getDate()).slice(-2), - mm = ('0' + (today.getMonth() + 1)).slice(-2), - yyyy = today.getFullYear(), - postLink = '/' + yyyy + '/' + mm + '/' + dd + '/welcome-to-ghost/'; - - test.assertExists(lastPost, 'there is a last child on the page'); - test.assertSelectorHasText(lastPostLink, 'Welcome to Ghost', 'Is correct post'); - - casper.then(function testLink() { - var link = this.evaluate(function (lastPostLink) { - return document.querySelector(lastPostLink).getAttribute('href'); - }, lastPostLink); - - test.assert(link === postLink, 'Has correct link'); - }); - - casper.thenClick(lastPostLink); - - casper.waitForResource(postLink).then(function (resource) { - test.assert(resource.status === 200, 'resource got 200'); - }); - }); - CasperTest.Routines.togglePermalinks.run('off'); -}, false); diff --git a/core/test/functional/frontend/post_test.js b/core/test/functional/frontend/post_test.js deleted file mode 100644 index 232c71efb1..0000000000 --- a/core/test/functional/frontend/post_test.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Tests the default post page - */ - -/*globals CasperTest, casper, url */ - -// Tests when permalinks is set to date -CasperTest.begin('Post page does not load as slug', 2, function suite(test) { - CasperTest.Routines.togglePermalinks.run('on'); - casper.thenOpen(url + 'welcome-to-ghost', function then() { - test.assertTitle('404 — Page Not Found', 'The post should return 404 page'); - test.assertElementCount('.content .post', 0, 'There is no post on this page'); - }); - CasperTest.Routines.togglePermalinks.run('off'); -}, false); - -CasperTest.begin('Post page loads', 3, function suite(test) { - casper.thenOpen(url + 'welcome-to-ghost', function then() { - test.assertTitle('Welcome to Ghost', 'The post should have a title and it should be "Welcome to Ghost"'); - test.assertElementCount('.content .post', 1, 'There is exactly one post on this page'); - test.assertSelectorHasText('.poweredby', 'Proudly published with Ghost'); - }); -}, true); - -CasperTest.begin('Test helpers on welcome post', 4, function suite(test) { - casper.start(url + 'welcome-to-ghost', function then() { - // body class - test.assertExists('body.post-template', 'body_class outputs correct post-template class'); - test.assertExists('body.tag-getting-started', 'body_class outputs correct tag class'); - // post class - test.assertExists('article.post', 'post_class outputs correct post class'); - test.assertExists('article.tag-getting-started', 'post_class outputs correct tag class'); - }); -}, true); diff --git a/core/test/functional/frontend/route_test.js b/core/test/functional/frontend/route_test.js deleted file mode 100644 index d24bb5dc47..0000000000 --- a/core/test/functional/frontend/route_test.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Tests archive page routing - */ - -/*globals CasperTest, casper, url */ -CasperTest.begin('Redirects page 1 request', 1, function suite(test) { - casper.thenOpen(url + 'page/1/', function then() { - test.assertEqual(casper.getCurrentUrl().indexOf('page/'), -1, 'Should be redirected to "/".'); - }); -}, true); diff --git a/core/test/functional/routes/frontend_test.js b/core/test/functional/routes/frontend_test.js index 2a1122d382..79a5e9fafb 100644 --- a/core/test/functional/routes/frontend_test.js +++ b/core/test/functional/routes/frontend_test.js @@ -8,6 +8,7 @@ var request = require('supertest'), should = require('should'), moment = require('moment'), + cheerio = require('cheerio'), testUtils = require('../../utils'), ghost = require('../../../../core'); @@ -40,189 +41,278 @@ describe('Frontend Routing', function () { }); }); - after(function (done) { - testUtils.clearData().then(function () { - done(); - }).catch(done); - }); + after(testUtils.teardown); - describe('Home', function () { - it('should respond with html', function (done) { - request.get('/') - .expect('Content-Type', /html/) - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(200) - .end(doEnd(done)); + describe('Test with Initial Fixtures', function () { + after(testUtils.teardown); + + describe('Error', function () { + it('should 404 for unknown post', function (done) { + request.get('/spectacular/') + .expect('Cache-Control', testUtils.cacheRules['private']) + .expect(404) + .expect(/Page Not Found/) + .end(doEnd(done)); + }); + + it('should 404 for unknown frontend route', function (done) { + request.get('/spectacular/marvellous/') + .expect('Cache-Control', testUtils.cacheRules['private']) + .expect(404) + .expect(/Page Not Found/) + .end(doEnd(done)); + }); + + it('should 404 for unknown tag', function (done) { + request.get('/tag/spectacular/') + .expect('Cache-Control', testUtils.cacheRules['private']) + .expect(404) + .expect(/Page Not Found/) + .end(doEnd(done)); + }); + + it('should 404 for unknown author', function (done) { + request.get('/author/spectacular/') + .expect('Cache-Control', testUtils.cacheRules['private']) + .expect(404) + .expect(/Page Not Found/) + .end(doEnd(done)); + }); }); - it('should not have as second page', function (done) { - request.get('/page/2/') - .expect('Location', '/') - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(302) - .end(doEnd(done)); + describe('Home', function () { + it('should respond with html', function (done) { + request.get('/') + .expect('Content-Type', /html/) + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(200) + .end(function (err, res) { + if (err) { + return done(err); + } + + var $ = cheerio.load(res.text); + + should.not.exist(res.headers['x-cache-invalidate']); + should.not.exist(res.headers['X-CSRF-Token']); + should.not.exist(res.headers['set-cookie']); + should.exist(res.headers.date); + + $('title').text().should.equal('Ghost'); + $('.content .post').length.should.equal(1); + $('.poweredby').text().should.equal('Proudly published with Ghost'); + $('body.home-template').length.should.equal(1); + $('article.post').length.should.equal(1); + $('article.tag-getting-started').length.should.equal(1); + + done(); + }); + }); + + it('should not have as second page', function (done) { + request.get('/page/2/') + .expect('Location', '/') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(302) + .end(doEnd(done)); + }); + }); + + describe('Single post', function () { + it('should redirect without slash', function (done) { + request.get('/welcome-to-ghost') + .expect('Location', '/welcome-to-ghost/') + .expect('Cache-Control', testUtils.cacheRules.year) + .expect(301) + .end(doEnd(done)); + }); + + it('should redirect uppercase', function (done) { + request.get('/Welcome-To-Ghost/') + .expect('Location', '/welcome-to-ghost/') + .expect(301) + .end(doEnd(done)); + }); + + it('should respond with html for valid url', function (done) { + request.get('/welcome-to-ghost/') + .expect('Content-Type', /html/) + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(200) + .end(function (err, res) { + if (err) { + return done(err); + } + + var $ = cheerio.load(res.text); + + should.not.exist(res.headers['x-cache-invalidate']); + should.not.exist(res.headers['X-CSRF-Token']); + should.not.exist(res.headers['set-cookie']); + should.exist(res.headers.date); + + $('title').text().should.equal('Welcome to Ghost'); + $('.content .post').length.should.equal(1); + $('.poweredby').text().should.equal('Proudly published with Ghost'); + $('body.post-template').length.should.equal(1); + $('body.tag-getting-started').length.should.equal(1); + $('article.post').length.should.equal(1); + $('article.tag-getting-started').length.should.equal(1); + + done(); + }); + }); + + it('should not work with date permalinks', function (done) { + // get today's date + var date = moment().format('YYYY/MM/DD'); + + request.get('/' + date + '/welcome-to-ghost/') + .expect('Cache-Control', testUtils.cacheRules['private']) + .expect(404) + .expect(/Page Not Found/) + .end(doEnd(done)); + }); + }); + + describe('Post edit', function () { + it('should redirect without slash', function (done) { + request.get('/welcome-to-ghost/edit') + .expect('Location', '/welcome-to-ghost/edit/') + .expect('Cache-Control', testUtils.cacheRules.year) + .expect(301) + .end(doEnd(done)); + }); + + it('should redirect to editor', function (done) { + request.get('/welcome-to-ghost/edit/') + .expect('Location', '/ghost/editor/1/') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(302) + .end(doEnd(done)); + }); + + it('should 404 for non-edit parameter', function (done) { + request.get('/welcome-to-ghost/notedit/') + .expect('Cache-Control', testUtils.cacheRules['private']) + .expect(404) + .expect(/Page Not Found/) + .end(doEnd(done)); + }); + }); + + describe('Static assets', function () { + it('should retrieve shared assets', function (done) { + request.get('/shared/img/user-image.png') + .expect('Cache-Control', testUtils.cacheRules.hour) + .expect(200) + .end(doEnd(done)); + }); + + it('should retrieve theme assets', function (done) { + request.get('/assets/css/screen.css') + .expect('Cache-Control', testUtils.cacheRules.year) + .expect(200) + .end(doEnd(done)); + }); + + it('should retrieve built assets', function (done) { + request.get('/ghost/scripts/vendor-dev.js') + .expect('Cache-Control', testUtils.cacheRules.year) + .expect(200) + .end(doEnd(done)); + }); + + it('should retrieve default robots.txt', function (done) { + request.get('/robots.txt') + .expect('Cache-Control', testUtils.cacheRules.hour) + .expect('ETag', /[0-9a-f]{32}/i) + .expect(200) + .end(doEnd(done)); + }); + + it('should retrieve default favicon.ico', function (done) { + request.get('/favicon.ico') + .expect('Cache-Control', testUtils.cacheRules.day) + .expect('ETag', /[0-9a-f]{32}/i) + .expect(200) + .end(doEnd(done)); + }); + + // at the moment there is no image fixture to test + // it('should retrieve image assets', function (done) { + // request.get('/content/images/some.jpg') + // .expect('Cache-Control', testUtils.cacheRules.year) + // .end(doEnd(done)); + // }); }); }); - describe('Welcome post', function () { - it('should redirect without slash', function (done) { - request.get('/welcome-to-ghost') - .expect('Location', '/welcome-to-ghost/') - .expect('Cache-Control', testUtils.cacheRules.year) - .expect(301) - .end(doEnd(done)); - }); - - it('should redirect uppercase', function (done) { - request.get('/Welcome-To-Ghost/') - .expect('Location', '/welcome-to-ghost/') - .expect(301) - .end(doEnd(done)); - }); - - it('should respond with html for valid url', function (done) { - request.get('/welcome-to-ghost/') - .expect('Content-Type', /html/) - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(200) - .end(doEnd(done)); - }); - - it('should not work with date permalinks', function (done) { - // get today's date - var date = moment().format('YYYY/MM/DD'); - - request.get('/' + date + '/welcome-to-ghost/') - // .expect('Cache-Control', testUtils.cacheRules['private']) - .expect(404) - .expect(/Page Not Found/) - .end(doEnd(done)); - }); - - it('should 404 for unknown post', function (done) { - request.get('/spectacular/') - .expect('Cache-Control', testUtils.cacheRules['private']) - .expect(404) - .expect(/Page Not Found/) - .end(doEnd(done)); - }); - }); - - describe('Post edit', function () { - it('should redirect without slash', function (done) { - request.get('/welcome-to-ghost/edit') - .expect('Location', '/welcome-to-ghost/edit/') - .expect('Cache-Control', testUtils.cacheRules.year) - .expect(301) - .end(doEnd(done)); - }); - - it('should redirect to editor', function (done) { - request.get('/welcome-to-ghost/edit/') - .expect('Location', '/ghost/editor/1/') - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(302) - .end(doEnd(done)); - }); - - it('should 404 for non-edit parameter', function (done) { - request.get('/welcome-to-ghost/notedit/') - .expect('Cache-Control', testUtils.cacheRules['private']) - .expect(404) - .expect(/Page Not Found/) - .end(doEnd(done)); - }); - }); - - // we'll use X-Forwarded-Proto: https to simulate an 'https://' request behind a proxy - describe('HTTPS', function () { - var forkedGhost, request; + describe('Static page', function () { before(function (done) { - var configTestHttps = testUtils.fork.config(); - configTestHttps.forceAdminSSL = {redirect: false}; - configTestHttps.urlSSL = 'https://localhost/'; - - testUtils.fork.ghost(configTestHttps, 'testhttps') - .then(function (child) { - forkedGhost = child; - request = require('supertest'); - request = request(configTestHttps.url.replace(/\/$/, '')); - }).then(done).catch(done); + testUtils.initData().then(function () { + return testUtils.fixtures.insertPosts(); + }).then(function () { + done(); + }); }); - after(function (done) { - if (forkedGhost) { - forkedGhost.kill(done); - } - }); + after(testUtils.teardown); - it('should set links to url over non-HTTPS', function (done) { - request.get('/') - .expect(200) - .expect(//) - .expect(/Ghost<\/a\>/) - .end(doEnd(done)); - }); - - it('should set links to urlSSL over HTTPS', function (done) { - request.get('/') - .set('X-Forwarded-Proto', 'https') - .expect(200) - .expect(//) - .expect(/Ghost<\/a\>/) - .end(doEnd(done)); - }); - }); - - describe('RSS', function () { it('should redirect without slash', function (done) { - request.get('/rss') - .expect('Location', '/rss/') + request.get('/static-page-test') + .expect('Location', '/static-page-test/') .expect('Cache-Control', testUtils.cacheRules.year) .expect(301) .end(doEnd(done)); }); it('should respond with xml', function (done) { - request.get('/rss/') - .expect('Content-Type', /xml/) + request.get('/static-page-test/') + .expect('Content-Type', /html/) .expect('Cache-Control', testUtils.cacheRules['public']) .expect(200) .end(doEnd(done)); }); + }); - it('should not have as second page', function (done) { - request.get('/rss/2/') - // TODO this should probably redirect straight to /rss/ with 301? - .expect('Location', '/rss/1/') - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(302) - .end(doEnd(done)); + describe('Post with Ghost in the url', function () { + before(function (done) { + testUtils.initData().then(function () { + return testUtils.fixtures.insertPosts(); + }).then(function () { + done(); + }); }); - it('should get redirected to /rss/ from /feed/', function (done) { - request.get('/feed/') - .expect('Location', '/rss/') - .expect('Cache-Control', testUtils.cacheRules.year) - .expect(301) + after(testUtils.teardown); + + // All of Ghost's admin depends on the /ghost/ in the url to work properly + // Badly formed regexs can cause breakage if a post slug starts with the 5 letters ghost + it('should retrieve a blog post with ghost at the start of the url', function (done) { + request.get('/ghostly-kitchen-sink/') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(200) .end(doEnd(done)); }); }); - // ### The rest of the tests require more data - describe('Archive pages', function () { // Add enough posts to trigger pages for both the archive (5 pp) and rss (15 pp) // insertPosts adds 5 published posts, 1 draft post, 1 published static page and one draft page // we then insert with max 11 which ensures we have 16 published posts before(function (done) { - testUtils.fixtures.insertPosts().then(function () { + testUtils.initData().then(function () { + return testUtils.fixtures.insertPosts(); + }).then(function () { return testUtils.fixtures.insertMorePosts(9); }).then(function () { done(); }).catch(done); }); + after(testUtils.teardown); + it('should redirect without slash', function (done) { request.get('/page/2') .expect('Location', '/page/2/') @@ -265,271 +355,133 @@ describe('Frontend Routing', function () { }); }); - describe('RSS pages', function () { + describe('RSS', function () { before(function (done) { - testUtils.fixtures.insertMorePosts(2).then(function () { + testUtils.initData().then(function () { + return testUtils.fixtures.overrideOwnerUser(); + }).then(function () { done(); - }).catch(done); + }); }); + + after(testUtils.teardown); + it('should redirect without slash', function (done) { - request.get('/rss/2') - .expect('Location', '/rss/2/') + request.get('/rss') + .expect('Location', '/rss/') .expect('Cache-Control', testUtils.cacheRules.year) .expect(301) .end(doEnd(done)); }); it('should respond with xml', function (done) { + request.get('/rss/') + .expect('Content-Type', 'text/xml; charset=utf-8') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(200) + .end(function (err, res) { + if (err) { + return done(err); + } + + should.not.exist(res.headers['x-cache-invalidate']); + should.not.exist(res.headers['X-CSRF-Token']); + should.not.exist(res.headers['set-cookie']); + should.exist(res.headers.date); + + var content = res.text, + siteTitle = '<![CDATA[Ghost]]>', + siteDescription = '', + siteUrl = 'http://127.0.0.1:2369/', + postTitle = '', + postStart = 'You\'re live!', + postEnd = 'you think :)

]]>
', + postLink = 'http://127.0.0.1:2369/welcome-to-ghost/', + postCreator = '', + author = ''; + + content.indexOf('').should.be.above(0); + content.indexOf(author).should.be.below(0); + content.indexOf(postCreator).should.be.above(0); + + done(); + }); + }); + + it('should not have as second page', function (done) { request.get('/rss/2/') - .expect('Content-Type', /xml/) + // TODO this should probably redirect straight to /rss/ with 301? + .expect('Location', '/rss/1/') .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(200) + .expect(302) .end(doEnd(done)); }); - it('should redirect page 1', function (done) { - request.get('/rss/1/') + it('should get redirected to /rss/ from /feed/', function (done) { + request.get('/feed/') .expect('Location', '/rss/') - .expect('Cache-Control', testUtils.cacheRules['public']) - // TODO: This should probably be a 301? - .expect(302) - .end(doEnd(done)); - }); - - it('should redirect to last page if page too high', function (done) { - request.get('/rss/3/') - .expect('Location', '/rss/2/') - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(302) - .end(doEnd(done)); - }); - - it('should redirect to first page if page too low', function (done) { - request.get('/rss/0/') - .expect('Location', '/rss/') - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(302) - .end(doEnd(done)); - }); - }); - - describe('Tag based RSS pages', function () { - it('should redirect without slash', function (done) { - request.get('/tag/getting-started/rss') - .expect('Location', '/tag/getting-started/rss/') .expect('Cache-Control', testUtils.cacheRules.year) .expect(301) .end(doEnd(done)); }); - it('should respond with xml', function (done) { - request.get('/tag/getting-started/rss/') - .expect('Content-Type', /xml/) - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(200) - .end(doEnd(done)); - }); + describe('RSS pages', function () { + before(function (done) { + testUtils.fixtures.insertPosts().then(function () { + return testUtils.fixtures.insertMorePosts(11); + }).then(function () { + done(); + }).catch(done); + }); + it('should redirect without slash', function (done) { + request.get('/rss/2') + .expect('Location', '/rss/2/') + .expect('Cache-Control', testUtils.cacheRules.year) + .expect(301) + .end(doEnd(done)); + }); - it('should redirect page 1', function (done) { - request.get('/tag/getting-started/rss/1/') - .expect('Location', '/tag/getting-started/rss/') - .expect('Cache-Control', testUtils.cacheRules['public']) - // TODO: This should probably be a 301? - .expect(302) - .end(doEnd(done)); - }); + it('should respond with xml', function (done) { + request.get('/rss/2/') + .expect('Content-Type', /xml/) + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(200) + .end(doEnd(done)); + }); - it('should redirect to last page if page too high', function (done) { - request.get('/tag/getting-started/rss/2/') - .expect('Location', '/tag/getting-started/rss/1/') - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(302) - .end(doEnd(done)); - }); + it('should redirect page 1', function (done) { + request.get('/rss/1/') + .expect('Location', '/rss/') + .expect('Cache-Control', testUtils.cacheRules['public']) + // TODO: This should probably be a 301? + .expect(302) + .end(doEnd(done)); + }); - it('should redirect to first page if page too low', function (done) { - request.get('/tag/getting-started/rss/0/') - .expect('Location', '/tag/getting-started/rss/') - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(302) - .end(doEnd(done)); - }); - }); + it('should redirect to last page if page too high', function (done) { + request.get('/rss/3/') + .expect('Location', '/rss/2/') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(302) + .end(doEnd(done)); + }); - describe('Author based RSS pages', function () { - it('should redirect without slash', function (done) { - request.get('/author/ghost-owner/rss') - .expect('Location', '/author/ghost-owner/rss/') - .expect('Cache-Control', testUtils.cacheRules.year) - .expect(301) - .end(doEnd(done)); - }); - - it('should respond with xml', function (done) { - request.get('/author/ghost-owner/rss/') - .expect('Content-Type', /xml/) - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(200) - .end(doEnd(done)); - }); - - it('should redirect page 1', function (done) { - request.get('/author/ghost-owner/rss/1/') - .expect('Location', '/author/ghost-owner/rss/') - .expect('Cache-Control', testUtils.cacheRules['public']) - // TODO: This should probably be a 301? - .expect(302) - .end(doEnd(done)); - }); - - it('should redirect to last page if page too high', function (done) { - request.get('/author/ghost-owner/rss/3/') - .expect('Location', '/author/ghost-owner/rss/2/') - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(302) - .end(doEnd(done)); - }); - - it('should redirect to first page if page too low', function (done) { - request.get('/author/ghost-owner/rss/0/') - .expect('Location', '/author/ghost-owner/rss/') - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(302) - .end(doEnd(done)); - }); - }); - - describe('Static page', function () { - it('should redirect without slash', function (done) { - request.get('/static-page-test') - .expect('Location', '/static-page-test/') - .expect('Cache-Control', testUtils.cacheRules.year) - .expect(301) - .end(doEnd(done)); - }); - - it('should respond with xml', function (done) { - request.get('/static-page-test/') - .expect('Content-Type', /html/) - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(200) - .end(doEnd(done)); - }); - }); - - describe('Post with Ghost in the url', function () { - // All of Ghost's admin depends on the /ghost/ in the url to work properly - // Badly formed regexs can cause breakage if a post slug starts with the 5 letters ghost - it('should retrieve a blog post with ghost at the start of the url', function (done) { - request.get('/ghostly-kitchen-sink/') - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(200) - .end(doEnd(done)); - }); - }); - - describe('Static assets', function () { - it('should retrieve shared assets', function (done) { - request.get('/shared/img/user-image.png') - .expect('Cache-Control', testUtils.cacheRules.hour) - .expect(200) - .end(doEnd(done)); - }); - - it('should retrieve theme assets', function (done) { - request.get('/assets/css/screen.css') - .expect('Cache-Control', testUtils.cacheRules.year) - .expect(200) - .end(doEnd(done)); - }); - - it('should retrieve built assets', function (done) { - request.get('/ghost/scripts/vendor-dev.js') - .expect('Cache-Control', testUtils.cacheRules.year) - .expect(200) - .end(doEnd(done)); - }); - - it('should retrieve default robots.txt', function (done) { - request.get('/robots.txt') - .expect('Cache-Control', testUtils.cacheRules.hour) - .expect('ETag', /[0-9a-f]{32}/i) - .expect(200) - .end(doEnd(done)); - }); - - it('should retrieve default favicon.ico', function (done) { - request.get('/favicon.ico') - .expect('Cache-Control', testUtils.cacheRules.day) - .expect('ETag', /[0-9a-f]{32}/i) - .expect(200) - .end(doEnd(done)); - }); - - // at the moment there is no image fixture to test - // it('should retrieve image assets', function (done) { - // request.get('/content/images/some.jpg') - // .expect('Cache-Control', testUtils.cacheRules.year) - // .end(doEnd(done)); - // }); - }); - - describe('Tag pages', function () { - // Add enough posts to trigger tag pages - before(function (done) { - testUtils.clearData().then(function () { - // we initialise data, but not a user. No user should be required for navigating the frontend - return testUtils.initData(); - }).then(function () { - return testUtils.fixtures.insertPosts(); - }).then(function () { - return testUtils.fixtures.insertMorePosts(22); - }).then(function () { - return testUtils.fixtures.insertMorePostsTags(22); - }).then(function () { - done(); - }).catch(done); - }); - - it('should redirect without slash', function (done) { - request.get('/tag/injection/page/2') - .expect('Location', '/tag/injection/page/2/') - .expect('Cache-Control', testUtils.cacheRules.year) - .expect(301) - .end(doEnd(done)); - }); - - it('should respond with html', function (done) { - request.get('/tag/injection/page/2/') - .expect('Content-Type', /html/) - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(200) - .end(doEnd(done)); - }); - - it('should redirect page 1', function (done) { - request.get('/tag/injection/page/1/') - .expect('Location', '/tag/injection/') - .expect('Cache-Control', testUtils.cacheRules['public']) - // TODO: This should probably be a 301? - .expect(302) - .end(doEnd(done)); - }); - - it('should redirect to last page if page too high', function (done) { - request.get('/tag/injection/page/4/') - .expect('Location', '/tag/injection/page/3/') - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(302) - .end(doEnd(done)); - }); - - it('should redirect to first page if page too low', function (done) { - request.get('/tag/injection/page/0/') - .expect('Location', '/tag/injection/') - .expect('Cache-Control', testUtils.cacheRules['public']) - .expect(302) - .end(doEnd(done)); + it('should redirect to first page if page too low', function (done) { + request.get('/rss/0/') + .expect('Location', '/rss/') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(302) + .end(doEnd(done)); + }); }); }); @@ -542,12 +494,14 @@ describe('Frontend Routing', function () { }).then(function () { return testUtils.fixtures.insertPosts(); }).then(function () { - return testUtils.fixtures.insertMorePosts(10); + return testUtils.fixtures.insertMorePosts(9); }).then(function () { done(); }).catch(done); }); + after(testUtils.teardown); + it('should redirect without slash', function (done) { request.get('/author/ghost-owner/page/2') .expect('Location', '/author/ghost-owner/page/2/') @@ -588,26 +542,237 @@ describe('Frontend Routing', function () { .expect(302) .end(doEnd(done)); }); + + describe('Author based RSS pages', function () { + it('should redirect without slash', function (done) { + request.get('/author/ghost-owner/rss') + .expect('Location', '/author/ghost-owner/rss/') + .expect('Cache-Control', testUtils.cacheRules.year) + .expect(301) + .end(doEnd(done)); + }); + + it('should respond with xml', function (done) { + request.get('/author/ghost-owner/rss/') + .expect('Content-Type', /xml/) + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(200) + .end(doEnd(done)); + }); + + it('should redirect page 1', function (done) { + request.get('/author/ghost-owner/rss/1/') + .expect('Location', '/author/ghost-owner/rss/') + .expect('Cache-Control', testUtils.cacheRules['public']) + // TODO: This should probably be a 301? + .expect(302) + .end(doEnd(done)); + }); + + it('should redirect to last page if page too high', function (done) { + request.get('/author/ghost-owner/rss/2/') + .expect('Location', '/author/ghost-owner/rss/1/') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(302) + .end(doEnd(done)); + }); + + it('should redirect to first page if page too low', function (done) { + request.get('/author/ghost-owner/rss/0/') + .expect('Location', '/author/ghost-owner/rss/') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(302) + .end(doEnd(done)); + }); + }); }); - // ### The rest of the tests switch to date permalinks + describe('Tag pages', function () { + // Add enough posts to trigger tag pages + before(function (done) { + testUtils.initData().then(function () { + return testUtils.fixtures.insertPosts(); + }).then(function () { + return testUtils.fixtures.insertMorePosts(22); + }).then(function () { + return testUtils.fixtures.insertMorePostsTags(22); + }).then(function () { + done(); + }).catch(done); + }); -// describe('Date permalinks', function () { -// before(function (done) { -// // Only way to swap permalinks setting is to login and visit the URL because -// // poking the database doesn't work as settings are cached -// }); -// -// it('should load a post with date permalink', function (done) { -// -// // get today's date -// var date = moment().format('YYYY/MM/DD'); -// -// -// request.get('/' + date + '/welcome-to-ghost/') -// .expect(200) -// .expect('Content-Type', /html/) -// .end(doEnd(done)); -// }); -// }); + after(testUtils.teardown); + + it('should redirect without slash', function (done) { + request.get('/tag/injection/page/2') + .expect('Location', '/tag/injection/page/2/') + .expect('Cache-Control', testUtils.cacheRules.year) + .expect(301) + .end(doEnd(done)); + }); + + it('should respond with html', function (done) { + request.get('/tag/injection/page/2/') + .expect('Content-Type', /html/) + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(200) + .end(doEnd(done)); + }); + + it('should redirect page 1', function (done) { + request.get('/tag/injection/page/1/') + .expect('Location', '/tag/injection/') + .expect('Cache-Control', testUtils.cacheRules['public']) + // TODO: This should probably be a 301? + .expect(302) + .end(doEnd(done)); + }); + + it('should redirect to last page if page too high', function (done) { + request.get('/tag/injection/page/4/') + .expect('Location', '/tag/injection/page/3/') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(302) + .end(doEnd(done)); + }); + + it('should redirect to first page if page too low', function (done) { + request.get('/tag/injection/page/0/') + .expect('Location', '/tag/injection/') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(302) + .end(doEnd(done)); + }); + + describe('Tag based RSS pages', function () { + it('should redirect without slash', function (done) { + request.get('/tag/getting-started/rss') + .expect('Location', '/tag/getting-started/rss/') + .expect('Cache-Control', testUtils.cacheRules.year) + .expect(301) + .end(doEnd(done)); + }); + + it('should respond with xml', function (done) { + request.get('/tag/getting-started/rss/') + .expect('Content-Type', /xml/) + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(200) + .end(doEnd(done)); + }); + + it('should redirect page 1', function (done) { + request.get('/tag/getting-started/rss/1/') + .expect('Location', '/tag/getting-started/rss/') + .expect('Cache-Control', testUtils.cacheRules['public']) + // TODO: This should probably be a 301? + .expect(302) + .end(doEnd(done)); + }); + + it('should redirect to last page if page too high', function (done) { + request.get('/tag/getting-started/rss/2/') + .expect('Location', '/tag/getting-started/rss/1/') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(302) + .end(doEnd(done)); + }); + + it('should redirect to first page if page too low', function (done) { + request.get('/tag/getting-started/rss/0/') + .expect('Location', '/tag/getting-started/rss/') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(302) + .end(doEnd(done)); + }); + }); + }); + + // we'll use X-Forwarded-Proto: https to simulate an 'https://' request behind a proxy + describe('HTTPS', function () { + var forkedGhost, request; + before(function (done) { + var configTestHttps = testUtils.fork.config(); + configTestHttps.forceAdminSSL = {redirect: false}; + configTestHttps.urlSSL = 'https://localhost/'; + + testUtils.fork.ghost(configTestHttps, 'testhttps') + .then(function (child) { + forkedGhost = child; + request = require('supertest'); + request = request(configTestHttps.url.replace(/\/$/, '')); + }).then(done).catch(done); + }); + + after(function (done) { + if (forkedGhost) { + forkedGhost.kill(done); + } + }); + + it('should set links to url over non-HTTPS', function (done) { + request.get('/') + .expect(200) + .expect(//) + .expect(/
Ghost<\/a\>/) + .end(doEnd(done)); + }); + + it('should set links to urlSSL over HTTPS', function (done) { + request.get('/') + .set('X-Forwarded-Proto', 'https') + .expect(200) + .expect(//) + .expect(/Ghost<\/a\>/) + .end(doEnd(done)); + }); + }); + + describe('Date permalinks', function () { + before(function (done) { + // Only way to swap permalinks setting is to login and visit the URL because + // poking the database doesn't work as settings are cached + testUtils.togglePermalinks(request, 'date').then(function () { + done(); + }); + }); + + it('should load a post with date permalink', function (done) { + // get today's date + var date = moment().format('YYYY/MM/DD'); + + request.get('/' + date + '/welcome-to-ghost/') + .expect(200) + .expect('Content-Type', /html/) + .end(doEnd(done)); + }); + + it('should serve RSS with date permalink', function (done) { + request.get('/rss/') + .expect('Content-Type', 'text/xml; charset=utf-8') + .expect('Cache-Control', testUtils.cacheRules['public']) + .expect(200) + .end(function (err, res) { + if (err) { + return done(err); + } + + should.not.exist(res.headers['x-cache-invalidate']); + should.not.exist(res.headers['X-CSRF-Token']); + should.not.exist(res.headers['set-cookie']); + should.exist(res.headers.date); + + var content = res.text, + today = new Date(), + dd = ('0' + today.getDate()).slice(-2), + mm = ('0' + (today.getMonth() + 1)).slice(-2), + yyyy = today.getFullYear(), + postLink = '/' + yyyy + '/' + mm + '/' + dd + '/welcome-to-ghost/'; + + content.indexOf(postLink).should.be.above(0); + + done(); + }); + }); + }); }); diff --git a/core/test/utils/index.js b/core/test/utils/index.js index 387e6f1b0d..94a8798081 100644 --- a/core/test/utils/index.js +++ b/core/test/utils/index.js @@ -21,6 +21,8 @@ var Promise = require('bluebird'), teardown, setup, doAuth, + login, + togglePermalinks, initFixtures, initData, @@ -462,7 +464,6 @@ setup = function setup() { doAuth = function doAuth() { var options = arguments, request = arguments[0], - user = DataGenerator.forModel.users[0], fixtureOps; // Remove request from this list @@ -474,16 +475,52 @@ doAuth = function doAuth() { fixtureOps = getFixtureOps(options); + return sequence(fixtureOps).then(function () { + return login(request); + }); +}; + +login = function login(request) { + var user = DataGenerator.forModel.users[0]; + return new Promise(function (resolve, reject) { - return sequence(fixtureOps).then(function () { - request.post('/ghost/api/v0.1/authentication/token/') - .send({grant_type: 'password', username: user.email, password: user.password, client_id: 'ghost-admin'}) + request.post('/ghost/api/v0.1/authentication/token/') + .send({grant_type: 'password', username: user.email, password: user.password, client_id: 'ghost-admin'}) + .end(function (err, res) { + if (err) { + return reject(err); + } + + resolve(res.body.access_token); + }); + }); +}; + +togglePermalinks = function togglePermalinks(request, toggle) { + var permalinkString = toggle === 'date' ? '/:year/:month/:day/:slug/' : '/:slug/'; + + return new Promise(function (resolve, reject) { + doAuth(request).then(function (token) { + request.put('/ghost/api/v0.1/settings/') + .set('Authorization', 'Bearer ' + token) + .send({settings: [ + { + uuid: '75e994ae-490e-45e6-9207-0eab409c1c04', + key: 'permalinks', + value: permalinkString, + type: 'blog', + created_at: '2014-10-16T17:39:16.005Z', + created_by: 1, + updated_at: '2014-10-20T19:44:18.077Z', + updated_by: 1 + } + ]}) .end(function (err, res) { if (err) { return reject(err); } - resolve(res.body.access_token); + resolve(res.body); }); }); }); @@ -499,6 +536,8 @@ module.exports = { teardown: teardown, setup: setup, doAuth: doAuth, + login: login, + togglePermalinks: togglePermalinks, initFixtures: initFixtures, initData: initData,