0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-15 03:01:37 -05:00

Updated emails to use newsletter settings (#14588)

refs https://github.com/TryGhost/Team/issues/1550

- Updated email template and seder options to use the settings specified for the related newsletter
- Falls back to the default newsletter, and uses the default newsletter settings for the publishing preview because we only assign a newsletter at the point a post is published

Co-authored-by: Thibaut Patel <thibaut.patel@gmail.com>
Co-authored-by: Matt Hanley <git@matthanley.co.uk>
This commit is contained in:
Thibaut Patel 2022-04-27 19:48:36 +02:00 committed by GitHub
parent 810c3077e8
commit f5bd647100
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 577 additions and 97 deletions

View file

@ -86,6 +86,25 @@ const Newsletter = ghostBookshelf.Model.extend({
};
},
getDefaultNewsletter: async function getDefaultNewsletter(unfilteredOptions = {}) {
const options = {
filter: 'status:active',
order: this.orderDefaultRaw(),
limit: 1
};
if (unfilteredOptions.transacting) {
options.transacting = unfilteredOptions.transacting;
}
const newsletters = await this.findPage(options);
if (newsletters.data.length > 0) {
return newsletters.data[0];
}
return null;
},
getNextAvailableSortOrder: async function getNextAvailableSortOrder(unfilteredOptions = {}) {
const options = {
filter: 'status:active',

View file

@ -1,4 +1,5 @@
const postEmailSerializer = require('./post-email-serializer');
const models = require('../../models');
class EmailPreview {
/**
@ -16,7 +17,9 @@ class EmailPreview {
* @returns {Promise<Object>}
*/
async generateEmailContent(post, memberSegment) {
let emailContent = await postEmailSerializer.serialize(post, {
const newsletter = await models.Newsletter.getDefaultNewsletter();
let emailContent = await postEmailSerializer.serialize(post, newsletter, {
isBrowserPreview: true,
apiVersion: this.apiVersion
});

View file

@ -31,24 +31,18 @@ const messages = {
newsletterVisibilityError: 'Unexpected visibility value "{value}". Use one of the valid: "members", "paid".'
};
const getFromAddress = () => {
let fromAddress = membersService.config.getEmailFromAddress();
const getFromAddress = (senderName, fromAddress) => {
if (/@localhost$/.test(fromAddress) || /@ghost.local$/.test(fromAddress)) {
const localAddress = 'localhost@example.com';
logging.warn(`Rewriting bulk email from address ${fromAddress} to ${localAddress}`);
fromAddress = localAddress;
}
const siteTitle = settingsCache.get('title') ? settingsCache.get('title').replace(/"/g, '\\"') : '';
return siteTitle ? `"${siteTitle}"<${fromAddress}>` : fromAddress;
return senderName ? `"${senderName}"<${fromAddress}>` : fromAddress;
};
const getReplyToAddress = () => {
const fromAddress = membersService.config.getEmailFromAddress();
const getReplyToAddress = (fromAddress, replyAddressOption) => {
const supportAddress = membersService.config.getEmailSupportAddress();
const replyAddressOption = settingsCache.get('members_reply_address');
return (replyAddressOption === 'support') ? supportAddress : fromAddress;
};
@ -60,14 +54,28 @@ const getReplyToAddress = () => {
* @param {ValidAPIVersion} options.apiVersion - api version to be used when serializing email data
*/
const getEmailData = async (postModel, options) => {
const {subject, html, plaintext} = await postEmailSerializer.serialize(postModel, options);
let newsletter = await postModel.related('newsletter').fetch();
if (!newsletter) {
newsletter = await models.Newsletter.getDefaultNewsletter();
}
const {subject, html, plaintext} = await postEmailSerializer.serialize(postModel, newsletter, options);
let senderName = settingsCache.get('title') ? settingsCache.get('title').replace(/"/g, '\\"') : '';
if (newsletter.get('sender_name')) {
senderName = newsletter.get('sender_name');
}
let fromAddress = membersService.config.getEmailFromAddress();
if (newsletter.get('sender_email')) {
fromAddress = newsletter.get('sender_email');
}
return {
subject,
html,
plaintext,
from: getFromAddress(),
replyTo: getReplyToAddress()
from: getFromAddress(senderName, fromAddress),
replyTo: getReplyToAddress(fromAddress, newsletter.get('sender_reply_to'))
};
};
@ -601,7 +609,9 @@ module.exports = {
// NOTE: below are only exposed for testing purposes
_transformEmailRecipientFilter: transformEmailRecipientFilter,
_partitionMembersBySegment: partitionMembersBySegment,
_getEmailMemberRows: getEmailMemberRows
_getEmailMemberRows: getEmailMemberRows,
_getFromAddress: getFromAddress,
_getReplyToAddress: getReplyToAddress
};
/**

View file

@ -10,7 +10,6 @@ const htmlToText = require('html-to-text');
const {isUnsplashImage, isLocalContentImage} = require('@tryghost/kg-default-cards/lib/utils');
const {textColorForBackgroundColor, darkenToContrastThreshold} = require('@tryghost/color-utils');
const logging = require('@tryghost/logging');
const models = require('../../models');
const ALLOWED_REPLACEMENTS = ['first_name'];
@ -170,15 +169,7 @@ const parseReplacements = (email) => {
return replacements;
};
const getTemplateSettings = async (newsletterId = null) => {
let newsletter;
if (newsletterId) {
newsletter = await models.Newsletter.findOne({id: newsletterId, filter: 'status:active'});
} else {
const newsletters = await models.Newsletter.findPage({filter: 'status:active', limit: 1});
newsletter = newsletters.data[0];
}
const getTemplateSettings = async (newsletter) => {
const accentColor = settingsCache.get('accent_color');
const adjustedAccentColor = accentColor && darkenToContrastThreshold(accentColor, '#ffffff', 2).hex();
const adjustedAccentContrastColor = accentColor && textColorForBackgroundColor(adjustedAccentColor).hex();
@ -231,7 +222,7 @@ const getTemplateSettings = async (newsletterId = null) => {
return templateSettings;
};
const serialize = async (postModel, options = {isBrowserPreview: false, apiVersion: 'v4'}) => {
const serialize = async (postModel, newsletter, options = {isBrowserPreview: false, apiVersion: 'v4'}) => {
const post = await serializePostModel(postModel, options.apiVersion);
const timezone = settingsCache.get('timezone');
@ -300,11 +291,11 @@ const serialize = async (postModel, options = {isBrowserPreview: false, apiVersi
}
}
const templateSettings = await getTemplateSettings(post.newsletter_id);
const templateSettings = await getTemplateSettings(newsletter);
const render = template;
let htmlTemplate = render({post, site: getSite(), templateSettings});
let htmlTemplate = render({post, site: getSite(), templateSettings, newsletter: newsletter.toJSON()});
if (options.isBrowserPreview) {
const previewUnsubscribeUrl = createUnsubscribeUrl(null);

View file

@ -1,6 +1,6 @@
/* eslint indent: warn, no-irregular-whitespace: warn */
const iff = (cond, yes, no) => (cond ? yes : no);
module.exports = ({post, site, templateSettings}) => {
module.exports = ({post, site, newsletter, templateSettings}) => {
const date = new Date();
const hasFeatureImageCaption = templateSettings.showFeatureImage && post.feature_image && post.feature_image_caption;
return `<!doctype html>
@ -322,6 +322,9 @@ figure blockquote p {
font-weight: 700;
text-transform: uppercase;
text-align: center;
}
.site-url-bottom-padding {
padding-bottom: 50px;
}
@ -329,6 +332,13 @@ figure blockquote p {
color: #15212A;
}
.site-subtitle {
color: #8695a4;
font-size: 14px;
font-weight: 400;
text-transform: none;
}
.post-title {
padding-bottom: 10px;
font-size: 42px;
@ -1158,7 +1168,7 @@ ${ templateSettings.showBadge ? `
` : ''}
${ templateSettings.showHeaderIcon || templateSettings.showHeaderTitle ? `
${ templateSettings.showHeaderIcon || templateSettings.showHeaderTitle || templateSettings.showHeaderName ? `
<tr>
<td class="${templateSettings.showHeaderTitle ? `site-info-bordered` : `site-info`}" width="100%" align="center">
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
@ -1169,9 +1179,20 @@ ${ templateSettings.showBadge ? `
` : ``}
${ templateSettings.showHeaderTitle ? `
<tr>
<td class="site-url"><div style="width: 100% !important;"><a href="${site.url}" class="site-title">${site.title}</a></div></td>
<td class="site-url ${!templateSettings.showHeaderName ? 'site-url-bottom-padding' : ''}"><div style="width: 100% !important;"><a href="${site.url}" class="site-title">${site.title}</a></div></td>
</tr>
` : ``}
${ templateSettings.showHeaderName && templateSettings.showHeaderTitle ? `
<tr>
<td class="site-url site-url-bottom-padding"><div style="width: 100% !important;"><a href="${site.url}" class="site-subtitle">${newsletter.name}</a></div></td>
</tr>
` : ``}
${ templateSettings.showHeaderName && !templateSettings.showHeaderTitle ? `
<tr>
<td class="site-url site-url-bottom-padding"><div style="width: 100% !important;"><a href="${site.url}" class="site-title">${newsletter.name}</a></div></td>
</tr>
` : ``}
</table>
</td>
</tr>

View file

@ -3,7 +3,7 @@ const sinon = require('sinon');
const errors = require('@tryghost/errors');
const labs = require('../../../../../core/shared/labs');
const {addEmail, _partitionMembersBySegment, _getEmailMemberRows, _transformEmailRecipientFilter, handleUnsubscribeRequest} = require('../../../../../core/server/services/mega/mega');
const {addEmail, _partitionMembersBySegment, _getEmailMemberRows, _transformEmailRecipientFilter, handleUnsubscribeRequest, _getFromAddress, _getReplyToAddress} = require('../../../../../core/server/services/mega/mega');
const membersService = require('../../../../../core/server/services/members');
describe('MEGA', function () {
@ -240,4 +240,38 @@ describe('MEGA', function () {
}, errors.ValidationError);
});
});
describe('getFromAddress', function () {
it('Returns only the email when only fromAddress is specified', function () {
should(_getFromAddress('', 'test@example.com')).eql('test@example.com');
});
it('Adds a sender name when it\'s specified', function () {
should(_getFromAddress(' Unnamed sender!! ', 'test@example.com')).eql('" Unnamed sender!! "<test@example.com>');
});
it('Overwrites the fromAddress when the domain is localhost', function () {
should(_getFromAddress('Test', 'test@localhost')).eql('"Test"<localhost@example.com>');
});
it('Overwrites the fromAddress when the domain is ghost.local', function () {
should(_getFromAddress('123', '456@ghost.local')).eql('"123"<localhost@example.com>');
});
});
describe('getReplyToAddress', function () {
afterEach(function () {
sinon.restore();
});
it('Returns the from address by default', function () {
should(_getReplyToAddress('test@example.com')).eql('test@example.com');
should(_getReplyToAddress('test2@example.com', 'invalid')).eql('test2@example.com');
should(_getReplyToAddress('test3@example.com', 'newsletter')).eql('test3@example.com');
});
it('Returns the support email when the option is set to "support"', function () {
sinon.stub(membersService.config, 'getEmailSupportAddress').returns('support@example.com');
should(_getReplyToAddress('test4@example.com', 'support')).eql('support@example.com');
});
});
});

View file

@ -94,78 +94,30 @@ describe('Post Email Serializer', function () {
sinon.restore();
});
it('uses the default newsletter settings when no newsletter_id is defined', async function () {
sinon.stub(settingsCache, 'get').callsFake(function (key) {
return {
icon: 'icon',
accent_color: '#990000'
}[key];
});
sinon.stub(models.Newsletter, 'findPage').returns(
Promise.resolve({
data: [{
get: function (key) {
return {
header_image: 'image',
show_header_icon: true,
show_header_title: true,
show_feature_image: true,
title_font_category: 'sans_serif',
title_alignment: 'center',
body_font_category: 'serif',
show_badge: true,
footer_content: '',
show_header_name: true
}[key];
}
}]
})
);
const res = await _getTemplateSettings();
should(res).eql({
headerImage: 'image',
showHeaderIcon: 'icon',
showHeaderTitle: true,
showHeaderName: true,
showFeatureImage: true,
titleFontCategory: 'sans_serif',
titleAlignment: 'center',
bodyFontCategory: 'serif',
showBadge: true,
footerContent: '',
accentColor: '#990000',
adjustedAccentColor: '#990000',
adjustedAccentContrastColor: '#FFFFFF'
});
});
it('uses the newsletter settings when a newsletter_id is defined', async function () {
it('uses the newsletter settings', async function () {
sinon.stub(settingsCache, 'get').callsFake(function (key) {
return {
icon: 'icon2',
accent_color: '#000099'
}[key];
});
sinon.stub(models.Newsletter, 'findOne').returns(
Promise.resolve({
get: function (key) {
return {
header_image: 'image',
show_header_icon: true,
show_header_title: true,
show_feature_image: true,
title_font_category: 'sans-serif',
title_alignment: 'center',
body_font_category: 'serif',
show_badge: true,
footer_content: 'footer',
show_header_name: true
}[key];
}
})
);
const res = await _getTemplateSettings('123');
const newsletterMock = {
get: function (key) {
return {
header_image: 'image',
show_header_icon: true,
show_header_title: true,
show_feature_image: true,
title_font_category: 'sans-serif',
title_alignment: 'center',
body_font_category: 'serif',
show_badge: true,
footer_content: 'footer',
show_header_name: true
}[key];
}
};
const res = await _getTemplateSettings(newsletterMock);
should(res).eql({
headerImage: 'image',
showHeaderIcon: 'icon2',

View file

@ -0,0 +1,450 @@
const should = require('should');
const sinon = require('sinon');
const cheerio = require('cheerio');
const render = require('../../../../../core/server/services/mega/template');
describe('Mega template', function () {
afterEach(function () {
sinon.restore();
});
it('Renders html correctly', function () {
const post = {
title: 'My post title',
excerpt: 'My post excerpt',
url: 'post url',
authors: 'post authors',
published_at: 'post published_at',
feature_image: 'post feature image',
feature_image_caption: 'post feature image caption',
feature_image_width: 'post feature image width',
feature_image_alt: 'post feature image alt',
html: '<div class="post-content-html"></div>'
};
const site = {
iconUrl: 'site icon url',
url: 'site url',
title: 'site title'
};
const templateSettings = {
headerImage: 'header image',
headerImageWidth: '600',
showHeaderIcon: true,
showHeaderTitle: true,
showHeaderName: true,
titleAlignment: 'left',
titleFontCategory: 'serif',
showFeatureImage: true,
bodyFontCategory: 'sans_serif',
footerContent: 'footer content',
showBadge: true
};
const newsletter = {
name: 'newsletter name'
};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('title').text()).eql(post.title);
should($('.preheader').text()).eql(post.excerpt);
should($('.header-image').length).eql(1);
const headerImage = $('.header-image img');
should(headerImage.length).eql(1);
should(headerImage.attr('src')).eql(templateSettings.headerImage);
should(headerImage.attr('width')).eql(templateSettings.headerImageWidth);
should($('td.site-info-bordered').length).eql(1);
should($('.site-info').length).eql(0);
should($('.site-url').length).eql(2);
should($('.site-icon').length).eql(1);
should($('.site-icon a').attr('href')).eql(site.url);
should($('.site-icon a img').attr('src')).eql(site.iconUrl);
should($('.site-icon a img').attr('alt')).eql(site.title);
should($('.site-title').length).eql(1);
const headerTitle = $($('.site-url').first());
should(headerTitle.length).eql(1);
should(headerTitle.hasClass('site-url-bottom-padding')).eql(false);
should(headerTitle.find('.site-title').attr('href')).eql(site.url);
should(headerTitle.find('.site-title').text()).eql(site.title);
const headerSubtitle = $($('.site-url').get()[1]);
should(headerSubtitle.length).eql(1);
should(headerSubtitle.hasClass('site-url-bottom-padding')).eql(true);
should(headerSubtitle.find('.site-subtitle').attr('href')).eql(site.url);
should(headerSubtitle.find('.site-subtitle').text()).eql(newsletter.name);
const postTitle = $('.post-title');
should(postTitle.length).eql(1);
should(postTitle.hasClass('post-title-serif')).eql(true);
should(postTitle.hasClass('post-title-left')).eql(true);
should($('.post-title a').attr('href')).eql(post.url);
should($('.post-title a').hasClass('post-title-link-left')).eql(true);
should($('.post-title a').text()).eql(post.title);
const postMeta = $('.post-meta');
should(postMeta.length).eql(1);
should(postMeta.hasClass('post-meta-left')).eql(true);
should(postMeta.text().trim().replace(/ *\n */g, '\n')).eql(`By ${post.authors} \n${post.published_at} \nView online →`);
should(postMeta.find('a').attr('href')).eql(post.url);
const featureImage = $('.feature-image');
should(featureImage.length).eql(1);
should(featureImage.hasClass('feature-image-with-caption')).eql(true);
should(featureImage.find('img').attr('src')).eql(post.feature_image);
should(featureImage.find('img').attr('width')).eql(post.feature_image_width);
should(featureImage.find('img').attr('alt')).eql(post.feature_image_alt);
const imageCaption = $('.feature-image-caption');
should(imageCaption.length).eql(1);
should(imageCaption.text()).eql(post.feature_image_caption);
should($('.post-content-sans-serif').length).eql(1);
should($('.post-content').length).eql(0);
should($('.post-content-html').length).eql(1);
const footers = $('.footer').get();
should(footers.length).eql(2);
should($(footers[0]).text()).eql(templateSettings.footerContent);
should($(footers[1]).text()).eql(`${site.title} © ${(new Date()).getFullYear()} Unsubscribe`);
should($(footers[1]).find('a').attr('href')).eql('%recipient.unsubscribe_url%');
const footerPowered = $('.footer-powered');
should(footerPowered.length).eql(1);
should(footerPowered.find('a img').attr('alt')).eql('Powered by Ghost');
});
it('Uses the post title as a fallback for the excerpt', function () {
const post = {
title: 'My post title'
};
const site = {};
const templateSettings = {};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('.preheader').text()).eql(post.title + ' ');
});
it('Hides the header image if it isn\'t set', function () {
const post = {};
const site = {};
const templateSettings = {
headerImage: ''
};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('.header-image').length).eql(0);
});
it('Shows no width in the header if headerImageWidth isn\'t defined', function () {
const post = {
title: 'My post title'
};
const site = {};
const templateSettings = {
headerImage: 'header image'
};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('.header-image').length).eql(1);
should($('.header-image img').length).eql(1);
should(typeof $('.header-image img').attr('width')).eql('undefined');
});
it('Shows no header when all header features are disabled', function () {
const post = {
title: 'My post title'
};
const site = {};
const templateSettings = {
showHeaderIcon: false,
showHeaderTitle: false,
showHeaderName: false
};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('.site-info-bordered').length).eql(0);
should($('.site-info').length).eql(0);
should($('.site-url').length).eql(0);
should($('.site-icon').length).eql(0);
should($('.site-title').length).eql(0);
should($('.site-subtitle').length).eql(0);
should($('.site-url-bottom-padding').length).eql(0);
});
it('Shows the right header for showHeaderIcon:true, showHeaderTitle:false, showHeaderName:false', function () {
/**
* The edge case where the iconUrl is falsy in the current configuration wasn't tested.
* The reason is that the Ghost admin is guarding against the edge case.
*/
const post = {};
const site = {
iconUrl: 'site icon url'
};
const templateSettings = {
showHeaderIcon: true,
showHeaderTitle: false,
showHeaderName: false
};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('.site-info-bordered').length).eql(0);
should($('.site-info').length).eql(1);
should($('.site-icon').length).eql(1);
should($('.site-url').length).eql(0);
should($('.site-title').length).eql(0);
should($('.site-subtitle').length).eql(0);
});
it('Shows the right header for showHeaderIcon:false, showHeaderTitle:true, showHeaderName:false', function () {
const post = {};
const site = {
title: 'site title'
};
const templateSettings = {
showHeaderIcon: false,
showHeaderTitle: true,
showHeaderName: false
};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('.site-info-bordered').length).eql(1);
should($('.site-info').length).eql(0);
should($('.site-icon').length).eql(0);
should($('.site-url').length).eql(1);
should($('.site-url').hasClass('site-url-bottom-padding')).eql(true);
should($('.site-url').text()).eql(site.title);
should($('.site-title').length).eql(1);
should($('.site-subtitle').length).eql(0);
});
it('Shows the right header for showHeaderIcon:false, showHeaderTitle:false, showHeaderName:true', function () {
const post = {};
const site = {};
const templateSettings = {
showHeaderIcon: false,
showHeaderTitle: false,
showHeaderName: true
};
const newsletter = {
name: 'newsletter name'
};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('.site-info-bordered').length).eql(0);
should($('.site-info').length).eql(1);
should($('.site-icon').length).eql(0);
should($('.site-url').length).eql(1);
should($('.site-url').hasClass('site-url-bottom-padding')).eql(true);
should($('.site-url').text()).eql(newsletter.name);
should($('.site-title').length).eql(1);
should($('.site-subtitle').length).eql(0);
});
it('Shows the right header for showHeaderIcon:true, showHeaderTitle:true, showHeaderName:false', function () {
const post = {};
const site = {
iconUrl: 'site icon url',
title: 'site title'
};
const templateSettings = {
showHeaderIcon: true,
showHeaderTitle: true,
showHeaderName: false
};
const newsletter = {
};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('.site-info-bordered').length).eql(1);
should($('.site-info').length).eql(0);
should($('.site-icon').length).eql(1);
should($('.site-url').length).eql(1);
should($('.site-url').hasClass('site-url-bottom-padding')).eql(true);
should($('.site-url').text()).eql(site.title);
should($('.site-title').length).eql(1);
should($('.site-subtitle').length).eql(0);
});
it('Shows the right header for showHeaderIcon:true, showHeaderTitle:false, showHeaderName:true', function () {
const post = {};
const site = {
iconUrl: 'site icon url'
};
const templateSettings = {
showHeaderIcon: true,
showHeaderTitle: false,
showHeaderName: true
};
const newsletter = {
name: 'newsletter name'
};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('.site-info-bordered').length).eql(0);
should($('.site-info').length).eql(1);
should($('.site-icon').length).eql(1);
should($('.site-url').length).eql(1);
should($('.site-url').hasClass('site-url-bottom-padding')).eql(true);
should($('.site-url').text()).eql(newsletter.name);
should($('.site-title').length).eql(1);
should($('.site-subtitle').length).eql(0);
});
it('Shows the right html titleFontCategory isn\'t set to `serif` and when titleAlignment is set to `center`', function () {
const post = {};
const site = {};
const templateSettings = {
titleFontCategory: 'sans_serif',
titleAlignment: 'center'
};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
const postTitle = $('.post-title');
should(postTitle.hasClass('post-title-serif')).eql(false);
should(postTitle.hasClass('post-title-left')).eql(false);
should($('.post-title a').hasClass('post-title-link-left')).eql(false);
should($('.post-meta').hasClass('post-meta-left')).eql(false);
});
it('Renders correctly without a feature image (showFeatureImage set to `false`)', function () {
const post = {
feature_image: 'post feature image'
};
const site = {};
const templateSettings = {
showFeatureImage: false
};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('.feature-image').length).eql(0);
should($('.feature-image-caption').length).eql(0);
});
it('Renders correctly without a feature image (post doesn\'t have a feature image)', function () {
const post = {};
const site = {};
const templateSettings = {
showFeatureImage: true
};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('.feature-image').length).eql(0);
should($('.feature-image-caption').length).eql(0);
});
it('Renders correctly a feature image without width nor alt', function () {
const post = {
feature_image: 'post feature image'
};
const site = {};
const templateSettings = {
showFeatureImage: true
};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
const featureImage = $('.feature-image');
should(featureImage.length).eql(1);
should(featureImage.hasClass('feature-image-with-caption')).eql(false);
should(featureImage.find('img').attr('src')).eql(post.feature_image);
should(typeof featureImage.find('img').attr('width')).eql('undefined');
should(typeof featureImage.find('img').attr('alt')).eql('undefined');
});
it('Renders correctly without a feature image caption', function () {
const post = {
feature_image: 'post feature image'
};
const site = {};
const templateSettings = {
showFeatureImage: true
};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
const featureImage = $('.feature-image');
should(featureImage.length).eql(1);
should(featureImage.hasClass('feature-image-with-caption')).eql(false);
const imageCaption = $('.feature-image-caption');
should(imageCaption.length).eql(0);
});
it('Shows no footer when `footerContent` is falsy', function () {
const post = {};
const site = {};
const templateSettings = {
footerContent: ''
};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
const footer = $('.footer');
should(footer.length).eql(1);
should(footer.text()).eql(`${site.title} © ${(new Date()).getFullYear()} Unsubscribe`);
});
it('Shows no badge when `showBadge` is false', function () {
const post = {};
const site = {};
const templateSettings = {
showBadge: false
};
const newsletter = {};
const html = render({post, site, templateSettings, newsletter});
const $ = cheerio.load(html);
should($('.footer-powered').length).eql(0);
});
});