2023-06-21 11:56:59 +03:00
const assert = require ( 'assert/strict' ) ;
2019-11-21 06:01:24 +03:00
const should = require ( 'should' ) ;
const sinon = require ( 'sinon' ) ;
const supertest = require ( 'supertest' ) ;
2021-03-15 09:13:48 +03:00
const moment = require ( 'moment' ) ;
2020-04-27 14:44:00 +03:00
const testUtils = require ( '../utils' ) ;
const configUtils = require ( '../utils/configUtils' ) ;
2021-06-30 16:56:57 +03:00
const settingsCache = require ( '../../core/shared/settings-cache' ) ;
2022-02-24 12:33:24 +03:00
const DomainEvents = require ( '@tryghost/domain-events' ) ;
const { MemberPageViewEvent } = require ( '@tryghost/member-events' ) ;
const models = require ( '../../core/server/models' ) ;
2024-07-10 12:52:13 +03:00
const { fixtureManager } = require ( '../utils/e2e-framework' ) ;
2022-04-28 14:44:17 +03:00
const DataGenerator = require ( '../utils/fixtures/data-generator' ) ;
2023-10-25 17:16:31 +03:00
const members = require ( '../../core/server/services/members' ) ;
2019-11-21 06:01:24 +03:00
2021-05-27 19:50:14 +03:00
function assertContentIsPresent ( res ) {
res . text . should . containEql ( '<h2 id="markdown">markdown</h2>' ) ;
}
function assertContentIsAbsent ( res ) {
res . text . should . not . containEql ( '<h2 id="markdown">markdown</h2>' ) ;
}
2023-10-25 17:16:31 +03:00
async function createMember ( data ) {
return await members . api . members . create ( {
... data
} ) ;
}
2023-11-16 14:01:50 +03:00
async function cycleTransientId ( data ) {
return await members . api . members . cycleTransientId ( {
... data
} ) ;
}
2022-08-04 17:36:40 +03:00
describe ( 'Front-end members behavior' , function ( ) {
2019-11-21 06:01:24 +03:00
let request ;
2021-05-10 21:32:11 +03:00
async function loginAsMember ( email ) {
2022-10-05 13:42:42 +03:00
// Member should exist, because we are signin in
2023-11-15 19:10:28 +03:00
const member = await models . Member . findOne ( { email } , { require : true } ) ;
2022-10-05 13:42:42 +03:00
2021-05-10 21:32:11 +03:00
// membersService needs to be required after Ghost start so that settings
// are pre-populated with defaults
const membersService = require ( '../../core/server/services/members' ) ;
2022-10-05 13:42:42 +03:00
const signinLink = await membersService . api . getMagicLink ( email , 'signin' ) ;
2021-05-10 21:32:11 +03:00
const signinURL = new URL ( signinLink ) ;
// request needs a relative path rather than full url with host
const signinPath = ` ${ signinURL . pathname } ${ signinURL . search } ` ;
// perform a sign-in request to set members cookies on superagent
await request . get ( signinPath )
. expect ( 302 )
2021-05-27 19:50:14 +03:00
. expect ( ( res ) => {
2021-05-10 21:32:11 +03:00
const redirectUrl = new URL ( res . headers . location , testUtils . API . getURL ( ) ) ;
should . exist ( redirectUrl . searchParams . get ( 'success' ) ) ;
redirectUrl . searchParams . get ( 'success' ) . should . eql ( 'true' ) ;
} ) ;
2023-11-15 19:10:28 +03:00
return member ;
2021-05-10 21:32:11 +03:00
}
2020-11-30 17:25:22 +03:00
before ( async function ( ) {
2021-01-28 21:07:45 +03:00
const originalSettingsCacheGetFn = settingsCache . get ;
2019-11-21 06:01:24 +03:00
2021-01-28 21:07:45 +03:00
sinon . stub ( settingsCache , 'get' ) . callsFake ( function ( key , options ) {
if ( key === 'labs' ) {
return { members : true } ;
}
2019-11-21 06:01:24 +03:00
2021-03-18 19:13:10 +03:00
if ( key === 'active_theme' ) {
2022-01-24 12:52:25 +03:00
return 'members-test-theme' ;
2021-03-18 19:13:10 +03:00
}
2021-01-28 21:07:45 +03:00
return originalSettingsCacheGetFn ( key , options ) ;
2019-11-21 06:01:24 +03:00
} ) ;
2022-01-04 14:53:07 +03:00
await testUtils . startGhost ( {
copyThemes : true
} ) ;
2022-04-28 14:44:17 +03:00
await testUtils . initFixtures ( 'newsletters' , 'members:newsletters' ) ;
2021-03-18 19:13:10 +03:00
request = supertest . agent ( configUtils . config . get ( 'url' ) ) ;
2021-01-28 21:07:45 +03:00
} ) ;
after ( function ( ) {
sinon . restore ( ) ;
} ) ;
2019-11-21 06:01:24 +03:00
2021-03-15 09:13:48 +03:00
describe ( 'Member routes' , function ( ) {
2021-01-28 21:07:45 +03:00
it ( 'should error serving webhook endpoint without any parameters' , async function ( ) {
await request . post ( '/members/webhooks/stripe' )
. expect ( 400 ) ;
2019-11-21 06:01:24 +03:00
} ) ;
2022-02-15 14:38:45 +03:00
it ( 'should fail processing a webhook endpoint with stripe header' , async function ( ) {
await request . post ( '/members/webhooks/stripe' )
. set ( 'Stripe-Signature' , 'test-invalid-signature' )
. expect ( 401 ) ;
} ) ;
2021-07-30 08:14:19 +03:00
it ( 'should return no content for invalid token passed in session' , async function ( ) {
2021-01-28 21:07:45 +03:00
await request . get ( '/members/api/session' )
2021-07-30 08:14:19 +03:00
. expect ( 204 ) ;
2021-01-28 21:07:45 +03:00
} ) ;
it ( 'should return no content when removing member sessions' , async function ( ) {
await request . del ( '/members/api/session' )
2023-11-16 14:01:50 +03:00
. expect ( 'set-cookie' , /ghost-members-ssr=.*;.*?expires=Thu, 01 Jan 1970 00:00:00 GMT;.*?/ )
2021-01-28 21:07:45 +03:00
. expect ( 204 ) ;
} ) ;
it ( 'should error for invalid member token on member data endpoint' , async function ( ) {
await request . get ( '/members/api/member' )
2021-05-13 13:05:14 +03:00
. expect ( 204 ) ;
2021-01-28 21:07:45 +03:00
} ) ;
it ( 'should error for invalid data on member magic link endpoint' , async function ( ) {
await request . post ( '/members/api/send-magic-link' )
. expect ( 400 ) ;
} ) ;
2019-11-21 06:01:24 +03:00
2021-01-28 21:07:45 +03:00
it ( 'should error for invalid data on members create checkout session endpoint' , async function ( ) {
await request . post ( '/members/api/create-stripe-checkout-session' )
. expect ( 400 ) ;
} ) ;
2022-05-18 12:40:07 +03:00
2024-06-04 13:27:45 +03:00
it ( 'should error for using an invalid offer id on members create checkout session endpoint' , async function ( ) {
2022-05-18 12:40:07 +03:00
await request . post ( '/members/api/create-stripe-checkout-session' )
. send ( {
2024-06-04 13:27:45 +03:00
offerId : 'invalid' ,
2022-05-18 12:40:07 +03:00
identity : null ,
metadata : {
name : 'Jamie Larsen'
} ,
cancelUrl : 'https://example.com/blog/?stripe=cancel' ,
customerEmail : 'jamie@example.com' ,
tierId : null ,
cadence : null
} )
2024-06-04 13:27:45 +03:00
. expect ( 400 ) ;
2022-05-18 12:40:07 +03:00
} ) ;
2021-01-28 21:07:45 +03:00
it ( 'should error for invalid data on members create update session endpoint' , async function ( ) {
await request . post ( '/members/api/create-stripe-update-session' )
. expect ( 400 ) ;
} ) ;
2019-11-21 06:01:24 +03:00
2022-08-29 15:02:58 +03:00
it ( 'should error for invalid subscription id on members create update session endpoint' , async function ( ) {
const membersService = require ( '../../core/server/services/members' ) ;
const email = 'test-member-create-update-session@email.com' ;
2023-11-15 19:10:28 +03:00
const member = await membersService . api . members . create ( { email } ) ;
const token = await membersService . api . getMemberIdentityToken ( member . get ( 'transient_id' ) ) ;
2022-08-29 15:02:58 +03:00
await request . post ( '/members/api/create-stripe-update-session' )
. send ( {
identity : token ,
subscription _id : 'invalid'
} )
. expect ( 404 )
. expect ( 'Content-Type' , 'text/plain;charset=UTF-8' )
. expect ( ( res ) => {
res . text . should . eql ( 'Could not find subscription invalid' ) ;
} ) ;
} ) ;
2021-01-28 21:07:45 +03:00
it ( 'should error for invalid data on members subscription endpoint' , async function ( ) {
await request . put ( '/members/api/subscriptions/123' )
. expect ( 400 ) ;
2019-11-21 06:01:24 +03:00
} ) ;
2022-04-28 14:44:17 +03:00
it ( 'should error for fetching member newsletters with missing uuid' , async function ( ) {
await request . get ( '/members/api/member/newsletters' )
. expect ( 400 ) ;
} ) ;
it ( 'should error for fetching member newsletters with invalid uuid' , async function ( ) {
await request . get ( '/members/api/member/newsletters?uuid=abc' )
. expect ( 404 ) ;
} ) ;
it ( 'should error for updating member newsletters with missing uuid' , async function ( ) {
await request . put ( '/members/api/member/newsletters' )
. expect ( 400 ) ;
} ) ;
it ( 'should error for updating member newsletters with invalid uuid' , async function ( ) {
await request . put ( '/members/api/member/newsletters?uuid=abc' )
. expect ( 404 ) ;
} ) ;
it ( 'should fetch and update member newsletters with valid uuid' , async function ( ) {
const memberUUID = DataGenerator . Content . members [ 0 ] . uuid ;
// Can fetch newsletter subscriptions
const getRes = await request . get ( ` /members/api/member/newsletters?uuid= ${ memberUUID } ` )
. expect ( 200 ) ;
const getJsonResponse = getRes . body ;
should . exist ( getJsonResponse ) ;
getJsonResponse . should . have . properties ( [ 'email' , 'uuid' , 'status' , 'name' , 'newsletters' ] ) ;
getJsonResponse . should . not . have . property ( 'id' ) ;
getJsonResponse . newsletters . should . have . length ( 1 ) ;
2023-09-12 15:46:27 +03:00
// NOTE: these should be snapshots not code
2023-11-08 14:32:12 +03:00
Object . keys ( getJsonResponse . newsletters [ 0 ] ) . should . have . length ( 5 ) ;
2023-09-12 15:46:27 +03:00
getJsonResponse . newsletters [ 0 ] . should . have . properties ( [
'id' ,
2023-11-08 14:32:12 +03:00
'uuid' ,
2023-09-12 15:46:27 +03:00
'name' ,
'description' ,
'sort_order'
] ) ;
2022-04-28 14:44:17 +03:00
// Can update newsletter subscription
2022-11-07 13:18:36 +03:00
const originalNewsletters = getJsonResponse . newsletters ;
2022-11-10 13:29:28 +03:00
const originalNewsletterName = originalNewsletters [ 0 ] . name ;
originalNewsletters [ 0 ] . name = 'cannot change me' ;
2022-11-07 13:18:36 +03:00
2022-04-28 14:44:17 +03:00
const res = await request . put ( ` /members/api/member/newsletters?uuid= ${ memberUUID } ` )
. send ( {
newsletters : [ ]
} )
. expect ( 200 ) ;
const jsonResponse = res . body ;
should . exist ( jsonResponse ) ;
jsonResponse . should . have . properties ( [ 'email' , 'uuid' , 'status' , 'name' , 'newsletters' ] ) ;
jsonResponse . should . not . have . property ( 'id' ) ;
jsonResponse . newsletters . should . have . length ( 0 ) ;
2022-11-07 13:18:36 +03:00
const resRestored = await request . put ( ` /members/api/member/newsletters?uuid= ${ memberUUID } ` )
. send ( {
newsletters : originalNewsletters
} )
. expect ( 200 ) ;
const restoreJsonResponse = resRestored . body ;
should . exist ( restoreJsonResponse ) ;
restoreJsonResponse . should . have . properties ( [ 'email' , 'uuid' , 'status' , 'name' , 'newsletters' ] ) ;
restoreJsonResponse . should . not . have . property ( 'id' ) ;
restoreJsonResponse . newsletters . should . have . length ( 1 ) ;
2022-11-10 13:29:28 +03:00
// @NOTE: this seems like too much exposed information, needs a review
2023-11-08 14:32:12 +03:00
Object . keys ( restoreJsonResponse . newsletters [ 0 ] ) . should . have . length ( 5 ) ;
2022-11-10 13:29:28 +03:00
restoreJsonResponse . newsletters [ 0 ] . should . have . properties ( [
'id' ,
2023-11-08 14:32:12 +03:00
'uuid' ,
2022-11-10 13:29:28 +03:00
'name' ,
'description' ,
2023-09-12 15:46:27 +03:00
'sort_order'
2022-11-10 13:29:28 +03:00
] ) ;
should . equal ( restoreJsonResponse . newsletters [ 0 ] . name , originalNewsletterName ) ;
2022-04-28 14:44:17 +03:00
} ) ;
2021-01-28 21:07:45 +03:00
it ( 'should serve theme 404 on members endpoint' , async function ( ) {
await request . get ( '/members/' )
. expect ( 404 )
. expect ( 'Content-Type' , 'text/html; charset=utf-8' ) ;
2019-11-21 06:01:24 +03:00
} ) ;
2021-01-28 21:07:45 +03:00
it ( 'should redirect invalid token on members endpoint' , async function ( ) {
await request . get ( '/members/?token=abc&action=signup' )
. expect ( 302 )
. expect ( 'Location' , '/?action=signup&success=false' ) ;
2019-11-21 06:01:24 +03:00
} ) ;
} ) ;
2021-03-15 09:13:48 +03:00
2022-04-28 11:03:07 +03:00
describe ( 'Unsubscribe' , function ( ) {
it ( 'should redirect with uuid and action param' , async function ( ) {
await request . get ( '/unsubscribe/?uuid=XXX' )
. expect ( 302 )
. expect ( 'Location' , 'http://127.0.0.1:2369/?uuid=XXX&action=unsubscribe' ) ;
} ) ;
it ( 'should pass through an optional newsletter param' , async function ( ) {
await request . get ( '/unsubscribe/?uuid=XXX&newsletter=YYY' )
. expect ( 302 )
. expect ( 'Location' , 'http://127.0.0.1:2369/?uuid=XXX&newsletter=YYY&action=unsubscribe' ) ;
} ) ;
it ( 'should reject when missing a uuid' , async function ( ) {
await request . get ( '/unsubscribe/' )
. expect ( 400 ) ;
} ) ;
2023-10-25 17:16:31 +03:00
it ( 'should do an actual unsubscribe on POST' , async function ( ) {
const newsletterId = fixtureManager . get ( 'newsletters' , 0 ) . id ;
const member = await createMember ( {
email : 'unsubscribe-member-test@example.com' ,
newsletters : [
{ id : newsletterId }
]
} ) ;
const memberUUID = member . get ( 'uuid' ) ;
// Can fetch newsletter subscriptions
let getRes = await request
. get ( ` /members/api/member/newsletters?uuid= ${ memberUUID } ` )
. expect ( 200 ) ;
let getJsonResponse = getRes . body ;
assert . equal ( getJsonResponse . newsletters . length , 1 ) ;
await request . post ( ` /unsubscribe/?uuid= ${ memberUUID } ` )
. expect ( 201 ) ;
getRes = await request
. get ( ` /members/api/member/newsletters?uuid= ${ memberUUID } ` )
. expect ( 200 ) ;
getJsonResponse = getRes . body ;
assert . equal ( getJsonResponse . newsletters . length , 0 , 'Sending a POST request to the unsubscribe endpoint should unsubscribe the member' ) ;
} ) ;
it ( 'should only do a partial unsubscribe on POST' , async function ( ) {
const newsletterId = fixtureManager . get ( 'newsletters' , 0 ) . id ;
const newsletter2Id = fixtureManager . get ( 'newsletters' , 1 ) . id ;
const newsletter2Uuid = fixtureManager . get ( 'newsletters' , 1 ) . uuid ;
const member = await createMember ( {
email : 'unsubscribe-member-test-2@example.com' ,
newsletters : [
{ id : newsletterId } ,
{ id : newsletter2Id }
]
} ) ;
const memberUUID = member . get ( 'uuid' ) ;
// Can fetch newsletter subscriptions
let getRes = await request
. get ( ` /members/api/member/newsletters?uuid= ${ memberUUID } ` )
. expect ( 200 ) ;
let getJsonResponse = getRes . body ;
assert . equal ( getJsonResponse . newsletters . length , 2 ) ;
await request . post ( ` /unsubscribe/?uuid= ${ memberUUID } &newsletter= ${ newsletter2Uuid } ` )
. expect ( 201 ) ;
getRes = await request
. get ( ` /members/api/member/newsletters?uuid= ${ memberUUID } ` )
. expect ( 200 ) ;
getJsonResponse = getRes . body ;
assert . equal ( getJsonResponse . newsletters . length , 1 , 'Sending a POST request to the unsubscribe endpoint should unsubscribe the member from that specific newsletter' ) ;
} ) ;
it ( 'should unsubscribe from comment notifications on POST' , async function ( ) {
const newsletterId = fixtureManager . get ( 'newsletters' , 0 ) . id ;
const member = await createMember ( {
email : 'unsubscribe-member-test-3@example.com' ,
newsletters : [
{ id : newsletterId }
] ,
enable _comment _notifications : true
} ) ;
const memberUUID = member . get ( 'uuid' ) ;
// Can fetch newsletter subscriptions
let getRes = await request
. get ( ` /members/api/member/newsletters?uuid= ${ memberUUID } ` )
. expect ( 200 ) ;
let getJsonResponse = getRes . body ;
assert . equal ( getJsonResponse . newsletters . length , 1 ) ;
await request . post ( ` /unsubscribe/?uuid= ${ memberUUID } &comments=1 ` )
. expect ( 201 ) ;
const updatedMember = await members . api . members . get ( { id : member . id } , { withRelated : [ 'newsletters' ] } ) ;
assert . equal ( updatedMember . get ( 'enable_comment_notifications' ) , false ) ;
assert . equal ( updatedMember . related ( 'newsletters' ) . models . length , 1 ) ;
} ) ;
it ( 'unsubscribe post works with x-www-form-urlencoded' , async function ( ) {
const newsletterId = fixtureManager . get ( 'newsletters' , 0 ) . id ;
const member = await createMember ( {
email : 'unsubscribe-member-test-4@example.com' ,
newsletters : [
{ id : newsletterId }
]
} ) ;
const memberUUID = member . get ( 'uuid' ) ;
// Can fetch newsletter subscriptions
let getRes = await request
. get ( ` /members/api/member/newsletters?uuid= ${ memberUUID } ` )
. expect ( 200 ) ;
let getJsonResponse = getRes . body ;
assert . equal ( getJsonResponse . newsletters . length , 1 ) ;
await request . post ( ` /unsubscribe/?uuid= ${ memberUUID } ` )
. type ( 'form' )
. send ( { 'List-Unsubscribe' : 'One-Click' } )
. expect ( 201 ) ;
getRes = await request
. get ( ` /members/api/member/newsletters?uuid= ${ memberUUID } ` )
. expect ( 200 ) ;
getJsonResponse = getRes . body ;
assert . equal ( getJsonResponse . newsletters . length , 0 , 'Sending a POST request to the unsubscribe endpoint should unsubscribe the member' ) ;
} ) ;
it ( 'unsubscribe post works with multipart/form-data' , async function ( ) {
const newsletterId = fixtureManager . get ( 'newsletters' , 0 ) . id ;
const member = await createMember ( {
email : 'unsubscribe-member-test-5@example.com' ,
newsletters : [
{ id : newsletterId }
]
} ) ;
const memberUUID = member . get ( 'uuid' ) ;
// Can fetch newsletter subscriptions
let getRes = await request
. get ( ` /members/api/member/newsletters?uuid= ${ memberUUID } ` )
. expect ( 200 ) ;
let getJsonResponse = getRes . body ;
assert . equal ( getJsonResponse . newsletters . length , 1 ) ;
await request . post ( ` /unsubscribe/?uuid= ${ memberUUID } ` )
. field ( 'List-Unsubscribe' , 'One-Click' )
. expect ( 201 ) ;
getRes = await request
. get ( ` /members/api/member/newsletters?uuid= ${ memberUUID } ` )
. expect ( 200 ) ;
getJsonResponse = getRes . body ;
assert . equal ( getJsonResponse . newsletters . length , 0 , 'Sending a POST request to the unsubscribe endpoint should unsubscribe the member' ) ;
} ) ;
2022-04-28 11:03:07 +03:00
} ) ;
2021-03-15 09:13:48 +03:00
describe ( 'Content gating' , function ( ) {
let publicPost ;
let membersPost ;
let paidPost ;
let membersPostWithPaywallCard ;
2021-05-10 21:32:11 +03:00
let labelPost ;
let productPost ;
2021-03-15 09:13:48 +03:00
before ( function ( ) {
publicPost = testUtils . DataGenerator . forKnex . createPost ( {
slug : 'free-to-see' ,
visibility : 'public' ,
published _at : moment ( ) . add ( 15 , 'seconds' ) . toDate ( ) // here to ensure sorting is not modified
} ) ;
membersPost = testUtils . DataGenerator . forKnex . createPost ( {
slug : 'thou-shalt-not-be-seen' ,
visibility : 'members' ,
published _at : moment ( ) . add ( 45 , 'seconds' ) . toDate ( ) // here to ensure sorting is not modified
} ) ;
paidPost = testUtils . DataGenerator . forKnex . createPost ( {
slug : 'thou-shalt-be-paid-for' ,
visibility : 'paid' ,
published _at : moment ( ) . add ( 30 , 'seconds' ) . toDate ( ) // here to ensure sorting is not modified
} ) ;
membersPostWithPaywallCard = testUtils . DataGenerator . forKnex . createPost ( {
slug : 'thou-shalt-have-a-taste' ,
visibility : 'members' ,
mobiledoc : '{"version":"0.3.1","markups":[],"atoms":[],"cards":[["paywall",{}]],"sections":[[1,"p",[[0,[],0,"Free content"]]],[10,0],[1,"p",[[0,[],0,"Members content"]]]]}' ,
html : '<p>Free content</p><!--members-only--><p>Members content</p>' ,
published _at : moment ( ) . add ( 5 , 'seconds' ) . toDate ( )
} ) ;
2021-05-10 21:32:11 +03:00
labelPost = testUtils . DataGenerator . forKnex . createPost ( {
slug : 'thou-must-be-labelled-vip' ,
visibility : 'label:vip' ,
published _at : moment ( ) . toDate ( )
} ) ;
productPost = testUtils . DataGenerator . forKnex . createPost ( {
slug : 'thou-must-have-default-product' ,
visibility : 'product:default-product' ,
published _at : moment ( ) . toDate ( )
} ) ;
2021-03-15 09:13:48 +03:00
return testUtils . fixtures . insertPosts ( [
publicPost ,
membersPost ,
paidPost ,
2021-05-10 21:32:11 +03:00
membersPostWithPaywallCard ,
labelPost ,
productPost
2021-03-15 09:13:48 +03:00
] ) ;
} ) ;
describe ( 'as non-member' , function ( ) {
2021-05-27 19:50:14 +03:00
it ( 'can read public post content' , async function ( ) {
await request
2021-03-15 09:13:48 +03:00
. get ( '/free-to-see/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsPresent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-27 19:50:14 +03:00
it ( 'cannot read members post content' , async function ( ) {
await request
2021-03-15 09:13:48 +03:00
. get ( '/thou-shalt-not-be-seen/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsAbsent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-10 21:32:11 +03:00
2021-05-27 19:50:14 +03:00
it ( 'cannot read paid post content' , async function ( ) {
await request
2021-03-15 09:13:48 +03:00
. get ( '/thou-shalt-be-paid-for/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsAbsent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-27 19:50:14 +03:00
it ( 'cannot read label-only post content' , async function ( ) {
await request
2021-05-10 21:32:11 +03:00
. get ( '/thou-must-be-labelled-vip/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsAbsent ) ;
2021-05-10 21:32:11 +03:00
} ) ;
2021-03-15 09:13:48 +03:00
2021-05-27 19:50:14 +03:00
it ( 'cannot read product-only post content' , async function ( ) {
await request
2021-05-10 21:32:11 +03:00
. get ( '/thou-must-have-default-product/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsAbsent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2022-02-24 12:33:24 +03:00
it ( 'doesn\'t generate a MemberPageView event' , async function ( ) {
const spy = sinon . spy ( ) ;
DomainEvents . subscribe ( MemberPageViewEvent , spy ) ;
await request
. get ( '/free-to-see/' )
. expect ( 200 )
. expect ( assertContentIsPresent ) ;
assert ( spy . notCalled , 'A page view from a non-member shouldn\'t generate a MemberPageViewEvent event' ) ;
} ) ;
2021-05-10 21:32:11 +03:00
} ) ;
2023-11-15 19:10:28 +03:00
describe ( 'log out' , function ( ) {
let member ;
beforeEach ( async function ( ) {
member = await loginAsMember ( 'member1@test.com' ) ;
} ) ;
2023-11-16 14:01:50 +03:00
it ( 'an invalid token causes a set-cookie logout when requesting the identity' , async function ( ) {
// Check logged in
await request . get ( '/members/api/member' )
. expect ( 200 ) ;
// Cycle the transient id manually
await cycleTransientId ( { id : member . id } ) ;
await member . refresh ( ) ;
const transientId = member . get ( 'transient_id' ) ;
await request . get ( '/members/api/session' )
. expect ( 'set-cookie' , /ghost-members-ssr=.*;.*?expires=Thu, 01 Jan 1970 00:00:00 GMT;.*?/ )
. expect ( 204 ) ;
// Check logged out
await request . get ( '/members/api/member' )
. expect ( 204 ) ;
// Check transient id has NOT changed
await member . refresh ( ) ;
assert . equal ( member . get ( 'transient_id' ) , transientId ) ;
} ) ;
2023-11-15 19:10:28 +03:00
it ( 'by default only destroys current session' , async function ( ) {
const transientId = member . get ( 'transient_id' ) ;
// Check logged in
await request . get ( '/members/api/member' )
. expect ( 200 ) ;
await request . del ( '/members/api/session' )
. expect ( 'set-cookie' , /ghost-members-ssr=.*;.*?expires=Thu, 01 Jan 1970 00:00:00 GMT;.*?/ )
. expect ( 204 ) ;
// Check logged out
await request . get ( '/members/api/member' )
. expect ( 204 ) ;
// Check transient id has NOT changed
await member . refresh ( ) ;
assert . equal ( member . get ( 'transient_id' ) , transientId ) ;
} ) ;
it ( 'can destroy all sessions' , async function ( ) {
const transientId = member . get ( 'transient_id' ) ;
// Check logged in
await request . get ( '/members/api/member' )
. expect ( 200 ) ;
await request . del ( '/members/api/session' )
. send ( {
all : true
} )
. expect ( 'set-cookie' , /ghost-members-ssr=.*;.*?expires=Thu, 01 Jan 1970 00:00:00 GMT;.*?/ )
. expect ( 204 ) ;
// Check logged out
await request . get ( '/members/api/member' )
. expect ( 204 ) ;
// Check transient id has changed
await member . refresh ( ) ;
assert . notEqual ( member . get ( 'transient_id' ) , transientId ) ;
} ) ;
it ( 'can destroy only current session' , async function ( ) {
const transientId = member . get ( 'transient_id' ) ;
// Check logged in
await request . get ( '/members/api/member' )
. expect ( 200 ) ;
await request . del ( '/members/api/session' )
. send ( {
all : false
} )
. expect ( 'set-cookie' , /ghost-members-ssr=.*;.*?expires=Thu, 01 Jan 1970 00:00:00 GMT;.*?/ )
. expect ( 204 ) ;
// Check logged out
await request . get ( '/members/api/member' )
. expect ( 204 ) ;
// Check transient id has NOT changed
await member . refresh ( ) ;
assert . equal ( member . get ( 'transient_id' ) , transientId ) ;
} ) ;
} ) ;
2021-05-10 21:32:11 +03:00
describe ( 'as free member' , function ( ) {
before ( async function ( ) {
await loginAsMember ( 'member1@test.com' ) ;
} ) ;
2021-03-15 09:13:48 +03:00
2021-05-27 19:50:14 +03:00
it ( 'can read public post content' , async function ( ) {
await request
2021-03-15 09:13:48 +03:00
. get ( '/free-to-see/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsPresent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-27 19:50:14 +03:00
it ( 'can read members post content' , async function ( ) {
await request
2021-03-15 09:13:48 +03:00
. get ( '/thou-shalt-not-be-seen/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsPresent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-27 19:50:14 +03:00
it ( 'cannot read paid post content' , async function ( ) {
await request
2021-03-15 09:13:48 +03:00
. get ( '/thou-shalt-be-paid-for/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsAbsent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-10 21:32:11 +03:00
2021-05-27 19:50:14 +03:00
it ( 'cannot read label-only post content' , async function ( ) {
await request
2021-05-10 21:32:11 +03:00
. get ( '/thou-must-be-labelled-vip/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsAbsent ) ;
2021-05-10 21:32:11 +03:00
} ) ;
2021-05-27 19:50:14 +03:00
it ( 'cannot read product-only post content' , async function ( ) {
await request
2021-05-10 21:32:11 +03:00
. get ( '/thou-must-have-default-product/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsAbsent ) ;
2021-05-10 21:32:11 +03:00
} ) ;
} ) ;
describe ( 'as free member with vip label' , function ( ) {
2022-02-24 12:33:24 +03:00
const email = 'vip@test.com' ;
2021-05-10 21:32:11 +03:00
before ( async function ( ) {
2022-02-24 12:33:24 +03:00
await loginAsMember ( email ) ;
2021-05-10 21:32:11 +03:00
} ) ;
2022-02-24 12:33:24 +03:00
it ( 'generates a MemberPageView event' , async function ( ) {
const spy = sinon . spy ( ) ;
DomainEvents . subscribe ( MemberPageViewEvent , spy ) ;
// Reset last_seen_at property
let member = await models . Member . findOne ( { email } ) ;
await models . Member . edit ( { last _seen _at : null } , { id : member . get ( 'id' ) } ) ;
member = await models . Member . findOne ( { email } ) ;
assert . equal ( member . get ( 'last_seen_at' ) , null , 'The member shouldn\'t have a `last_seen_at` property set before this test.' ) ;
await request
. get ( '/free-to-see/' )
. expect ( 200 )
. expect ( assertContentIsPresent ) ;
2023-06-06 16:57:46 +03:00
await DomainEvents . allSettled ( ) ;
2022-02-24 12:33:24 +03:00
assert ( spy . calledOnce , 'A page view from a member should generate a MemberPageViewEvent event' ) ;
member = await models . Member . findOne ( { email } ) ;
assert . notEqual ( member . get ( 'last_seen_at' ) , null , 'The member should have a `last_seen_at` property after having visited a page while logged-in.' ) ;
} ) ;
2021-03-15 09:13:48 +03:00
} ) ;
describe ( 'as paid member' , function ( ) {
2022-02-24 12:33:24 +03:00
const email = 'paid@test.com' ;
2021-03-15 09:13:48 +03:00
before ( async function ( ) {
2022-10-05 13:42:42 +03:00
// Member should exist, because we are signin in
await models . Member . findOne ( { email } , { require : true } ) ;
2021-03-15 09:13:48 +03:00
// membersService needs to be required after Ghost start so that settings
// are pre-populated with defaults
const membersService = require ( '../../core/server/services/members' ) ;
2022-10-05 13:42:42 +03:00
const signinLink = await membersService . api . getMagicLink ( email , 'signin' ) ;
2021-03-15 09:13:48 +03:00
const signinURL = new URL ( signinLink ) ;
// request needs a relative path rather than full url with host
const signinPath = ` ${ signinURL . pathname } ${ signinURL . search } ` ;
// perform a sign-in request to set members cookies on superagent
await request . get ( signinPath )
. expect ( 302 )
. then ( ( res ) => {
const redirectUrl = new URL ( res . headers . location , testUtils . API . getURL ( ) ) ;
should . exist ( redirectUrl . searchParams . get ( 'success' ) ) ;
redirectUrl . searchParams . get ( 'success' ) . should . eql ( 'true' ) ;
} ) ;
} ) ;
2023-09-12 15:21:36 +03:00
it ( 'can fetch member data' , async function ( ) {
const res = await request . get ( '/members/api/member' )
. expect ( 200 ) ;
const memberData = res . body ;
should . exist ( memberData ) ;
// @NOTE: this should be a snapshot test not code
memberData . should . have . properties ( [
'uuid' ,
'email' ,
'name' ,
'firstname' ,
'expertise' ,
'avatar_image' ,
'subscribed' ,
'subscriptions' ,
'paid' ,
'created_at' ,
'enable_comment_notifications' ,
'newsletters' ,
'email_suppression'
] ) ;
Object . keys ( memberData ) . should . have . length ( 13 ) ;
memberData . should . not . have . property ( 'id' ) ;
memberData . newsletters . should . have . length ( 1 ) ;
// @NOTE: this should be a snapshot test not code
2023-11-08 14:32:12 +03:00
Object . keys ( memberData . newsletters [ 0 ] ) . should . have . length ( 5 ) ;
2023-09-12 15:21:36 +03:00
memberData . newsletters [ 0 ] . should . have . properties ( [
'id' ,
2023-11-08 14:32:12 +03:00
'uuid' ,
2023-09-12 15:21:36 +03:00
'name' ,
'description' ,
'sort_order'
] ) ;
} ) ;
2021-05-27 19:50:14 +03:00
it ( 'can read public post content' , async function ( ) {
await request
2021-03-15 09:13:48 +03:00
. get ( '/free-to-see/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsPresent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-27 19:50:14 +03:00
it ( 'can read members post content' , async function ( ) {
await request
2021-03-15 09:13:48 +03:00
. get ( '/thou-shalt-not-be-seen/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsPresent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-27 19:50:14 +03:00
it ( 'can read paid post content' , async function ( ) {
await request
2021-03-15 09:13:48 +03:00
. get ( '/thou-shalt-be-paid-for/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsPresent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-10 21:32:11 +03:00
2021-05-27 19:50:14 +03:00
it ( 'cannot read label-only post content' , async function ( ) {
await request
2021-05-10 21:32:11 +03:00
. get ( '/thou-must-be-labelled-vip/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsAbsent ) ;
2021-05-10 21:32:11 +03:00
} ) ;
2022-02-20 17:02:42 +03:00
it ( 'can read product-only post content' , async function ( ) {
2021-05-27 19:50:14 +03:00
await request
2021-05-10 21:32:11 +03:00
. get ( '/thou-must-have-default-product/' )
. expect ( 200 )
2022-02-20 17:02:42 +03:00
. expect ( assertContentIsPresent ) ;
2021-05-10 21:32:11 +03:00
} ) ;
2022-02-24 12:33:24 +03:00
it ( 'generates a MemberPageView event' , async function ( ) {
const spy = sinon . spy ( ) ;
DomainEvents . subscribe ( MemberPageViewEvent , spy ) ;
// Reset last_seen_at property
let member = await models . Member . findOne ( { email } ) ;
await models . Member . edit ( { last _seen _at : null } , { id : member . get ( 'id' ) } ) ;
member = await models . Member . findOne ( { email } ) ;
assert . equal ( member . get ( 'last_seen_at' ) , null , 'The member shouldn\'t have a `last_seen_at` property set before this test.' ) ;
await request
. get ( '/free-to-see/' )
. expect ( 200 )
. expect ( assertContentIsPresent ) ;
2023-06-06 16:57:46 +03:00
await DomainEvents . allSettled ( ) ;
2022-02-24 12:33:24 +03:00
assert ( spy . calledOnce , 'A page view from a member should generate a MemberPageViewEvent event' ) ;
member = await models . Member . findOne ( { email } ) ;
assert . notEqual ( member . get ( 'last_seen_at' ) , null , 'The member should have a `last_seen_at` property after having visited a page while logged-in.' ) ;
} ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-10 21:32:11 +03:00
describe ( 'as comped member' , function ( ) {
before ( async function ( ) {
await loginAsMember ( 'comped@test.com' ) ;
} ) ;
2021-03-15 09:13:48 +03:00
2021-05-27 19:50:14 +03:00
it ( 'can read public post content' , async function ( ) {
await request
2021-03-15 09:13:48 +03:00
. get ( '/free-to-see/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsPresent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-27 19:50:14 +03:00
it ( 'can read members post content' , async function ( ) {
await request
2021-03-15 09:13:48 +03:00
. get ( '/thou-shalt-not-be-seen/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsPresent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-27 19:50:14 +03:00
it ( 'can read paid post content' , async function ( ) {
await request
2021-03-15 09:13:48 +03:00
. get ( '/thou-shalt-be-paid-for/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsPresent ) ;
2021-03-15 09:13:48 +03:00
} ) ;
2021-05-10 21:32:11 +03:00
2021-05-27 19:50:14 +03:00
it ( 'cannot read label-only post content' , async function ( ) {
await request
2021-05-10 21:32:11 +03:00
. get ( '/thou-must-be-labelled-vip/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsAbsent ) ;
2021-05-10 21:32:11 +03:00
} ) ;
2022-02-20 17:02:42 +03:00
it ( 'can read product-only post content' , async function ( ) {
2021-05-27 19:50:14 +03:00
await request
2021-05-10 21:32:11 +03:00
. get ( '/thou-must-have-default-product/' )
. expect ( 200 )
2022-02-20 17:02:42 +03:00
. expect ( assertContentIsPresent ) ;
2021-05-10 21:32:11 +03:00
} ) ;
} ) ;
describe ( 'as member with product' , function ( ) {
before ( async function ( ) {
await loginAsMember ( 'with-product@test.com' ) ;
} ) ;
2021-05-27 19:50:14 +03:00
it ( 'can read product-only post content' , async function ( ) {
await request
2021-05-10 21:32:11 +03:00
. get ( '/thou-must-have-default-product/' )
. expect ( 200 )
2021-05-27 19:50:14 +03:00
. expect ( assertContentIsPresent ) ;
2021-05-10 21:32:11 +03:00
} ) ;
2021-03-15 09:13:48 +03:00
} ) ;
} ) ;
2019-11-21 06:01:24 +03:00
} ) ;