b9b1502772
ref https://github.com/TryGhost/Product/issues/4110 Made this change to increase clarity in data export --- <!-- Leave the line below if you'd like GitHub Copilot to generate a summary from your commit --> <!-- copilot:summary --> ### <samp>🤖 Generated by Copilot at 6c0508d</samp> Renamed a column in posts export data and updated the corresponding test case. This change makes the export data more consistent and clear for users who have different member features enabled.
541 lines
17 KiB
JavaScript
541 lines
17 KiB
JavaScript
const {PostsExporter} = require('../index');
|
|
const assert = require('assert/strict');
|
|
const {createModelClass, createModel} = require('./utils');
|
|
|
|
class SettingsCache {
|
|
constructor(settings) {
|
|
this.settings = settings;
|
|
}
|
|
|
|
get(key) {
|
|
return this.settings[key];
|
|
}
|
|
|
|
set(key, value) {
|
|
this.settings[key] = value;
|
|
}
|
|
}
|
|
|
|
describe('PostsExporter', function () {
|
|
it('Can construct class', function () {
|
|
new PostsExporter({});
|
|
});
|
|
|
|
describe('export', function () {
|
|
let exporter;
|
|
let models;
|
|
let post;
|
|
let settingsCache;
|
|
let settingsHelpers;
|
|
let defaultNewsletter;
|
|
|
|
beforeEach(function () {
|
|
defaultNewsletter = {
|
|
id: createModel({}).id,
|
|
name: 'Daily Newsletter',
|
|
feedback_enabled: true
|
|
};
|
|
|
|
post = {
|
|
title: 'Test Post',
|
|
status: 'published',
|
|
created_at: new Date(),
|
|
published_at: new Date(),
|
|
updated_at: new Date(),
|
|
featured: false,
|
|
loaded: ['tiers','tags','authors','email'],
|
|
email: createModel({
|
|
feedback_enabled: true,
|
|
track_clicks: true,
|
|
email_count: 256,
|
|
opened_count: 128
|
|
}),
|
|
count__clicks: 64,
|
|
count__signups: 32,
|
|
count__paid_conversions: 16,
|
|
count__positive_feedback: 8,
|
|
count__negative_feedback: 4,
|
|
newsletter_id: defaultNewsletter.id,
|
|
authors: [
|
|
createModel({
|
|
name: 'Test Author'
|
|
})
|
|
],
|
|
tags: [
|
|
createModel({
|
|
name: 'Test Tag'
|
|
})
|
|
],
|
|
visibility: 'tiers',
|
|
tiers: [
|
|
createModel({
|
|
name: 'Silver'
|
|
}),
|
|
createModel({
|
|
name: 'Gold'
|
|
})
|
|
]
|
|
};
|
|
models = {
|
|
Post: createModelClass({
|
|
findAll: [
|
|
post
|
|
]
|
|
}),
|
|
Newsletter: createModelClass({
|
|
findAll: [
|
|
defaultNewsletter
|
|
]
|
|
}),
|
|
Label: createModelClass({
|
|
findAll: []
|
|
}),
|
|
Product: createModelClass({
|
|
findAll: []
|
|
})
|
|
};
|
|
|
|
settingsCache = new SettingsCache({
|
|
members_track_sources: true,
|
|
email_track_opens: true,
|
|
email_track_clicks: true
|
|
});
|
|
|
|
settingsHelpers = {
|
|
isMembersEnabled: () => true,
|
|
arePaidMembersEnabled: () => true
|
|
};
|
|
|
|
exporter = new PostsExporter({
|
|
models,
|
|
settingsCache,
|
|
settingsHelpers,
|
|
getPostUrl: () => 'https://example.com/post'
|
|
});
|
|
});
|
|
|
|
it('Can export posts', async function () {
|
|
const posts = await exporter.export({});
|
|
assert.equal(posts.length, 1);
|
|
|
|
// Hides newsletter column
|
|
assert.equal(posts[0].newsletter_name, undefined);
|
|
|
|
// Check status
|
|
assert.equal(posts[0].status, 'published and emailed');
|
|
});
|
|
|
|
it('Can export posts without an email', async function () {
|
|
post.email = null;
|
|
const posts = await exporter.export({});
|
|
assert.equal(posts.length, 1);
|
|
|
|
// Hides newsletter column
|
|
assert.equal(posts[0].newsletter_name, undefined);
|
|
|
|
// Check status
|
|
assert.equal(posts[0].status, 'published only');
|
|
});
|
|
|
|
it('Adds newsletter columns if multiple newsletters', async function () {
|
|
const secondNewsletter = {
|
|
id: createModel({}).id,
|
|
name: 'Weekly Newsletter',
|
|
feedback_enabled: true
|
|
};
|
|
models.Newsletter.options.findAll.push(secondNewsletter);
|
|
models.Post.options.findAll.push({
|
|
...post,
|
|
newsletter_id: models.Newsletter.options.findAll[1].id
|
|
});
|
|
const posts = await exporter.export({});
|
|
assert.equal(posts.length, 2);
|
|
|
|
// Shows newsletter column
|
|
assert.equal(posts[0].newsletter_name, 'Daily Newsletter');
|
|
assert.equal(posts[1].newsletter_name, 'Weekly Newsletter');
|
|
|
|
// Shows feedback columns
|
|
assert.equal(posts[0].feedback_more_like_this, post.count__positive_feedback);
|
|
assert.equal(posts[0].feedback_less_like_this, post.count__negative_feedback);
|
|
});
|
|
|
|
it('Hides feedback columns if feedback disabled for all newsletters', async function () {
|
|
defaultNewsletter.feedback_enabled = false;
|
|
const posts = await exporter.export({});
|
|
|
|
// Hides feedback columns
|
|
assert.equal(posts[0].feedback_more_like_this, undefined);
|
|
assert.equal(posts[0].feedback_less_like_this, undefined);
|
|
});
|
|
|
|
it('Hides email related analytics when post status is draft', async function () {
|
|
const secondNewsletter = {
|
|
id: createModel({}).id,
|
|
name: 'Weekly Newsletter',
|
|
feedback_enabled: true
|
|
};
|
|
models.Newsletter.options.findAll.push(secondNewsletter);
|
|
post.status = 'draft';
|
|
const posts = await exporter.export({});
|
|
|
|
// Feedback columns are empty, but present because of global settings (newsletter with feedback enabled)
|
|
assert.equal(posts[0].feedback_more_like_this, null);
|
|
assert.equal(posts[0].feedback_less_like_this, null);
|
|
|
|
// Sends etc
|
|
assert.equal(posts[0].sends, null);
|
|
assert.equal(posts[0].opens, null);
|
|
assert.equal(posts[0].clicks, null);
|
|
assert.equal(posts[0].newsletter_name, null);
|
|
|
|
// Signups
|
|
assert.equal(posts[0].signups, null);
|
|
assert.equal(posts[0].paid_conversions, null);
|
|
});
|
|
|
|
it('Hides member related columns if members disabled', async function () {
|
|
settingsHelpers.isMembersEnabled = () => false;
|
|
const posts = await exporter.export({});
|
|
assert.equal(posts[0].email_recipients, undefined);
|
|
|
|
// No feedback columns
|
|
assert.equal(posts[0].feedback_more_like_this, undefined);
|
|
assert.equal(posts[0].feedback_less_like_this, undefined);
|
|
|
|
// Sends etc
|
|
assert.equal(posts[0].sends, undefined);
|
|
assert.equal(posts[0].opens, undefined);
|
|
assert.equal(posts[0].clicks, undefined);
|
|
|
|
// Signups
|
|
assert.equal(posts[0].signups, undefined);
|
|
assert.equal(posts[0].paid_conversions, undefined);
|
|
});
|
|
|
|
it('Hides clicks if disabled', async function () {
|
|
settingsCache.set('email_track_clicks', false);
|
|
const posts = await exporter.export({});
|
|
|
|
assert.notEqual(posts[0].email_recipients, undefined);
|
|
assert.notEqual(posts[0].feedback_more_like_this, undefined);
|
|
assert.notEqual(posts[0].feedback_less_like_this, undefined);
|
|
assert.notEqual(posts[0].sends, undefined);
|
|
assert.notEqual(posts[0].opens, undefined);
|
|
assert.notEqual(posts[0].signups, undefined);
|
|
assert.notEqual(posts[0].paid_conversions, undefined);
|
|
|
|
assert.equal(posts[0].clicks, undefined);
|
|
});
|
|
|
|
it('Hides opens if disabled', async function () {
|
|
settingsCache.set('email_track_opens', false);
|
|
const posts = await exporter.export({});
|
|
|
|
assert.notEqual(posts[0].email_recipients, undefined);
|
|
assert.notEqual(posts[0].feedback_more_like_this, undefined);
|
|
assert.notEqual(posts[0].feedback_less_like_this, undefined);
|
|
assert.notEqual(posts[0].sends, undefined);
|
|
assert.notEqual(posts[0].clicks, undefined);
|
|
assert.notEqual(posts[0].signups, undefined);
|
|
assert.notEqual(posts[0].paid_conversions, undefined);
|
|
|
|
assert.equal(posts[0].opens, undefined);
|
|
});
|
|
|
|
it('Hides paid member related columns if paid members disabled', async function () {
|
|
settingsHelpers.arePaidMembersEnabled = () => false;
|
|
const posts = await exporter.export({});
|
|
|
|
assert.notEqual(posts[0].email_recipients, undefined);
|
|
assert.notEqual(posts[0].feedback_more_like_this, undefined);
|
|
assert.notEqual(posts[0].feedback_less_like_this, undefined);
|
|
assert.notEqual(posts[0].sends, undefined);
|
|
assert.notEqual(posts[0].clicks, undefined);
|
|
assert.notEqual(posts[0].signups, undefined);
|
|
assert.notEqual(posts[0].opens, undefined);
|
|
|
|
assert.equal(posts[0].paid_conversions, undefined);
|
|
});
|
|
|
|
it('Works if relations not loaded correctly', async function () {
|
|
delete post.count__clicks;
|
|
delete post.count__signups;
|
|
delete post.count__paid_conversions;
|
|
delete post.count__positive_feedback;
|
|
delete post.count__negative_feedback;
|
|
|
|
const posts = await exporter.export({});
|
|
assert.equal(posts.length, 1);
|
|
|
|
assert.equal(posts[0].clicks, 0);
|
|
assert.equal(posts[0].signups, 0);
|
|
assert.equal(posts[0].paid_conversions, 0);
|
|
assert.equal(posts[0].feedback_more_like_this, 0);
|
|
assert.equal(posts[0].feedback_less_like_this, 0);
|
|
});
|
|
});
|
|
|
|
describe('mapPostStatus', function () {
|
|
const exporter = new PostsExporter({});
|
|
|
|
it('Returns draft', function () {
|
|
assert.equal(
|
|
exporter.mapPostStatus('draft', false),
|
|
'draft'
|
|
);
|
|
});
|
|
|
|
it('Returns scheduled', function () {
|
|
assert.equal(
|
|
exporter.mapPostStatus('scheduled', false),
|
|
'scheduled'
|
|
);
|
|
});
|
|
|
|
it('Returns emailed only', function () {
|
|
assert.equal(
|
|
exporter.mapPostStatus('sent', false),
|
|
'emailed only'
|
|
);
|
|
});
|
|
|
|
it('Returns published and emailed', function () {
|
|
assert.equal(
|
|
exporter.mapPostStatus('published', true),
|
|
'published and emailed'
|
|
);
|
|
});
|
|
|
|
it('Returns published only', function () {
|
|
assert.equal(
|
|
exporter.mapPostStatus('published', false),
|
|
'published only'
|
|
);
|
|
});
|
|
|
|
it('Returns unsupported', function () {
|
|
assert.equal(
|
|
exporter.mapPostStatus('unsupported', false),
|
|
'unsupported'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('postAccessToString', function () {
|
|
const exporter = new PostsExporter({});
|
|
|
|
it('Returns public', function () {
|
|
const access = exporter.postAccessToString(
|
|
createModel({
|
|
visibility: 'public'
|
|
})
|
|
);
|
|
assert.equal(
|
|
access,
|
|
'Public'
|
|
);
|
|
});
|
|
|
|
it('Returns members', function () {
|
|
const access = exporter.postAccessToString(
|
|
createModel({
|
|
visibility: 'members'
|
|
})
|
|
);
|
|
assert.equal(
|
|
access,
|
|
'Members-only'
|
|
);
|
|
});
|
|
|
|
it('Returns paid', function () {
|
|
const access = exporter.postAccessToString(
|
|
createModel({
|
|
visibility: 'paid'
|
|
})
|
|
);
|
|
assert.equal(
|
|
access,
|
|
'Paid members-only'
|
|
);
|
|
});
|
|
|
|
it('Returns empty tiers', function () {
|
|
const access = exporter.postAccessToString(
|
|
createModel({
|
|
visibility: 'tiers',
|
|
loaded: ['tiers'],
|
|
tiers: []
|
|
})
|
|
);
|
|
assert.equal(
|
|
access,
|
|
'Specific tiers: none'
|
|
);
|
|
});
|
|
|
|
it('Returns multiple tiers', function () {
|
|
const access = exporter.postAccessToString(
|
|
createModel({
|
|
visibility: 'tiers',
|
|
loaded: ['tiers'],
|
|
tiers: [
|
|
createModel({
|
|
name: 'Silver'
|
|
}),
|
|
createModel({
|
|
name: 'Gold'
|
|
})
|
|
]
|
|
})
|
|
);
|
|
assert.equal(
|
|
access,
|
|
'Specific tiers: Silver, Gold'
|
|
);
|
|
});
|
|
|
|
it('Returns unsupported', function () {
|
|
const access = exporter.postAccessToString(
|
|
createModel({
|
|
visibility: 'unsupported'
|
|
})
|
|
);
|
|
assert.equal(
|
|
access,
|
|
'unsupported'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('humanReadableEmailRecipientFilter', function () {
|
|
const exporter = new PostsExporter({});
|
|
let labels;
|
|
let tiers;
|
|
|
|
beforeEach(function () {
|
|
labels = [
|
|
createModel({
|
|
slug: 'imported',
|
|
name: 'Imported'
|
|
}),
|
|
createModel({
|
|
slug: 'vip',
|
|
name: 'VIP'
|
|
})
|
|
];
|
|
tiers = [
|
|
createModel({
|
|
slug: 'silver',
|
|
name: 'Silver'
|
|
}),
|
|
createModel({
|
|
slug: 'gold',
|
|
name: 'Gold'
|
|
})
|
|
];
|
|
});
|
|
|
|
it('Returns all', function () {
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('all'),
|
|
'All subscribers'
|
|
);
|
|
});
|
|
|
|
it('Returns empty', function () {
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter(''),
|
|
''
|
|
);
|
|
});
|
|
|
|
it('Returns labels', function () {
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('label:imported', labels, tiers),
|
|
'Imported'
|
|
);
|
|
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('label:imported,label:vip', labels, tiers),
|
|
'Imported, VIP'
|
|
);
|
|
});
|
|
|
|
it('Returns invalid labels', function () {
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('label:invalidone', labels, tiers),
|
|
'invalidone'
|
|
);
|
|
});
|
|
|
|
it('Returns tiers', function () {
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('tier:silver', labels, tiers),
|
|
'Silver'
|
|
);
|
|
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('tier:silver,tier:gold', labels, tiers),
|
|
'Silver, Gold'
|
|
);
|
|
});
|
|
|
|
it('Returns invalid tiers', function () {
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('tier:invalidone', labels, tiers),
|
|
'invalidone'
|
|
);
|
|
});
|
|
|
|
it('Returns status', function () {
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('status:free'),
|
|
'Free subscribers'
|
|
);
|
|
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('status:-free'),
|
|
'Paid subscribers'
|
|
);
|
|
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('status:paid'),
|
|
'Paid subscribers'
|
|
);
|
|
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('status:comped'),
|
|
'Complimentary subscribers'
|
|
);
|
|
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('status:-paid'),
|
|
'Free subscribers'
|
|
);
|
|
});
|
|
|
|
it('Ignores AND', function () {
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('status:free+status:paid', labels, tiers),
|
|
''
|
|
);
|
|
});
|
|
|
|
it('Single brackets filter', function () {
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('(status:free)', labels, tiers),
|
|
'Free subscribers'
|
|
);
|
|
});
|
|
|
|
it('Ignores invalid filters', function () {
|
|
assert.equal(
|
|
exporter.humanReadableEmailRecipientFilter('sdgsdgsdg sdg sdg sdgs dgs', labels, tiers),
|
|
'sdgsdgsdg sdg sdg sdgs dgs'
|
|
);
|
|
});
|
|
});
|
|
});
|