From 9eadeb9fbbdf40062086209282b75433fff9af78 Mon Sep 17 00:00:00 2001 From: Hannah Wolfe Date: Tue, 15 Dec 2015 10:41:53 +0000 Subject: [PATCH] Prep shared API URL util for use on external sites refs #5942, #6150 There were a few key problems I was looking to solve with this: - Introduce a single point of truth for what the URL for accessing the API should be - Provide a simple way to configure the utility (much like a true SDK) As of this commit, this utility is still automatically available in a Ghost theme. To use it on an external site, the code would look like: ``` ``` To achieve this, there have been a number of changes: - A new `apiUrl` function has been added to config, which calculates the correct URL. This needs to be unified with the other url generation functions as a separate piece of work. - The serveSharedFile middleware has been updated, so that it can serve files from / or /shared and to substitute `{{api-url}}` as it does `{{blog-url}}`. - ghost-url.js and ghost-url.min.js have been updated to be served via the serveSharedFile middleware - ghost-url.js has been changed slightly, to take the url from an inline variable which is substituted the first time it is served - `{{ghost_head}}` has been updated, removing the api url handling which is now in config/url.js and removing the configuration of the utility in favour of calling `init()` after the script is required - `{{ghost_head}}` has also had the meta tags for client id and secret removed - tests have been updated --- core/server/config/index.js | 1 + core/server/config/url.js | 24 +++- core/server/helpers/ghost_head.js | 34 ++--- core/server/middleware/index.js | 4 + core/server/middleware/serve-shared-file.js | 16 ++- core/shared/ghost-url.js | 32 +++-- core/test/unit/config_spec.js | 73 +++++++++- core/test/unit/ghost_url_spec.js | 125 +++++++++++------- .../unit/middleware/serve-shared-file_spec.js | 16 +-- .../unit/server_helpers/ghost_head_spec.js | 123 ++--------------- 10 files changed, 237 insertions(+), 211 deletions(-) diff --git a/core/server/config/index.js b/core/server/config/index.js index 4754be544a6b..f208bdcaa279 100644 --- a/core/server/config/index.js +++ b/core/server/config/index.js @@ -32,6 +32,7 @@ function ConfigManager(config) { this.urlJoin = configUrl.urlJoin; this.urlFor = configUrl.urlFor; this.urlPathForPost = configUrl.urlPathForPost; + this.apiUrl = configUrl.apiUrl; // If we're given an initial config object then we can set it. if (config && _.isObject(config)) { diff --git a/core/server/config/url.js b/core/server/config/url.js index a974a6ab2565..c44fc982ca00 100644 --- a/core/server/config/url.js +++ b/core/server/config/url.js @@ -3,7 +3,9 @@ var moment = require('moment'), _ = require('lodash'), - ghostConfig = ''; + ghostConfig = '', + // @TODO: unify this with routes.apiBaseUrl + apiPath = '/ghost/api/v0.1'; // ## setConfig // Simple utility function to allow @@ -146,7 +148,7 @@ function urlFor(context, data, absolute) { knownPaths = { home: '/', rss: '/rss/', - api: '/ghost/api/v0.1', + api: apiPath, sitemap_xsl: '/sitemap.xsl' }; @@ -218,7 +220,25 @@ function urlFor(context, data, absolute) { return createUrl(urlPath, absolute, secure); } +function apiUrl() { + // @TODO unify this with urlFor + var url; + + if (ghostConfig.forceAdminSSL) { + url = (ghostConfig.urlSSL || ghostConfig.url).replace(/^.*?:\/\//g, 'https://'); + } else if (ghostConfig.urlSSL) { + url = ghostConfig.urlSSL.replace(/^.*?:\/\//g, 'https://'); + } else if (ghostConfig.url.match(/^https:/)) { + url = ghostConfig.url; + } else { + url = ghostConfig.url.replace(/^.*?:\/\//g, '//'); + } + + return url.replace(/\/$/, '') + apiPath + '/'; +} + module.exports.setConfig = setConfig; module.exports.urlJoin = urlJoin; module.exports.urlFor = urlFor; module.exports.urlPathForPost = urlPathForPost; +module.exports.apiUrl = apiUrl; diff --git a/core/server/helpers/ghost_head.js b/core/server/helpers/ghost_head.js index 3213c530723f..d267d521801a 100644 --- a/core/server/helpers/ghost_head.js +++ b/core/server/helpers/ghost_head.js @@ -278,30 +278,14 @@ function finaliseSchema(schema, head) { } function getAjaxHelper(clientId, clientSecret) { - var apiPath = require('../routes').apiBaseUri, - url, useOrigin; - - if (config.forceAdminSSL) { - url = 'https://' + (config.urlSSL || config.url).replace(/.*?:\/\//g, '').replace(/\/$/, '') + apiPath; - useOrigin = false; - } else { - url = config.paths.subdir + apiPath; - useOrigin = true; - } - - return '' + - ''; + return '\n' + + ''; } ghost_head = function (options) { @@ -364,8 +348,6 @@ ghost_head = function (options) { } if (metaData.clientId && metaData.clientSecret) { - head.push(writeMetaTag('ghost:client_id', metaData.clientId)); - head.push(writeMetaTag('ghost:client_secret', metaData.clientSecret)); head.push(getAjaxHelper(metaData.clientId, metaData.clientSecret)); } } diff --git a/core/server/middleware/index.js b/core/server/middleware/index.js index a34aebf8acf9..f0b4bdf94465 100644 --- a/core/server/middleware/index.js +++ b/core/server/middleware/index.js @@ -73,6 +73,10 @@ setupMiddleware = function setupMiddleware(blogApp, adminApp) { // Favicon blogApp.use(serveSharedFile('favicon.ico', 'image/x-icon', utils.ONE_DAY_S)); + // Ghost-Url + blogApp.use(serveSharedFile('shared/ghost-url.js', 'application/javascript', utils.ONE_HOUR_S)); + blogApp.use(serveSharedFile('shared/ghost-url.min.js', 'application/javascript', utils.ONE_HOUR_S)); + // Static assets blogApp.use('/shared', express.static(path.join(corePath, '/shared'), {maxAge: utils.ONE_HOUR_MS})); blogApp.use('/content/images', storage.getStorage().serve()); diff --git a/core/server/middleware/serve-shared-file.js b/core/server/middleware/serve-shared-file.js index 0bd38dfdc4bc..5879e1a3ffcc 100644 --- a/core/server/middleware/serve-shared-file.js +++ b/core/server/middleware/serve-shared-file.js @@ -7,11 +7,15 @@ var crypto = require('crypto'), // Handles requests to robots.txt and favicon.ico (and caches them) function serveSharedFile(file, type, maxAge) { var content, - filePath = path.join(config.paths.corePath, 'shared', file), - re = /(\{\{blog-url\}\})/g; + corePath = config.paths.corePath, + filePath, + blogRegex = /(\{\{blog-url\}\})/g, + apiRegex = /(\{\{api-url\}\})/g; + + filePath = file.match(/^shared/) ? path.join(corePath, file) : path.join(corePath, 'shared', file); return function serveSharedFile(req, res, next) { - if (req.url === '/' + file) { + if (req.path === '/' + file) { if (content) { res.writeHead(200, content.headers); res.end(content.body); @@ -20,8 +24,10 @@ function serveSharedFile(file, type, maxAge) { if (err) { return next(err); } - if (type === 'text/xsl' || type === 'text/plain') { - buf = buf.toString().replace(re, config.url.replace(/\/$/, '')); + + if (type === 'text/xsl' || type === 'text/plain' || type === 'application/javascript') { + buf = buf.toString().replace(blogRegex, config.url.replace(/\/$/, '')); + buf = buf.toString().replace(apiRegex, config.apiUrl()); } content = { headers: { diff --git a/core/shared/ghost-url.js b/core/shared/ghost-url.js index e04f6e12258b..c8c43c6c2352 100644 --- a/core/shared/ghost-url.js +++ b/core/shared/ghost-url.js @@ -1,6 +1,12 @@ (function () { 'use strict'; + var apiUrl = '{{api-url}}', + clientId, + clientSecret, + url, + init; + function generateQueryString(object) { var queries = [], i; @@ -21,12 +27,9 @@ return ''; } - var url = { - config: {}, - + url = { api: function () { var args = Array.prototype.slice.call(arguments), - url = (this.config.useOrigin) ? this.config.origin + this.config.url : this.config.url, queryOptions; if (args.length && typeof args[args.length - 1] === 'object') { @@ -35,26 +38,35 @@ queryOptions = {}; } - queryOptions.client_id = this.config.clientId; - queryOptions.client_secret = this.config.clientSecret; + queryOptions.client_id = clientId; + queryOptions.client_secret = clientSecret; if (args.length) { args.forEach(function (el) { - url += el.replace(/^\/|\/$/g, '') + '/'; + apiUrl += el.replace(/^\/|\/$/g, '') + '/'; }); } - return url + generateQueryString(queryOptions); + return apiUrl + generateQueryString(queryOptions); } }; + init = function (options) { + clientId = options.clientId ? options.clientId : ''; + clientSecret = options.clientSecret ? options.clientSecret : ''; + apiUrl = options.url ? options.url : ''; + }; + if (typeof window !== 'undefined') { window.ghost = window.ghost || {}; - url.config = window.ghost.config || {}; window.ghost.url = url; + window.ghost.init = init; } if (typeof module !== 'undefined') { - module.exports = url; + module.exports = { + url: url, + init: init + }; } })(); diff --git a/core/test/unit/config_spec.js b/core/test/unit/config_spec.js index 4590abb2a0b7..95c8d83a4eb3 100644 --- a/core/test/unit/config_spec.js +++ b/core/test/unit/config_spec.js @@ -372,7 +372,7 @@ describe('Config', function () { describe('urlPathForPost', function () { it('should output correct url for post', function () { - config.set({theme: {permalinks: '/:slug/'}}); + configUtils.set({theme: {permalinks: '/:slug/'}}); var testData = testUtils.DataGenerator.Content.posts[2], postLink = '/short-and-sweet/'; @@ -381,7 +381,7 @@ describe('Config', function () { }); it('should output correct url for post with date permalink', function () { - config.set({theme: {permalinks: '/:year/:month/:day/:slug/'}}); + configUtils.set({theme: {permalinks: '/:year/:month/:day/:slug/'}}); var testData = testUtils.DataGenerator.Content.posts[2], today = testData.published_at, dd = ('0' + today.getDate()).slice(-2), @@ -393,7 +393,7 @@ describe('Config', function () { }); it('should output correct url for page with date permalink', function () { - config.set({theme: {permalinks: '/:year/:month/:day/:slug/'}}); + configUtils.set({theme: {permalinks: '/:year/:month/:day/:slug/'}}); var testData = testUtils.DataGenerator.Content.posts[5], postLink = '/static-page-test/'; @@ -402,7 +402,7 @@ describe('Config', function () { }); it('should output correct url for post with complex permalink', function () { - config.set({theme: {permalinks: '/:year/:id/:author/'}}); + configUtils.set({theme: {permalinks: '/:year/:id/:author/'}}); var testData = _.extend( {}, testUtils.DataGenerator.Content.posts[2], {id: 3}, {author: {slug: 'joe-bloggs'}} @@ -414,6 +414,71 @@ describe('Config', function () { config.urlPathForPost(testData).should.equal(postLink); }); }); + + describe('apiUrl', function () { + it('should return https config.url if forceAdminSSL set', function () { + configUtils.set({ + url: 'http://my-ghost-blog.com', + forceAdminSSL: true + }); + + config.apiUrl().should.eql('https://my-ghost-blog.com/ghost/api/v0.1/'); + }); + + it('should return https config.urlSSL if forceAdminSSL set and urlSSL is misconfigured', function () { + configUtils.set({ + url: 'http://my-ghost-blog.com', + urlSSL: 'http://other-ghost-blog.com', + forceAdminSSL: true + }); + + config.apiUrl().should.eql('https://other-ghost-blog.com/ghost/api/v0.1/'); + }); + + it('should return https config.urlSSL if forceAdminSSL set', function () { + configUtils.set({ + url: 'http://my-ghost-blog.com', + urlSSL: 'https://other-ghost-blog.com', + forceAdminSSL: true + }); + + config.apiUrl().should.eql('https://other-ghost-blog.com/ghost/api/v0.1/'); + }); + + it('should return https config.urlSSL if set and misconfigured & forceAdminSSL is NOT set', function () { + configUtils.set({ + url: 'http://my-ghost-blog.com', + urlSSL: 'http://other-ghost-blog.com' + }); + + config.apiUrl().should.eql('https://other-ghost-blog.com/ghost/api/v0.1/'); + }); + + it('should return https config.urlSSL if set & forceAdminSSL is NOT set', function () { + configUtils.set({ + url: 'http://my-ghost-blog.com', + urlSSL: 'https://other-ghost-blog.com' + }); + + config.apiUrl().should.eql('https://other-ghost-blog.com/ghost/api/v0.1/'); + }); + + it('should return https config.url if config.url is https & forceAdminSSL is NOT set', function () { + configUtils.set({ + url: 'https://my-ghost-blog.com' + }); + + config.apiUrl().should.eql('https://my-ghost-blog.com/ghost/api/v0.1/'); + }); + + it('should return no protocol config.url if config.url is NOT https & forceAdminSSL/urlSSL is NOT set', function () { + configUtils.set({ + url: 'http://my-ghost-blog.com' + }); + + config.apiUrl().should.eql('//my-ghost-blog.com/ghost/api/v0.1/'); + }); + }); }); describe('File', function () { diff --git a/core/test/unit/ghost_url_spec.js b/core/test/unit/ghost_url_spec.js index e644a01400c1..0b27b472896b 100644 --- a/core/test/unit/ghost_url_spec.js +++ b/core/test/unit/ghost_url_spec.js @@ -1,68 +1,63 @@ -/* globals describe, afterEach, it */ +/* globals describe, beforeEach, afterEach, it */ /*jshint expr:true*/ -var url = require('../../shared/ghost-url'); +var should = require('should'), + ghostUrl = require('../../shared/ghost-url'), + + configUtils = require('../utils/configUtils'); + +should.equal(true, true); describe('Ghost Ajax Helper', function () { - afterEach(function () { - url.config = {}; + beforeEach(function () { + configUtils.set({ + url: 'http://testblog.com/' + }); }); - it('renders basic url correctly when no arguments are presented & useOrigin is set to false', function () { - url.config = { - url: 'http://testblog.com/', - useOrigin: false, - clientId: '', - clientSecret: '' - }; - - url.api().should.equal('http://testblog.com/'); + afterEach(function () { + configUtils.restore(); }); - it('renders basic url correctly when no arguments are presented & useOrigin is set to true', function () { - url.config = { - url: '/url/', - useOrigin: true, - origin: 'http://originblog.com', + it('renders basic url correctly when no arguments are presented', function () { + ghostUrl.init({ clientId: '', - clientSecret: '' - }; + clientSecret: '', + url: configUtils.config.apiUrl() + }); - url.api().should.equal('http://originblog.com/url/'); + ghostUrl.url.api().should.equal('//testblog.com/ghost/api/v0.1/'); }); it('strips arguments of forward and trailing slashes correctly', function () { - url.config = { - url: 'http://testblog.com/', - useOrigin: false, + ghostUrl.init({ clientId: '', - clientSecret: '' - }; + clientSecret: '', + url: configUtils.config.apiUrl() + }); - url.api('a/', '/b', '/c/').should.equal('http://testblog.com/a/b/c/'); + ghostUrl.url.api('a/', '/b', '/c/').should.equal('//testblog.com/ghost/api/v0.1/a/b/c/'); }); it('appends client_id & client_secret to query string automatically', function () { - url.config = { - url: 'http://testblog.com/', - useOrigin: false, + ghostUrl.init({ clientId: 'ghost-frontend', - clientSecret: 'notasecret' - }; + clientSecret: 'notasecret', + url: configUtils.config.apiUrl() + }); - url.api().should.equal('http://testblog.com/?client_id=ghost-frontend&client_secret=notasecret'); + ghostUrl.url.api().should.equal('//testblog.com/ghost/api/v0.1/?client_id=ghost-frontend&client_secret=notasecret'); }); it('generates query parameters correctly', function () { - url.config = { - url: 'http://testblog.com/', - useOrigin: false, + ghostUrl.init({ clientId: 'ghost-frontend', - clientSecret: 'notasecret' - }; + clientSecret: 'notasecret', + url: configUtils.config.apiUrl() + }); - var rendered = url.api({a: 'string', b: 5, c: 'en coded'}); + var rendered = ghostUrl.url.api({a: 'string', b: 5, c: 'en coded'}); - rendered.should.match(/http:\/\/testblog\.com\/\?/); + rendered.should.match(/\/\/testblog\.com\/ghost\/api\/v0\.1\/\?/); rendered.should.match(/client_id=ghost-frontend/); rendered.should.match(/client_secret=notasecret/); rendered.should.match(/a/); @@ -71,15 +66,51 @@ describe('Ghost Ajax Helper', function () { }); it('generates complex query correctly', function () { - url.config = { - url: '/blog/ghost/api/v0.1/', - useOrigin: true, - origin: 'https://testblog.com', + ghostUrl.init({ + clientId: 'ghost-frontend', + clientSecret: 'notasecret', + url: configUtils.config.apiUrl() + }); + + var rendered = ghostUrl.url.api('posts/', '/tags/', '/count', {include: 'tags,tests', page: 2}); + + rendered.should.match(/\/\/testblog\.com\/ghost\/api\/v0\.1\/posts\/tags\/count\/\?/); + rendered.should.match(/client_id=ghost-frontend/); + rendered.should.match(/client_secret=notasecret/); + rendered.should.match(/include=tags%2Ctests/); + rendered.should.match(/page=2/); + }); + + it('works with an https config', function () { + configUtils.set({ + url: 'https://testblog.com/' + }); + ghostUrl.init({ + clientId: 'ghost-frontend', + clientSecret: 'notasecret', + url: configUtils.config.apiUrl() + }); + + var rendered = ghostUrl.url.api('posts/', '/tags/', '/count', {include: 'tags,tests', page: 2}); + + rendered.should.match(/https:\/\/testblog\.com\/ghost\/api\/v0\.1\/posts\/tags\/count\/\?/); + rendered.should.match(/client_id=ghost-frontend/); + rendered.should.match(/client_secret=notasecret/); + rendered.should.match(/include=tags%2Ctests/); + rendered.should.match(/page=2/); + }); + + it('works with an https config and subdirectory', function () { + configUtils.set({ + url: 'https://testblog.com/blog/' + }); + ghostUrl.init({ clientId: 'ghost-frontend', - clientSecret: 'notasecret' - }; + clientSecret: 'notasecret', + url: configUtils.config.apiUrl() + }); - var rendered = url.api('posts/', '/tags/', '/count', {include: 'tags,tests', page: 2}); + var rendered = ghostUrl.url.api('posts/', '/tags/', '/count', {include: 'tags,tests', page: 2}); rendered.should.match(/https:\/\/testblog\.com\/blog\/ghost\/api\/v0\.1\/posts\/tags\/count\/\?/); rendered.should.match(/client_id=ghost-frontend/); diff --git a/core/test/unit/middleware/serve-shared-file_spec.js b/core/test/unit/middleware/serve-shared-file_spec.js index a1aca7fde772..8b6838e41eb3 100644 --- a/core/test/unit/middleware/serve-shared-file_spec.js +++ b/core/test/unit/middleware/serve-shared-file_spec.js @@ -2,14 +2,14 @@ /*jshint expr:true*/ var fs = require('fs'), sinon = require('sinon'), - serveSharedFile = require('../../../server/middleware/serve-shared-file'); + serveSharedFile = require('../../../server/middleware/serve-shared-file'), + + sandbox = sinon.sandbox.create(); describe('serveSharedFile', function () { - var res, req, next, sandbox; + var res, req, next; beforeEach(function () { - sandbox = sinon.sandbox.create(); - res = sinon.spy(); req = sinon.spy(); next = sinon.spy(); @@ -27,7 +27,7 @@ describe('serveSharedFile', function () { it('should skip if the request does NOT match the file', function () { var middleware = serveSharedFile('robots.txt', 'text/plain', 3600); - req.url = '/favicon.ico'; + req.path = '/favicon.ico'; middleware(req, res, next); next.called.should.be.true; }); @@ -35,7 +35,7 @@ describe('serveSharedFile', function () { it('should load the file and send it', function () { var middleware = serveSharedFile('robots.txt', 'text/plain', 3600), body = 'User-agent: * Disallow: /'; - req.url = '/robots.txt'; + req.path = '/robots.txt'; sandbox.stub(fs, 'readFile', function (file, cb) { cb(null, body); @@ -61,7 +61,7 @@ describe('serveSharedFile', function () { it('should send the correct headers', function () { var middleware = serveSharedFile('robots.txt', 'text/plain', 3600), body = 'User-agent: * Disallow: /'; - req.url = '/robots.txt'; + req.path = '/robots.txt'; sandbox.stub(fs, 'readFile', function (file, cb) { cb(null, body); @@ -85,7 +85,7 @@ describe('serveSharedFile', function () { it('should replace {{blog-url}} in text/plain', function () { var middleware = serveSharedFile('robots.txt', 'text/plain', 3600), body = 'User-agent: {{blog-url}}'; - req.url = '/robots.txt'; + req.path = '/robots.txt'; sandbox.stub(fs, 'readFile', function (file, cb) { cb(null, body); diff --git a/core/test/unit/server_helpers/ghost_head_spec.js b/core/test/unit/server_helpers/ghost_head_spec.js index 03e05178436c..a1c23b96af5f 100644 --- a/core/test/unit/server_helpers/ghost_head_spec.js +++ b/core/test/unit/server_helpers/ghost_head_spec.js @@ -45,11 +45,6 @@ describe('{{ghost_head}} helper', function () { sandbox.stub(labs, 'isSet').returns(true); }); - function expectGhostClientMeta(rendered) { - rendered.string.should.match(//); - rendered.string.should.match(//); - } - describe('without Code Injection', function () { beforeEach(function () { configUtils.set({ @@ -72,7 +67,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: ['paged', 'index']}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -89,7 +83,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: ['home', 'index']}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -130,7 +123,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: ['tag']}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -172,7 +164,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: ['tag']}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -212,14 +203,13 @@ describe('{{ghost_head}} helper', function () { {safeVersion: '0.3', relativeUrl: '/tag/tagtitle/', tag: tag, context: ['tag']}, {data: {root: {context: ['tag']}}} ).then(function (rendered) { - should.exist(rendered); - expectGhostClientMeta(rendered); - rendered.string.should.not.match(//); - rendered.string.should.not.match(//); - rendered.string.should.not.match(/"description":/); + should.exist(rendered); + rendered.string.should.not.match(//); + rendered.string.should.not.match(//); + rendered.string.should.not.match(/"description":/); - done(); - }).catch(done); + done(); + }).catch(done); }); it('does not return structured data on paginated tag pages', function (done) { @@ -235,7 +225,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: ['tag']}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -261,7 +250,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: ['author']}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -304,7 +292,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: ['paged', 'author']}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -356,7 +343,6 @@ describe('{{ghost_head}} helper', function () { re4 = new RegExp('"dateModified": "' + post.updated_at); should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -428,7 +414,6 @@ describe('{{ghost_head}} helper', function () { re4 = new RegExp('"dateModified": "' + post.updated_at); should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -498,7 +483,6 @@ describe('{{ghost_head}} helper', function () { re4 = new RegExp('"dateModified": "' + post.updated_at); should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -567,7 +551,6 @@ describe('{{ghost_head}} helper', function () { re4 = new RegExp('"dateModified": "' + post.updated_at); should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -614,7 +597,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: ['page']}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -631,7 +613,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: ['index', 'paged'], pagination: {total: 4, page: 3, next: 4, prev: 2}}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -650,7 +631,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: ['index', 'paged'], pagination: {total: 3, page: 2, next: 3, prev: 1}}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -681,7 +661,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: []}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -729,7 +708,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: ['post']}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -763,7 +741,6 @@ describe('{{ghost_head}} helper', function () { {data: {root: {context: []}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); rendered.string.should.match(//); rendered.string.should.match(//); rendered.string.should.match(//); @@ -775,102 +752,30 @@ describe('{{ghost_head}} helper', function () { }); describe('with Ajax Helper', function () { - beforeEach(function () { - configUtils.set({ - url: '', - urlSSL: '', - forceAdminSSL: false - }); - }); - - it('renders script tags with basic configuration', function (done) { - configUtils.set({ - url: 'http://example.com/' - }); - + it('renders script tag with src', function (done) { helpers.ghost_head.call( {safeVersion: '0.3', context: ['paged', 'index'], post: false}, {data: {root: {context: []}}} ).then(function (rendered) { should.exist(rendered); - expectGhostClientMeta(rendered); - rendered.string.should.match(/