mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-06 22:40:14 -05:00
c36e749820
refs https://github.com/TryGhost/Team/issues/581 closes https://github.com/TryGhost/Team/issues/582 Emails can now be sent to members with specific associated labels or products by specifying an NQL string. We want to bring the same members segment feature to content by allowing `visibility` to be an NQL filter string on top of the `public/members/paid` special-case strings. As an example it's possible to set `posts.visibility` to `label:vip` to make a post available only to those members with the `vip` label. - removed enum validations for `visibility` so it now accepts any string or `null` - bumped `@tryghost/admin-api-schema` for API-level validation changes - added nql validation to API input validators by running the visibility query against the members model - added transform of NQL to special-case visibility values when saving post model - ensures there's a single way of representing "members" and "paid" where NQL gives multiple ways of representing the same segment - useful for keeping theme-level checks such as `{{#has visibility="paid"}}` working as expected - updated content-gating to parse nql from post's visibility and use it to query the currently logged in member to see if there's a match - bumped @tryghost/members-api to include label and product data when loading member
699 lines
30 KiB
JavaScript
699 lines
30 KiB
JavaScript
const path = require('path');
|
|
const querystring = require('querystring');
|
|
const should = require('should');
|
|
const supertest = require('supertest');
|
|
const sinon = require('sinon');
|
|
const testUtils = require('../../../../utils');
|
|
const localUtils = require('./utils');
|
|
const config = require('../../../../../core/shared/config');
|
|
const labs = require('../../../../../core/server/services/labs');
|
|
const mailService = require('../../../../../core/server/services/mail');
|
|
|
|
const ghost = testUtils.startGhost;
|
|
|
|
let request;
|
|
|
|
describe('Members API', function () {
|
|
before(function () {
|
|
sinon.stub(labs, 'isSet').withArgs('members').returns(true);
|
|
});
|
|
|
|
after(function () {
|
|
sinon.restore();
|
|
});
|
|
|
|
before(function () {
|
|
return ghost()
|
|
.then(function () {
|
|
request = supertest.agent(config.get('url'));
|
|
})
|
|
.then(function () {
|
|
return localUtils.doAuth(request, 'members');
|
|
});
|
|
});
|
|
|
|
beforeEach(function () {
|
|
sinon.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled');
|
|
});
|
|
|
|
afterEach(function () {
|
|
sinon.restore();
|
|
});
|
|
|
|
it('Can add and send a signup confirmation email', async function () {
|
|
const member = {
|
|
name: 'Send Me Confirmation',
|
|
email: 'member_getting_confirmation@test.com',
|
|
subscribed: true
|
|
};
|
|
|
|
const queryParams = {
|
|
send_email: true,
|
|
email_type: 'signup'
|
|
};
|
|
|
|
const res = await request
|
|
.post(localUtils.API.getApiQuery(`members/?${querystring.stringify(queryParams)}`))
|
|
.send({members: [member]})
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(201);
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
jsonResponse.members.should.have.length(1);
|
|
|
|
jsonResponse.members[0].name.should.equal(member.name);
|
|
jsonResponse.members[0].email.should.equal(member.email);
|
|
jsonResponse.members[0].subscribed.should.equal(member.subscribed);
|
|
testUtils.API.isISO8601(jsonResponse.members[0].created_at).should.be.true();
|
|
|
|
should.exist(res.headers.location);
|
|
res.headers.location.should.equal(`http://127.0.0.1:2369${localUtils.API.getApiQuery('members/')}${res.body.members[0].id}/`);
|
|
|
|
mailService.GhostMailer.prototype.send.called.should.be.true();
|
|
mailService.GhostMailer.prototype.send.args[0][0].to.should.equal('member_getting_confirmation@test.com');
|
|
|
|
await request
|
|
.delete(localUtils.API.getApiQuery(`members/${jsonResponse.members[0].id}/`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(204);
|
|
});
|
|
|
|
it('Can order by email_open_rate', async function () {
|
|
await request
|
|
.get(localUtils.API.getApiQuery('members/?order=email_open_rate%20desc'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse.members);
|
|
localUtils.API.checkResponse(jsonResponse, 'members');
|
|
jsonResponse.members.should.have.length(8);
|
|
|
|
jsonResponse.members[0].email.should.equal('paid@test.com');
|
|
jsonResponse.members[0].email_open_rate.should.equal(80);
|
|
jsonResponse.members[1].email.should.equal('member2@test.com');
|
|
jsonResponse.members[1].email_open_rate.should.equal(50);
|
|
jsonResponse.members[2].email.should.equal('member1@test.com');
|
|
should.equal(null, jsonResponse.members[2].email_open_rate);
|
|
jsonResponse.members[3].email.should.equal('trialing@test.com');
|
|
should.equal(null, jsonResponse.members[3].email_open_rate);
|
|
});
|
|
|
|
await request
|
|
.get(localUtils.API.getApiQuery('members/?order=email_open_rate%20asc'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then((res) => {
|
|
const jsonResponse = res.body;
|
|
localUtils.API.checkResponse(jsonResponse, 'members');
|
|
jsonResponse.members.should.have.length(8);
|
|
|
|
jsonResponse.members[0].email.should.equal('member2@test.com');
|
|
jsonResponse.members[0].email_open_rate.should.equal(50);
|
|
jsonResponse.members[1].email.should.equal('paid@test.com');
|
|
jsonResponse.members[1].email_open_rate.should.equal(80);
|
|
jsonResponse.members[2].email.should.equal('member1@test.com');
|
|
should.equal(null, jsonResponse.members[2].email_open_rate);
|
|
jsonResponse.members[3].email.should.equal('trialing@test.com');
|
|
should.equal(null, jsonResponse.members[3].email_open_rate);
|
|
});
|
|
});
|
|
|
|
it('Can search by case-insensitive name', function () {
|
|
return request
|
|
.get(localUtils.API.getApiQuery('members/?search=egg'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
jsonResponse.members.should.have.length(1);
|
|
jsonResponse.members[0].email.should.equal('member1@test.com');
|
|
localUtils.API.checkResponse(jsonResponse, 'members');
|
|
localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe');
|
|
localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
|
|
});
|
|
});
|
|
|
|
it('Can search by case-insensitive email', function () {
|
|
return request
|
|
.get(localUtils.API.getApiQuery('members/?search=MEMBER2'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
jsonResponse.members.should.have.length(1);
|
|
jsonResponse.members[0].email.should.equal('member2@test.com');
|
|
localUtils.API.checkResponse(jsonResponse, 'members');
|
|
localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe');
|
|
localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
|
|
});
|
|
});
|
|
|
|
it('Can search for paid members', function () {
|
|
return request
|
|
.get(localUtils.API.getApiQuery('members/?search=egon&paid=true'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
jsonResponse.members.should.have.length(1);
|
|
jsonResponse.members[0].email.should.equal('paid@test.com');
|
|
localUtils.API.checkResponse(jsonResponse, 'members');
|
|
localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe');
|
|
localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
|
|
});
|
|
});
|
|
|
|
it('Search for non existing member returns empty result set', function () {
|
|
return request
|
|
.get(localUtils.API.getApiQuery('members/?search=do_not_exist'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
jsonResponse.members.should.have.length(0);
|
|
});
|
|
});
|
|
|
|
it('Add should fail when passing incorrect email_type query parameter', function () {
|
|
const member = {
|
|
name: 'test',
|
|
email: 'memberTestAdd@test.com'
|
|
};
|
|
|
|
return request
|
|
.post(localUtils.API.getApiQuery(`members/?send_email=true&email_type=lel`))
|
|
.send({members: [member]})
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(422);
|
|
});
|
|
|
|
it('Add should fail when comped flag is passed in but Stripe is not enabled', function () {
|
|
const member = {
|
|
email: 'memberTestAdd@test.com',
|
|
comped: true
|
|
};
|
|
|
|
return request
|
|
.post(localUtils.API.getApiQuery(`members/`))
|
|
.send({members: [member]})
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(422)
|
|
.then((res) => {
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.errors);
|
|
|
|
jsonResponse.errors[0].message.should.eql('Validation error, cannot save member.');
|
|
jsonResponse.errors[0].context.should.match(/Missing Stripe connection./);
|
|
});
|
|
});
|
|
|
|
// NOTE: this test should be enabled and expanded once test suite fully supports Stripe mocking
|
|
it.skip('Can set a "Complimentary" subscription', function () {
|
|
const memberToChange = {
|
|
name: 'Comped Member',
|
|
email: 'member2comp@test.com'
|
|
};
|
|
|
|
const memberChanged = {
|
|
comped: true
|
|
};
|
|
|
|
return request
|
|
.post(localUtils.API.getApiQuery(`members/`))
|
|
.send({members: [memberToChange]})
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(201)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
jsonResponse.members.should.have.length(1);
|
|
|
|
return jsonResponse.members[0];
|
|
})
|
|
.then((newMember) => {
|
|
return request
|
|
.put(localUtils.API.getApiQuery(`members/${newMember.id}/`))
|
|
.send({members: [memberChanged]})
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
jsonResponse.members.should.have.length(1);
|
|
localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe');
|
|
jsonResponse.members[0].name.should.equal(memberToChange.name);
|
|
jsonResponse.members[0].email.should.equal(memberToChange.email);
|
|
jsonResponse.members[0].comped.should.equal(memberToChange.comped);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Can delete a member without cancelling Stripe Subscription', async function () {
|
|
const member = {
|
|
name: 'Member 2 Delete',
|
|
email: 'Member2Delete@test.com'
|
|
};
|
|
|
|
const createdMember = await request.post(localUtils.API.getApiQuery(`members/`))
|
|
.send({members: [member]})
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(201)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
jsonResponse.members.should.have.length(1);
|
|
|
|
return jsonResponse.members[0];
|
|
});
|
|
|
|
await request.delete(localUtils.API.getApiQuery(`members/${createdMember.id}/`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(204)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
});
|
|
});
|
|
|
|
// NOTE: this test should be enabled and expanded once test suite fully supports Stripe mocking
|
|
it.skip('Can delete a member and cancel Stripe Subscription', async function () {
|
|
const member = {
|
|
name: 'Member 2 Delete',
|
|
email: 'Member2Delete@test.com',
|
|
comped: true
|
|
};
|
|
|
|
const createdMember = await request.post(localUtils.API.getApiQuery(`members/`))
|
|
.send({members: [member]})
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(201)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
jsonResponse.members.should.have.length(1);
|
|
|
|
return jsonResponse.members[0];
|
|
});
|
|
|
|
await request.delete(localUtils.API.getApiQuery(`members/${createdMember.id}/?cancel=true`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(204)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
});
|
|
});
|
|
|
|
// NOTE: this test should be enabled and expanded once test suite fully supports Stripe mocking
|
|
it.skip('Does not cancel Stripe Subscription if cancel_subscriptions is not set to "true"', async function () {
|
|
const member = {
|
|
name: 'Member 2 Delete',
|
|
email: 'Member2Delete@test.com',
|
|
comped: true
|
|
};
|
|
|
|
const createdMember = await request.post(localUtils.API.getApiQuery(`members/`))
|
|
.send({members: [member]})
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(201)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
jsonResponse.members.should.have.length(1);
|
|
|
|
return jsonResponse.members[0];
|
|
});
|
|
|
|
await request.delete(localUtils.API.getApiQuery(`members/${createdMember.id}/?cancel=false`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(204)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
});
|
|
});
|
|
|
|
it('Can import CSV with minimum one field and labels', function () {
|
|
let importLabel;
|
|
|
|
return request
|
|
.post(localUtils.API.getApiQuery(`members/upload/`))
|
|
.field('labels', ['global-label-1', 'global-label-1'])
|
|
.attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/valid-members-labels.csv'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(201)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.meta);
|
|
should.exist(jsonResponse.meta.stats);
|
|
|
|
should.exist(jsonResponse.meta.import_label);
|
|
jsonResponse.meta.import_label.slug.should.match(/^import-/);
|
|
jsonResponse.meta.stats.imported.should.equal(2);
|
|
jsonResponse.meta.stats.invalid.length.should.equal(0);
|
|
|
|
importLabel = jsonResponse.meta.import_label.slug;
|
|
return request
|
|
.get(localUtils.API.getApiQuery(`members/?&filter=label:${importLabel}`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200);
|
|
})
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
should.equal(jsonResponse.members.length, 2);
|
|
|
|
const importedMember1 = jsonResponse.members.find(m => m.email === 'member+labels_1@example.com');
|
|
should.exist(importedMember1);
|
|
should(importedMember1.name).equal(null);
|
|
should(importedMember1.note).equal(null);
|
|
importedMember1.subscribed.should.equal(true);
|
|
importedMember1.comped.should.equal(false);
|
|
importedMember1.stripe.should.not.be.undefined();
|
|
importedMember1.stripe.subscriptions.length.should.equal(0);
|
|
|
|
// check label order
|
|
// 1 unique global + 1 record labels + 1 auto generated label
|
|
importedMember1.labels.length.should.equal(3);
|
|
should.exist(importedMember1.labels.find(({slug}) => slug === 'label'));
|
|
should.exist(importedMember1.labels.find(({slug}) => slug === 'global-label-1'));
|
|
should.exist(importedMember1.labels.find(({slug}) => slug.match(/^import-/)));
|
|
|
|
const importedMember2 = jsonResponse.members.find(m => m.email === 'member+labels_2@example.com');
|
|
should.exist(importedMember2);
|
|
// 1 unique global + 2 record labels
|
|
importedMember2.labels.length.should.equal(4);
|
|
should.exist(importedMember2.labels.find(({slug}) => slug === 'another-label'));
|
|
should.exist(importedMember2.labels.find(({slug}) => slug === 'and-one-more'));
|
|
should.exist(importedMember2.labels.find(({slug}) => slug === 'global-label-1'));
|
|
should.exist(importedMember2.labels.find(({slug}) => slug.match(/^import-/)));
|
|
});
|
|
});
|
|
|
|
it('Can import CSV with mapped fields', function () {
|
|
return request
|
|
.post(localUtils.API.getApiQuery(`members/upload/`))
|
|
.field('mapping[correo_electrpnico]', 'email')
|
|
.field('mapping[nombre]', 'name')
|
|
.attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/members-with-mappings.csv'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(201)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.meta);
|
|
should.exist(jsonResponse.meta.stats);
|
|
|
|
jsonResponse.meta.stats.imported.should.equal(1);
|
|
jsonResponse.meta.stats.invalid.length.should.equal(0);
|
|
|
|
should.exist(jsonResponse.meta.import_label);
|
|
jsonResponse.meta.import_label.slug.should.match(/^import-/);
|
|
})
|
|
.then(() => {
|
|
return request
|
|
.get(localUtils.API.getApiQuery(`members/?search=${encodeURIComponent('member+mapped_1@example.com')}`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200);
|
|
})
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
should.exist(jsonResponse.members[0]);
|
|
|
|
const importedMember1 = jsonResponse.members[0];
|
|
should(importedMember1.email).equal('member+mapped_1@example.com');
|
|
should(importedMember1.name).equal('Hannah');
|
|
should(importedMember1.note).equal('no need to map me');
|
|
importedMember1.subscribed.should.equal(true);
|
|
importedMember1.comped.should.equal(false);
|
|
importedMember1.stripe.should.not.be.undefined();
|
|
importedMember1.stripe.subscriptions.length.should.equal(0);
|
|
importedMember1.labels.length.should.equal(1); // auto-generated import label
|
|
});
|
|
});
|
|
|
|
it('Can import CSV with labels and provide additional labels', function () {
|
|
return request
|
|
.post(localUtils.API.getApiQuery(`members/upload/`))
|
|
.attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/valid-members-defaults.csv'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(201)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.meta);
|
|
should.exist(jsonResponse.meta.stats);
|
|
|
|
jsonResponse.meta.stats.imported.should.equal(2);
|
|
jsonResponse.meta.stats.invalid.length.should.equal(0);
|
|
})
|
|
.then(() => {
|
|
return request
|
|
.get(localUtils.API.getApiQuery(`members/`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200);
|
|
})
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.members);
|
|
|
|
const defaultMember1 = jsonResponse.members.find(member => (member.email === 'member+defaults_1@example.com'));
|
|
should(defaultMember1.name).equal(null);
|
|
should(defaultMember1.note).equal(null);
|
|
defaultMember1.subscribed.should.equal(true);
|
|
defaultMember1.comped.should.equal(false);
|
|
defaultMember1.stripe.should.not.be.undefined();
|
|
defaultMember1.stripe.subscriptions.length.should.equal(0);
|
|
defaultMember1.labels.length.should.equal(1); // auto-generated import label
|
|
|
|
const defaultMember2 = jsonResponse.members.find(member => (member.email === 'member+defaults_2@example.com'));
|
|
should(defaultMember2).not.be.undefined();
|
|
});
|
|
});
|
|
|
|
it('Runs imports with stripe_customer_id as background job', function () {
|
|
return request
|
|
.post(localUtils.API.getApiQuery(`members/upload/`))
|
|
.attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/members-with-stripe-ids.csv'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(202)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.not.exist(jsonResponse.meta);
|
|
});
|
|
});
|
|
|
|
it('Fails to import member with invalid values', function () {
|
|
return request
|
|
.post(localUtils.API.getApiQuery(`members/upload/`))
|
|
.field('labels', ['new-global-label'])
|
|
.attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/members-invalid-values.csv'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(201)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.meta);
|
|
should.exist(jsonResponse.meta.stats);
|
|
|
|
jsonResponse.meta.stats.imported.should.equal(1);
|
|
jsonResponse.meta.stats.invalid.length.should.equal(1);
|
|
|
|
jsonResponse.meta.stats.invalid[0].error.should.match(/Validation \(isEmail\) failed for email/);
|
|
|
|
should.exist(jsonResponse.meta.import_label);
|
|
jsonResponse.meta.import_label.slug.should.match(/^import-/);
|
|
});
|
|
});
|
|
|
|
it('Can fetch stats with no ?days param', function () {
|
|
return request
|
|
.get(localUtils.API.getApiQuery('members/stats/'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
// .expect(200) - doesn't surface underlying errors in tests
|
|
.then((res) => {
|
|
res.status.should.equal(200, JSON.stringify(res.body));
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.total);
|
|
should.exist(jsonResponse.total_in_range);
|
|
should.exist(jsonResponse.total_on_date);
|
|
should.exist(jsonResponse.new_today);
|
|
|
|
// 8 from fixtures and 6 imported in previous tests
|
|
jsonResponse.total.should.equal(14);
|
|
});
|
|
});
|
|
|
|
it('Can fetch stats with ?days=90', function () {
|
|
return request
|
|
.get(localUtils.API.getApiQuery('members/stats/?days=90'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
// .expect(200) - doesn't surface underlying errors in tests
|
|
.then((res) => {
|
|
res.status.should.equal(200, JSON.stringify(res.body));
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.total);
|
|
should.exist(jsonResponse.total_in_range);
|
|
should.exist(jsonResponse.total_on_date);
|
|
should.exist(jsonResponse.new_today);
|
|
|
|
// 8 from fixtures and 6 imported in previous tests
|
|
jsonResponse.total.should.equal(14);
|
|
});
|
|
});
|
|
|
|
it('Can fetch stats with ?days=all-time', function () {
|
|
return request
|
|
.get(localUtils.API.getApiQuery('members/stats/?days=all-time'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
// .expect(200) - doesn't surface underlying errors in tests
|
|
.then((res) => {
|
|
res.status.should.equal(200, JSON.stringify(res.body));
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.total);
|
|
should.exist(jsonResponse.total_in_range);
|
|
should.exist(jsonResponse.total_on_date);
|
|
should.exist(jsonResponse.new_today);
|
|
|
|
// 8 from fixtures and 6 imported in previous tests
|
|
jsonResponse.total.should.equal(14);
|
|
});
|
|
});
|
|
|
|
it('Errors when fetching stats with unknown days param value', function () {
|
|
return request
|
|
.get(localUtils.API.getApiQuery('members/stats/?days=nope'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(422);
|
|
});
|
|
});
|