@@ -34,6 +34,7 @@ vi.mock('./context', () => ({ getActor: async () => actorRef.current }))
3434const {
3535 markAllNotificationsReadAction,
3636 markNotificationReadAction,
37+ saveMassMailOptInAction,
3738 saveNotificationPreferencesAction,
3839} = await import ( './notification-actions' )
3940const { EMPTY_STATE } = await import ( './auth-form-state' )
@@ -139,7 +140,16 @@ class FakeNotifications implements NotificationRepository {
139140 }
140141}
141142
143+ class FakeMemberSettings {
144+ readonly consent : Array < { userId : number ; optIn : boolean } > = [ ]
145+
146+ async saveMassMailOptIn ( input : { userId : number ; optIn : boolean } ) {
147+ this . consent . push ( input )
148+ }
149+ }
150+
142151let notifications : FakeNotifications
152+ let memberSettings : FakeMemberSettings
143153
144154async function actorFor ( groupId : number , userId : number | null ) : Promise < Actor > {
145155 const source = new InMemoryAuthorizationSource ( SEED_BOARD )
@@ -175,8 +185,9 @@ async function run(
175185
176186beforeEach ( async ( ) => {
177187 notifications = new FakeNotifications ( )
188+ memberSettings = new FakeMemberSettings ( )
178189 actorRef . current = await actorFor ( SEED_GROUP . registered , 7 )
179- installTestContainer ( { container : { notifications } } )
190+ installTestContainer ( { container : { notifications, memberSettings } } )
180191} )
181192
182193describe ( 'marking read' , ( ) => {
@@ -272,3 +283,34 @@ describe('saving preferences', () => {
272283 expect ( result . redirectedTo ) . toBe ( '/notifications/preferences?saved=1' )
273284 } )
274285} )
286+
287+ describe ( 'the announcements opt-in' , ( ) => {
288+ it ( 'records consent for the signed-in member, never for the id in the form' , async ( ) => {
289+ const result = await run (
290+ saveMassMailOptInAction ,
291+ form ( [
292+ [ 'announcements' , '1' ] ,
293+ [ 'userId' , '1' ] ,
294+ ] ) ,
295+ )
296+
297+ expect ( result . redirectedTo ) . toBe ( '/notifications/preferences?saved=announcements' )
298+ expect ( memberSettings . consent ) . toEqual ( [ { userId : 7 , optIn : true } ] )
299+ } )
300+
301+ it ( 'reads an unticked box as consent withdrawn' , async ( ) => {
302+ const result = await run ( saveMassMailOptInAction , form ( [ ] ) )
303+
304+ expect ( result . redirectedTo ) . toBe ( '/notifications/preferences?saved=announcements' )
305+ expect ( memberSettings . consent ) . toEqual ( [ { userId : 7 , optIn : false } ] )
306+ } )
307+
308+ it ( 'refuses a visitor who is not signed in' , async ( ) => {
309+ actorRef . current = await actorFor ( SEED_GROUP . guest , null )
310+
311+ const result = await run ( saveMassMailOptInAction , form ( [ [ 'announcements' , '1' ] ] ) )
312+
313+ expect ( result . error ) . toBeDefined ( )
314+ expect ( memberSettings . consent ) . toEqual ( [ ] )
315+ } )
316+ } )
0 commit comments