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 = '
',
- 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 = '',
+ 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,