mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
Structured Data 3.0
closes #6534 - new input fields in general settings incl. validation - facebook and twitter as new models in settings.js - adds values for facebook and twitter to default-settings.js - adds blog helpers for facebook and twittter - rather than saving the whole URL, the Twitter username incl. '@' will be extracted from URL and saved in the settings. The User will still input the full URL. After saving the blog setting, the stored Twitter username will be parsed again as the full URL and available in the input field. A custom transform is used for this. - adding meta fields to be rendered in {{ghost_head}}: - '<meta property="article:publisher" content="https://www.facebook.com/page" />' and - '<meta name="twitter:site" content="@user"/>' - adds facebook and twitter to unit test for structured data - adds unit test for general settings - adds acceptance test for new input fields in general settings - adds a custom transform for twitter model to save only the username to the server - adds unit test for transform
This commit is contained in:
parent
d928cd2ec2
commit
b7bd6d9968
26 changed files with 771 additions and 45 deletions
|
@ -6,7 +6,8 @@ const {
|
|||
Controller,
|
||||
computed,
|
||||
inject: {service},
|
||||
observer
|
||||
observer,
|
||||
run
|
||||
} = Ember;
|
||||
|
||||
export default Controller.extend(SettingsSaveMixin, {
|
||||
|
@ -16,6 +17,8 @@ export default Controller.extend(SettingsSaveMixin, {
|
|||
|
||||
notifications: service(),
|
||||
config: service(),
|
||||
_scratchFacebook: null,
|
||||
_scratchTwitter: null,
|
||||
|
||||
selectedTheme: computed('model.activeTheme', 'themes', function () {
|
||||
let activeTheme = this.get('model.activeTheme');
|
||||
|
@ -88,6 +91,7 @@ export default Controller.extend(SettingsSaveMixin, {
|
|||
if (error) {
|
||||
notifications.showAPIError(error, {key: 'settings.save'});
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -110,6 +114,106 @@ export default Controller.extend(SettingsSaveMixin, {
|
|||
|
||||
toggleUploadLogoModal() {
|
||||
this.toggleProperty('showUploadLogoModal');
|
||||
},
|
||||
|
||||
validateFacebookUrl() {
|
||||
let newUrl = this.get('_scratchFacebook');
|
||||
let oldUrl = this.get('model.facebook');
|
||||
let errMessage = '';
|
||||
|
||||
if (!newUrl) {
|
||||
// Clear out the Facebook url
|
||||
this.set('model.facebook', '');
|
||||
this.get('model.errors').remove('facebook');
|
||||
return;
|
||||
}
|
||||
|
||||
// If new url didn't change, exit
|
||||
if (newUrl === oldUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newUrl.match(/(^https:\/\/www\.facebook\.com\/)(\S+)/g)) {
|
||||
if (newUrl.match(/(?:facebook\.com\/)(\S+)/) || (!validator.isURL(newUrl) && newUrl.match(/([a-zA-Z0-9\.]+)/))) {
|
||||
let [ , username] = newUrl.match(/(?:facebook\.com\/)(\S+)/) || newUrl.match(/([a-zA-Z0-9\.]+)/);
|
||||
newUrl = `https://www.facebook.com/${username}`;
|
||||
|
||||
this.set('model.facebook', newUrl);
|
||||
|
||||
this.get('model.errors').remove('facebook');
|
||||
this.get('model.hasValidated').pushObject('facebook');
|
||||
|
||||
// User input is validated
|
||||
return this.save().then(() => {
|
||||
this.set('model.facebook', '');
|
||||
run.schedule('afterRender', this, function () {
|
||||
this.set('model.facebook', newUrl);
|
||||
});
|
||||
});
|
||||
} else if (validator.isURL(newUrl)) {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://www.facebook.com/yourPage';
|
||||
this.get('model.errors').add('facebook', errMessage);
|
||||
this.get('model.hasValidated').pushObject('facebook');
|
||||
return;
|
||||
} else {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://www.facebook.com/yourPage';
|
||||
this.get('model.errors').add('facebook', errMessage);
|
||||
this.get('model.hasValidated').pushObject('facebook');
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
validateTwitterUrl() {
|
||||
let newUrl = this.get('_scratchTwitter');
|
||||
let oldUrl = this.get('model.twitter');
|
||||
let errMessage = '';
|
||||
|
||||
if (!newUrl) {
|
||||
// Clear out the Facebook url
|
||||
this.set('model.twitter', '');
|
||||
this.get('model.errors').remove('twitter');
|
||||
return;
|
||||
}
|
||||
|
||||
// If new url didn't change, exit
|
||||
if (newUrl === oldUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newUrl.match(/(^https:\/\/twitter\.com\/)(\S+)/g)) {
|
||||
if (newUrl.match(/(?:twitter\.com\/)(\S+)/) || (!validator.isURL(newUrl) && newUrl.match(/([a-zA-Z0-9\.]+)/))) {
|
||||
let [ , username] = newUrl.match(/(?:twitter\.com\/)(\S+)/) || newUrl.match(/([a-zA-Z0-9\.]+)/);
|
||||
newUrl = `https://twitter.com/${username}`;
|
||||
|
||||
this.set('model.twitter', newUrl);
|
||||
|
||||
this.get('model.errors').remove('twitter');
|
||||
this.get('model.hasValidated').pushObject('twitter');
|
||||
|
||||
// User input is validated
|
||||
return this.save().then(() => {
|
||||
this.set('model.twitter', '');
|
||||
run.schedule('afterRender', this, function () {
|
||||
this.set('model.twitter', newUrl);
|
||||
});
|
||||
});
|
||||
} else if (validator.isURL(newUrl)) {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://twitter.com/yourUsername';
|
||||
this.get('model.errors').add('twitter', errMessage);
|
||||
this.get('model.hasValidated').pushObject('twitter');
|
||||
return;
|
||||
} else {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://twitter.com/yourUsername';
|
||||
this.get('model.errors').add('twitter', errMessage);
|
||||
this.get('model.hasValidated').pushObject('twitter');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import Ember from 'ember';
|
||||
import isNumber from 'ghost/utils/isNumber';
|
||||
import boundOneWay from 'ghost/utils/bound-one-way';
|
||||
import { invoke } from 'ember-invoke-action';
|
||||
|
||||
const {
|
||||
Controller,
|
||||
RSVP,
|
||||
computed,
|
||||
inject: {service},
|
||||
run,
|
||||
isArray
|
||||
} = Ember;
|
||||
const {alias, and, not, or, readOnly} = computed;
|
||||
|
@ -18,6 +20,8 @@ export default Controller.extend({
|
|||
showTransferOwnerModal: false,
|
||||
showUploadCoverModal: false,
|
||||
showUplaodImageModal: false,
|
||||
_scratchFacebook: null,
|
||||
_scratchTwitter: null,
|
||||
|
||||
ajax: service(),
|
||||
dropdown: service(),
|
||||
|
@ -148,6 +152,7 @@ export default Controller.extend({
|
|||
});
|
||||
|
||||
this.set('lastPromise', promise);
|
||||
return promise;
|
||||
},
|
||||
|
||||
deleteUser() {
|
||||
|
@ -239,6 +244,110 @@ export default Controller.extend({
|
|||
this.set('lastPromise', promise);
|
||||
},
|
||||
|
||||
validateFacebookUrl() {
|
||||
let newUrl = this.get('_scratchFacebook');
|
||||
let oldUrl = this.get('user.facebook');
|
||||
let errMessage = '';
|
||||
|
||||
if (!newUrl) {
|
||||
// Clear out the Facebook url
|
||||
this.set('user.facebook', '');
|
||||
this.get('user.errors').remove('facebook');
|
||||
return;
|
||||
}
|
||||
|
||||
// If new url didn't change, exit
|
||||
if (newUrl === oldUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: put the validation here into a validator
|
||||
if (!newUrl.match(/(^https:\/\/www\.facebook\.com\/)(\S+)/g)) {
|
||||
if (newUrl.match(/(?:facebook\.com\/)(\S+)/) || (!validator.isURL(newUrl) && newUrl.match(/([a-zA-Z0-9\.]+)/))) {
|
||||
let [ , username] = newUrl.match(/(?:facebook\.com\/)(\S+)/) || newUrl.match(/([a-zA-Z0-9\.]+)/);
|
||||
newUrl = `https://www.facebook.com/${username}`;
|
||||
|
||||
this.set('user.facebook', newUrl);
|
||||
|
||||
this.get('user.errors').remove('facebook');
|
||||
this.get('user.hasValidated').pushObject('facebook');
|
||||
|
||||
// User input is validated
|
||||
invoke(this, 'save').then(() => {
|
||||
// necessary to update the value in the input field
|
||||
this.set('user.facebook', '');
|
||||
run.schedule('afterRender', this, function () {
|
||||
this.set('user.facebook', newUrl);
|
||||
});
|
||||
});
|
||||
} else if (validator.isURL(newUrl)) {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://www.facebook.com/yourUsername';
|
||||
this.get('user.errors').add('facebook', errMessage);
|
||||
this.get('user.hasValidated').pushObject('facebook');
|
||||
return;
|
||||
} else {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://www.facebook.com/yourUsername';
|
||||
this.get('user.errors').add('facebook', errMessage);
|
||||
this.get('user.hasValidated').pushObject('facebook');
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
validateTwitterUrl() {
|
||||
let newUrl = this.get('_scratchTwitter');
|
||||
let oldUrl = this.get('user.twitter');
|
||||
let errMessage = '';
|
||||
|
||||
if (!newUrl) {
|
||||
// Clear out the Twitter url
|
||||
this.set('user.twitter', '');
|
||||
this.get('user.errors').remove('twitter');
|
||||
return;
|
||||
}
|
||||
|
||||
// If new url didn't change, exit
|
||||
if (newUrl === oldUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: put the validation here into a validator
|
||||
if (!newUrl.match(/(^https:\/\/twitter\.com\/)(\S+)/g)) {
|
||||
if (newUrl.match(/(?:twitter\.com\/)(\S+)/) || (!validator.isURL(newUrl) && newUrl.match(/([a-zA-Z0-9\.]+)/))) {
|
||||
let [ , username] = newUrl.match(/(?:twitter\.com\/)(\S+)/) || newUrl.match(/([a-zA-Z0-9\.]+)/);
|
||||
newUrl = `https://twitter.com/${username}`;
|
||||
|
||||
this.set('user.twitter', newUrl);
|
||||
|
||||
this.get('user.errors').remove('twitter');
|
||||
this.get('user.hasValidated').pushObject('twitter');
|
||||
|
||||
// User input is validated
|
||||
invoke(this, 'save').then(() => {
|
||||
// necessary to update the value in the input field
|
||||
this.set('user.twitter', '');
|
||||
run.schedule('afterRender', this, function () {
|
||||
this.set('user.twitter', newUrl);
|
||||
});
|
||||
});
|
||||
} else if (validator.isURL(newUrl)) {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://twitter.com/yourUsername';
|
||||
this.get('user.errors').add('twitter', errMessage);
|
||||
this.get('user.hasValidated').pushObject('twitter');
|
||||
return;
|
||||
} else {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://twitter.com/yourUsername';
|
||||
this.get('user.errors').add('twitter', errMessage);
|
||||
this.get('user.hasValidated').pushObject('twitter');
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
transferOwnership() {
|
||||
let user = this.get('user');
|
||||
let url = this.get('ghostPaths.url').api('users', 'owner');
|
||||
|
|
|
@ -347,4 +347,14 @@ export function testConfig() {
|
|||
users: [db.users.find(request.params.id)]
|
||||
};
|
||||
});
|
||||
|
||||
this.put('/users/:id/', function (db, request) {
|
||||
let {id} = request.params;
|
||||
let [attrs] = JSON.parse(request.requestBody).users;
|
||||
let record = db.users.update(id, attrs);
|
||||
|
||||
return {
|
||||
user: record
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -179,6 +179,28 @@ export default [
|
|||
uuid: 'dd4ebaa8-dedb-40ff-a663-ec64a92d4111',
|
||||
value: '[{"url":""}]'
|
||||
},
|
||||
{
|
||||
created_at: '2016-05-05T15:40:12.133Z',
|
||||
created_by: 1,
|
||||
id: 23,
|
||||
key: 'facebook',
|
||||
type: 'blog',
|
||||
updated_at: '2016-05-08T15:20:25.953Z',
|
||||
updated_by: 1,
|
||||
uuid: 'd4387e5c-3230-46dd-a89b-0d8a40365c35',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
created_at: '2016-05-05T15:40:12.134Z',
|
||||
created_by: 1,
|
||||
id: 24,
|
||||
key: 'twitter',
|
||||
type: 'blog',
|
||||
updated_at: '2016-05-08T15:20:25.954Z',
|
||||
updated_by: 1,
|
||||
uuid: '5130441f-e4c7-4750-9692-a22d841ab049',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
key: 'availableThemes',
|
||||
value: [
|
||||
|
|
|
@ -18,6 +18,8 @@ export default Model.extend(ValidationEngine, {
|
|||
availableThemes: attr(),
|
||||
ghost_head: attr('string'),
|
||||
ghost_foot: attr('string'),
|
||||
facebook: attr('string'),
|
||||
twitter: attr('twitter-url-user'),
|
||||
labs: attr('string'),
|
||||
navigation: attr('navigation-settings'),
|
||||
isPrivate: attr('boolean'),
|
||||
|
|
|
@ -38,6 +38,8 @@ export default Model.extend(ValidationEngine, {
|
|||
async: false
|
||||
}),
|
||||
count: attr('raw'),
|
||||
facebook: attr('string'),
|
||||
twitter: attr('string'),
|
||||
|
||||
ghostPaths: service(),
|
||||
ajax: service(),
|
||||
|
|
|
@ -15,9 +15,7 @@ export default AuthenticatedRoute.extend(styleBody, CurrentUserSettings, {
|
|||
},
|
||||
|
||||
model() {
|
||||
return this.store.query('setting', {type: 'blog,theme,private'}).then((records) => {
|
||||
return records.get('firstObject');
|
||||
});
|
||||
return this.store.queryRecord('setting', {type: 'blog,theme,private'});
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
{{else}}
|
||||
<button type="button" class="btn btn-green js-modal-logo" {{action "toggleUploadLogoModal"}}>Upload Image</button>
|
||||
{{/if}}
|
||||
<p>Display a sexy logo for your publication</p>
|
||||
<p>Display a logo for your publication</p>
|
||||
|
||||
{{#if showUploadLogoModal}}
|
||||
{{gh-fullscreen-modal "upload-image"
|
||||
|
@ -96,6 +96,23 @@
|
|||
<p>Select a theme for your blog</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{#gh-form-group errors=model.errors hasValidated=model.hasValidated property="facebook"}}
|
||||
<label for="facebook">Facebook Page</label>
|
||||
<input value={{model.facebook}} oninput={{action (mut _scratchFacebook) value="target.value"}} {{action "validateFacebookUrl" on="focusOut"}} type="url" class="gh-input" id="facebook" name="general[facebook]" placeholder="https://www.facebook.com/ghost" autocorrect="off" />
|
||||
{{gh-error-message errors=model.errors property="facebook"}}
|
||||
<p>URL of your blog's Facebook Page</p>
|
||||
{{/gh-form-group}}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{#gh-form-group errors=model.errors hasValidated=model.hasValidated property="twitter"}}
|
||||
<label for="twitter">Twitter Profile</label>
|
||||
<input value={{model.twitter}} oninput={{action (mut _scratchTwitter) value="target.value"}} {{action "validateTwitterUrl" on="focusOut"}} type="url" class="gh-input" id="facebook" name="general[twitter]" placeholder="https://twitter.com/tryghost" autocorrect="off" />
|
||||
{{gh-error-message errors=model.errors property="twitter"}}
|
||||
<p>URL of your blog's Twitter profile</p>
|
||||
{{/gh-form-group}}
|
||||
</div>
|
||||
|
||||
<div class="form-group for-checkbox">
|
||||
<label for="isPrivate">Make this blog private</label>
|
||||
<label class="checkbox" for="isPrivate">
|
||||
|
@ -114,6 +131,7 @@
|
|||
{{/gh-form-group}}
|
||||
{{/if}}
|
||||
</fieldset>
|
||||
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
|
|
|
@ -140,6 +140,20 @@
|
|||
<p>Have a website or blog other than this one? Link it!</p>
|
||||
{{/gh-form-group}}
|
||||
|
||||
{{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="facebook"}}
|
||||
<label for="user-facebook">Facebook Profile</label>
|
||||
<input value={{user.facebook}} oninput={{action (mut _scratchFacebook) value="target.value"}} {{action "validateFacebookUrl" on="focusOut"}} type="url" class="gh-input" id="user-facebook" name="user[facebook]" placeholder="https://www.facebook.com/username" autocorrect="off" />
|
||||
{{gh-error-message errors=user.errors property="facebook"}}
|
||||
<p>URL of your personal Facebook Profile</p>
|
||||
{{/gh-form-group}}
|
||||
|
||||
{{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="twitter"}}
|
||||
<label for="user-twitter">Twitter Profile</label>
|
||||
<input value={{user.twitter}} oninput={{action (mut _scratchTwitter) value="target.value"}} {{action "validateTwitterUrl" on="focusOut"}} type="url" class="gh-input" id="user-twitter" name="user[twitter]" placeholder="https://twitter.com/username" autocorrect="off" />
|
||||
{{gh-error-message errors=user.errors property="twitter"}}
|
||||
<p>URL of your personal Twitter profile</p>
|
||||
{{/gh-form-group}}
|
||||
|
||||
{{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="bio" class="bio-container"}}
|
||||
<label for="user-bio">Bio</label>
|
||||
{{textarea id="user-bio" class="gh-input" value=user.bio focusOut=(action "validate" "bio" target=user)}}
|
||||
|
|
21
core/client/app/transforms/twitter-url-user.js
Normal file
21
core/client/app/transforms/twitter-url-user.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import Transform from 'ember-data/transform';
|
||||
|
||||
export default Transform.extend({
|
||||
deserialize(serialized) {
|
||||
if (serialized) {
|
||||
let [ , user ] = serialized.match(/@?([^\/]*)/);
|
||||
|
||||
return `https://twitter.com/${user}`;
|
||||
}
|
||||
return serialized;
|
||||
},
|
||||
|
||||
serialize(deserialized) {
|
||||
if (deserialized) {
|
||||
let [ , user] = deserialized.match(/(?:https:\/\/)(?:twitter\.com)\/(?:#!\/)?@?([^\/]*)/);
|
||||
|
||||
return `@${user}`;
|
||||
}
|
||||
return deserialized;
|
||||
}
|
||||
});
|
|
@ -119,25 +119,15 @@ describe('Acceptance: Settings - General', function () {
|
|||
andThen(() => {
|
||||
expect(find('.fullscreen-modal').length).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders theme selector correctly', function () {
|
||||
visit('/settings/general');
|
||||
|
||||
// renders theme selector correctly
|
||||
andThen(() => {
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/general');
|
||||
|
||||
expect(find('#activeTheme select option').length, 'available themes').to.equal(1);
|
||||
expect(find('#activeTheme select option').text().trim()).to.equal('Blog - 1.0');
|
||||
});
|
||||
});
|
||||
|
||||
it('handles private blog settings correctly', function () {
|
||||
visit('/settings/general');
|
||||
|
||||
// handles private blog settings correctly
|
||||
andThen(() => {
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/general');
|
||||
|
||||
expect(find('input#isPrivate').prop('checked'), 'isPrivate checkbox').to.be.false;
|
||||
});
|
||||
|
||||
|
@ -150,12 +140,91 @@ describe('Acceptance: Settings - General', function () {
|
|||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[password]"]', '');
|
||||
click('.view-header .view-actions .btn-blue');
|
||||
triggerEvent('#settings-general input[name="general[password]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('Password must be supplied');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[password]"]', 'asdfg');
|
||||
triggerEvent('#settings-general input[name="general[password]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('');
|
||||
});
|
||||
|
||||
// validates a facebook url correctly
|
||||
fillIn('#settings-general input[name="general[facebook]"]', 'facebook.com/username');
|
||||
triggerEvent('#settings-general input[name="general[facebook]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general input[name="general[facebook]"]').val()).to.be.equal('https://www.facebook.com/username');
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[facebook]"]', '*(&*(%%))');
|
||||
triggerEvent('#settings-general input[name="general[facebook]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('The URL must be in a format like https://www.facebook.com/yourPage');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[facebook]"]', 'http://github.com/username');
|
||||
triggerEvent('#settings-general input[name="general[facebook]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('The URL must be in a format like https://www.facebook.com/yourPage');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[facebook]"]', 'testuser');
|
||||
triggerEvent('#settings-general input[name="general[facebook]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general input[name="general[facebook]"]').val()).to.be.equal('https://www.facebook.com/testuser');
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('');
|
||||
});
|
||||
|
||||
// validates a twitter url correctly
|
||||
fillIn('#settings-general input[name="general[twitter]"]', 'twitter.com/username');
|
||||
triggerEvent('#settings-general input[name="general[twitter]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general input[name="general[twitter]"]').val()).to.be.equal('https://twitter.com/username');
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[twitter]"]', '*(&*(%%))');
|
||||
triggerEvent('#settings-general input[name="general[twitter]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('The URL must be in a format like https://twitter.com/yourUsername');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[twitter]"]', 'http://github.com/username');
|
||||
triggerEvent('#settings-general input[name="general[twitter]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('The URL must be in a format like https://twitter.com/yourUsername');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[twitter]"]', 'testuser');
|
||||
triggerEvent('#settings-general input[name="general[twitter]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general input[name="general[twitter]"]').val()).to.be.equal('https://twitter.com/testuser');
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -240,6 +240,8 @@ describe('Acceptance: Team', function () {
|
|||
|
||||
beforeEach(function () {
|
||||
server.create('user', {slug: 'test-1', name: 'Test User'});
|
||||
|
||||
server.loadFixtures();
|
||||
});
|
||||
|
||||
it('input fields reset and validate correctly', function () {
|
||||
|
@ -311,12 +313,80 @@ describe('Acceptance: Team', function () {
|
|||
expect(find('.user-details-bottom .form-group:nth-of-type(4)').hasClass('error'), 'website input should be in error state').to.be.true;
|
||||
});
|
||||
|
||||
fillIn('#user-facebook', '');
|
||||
fillIn('#user-facebook', ')(*&%^%)');
|
||||
triggerEvent('#user-facebook', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(5)').hasClass('error'), 'facebook input should be in error state').to.be.true;
|
||||
});
|
||||
|
||||
fillIn('#user-facebook', '');
|
||||
fillIn('#user-facebook', 'name');
|
||||
triggerEvent('#user-facebook', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#user-facebook').val()).to.be.equal('https://www.facebook.com/name');
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(5)').hasClass('error'), 'facebook input should be in error state').to.be.false;
|
||||
});
|
||||
|
||||
fillIn('#user-facebook', '');
|
||||
fillIn('#user-facebook', 'http://twitter.com/user');
|
||||
triggerEvent('#user-facebook', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(5)').hasClass('error'), 'facebook input should be in error state').to.be.true;
|
||||
});
|
||||
|
||||
fillIn('#user-facebook', '');
|
||||
fillIn('#user-facebook', 'facebook.com/user');
|
||||
triggerEvent('#user-facebook', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#user-facebook').val()).to.be.equal('https://www.facebook.com/user');
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(5)').hasClass('error'), 'facebook input should be in error state').to.be.false;
|
||||
});
|
||||
|
||||
fillIn('#user-twitter', '');
|
||||
fillIn('#user-twitter', ')(*&%^%)');
|
||||
triggerEvent('#user-twitter', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(6)').hasClass('error'), 'twitter input should be in error state').to.be.true;
|
||||
});
|
||||
|
||||
fillIn('#user-twitter', '');
|
||||
fillIn('#user-twitter', 'name');
|
||||
triggerEvent('#user-twitter', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#user-twitter').val()).to.be.equal('https://twitter.com/name');
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(6)').hasClass('error'), 'twitter input should be in error state').to.be.false;
|
||||
});
|
||||
|
||||
fillIn('#user-twitter', '');
|
||||
fillIn('#user-twitter', 'http://github.com/user');
|
||||
triggerEvent('#user-twitter', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(6)').hasClass('error'), 'twitter input should be in error state').to.be.true;
|
||||
});
|
||||
|
||||
fillIn('#user-twitter', '');
|
||||
fillIn('#user-twitter', 'twitter.com/user');
|
||||
triggerEvent('#user-twitter', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#user-twitter').val()).to.be.equal('https://twitter.com/user');
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(6)').hasClass('error'), 'twitter input should be in error state').to.be.false;
|
||||
});
|
||||
|
||||
fillIn('#user-website', '');
|
||||
fillIn('#user-bio', new Array(210).join('a'));
|
||||
triggerEvent('#user-bio', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(5)').hasClass('error'), 'bio input should be in error state').to.be.true;
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(7)').hasClass('error'), 'bio input should be in error state').to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
32
core/client/tests/unit/transforms/twitter-url-user-test.js
Normal file
32
core/client/tests/unit/transforms/twitter-url-user-test.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* jshint expr:true */
|
||||
import { expect } from 'chai';
|
||||
import { describeModule, it } from 'ember-mocha';
|
||||
import Ember from 'ember';
|
||||
|
||||
const emberA = Ember.A;
|
||||
|
||||
describeModule(
|
||||
'transform:twitter-url-user',
|
||||
'Unit: Transform: twitter-url-user',
|
||||
{
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['transform:foo']
|
||||
},
|
||||
function() {
|
||||
it('deserializes twitter url', function () {
|
||||
let transform = this.subject();
|
||||
let serialized = '@testuser';
|
||||
let result = transform.deserialize(serialized);
|
||||
|
||||
expect(result).to.equal('https://twitter.com/testuser');
|
||||
});
|
||||
|
||||
it('serializes url to twitter username', function () {
|
||||
let transform = this.subject();
|
||||
let deserialized = 'https://twitter.com/testuser';
|
||||
let result = transform.serialize(deserialized);
|
||||
|
||||
expect(result).to.equal('@testuser');
|
||||
});
|
||||
}
|
||||
);
|
|
@ -58,7 +58,9 @@ updateConfigCache = function () {
|
|||
cover: (settingsCache.cover && settingsCache.cover.value) || '',
|
||||
navigation: (settingsCache.navigation && JSON.parse(settingsCache.navigation.value)) || [],
|
||||
postsPerPage: (settingsCache.postsPerPage && settingsCache.postsPerPage.value) || 5,
|
||||
permalinks: (settingsCache.permalinks && settingsCache.permalinks.value) || '/:slug/'
|
||||
permalinks: (settingsCache.permalinks && settingsCache.permalinks.value) || '/:slug/',
|
||||
twitter: (settingsCache.twitter && settingsCache.twitter.value) || '',
|
||||
facebook: (settingsCache.facebook && settingsCache.facebook.value) || ''
|
||||
},
|
||||
labs: labsValue
|
||||
});
|
||||
|
|
15
core/server/data/meta/author_fb_url.js
Normal file
15
core/server/data/meta/author_fb_url.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
var getContextObject = require('./context_object.js');
|
||||
|
||||
function getAuthorFacebookUrl(data) {
|
||||
var context = data.context ? data.context[0] : null,
|
||||
contextObject = getContextObject(data, context);
|
||||
|
||||
if ((context === 'post' || context === 'page') && contextObject.author && contextObject.author.facebook) {
|
||||
return contextObject.author.facebook;
|
||||
} else if (context === 'author' && contextObject.facebook) {
|
||||
return contextObject.facebook;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = getAuthorFacebookUrl;
|
14
core/server/data/meta/creator_url.js
Normal file
14
core/server/data/meta/creator_url.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
var getContextObject = require('./context_object.js');
|
||||
|
||||
function getCreatorTwitterUrl(data) {
|
||||
var context = data.context ? data.context[0] : null,
|
||||
contextObject = getContextObject(data, context);
|
||||
if ((context === 'post' || context === 'page') && contextObject.author && contextObject.author.twitter) {
|
||||
return contextObject.author.twitter;
|
||||
} else if (context === 'author' && contextObject.twitter) {
|
||||
return contextObject.twitter;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = getCreatorTwitterUrl;
|
|
@ -8,6 +8,8 @@ var config = require('../../config'),
|
|||
getDescription = require('./description'),
|
||||
getCoverImage = require('./cover_image'),
|
||||
getAuthorImage = require('./author_image'),
|
||||
getAuthorFacebook = require('./author_fb_url'),
|
||||
getCreatorTwitter = require('./creator_url'),
|
||||
getKeywords = require('./keywords'),
|
||||
getPublishedDate = require('./published_date'),
|
||||
getModifiedDate = require('./modified_date'),
|
||||
|
@ -30,6 +32,8 @@ function getMetaData(data, root) {
|
|||
metaDescription: getDescription(data, root),
|
||||
coverImage: getCoverImage(data, true),
|
||||
authorImage: getAuthorImage(data, true),
|
||||
authorFacebook: getAuthorFacebook(data),
|
||||
creatorTwitter: getCreatorTwitter(data),
|
||||
keywords: getKeywords(data),
|
||||
publishedDate: getPublishedDate(data),
|
||||
modifiedDate: getModifiedDate(data),
|
||||
|
|
|
@ -29,7 +29,11 @@ function getPostSchema(metaData, data) {
|
|||
name: escapeExpression(data.post.author.name),
|
||||
image: metaData.authorImage,
|
||||
url: metaData.authorUrl,
|
||||
sameAs: data.post.author.website || null,
|
||||
sameAs: [
|
||||
data.post.author.website || null,
|
||||
data.post.author.facebook || null,
|
||||
data.post.author.twitter || null
|
||||
],
|
||||
description: data.post.author.bio ?
|
||||
escapeExpression(data.post.author.bio) :
|
||||
null
|
||||
|
@ -81,7 +85,11 @@ function getAuthorSchema(metaData, data) {
|
|||
var schema = {
|
||||
'@context': 'http://schema.org',
|
||||
'@type': 'Person',
|
||||
sameAs: data.author.website || null,
|
||||
sameAs: [
|
||||
data.author.website || null,
|
||||
data.author.facebook || null,
|
||||
data.author.twitter || null
|
||||
],
|
||||
publisher: escapeExpression(metaData.blog.title),
|
||||
name: escapeExpression(data.author.name),
|
||||
url: metaData.authorUrl,
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
function getStructuredData(metaData) {
|
||||
var structuredData,
|
||||
card = 'summary';
|
||||
card = 'summary',
|
||||
twitterUser;
|
||||
|
||||
if (metaData.coverImage) {
|
||||
card = 'summary_large_image';
|
||||
}
|
||||
|
||||
if (metaData.creatorTwitter) {
|
||||
twitterUser = '@' + metaData.creatorTwitter.match(/(?:https:\/\/)(?:twitter\.com)\/(?:#!\/)?@?([^\/]*)/)[1];
|
||||
}
|
||||
|
||||
structuredData = {
|
||||
'og:site_name': metaData.blog.title,
|
||||
'og:type': metaData.ogType,
|
||||
|
@ -16,6 +21,8 @@ function getStructuredData(metaData) {
|
|||
'article:published_time': metaData.publishedDate,
|
||||
'article:modified_time': metaData.modifiedDate,
|
||||
'article:tag': metaData.keywords,
|
||||
'article:publisher': metaData.blog.facebook || undefined,
|
||||
'article:author': metaData.authorFacebook || undefined,
|
||||
'twitter:card': card,
|
||||
'twitter:title': metaData.metaTitle,
|
||||
'twitter:description': metaData.metaDescription || metaData.excerpt,
|
||||
|
@ -24,7 +31,9 @@ function getStructuredData(metaData) {
|
|||
'twitter:label1': metaData.authorName ? 'Written by' : undefined,
|
||||
'twitter:data1': metaData.authorName,
|
||||
'twitter:label2': metaData.keywords ? 'Filed under' : undefined,
|
||||
'twitter:data2': metaData.keywords ? metaData.keywords.join(', ') : undefined
|
||||
'twitter:data2': metaData.keywords ? metaData.keywords.join(', ') : undefined,
|
||||
'twitter:site': metaData.blog.twitter || undefined,
|
||||
'twitter:creator': twitterUser || undefined
|
||||
};
|
||||
|
||||
// return structured data removing null or undefined keys
|
||||
|
|
|
@ -61,6 +61,12 @@
|
|||
"ghost_foot": {
|
||||
"defaultValue" : ""
|
||||
},
|
||||
"facebook": {
|
||||
"defaultValue" : ""
|
||||
},
|
||||
"twitter": {
|
||||
"defaultValue" : ""
|
||||
},
|
||||
"labs": {
|
||||
"defaultValue": "{}"
|
||||
},
|
||||
|
|
|
@ -75,6 +75,7 @@ function ghost_head(options) {
|
|||
if (this.statusCode >= 400) {
|
||||
return;
|
||||
}
|
||||
|
||||
var metaData = getMetaData(this, options.data.root),
|
||||
head = [],
|
||||
context = this.context ? this.context[0] : null,
|
||||
|
|
68
core/test/unit/metadata/author_fb_url_spec.js
Normal file
68
core/test/unit/metadata/author_fb_url_spec.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*globals describe, it*/
|
||||
var getAuthorFacebookUrl = require('../../../server/data/meta/author_fb_url'),
|
||||
should = require('should');
|
||||
|
||||
describe('getAuthorFacebookUrl', function () {
|
||||
it('should return author facebook url if post and has url',
|
||||
function () {
|
||||
var facebookUrl = getAuthorFacebookUrl({
|
||||
context: ['post'],
|
||||
post: {
|
||||
author: {
|
||||
facebook: 'https://www.facebook.com/user'
|
||||
}
|
||||
}
|
||||
});
|
||||
facebookUrl.should.equal('https://www.facebook.com/user');
|
||||
});
|
||||
|
||||
it('should return null if context does not contain author facebook url and is a post',
|
||||
function () {
|
||||
var facebookUrl = getAuthorFacebookUrl({
|
||||
context: ['post'],
|
||||
post: {
|
||||
author: {
|
||||
facebook: ''
|
||||
}
|
||||
}
|
||||
});
|
||||
should(facebookUrl).equal(null);
|
||||
});
|
||||
|
||||
it('should return null if context does not contain author and is a post', function () {
|
||||
var facebookUrl = getAuthorFacebookUrl({
|
||||
context: ['post'],
|
||||
post: {}
|
||||
});
|
||||
should(facebookUrl).equal(null);
|
||||
});
|
||||
|
||||
it('should return author facebook url if author and has url',
|
||||
function () {
|
||||
var facebookUrl = getAuthorFacebookUrl({
|
||||
context: ['author'],
|
||||
author: {
|
||||
facebook: 'https://www.facebook.com/user'
|
||||
}
|
||||
});
|
||||
facebookUrl.should.equal('https://www.facebook.com/user');
|
||||
});
|
||||
|
||||
it('should return null if context does not contain author facebook url and is a author',
|
||||
function () {
|
||||
var facebookUrl = getAuthorFacebookUrl({
|
||||
context: ['author'],
|
||||
author: {
|
||||
facebook: ''
|
||||
}
|
||||
});
|
||||
should(facebookUrl).equal(null);
|
||||
});
|
||||
|
||||
it('should return null if context is not a post', function () {
|
||||
var facebookUrl = getAuthorFacebookUrl({
|
||||
context: ['tag']
|
||||
});
|
||||
should(facebookUrl).equal(null);
|
||||
});
|
||||
});
|
68
core/test/unit/metadata/creator_url_spec.js
Normal file
68
core/test/unit/metadata/creator_url_spec.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*globals describe, it*/
|
||||
var getCreatorTwitterUrl = require('../../../server/data/meta/creator_url'),
|
||||
should = require('should');
|
||||
|
||||
describe('getCreatorTwitterUrl', function () {
|
||||
it('should return author twitter url if post and has url',
|
||||
function () {
|
||||
var twitterUrl = getCreatorTwitterUrl({
|
||||
context: ['post'],
|
||||
post: {
|
||||
author: {
|
||||
twitter: 'https://twitter.com/user'
|
||||
}
|
||||
}
|
||||
});
|
||||
twitterUrl.should.equal('https://twitter.com/user');
|
||||
});
|
||||
|
||||
it('should return null if context does not contain author twitter url and is a post',
|
||||
function () {
|
||||
var twitterUrl = getCreatorTwitterUrl({
|
||||
context: ['post'],
|
||||
post: {
|
||||
author: {
|
||||
twitter: ''
|
||||
}
|
||||
}
|
||||
});
|
||||
should(twitterUrl).equal(null);
|
||||
});
|
||||
|
||||
it('should return null if context does not contain author and is a post', function () {
|
||||
var twitterUrl = getCreatorTwitterUrl({
|
||||
context: ['post'],
|
||||
post: {}
|
||||
});
|
||||
should(twitterUrl).equal(null);
|
||||
});
|
||||
|
||||
it('should return author twitter url if author and has url',
|
||||
function () {
|
||||
var twitterUrl = getCreatorTwitterUrl({
|
||||
context: ['author'],
|
||||
author: {
|
||||
twitter: 'https://twitter.com/user'
|
||||
}
|
||||
});
|
||||
twitterUrl.should.equal('https://twitter.com/user');
|
||||
});
|
||||
|
||||
it('should return null if context does not contain author twitter url and is a author',
|
||||
function () {
|
||||
var twitterUrl = getCreatorTwitterUrl({
|
||||
context: ['author'],
|
||||
author: {
|
||||
twitter: ''
|
||||
}
|
||||
});
|
||||
should(twitterUrl).equal(null);
|
||||
});
|
||||
|
||||
it('should return null if context is not a post', function () {
|
||||
var twitterUrl = getCreatorTwitterUrl({
|
||||
context: ['tag']
|
||||
});
|
||||
should(twitterUrl).equal(null);
|
||||
});
|
||||
});
|
|
@ -9,6 +9,8 @@ describe('getSchema', function () {
|
|||
title: 'Blog Title'
|
||||
},
|
||||
authorImage: 'http://mysite.com/author/image/url/me.jpg',
|
||||
authorFacebook: 'https://facebook.com/testuser',
|
||||
creatorTwitter: '@testuser',
|
||||
authorUrl: 'http://mysite.com/author/me/',
|
||||
metaTitle: 'Post Title',
|
||||
url: 'http://mysite.com/post/my-post-slug/',
|
||||
|
@ -23,7 +25,9 @@ describe('getSchema', function () {
|
|||
author: {
|
||||
name: 'Post Author',
|
||||
website: 'http://myblogsite.com/',
|
||||
bio: 'My author bio.'
|
||||
bio: 'My author bio.',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
}
|
||||
}, schema = getSchema(metadata, data);
|
||||
|
@ -36,7 +40,11 @@ describe('getSchema', function () {
|
|||
description: 'My author bio.',
|
||||
image: 'http://mysite.com/author/image/url/me.jpg',
|
||||
name: 'Post Author',
|
||||
sameAs: 'http://myblogsite.com/',
|
||||
sameAs: [
|
||||
'http://myblogsite.com/',
|
||||
'https://www.facebook.com/testuser',
|
||||
'https://twitter.com/testuser'
|
||||
],
|
||||
url: 'http://mysite.com/author/me/'
|
||||
},
|
||||
dateModified: '2016-01-21T22:13:05.412Z',
|
||||
|
@ -56,6 +64,8 @@ describe('getSchema', function () {
|
|||
title: 'Blog Title'
|
||||
},
|
||||
authorImage: null,
|
||||
authorFacebook: undefined,
|
||||
creatorTwitter: undefined,
|
||||
authorUrl: 'http://mysite.com/author/me/',
|
||||
metaTitle: 'Post Title',
|
||||
url: 'http://mysite.com/post/my-post-slug/',
|
||||
|
@ -70,7 +80,9 @@ describe('getSchema', function () {
|
|||
author: {
|
||||
name: 'Post Author',
|
||||
website: undefined,
|
||||
bio: null
|
||||
bio: null,
|
||||
facebook: null,
|
||||
twitter: null
|
||||
}
|
||||
}
|
||||
}, schema = getSchema(metadata, data);
|
||||
|
@ -81,6 +93,11 @@ describe('getSchema', function () {
|
|||
author: {
|
||||
'@type': 'Person',
|
||||
name: 'Post Author',
|
||||
sameAs: [
|
||||
null,
|
||||
null,
|
||||
null
|
||||
],
|
||||
url: 'http://mysite.com/author/me/'
|
||||
},
|
||||
dateModified: '2016-01-21T22:13:05.412Z',
|
||||
|
@ -152,7 +169,9 @@ describe('getSchema', function () {
|
|||
context: ['author'],
|
||||
author: {
|
||||
name: 'Author Name',
|
||||
website: 'http://myblogsite.com/'
|
||||
website: 'http://myblogsite.com/',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
}, schema = getSchema(metadata, data);
|
||||
|
||||
|
@ -162,7 +181,11 @@ describe('getSchema', function () {
|
|||
description: 'This is the author description!',
|
||||
name: 'Author Name',
|
||||
publisher: 'Blog Title',
|
||||
sameAs: 'http://myblogsite.com/',
|
||||
sameAs: [
|
||||
'http://myblogsite.com/',
|
||||
'https://www.facebook.com/testuser',
|
||||
'https://twitter.com/testuser'
|
||||
],
|
||||
url: 'http://mysite.com/author/me/'
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,9 @@ describe('getStructuredData', function () {
|
|||
it('should return structured data from metadata', function () {
|
||||
var metadata = {
|
||||
blog: {
|
||||
title: 'Blog Title'
|
||||
title: 'Blog Title',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: '@testuser'
|
||||
},
|
||||
authorName: 'Test User',
|
||||
ogType: 'article',
|
||||
|
@ -15,6 +17,8 @@ describe('getStructuredData', function () {
|
|||
publishedDate: '2015-12-25T05:35:01.234Z',
|
||||
modifiedDate: '2016-01-21T22:13:05.412Z',
|
||||
coverImage: 'http://mysite.com/content/image/mypostcoverimage.jpg',
|
||||
authorFacebook: 'https://www.facebook.com/testpage',
|
||||
creatorTwitter: 'https://twitter.com/twitterpage',
|
||||
keywords: ['one', 'two', 'tag'],
|
||||
metaDescription: 'Post meta description'
|
||||
}, structuredData = getStructuredData(metadata);
|
||||
|
@ -23,6 +27,8 @@ describe('getStructuredData', function () {
|
|||
'article:modified_time': '2016-01-21T22:13:05.412Z',
|
||||
'article:published_time': '2015-12-25T05:35:01.234Z',
|
||||
'article:tag': ['one', 'two', 'tag'],
|
||||
'article:publisher': 'https://www.facebook.com/testuser',
|
||||
'article:author': 'https://www.facebook.com/testpage',
|
||||
'og:description': 'Post meta description',
|
||||
'og:image': 'http://mysite.com/content/image/mypostcoverimage.jpg',
|
||||
'og:site_name': 'Blog Title',
|
||||
|
@ -37,20 +43,26 @@ describe('getStructuredData', function () {
|
|||
'twitter:label1': 'Written by',
|
||||
'twitter:label2': 'Filed under',
|
||||
'twitter:title': 'Post Title',
|
||||
'twitter:url': 'http://mysite.com/post/my-post-slug/'
|
||||
'twitter:url': 'http://mysite.com/post/my-post-slug/',
|
||||
'twitter:site': '@testuser',
|
||||
'twitter:creator': '@twitterpage'
|
||||
});
|
||||
});
|
||||
|
||||
it('should return structured data from metadata with no nulls', function () {
|
||||
var metadata = {
|
||||
blog: {
|
||||
title: 'Blog Title'
|
||||
title: 'Blog Title',
|
||||
facebook: '',
|
||||
twitter: ''
|
||||
},
|
||||
authorName: 'Test User',
|
||||
ogType: 'article',
|
||||
metaTitle: 'Post Title',
|
||||
canonicalUrl: 'http://mysite.com/post/my-post-slug/',
|
||||
modifiedDate: '2016-01-21T22:13:05.412Z',
|
||||
authorFacebook: null,
|
||||
creatorTwitter: null,
|
||||
coverImage: undefined,
|
||||
keywords: null,
|
||||
metaDescription: null
|
||||
|
|
|
@ -124,6 +124,8 @@ describe('{{ghost_head}} helper', function () {
|
|||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser',
|
||||
bio: 'Author bio'
|
||||
}
|
||||
};
|
||||
|
@ -141,8 +143,10 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta property="og:description" content="all about our blog" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:url" content="http:\/\/testurl.com\/about\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:image" content="http:\/\/testurl.com\/content\/images\/test-image-about.png" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:author" content="https:\/\/www.facebook.com\/testuser" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:card" content="summary_large_image" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:title" content="About" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:creator" content="@testuser" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:description" content="all about our blog" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/about\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:image:src" content="http:\/\/testurl.com\/content\/images\/test-image-about.png" \/>/);
|
||||
|
@ -153,6 +157,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/"@type": "Article"/);
|
||||
rendered.string.should.match(/"publisher": "Ghost"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/about\/"/);
|
||||
rendered.string.should.match(/"sameAs": \[\n "http:\/\/authorwebsite.com",\n "https:\/\/www.facebook.com\/testuser",\n "https:\/\/twitter.com\/testuser"\n \]/);
|
||||
rendered.string.should.match(/"image": "http:\/\/testurl.com\/content\/images\/test-image-about.png"/);
|
||||
rendered.string.should.match(/"image\": \"http:\/\/testurl.com\/content\/images\/test-author-image.png\"/);
|
||||
rendered.string.should.match(/"description": "all about our blog"/);
|
||||
|
@ -293,7 +298,9 @@ describe('{{ghost_head}} helper', function () {
|
|||
bio: 'Author bio',
|
||||
image: '/content/images/test-author-image.png',
|
||||
cover: '/content/images/author-cover-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
website: 'http://authorwebsite.com',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}, authorBk = _.cloneDeep(author);
|
||||
|
||||
helpers.ghost_head.call(
|
||||
|
@ -307,17 +314,19 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta property="og:description" content="Author bio" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:url" content="http:\/\/testurl.com\/author\/AuthorName\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:image" content="http:\/\/testurl.com\/content\/images\/author-cover-image.png" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:author" content="https:\/\/www.facebook.com\/testuser\" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:card" content="summary_large_image" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:title" content="Author name - Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:description" content="Author bio" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/author\/AuthorName\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:creator" content="@testuser" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:image:src" content="http:\/\/testurl.com\/content\/images\/author-cover-image.png" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
rendered.string.should.match(/<script type=\"application\/ld\+json\">/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
rendered.string.should.match(/"@type": "Person"/);
|
||||
rendered.string.should.match(/"sameAs": "http:\/\/authorwebsite.com"/);
|
||||
rendered.string.should.match(/"sameAs": \[\n "http:\/\/authorwebsite.com",\n "https:\/\/www.facebook.com\/testuser",\n "https:\/\/twitter.com\/testuser"\n \]/);
|
||||
rendered.string.should.match(/"publisher": "Ghost"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/author\/AuthorName\/"/);
|
||||
rendered.string.should.match(/"image": "http:\/\/testurl.com\/content\/images\/author-cover-image.png"/);
|
||||
|
@ -382,7 +391,9 @@ describe('{{ghost_head}} helper', function () {
|
|||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com',
|
||||
bio: 'Author bio'
|
||||
bio: 'Author bio',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
}, postBk = _.cloneDeep(post);
|
||||
|
||||
|
@ -408,12 +419,12 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta property="article:tag" content="tag1" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:tag" content="tag2" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:tag" content="tag3" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:card" content="summary_large_image" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:author" content="https:\/\/www.facebook.com\/testuser" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:title" content="Welcome to Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:description" content="blog description" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:image:src" content="http:\/\/testurl.com\/content\/images\/test-image.png" \/>/);
|
||||
rendered.string.should.match(/<script type=\"application\/ld\+json\">/);
|
||||
rendered.string.should.match(/<meta name="twitter:creator" content="@testuser" \/>/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
rendered.string.should.match(/"@type": "Article"/);
|
||||
rendered.string.should.match(/"publisher": "Ghost"/);
|
||||
|
@ -422,7 +433,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/"name": "Author name"/);
|
||||
rendered.string.should.match(/"image\": \"http:\/\/testurl.com\/content\/images\/test-author-image.png\"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/author\/Author\/"/);
|
||||
rendered.string.should.match(/"sameAs": "http:\/\/authorwebsite.com"/);
|
||||
rendered.string.should.match(/"sameAs": \[\n "http:\/\/authorwebsite.com",\n "https:\/\/www.facebook.com\/testuser",\n "https:\/\/twitter.com\/testuser"\n \]/);
|
||||
rendered.string.should.match(/"description": "Author bio"/);
|
||||
rendered.string.should.match(/"headline": "Welcome to Ghost"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/post\/"/);
|
||||
|
@ -455,7 +466,9 @@ describe('{{ghost_head}} helper', function () {
|
|||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
website: 'http://authorwebsite.com',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -476,6 +489,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta property="og:description" content="blog "test" description" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:image" content="http:\/\/testurl.com\/content\/images\/test-image.png" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:author" content="https:\/\/www.facebook.com\/testuser" \/>/);
|
||||
rendered.string.should.match(re1);
|
||||
rendered.string.should.match(re2);
|
||||
rendered.string.should.match(/<meta property="article:tag" content="tag1" \/>/);
|
||||
|
@ -485,6 +499,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta name="twitter:title" content="Welcome to Ghost "test"" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:description" content="blog "test" description" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:creator" content="@testuser" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:image:src" content="http:\/\/testurl.com\/content\/images\/test-image.png" \/>/);
|
||||
rendered.string.should.match(/<script type=\"application\/ld\+json\">/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
|
@ -495,7 +510,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/"name": "Author name"/);
|
||||
rendered.string.should.match(/"image\": \"http:\/\/testurl.com\/content\/images\/test-author-image.png\"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/author\/Author\/"/);
|
||||
rendered.string.should.match(/"sameAs": "http:\/\/authorwebsite.com"/);
|
||||
rendered.string.should.match(/"sameAs": \[\n "http:\/\/authorwebsite.com",\n "https:\/\/www.facebook.com\/testuser",\n "https:\/\/twitter.com\/testuser"\n \]/);
|
||||
rendered.string.should.match(/"headline": "Welcome to Ghost "test""/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/post\/"/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
|
@ -525,7 +540,9 @@ describe('{{ghost_head}} helper', function () {
|
|||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
website: 'http://authorwebsite.com',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -545,6 +562,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta property="og:description" content="blog description" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:image" content="http:\/\/testurl.com\/content\/images\/test-image.png" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:author" content="https:\/\/www.facebook.com\/testuser" \/>/);
|
||||
rendered.string.should.match(re1);
|
||||
rendered.string.should.match(re2);
|
||||
rendered.string.should.not.match(/<meta property="article:tag"/);
|
||||
|
@ -553,6 +571,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta name="twitter:description" content="blog description" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:image:src" content="http:\/\/testurl.com\/content\/images\/test-image.png" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:creator" content="@testuser" \/>/);
|
||||
rendered.string.should.match(/<script type=\"application\/ld\+json\">/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
rendered.string.should.match(/"@type": "Article"/);
|
||||
|
@ -562,7 +581,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/"name": "Author name"/);
|
||||
rendered.string.should.match(/"image\": \"http:\/\/testurl.com\/content\/images\/test-author-image.png\"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/author\/Author\/"/);
|
||||
rendered.string.should.match(/"sameAs": "http:\/\/authorwebsite.com"/);
|
||||
rendered.string.should.match(/"sameAs": \[\n "http:\/\/authorwebsite.com",\n "https:\/\/www.facebook.com\/testuser",\n "https:\/\/twitter.com\/testuser"\n \]/);
|
||||
rendered.string.should.match(/"headline": "Welcome to Ghost"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/post\/"/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
|
@ -592,7 +611,9 @@ describe('{{ghost_head}} helper', function () {
|
|||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: null,
|
||||
website: 'http://authorwebsite.com'
|
||||
website: 'http://authorwebsite.com',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -612,6 +633,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta property="og:title" content="Welcome to Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:description" content="blog description" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:author" content="https:\/\/www.facebook.com\/testuser" \/>/);
|
||||
rendered.string.should.not.match(/<meta property="og:image"/);
|
||||
rendered.string.should.match(re1);
|
||||
rendered.string.should.match(re2);
|
||||
|
@ -622,6 +644,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta name="twitter:title" content="Welcome to Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:description" content="blog description" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:creator" content="@testuser" \/>/);
|
||||
rendered.string.should.not.match(/<meta name="twitter:image:src"/);
|
||||
rendered.string.should.match(/<script type=\"application\/ld\+json\">/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
|
@ -632,7 +655,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/"name": "Author name"/);
|
||||
rendered.string.should.not.match(/"image\"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/author\/Author\/"/);
|
||||
rendered.string.should.match(/"sameAs": "http:\/\/authorwebsite.com"/);
|
||||
rendered.string.should.match(/"sameAs": \[\n "http:\/\/authorwebsite.com",\n "https:\/\/www.facebook.com\/testuser",\n "https:\/\/twitter.com\/testuser"\n \]/);
|
||||
rendered.string.should.match(/"headline": "Welcome to Ghost"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/post\/"/);
|
||||
rendered.string.should.match(re3);
|
||||
|
@ -801,7 +824,9 @@ describe('{{ghost_head}} helper', function () {
|
|||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: 'content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
website: 'http://authorwebsite.com',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue