diff --git a/package.json b/package.json index 9ab18c0e3f..cdd54f5e3e 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "@tryghost/logging": "2.1.5", "@tryghost/magic-link": "1.0.21", "@tryghost/member-events": "0.4.1", - "@tryghost/members-api": "5.11.1", + "@tryghost/members-api": "5.12.0", "@tryghost/members-events-service": "0.3.3", "@tryghost/members-importer": "0.5.8", "@tryghost/members-offers": "0.11.1", diff --git a/test/e2e-api/admin/__snapshots__/members.test.js.snap b/test/e2e-api/admin/__snapshots__/members.test.js.snap index 82106ca6ae..576761f5f1 100644 --- a/test/e2e-api/admin/__snapshots__/members.test.js.snap +++ b/test/e2e-api/admin/__snapshots__/members.test.js.snap @@ -537,7 +537,7 @@ exports[`Members API Can browse 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "8299", + "content-length": "8426", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Origin, Accept-Encoding", @@ -1126,7 +1126,7 @@ exports[`Members API Can filter by paid status 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "6764", + "content-length": "6891", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Origin, Accept-Encoding", @@ -1304,7 +1304,7 @@ exports[`Members API Can ignore any unknown includes 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "6764", + "content-length": "6891", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Origin, Accept-Encoding", @@ -1967,7 +1967,7 @@ exports[`Members API Search for paid members retrieves member with email paid@te Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1682", + "content-length": "1720", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Origin, Accept-Encoding", @@ -2409,7 +2409,7 @@ exports[`Members API: with multiple newsletters Can browse 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "8435", + "content-length": "8562", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Origin, Accept-Encoding", diff --git a/test/e2e-api/admin/members.test.js b/test/e2e-api/admin/members.test.js index dff01860a5..9287b4a587 100644 --- a/test/e2e-api/admin/members.test.js +++ b/test/e2e-api/admin/members.test.js @@ -847,6 +847,8 @@ describe('Members API', function () { }); }); + let memberWithPaidSubscription; + it('Can create a member with an existing paid subscription', async function () { const fakePrice = { id: 'price_1', @@ -934,6 +936,7 @@ describe('Members API', function () { }); const newMember = body.members[0]; + assert.equal(newMember.status, 'paid', 'The created member should have the paid status'); assert.equal(newMember.subscriptions.length, 1, 'The member should have a single subscription'); assert.equal(newMember.subscriptions[0].id, fakeSubscription.id, 'The returned subscription should have an ID assigned'); @@ -980,6 +983,30 @@ describe('Members API', function () { plan_currency: 'usd', mrr: 100 }); + + // Save this member for the next test + memberWithPaidSubscription = newMember; + }); + + it('Returns an identical member format for read, edit and browse', async function () { + if (!memberWithPaidSubscription) { + // Previous test failed + this.skip(); + } + + // Check status has been updated to 'free' after cancelling + const {body: readBody} = await agent.get('/members/' + memberWithPaidSubscription.id + '/'); + assert.equal(readBody.members.length, 1, 'The member was not found in read'); + const readMember = readBody.members[0]; + + // Note that we explicitly need to ask to include products while browsing + const {body: browseBody} = await agent.get(`/members/?search=${memberWithPaidSubscription.email}&include=products`); + assert.equal(browseBody.members.length, 1, 'The member was not found in browse'); + const browseMember = browseBody.members[0]; + + // Check for this member with a paid subscription that the body results for the patch, get and browse endpoints are 100% identical + should.deepEqual(browseMember, readMember, 'Browsing a member returns a different format than reading a member'); + should.deepEqual(memberWithPaidSubscription, readMember, 'Editing a member returns a different format than reading a member'); }); it('Can edit by id', async function () { diff --git a/test/e2e-api/members/webhooks.test.js b/test/e2e-api/members/webhooks.test.js index 793731a813..16db08a965 100644 --- a/test/e2e-api/members/webhooks.test.js +++ b/test/e2e-api/members/webhooks.test.js @@ -1203,6 +1203,13 @@ describe('Members API', function () { offer_id: offer_id }); + // Check whether the offer attribute is passed correctly in the response when fetching a single member + member.subscriptions[0].should.match({ + offer: { + id: offer_id + } + }); + await assertMemberEvents({ eventType: 'MemberPaidSubscriptionEvent', memberId: member.id, @@ -1245,7 +1252,10 @@ describe('Members API', function () { assert.equal(updatedMember.products.length, 0, 'The member should have no products'); should(updatedMember.subscriptions).match([ { - status: 'canceled' + status: 'canceled', + offer: { + id: offer_id + } } ]); @@ -1592,6 +1602,11 @@ describe('Members API', function () { offer_id: null }); + // Check whether the offer attribute is passed correctly in the response when fetching a single member + member.subscriptions[0].should.match({ + offer: null + }); + await assertMemberEvents({ eventType: 'MemberPaidSubscriptionEvent', memberId: member.id, @@ -1642,6 +1657,13 @@ describe('Members API', function () { offer_id: offer.id }); + // Check whether the offer attribute is passed correctly in the response when fetching a single member + updatedMember.subscriptions[0].should.match({ + offer: { + id: offer.id + } + }); + await assertMemberEvents({ eventType: 'MemberPaidSubscriptionEvent', memberId: updatedMember.id, @@ -1776,6 +1798,11 @@ describe('Members API', function () { offer_id: null }); + // Check whether the offer attribute is passed correctly in the response when fetching a single member + member.subscriptions[0].should.match({ + offer: null + }); + await assertMemberEvents({ eventType: 'MemberPaidSubscriptionEvent', memberId: member.id, diff --git a/yarn.lock b/yarn.lock index 80092055a2..1bfe256941 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2009,10 +2009,10 @@ "@tryghost/domain-events" "^0.1.9" "@tryghost/member-events" "^0.4.1" -"@tryghost/members-api@5.11.1": - version "5.11.1" - resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-5.11.1.tgz#5b32fe806d5ce6f5d6743525fda74ea6667ffff4" - integrity sha512-yeaC6WpqNA+4qXqpLN5+Yo5ms3R1diMmQe6JnsMcjI6Nuby8ltPJi4uazFd09uFI2b+Bj/yucEzStbkT5lUS4g== +"@tryghost/members-api@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-5.12.0.tgz#b26389fef5e66602768e953cdc37da6714bd693f" + integrity sha512-lczUcc5Vd3WKUaBu4x2aP5y9CqFqqHEFOS5Rh/KvYD6Nu8UDdjxtb6mok9IngH48UG2RS8y3WoWkJFNtSNBfpQ== dependencies: "@nexes/nql" "^0.6.0" "@tryghost/debug" "^0.1.2"