Skip to content
This repository was archived by the owner on Oct 9, 2025. It is now read-only.

Commit 4bef217

Browse files
filipecabacomandarini
authored andcommitted
feat: allow override presence enabled
1 parent cbc6ba3 commit 4bef217

File tree

3 files changed

+121
-2
lines changed

3 files changed

+121
-2
lines changed

src/RealtimeChannel.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,9 @@ export default class RealtimeChannel {
238238
this.bindings.postgres_changes?.map((r) => r.filter) ?? []
239239

240240
const presence_enabled =
241-
!!this.bindings[REALTIME_LISTEN_TYPES.PRESENCE] &&
242-
this.bindings[REALTIME_LISTEN_TYPES.PRESENCE].length > 0
241+
(!!this.bindings[REALTIME_LISTEN_TYPES.PRESENCE] &&
242+
this.bindings[REALTIME_LISTEN_TYPES.PRESENCE].length > 0) ||
243+
this.params.config.presence?.enabled === true
243244
const accessTokenPayload: { access_token?: string } = {}
244245
const config = {
245246
broadcast,

test/RealtimeChannel.lifecycle.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,33 @@ describe('Channel Lifecycle Management', () => {
137137
},
138138
testChannelDefaults: false,
139139
},
140+
{
141+
name: 'sets up joinPush object with presence enabled when config.presence.enabled is true even without bindings',
142+
config: {
143+
private: true,
144+
presence: { key: '', enabled: true },
145+
},
146+
setup: (channel: RealtimeChannel) => {
147+
// No presence bindings added
148+
channel.subscribe()
149+
},
150+
expectedParams: {
151+
config: {
152+
broadcast: { ack: false, self: false },
153+
presence: { key: '', enabled: true },
154+
private: true,
155+
},
156+
},
157+
expectedJoinPayload: {
158+
config: {
159+
broadcast: { ack: false, self: false },
160+
presence: { key: '', enabled: true },
161+
postgres_changes: [],
162+
private: true,
163+
},
164+
},
165+
testChannelDefaults: false,
166+
},
140167
])(
141168
'$name',
142169
({

test/RealtimeChannel.presence.test.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
TestSetup,
99
setupJoinedChannelWithSocket,
1010
} from './helpers/setup'
11+
import { REALTIME_LISTEN_TYPES } from '../src/RealtimeChannel'
1112

1213
const defaultTimeout = 1000
1314

@@ -167,6 +168,96 @@ describe('Presence helper methods', () => {
167168
)
168169
})
169170

171+
describe('Presence configuration override', () => {
172+
test('should enable presence when config.presence.enabled is true even without bindings', () => {
173+
const channelWithPresenceEnabled = testSetup.socket.channel(
174+
'test-presence-override',
175+
{ config: { presence: { enabled: true } } }
176+
)
177+
assert.equal(
178+
channelWithPresenceEnabled.bindings[REALTIME_LISTEN_TYPES.PRESENCE]
179+
?.length,
180+
undefined
181+
)
182+
channelWithPresenceEnabled.subscribe()
183+
const joinPayload = channelWithPresenceEnabled.joinPush.payload
184+
assert.equal(joinPayload.config.presence.enabled, true)
185+
channelWithPresenceEnabled.unsubscribe()
186+
})
187+
188+
test('should enable presence when both bindings exist and config.presence.enabled is true', () => {
189+
const channelWithBoth = testSetup.socket.channel('test-presence-both', {
190+
config: { presence: { enabled: true } },
191+
})
192+
channelWithBoth.on('presence', { event: 'sync' }, () => {})
193+
channelWithBoth.subscribe()
194+
const joinPayload = channelWithBoth.joinPush.payload
195+
assert.equal(joinPayload.config.presence.enabled, true)
196+
channelWithBoth.unsubscribe()
197+
})
198+
199+
test('should enable presence when only bindings exist (existing behavior)', () => {
200+
const channelWithBindingsOnly = testSetup.socket.channel(
201+
'test-presence-bindings-only'
202+
)
203+
channelWithBindingsOnly.on('presence', { event: 'sync' }, () => {})
204+
channelWithBindingsOnly.subscribe()
205+
const joinPayload = channelWithBindingsOnly.joinPush.payload
206+
assert.equal(joinPayload.config.presence.enabled, true)
207+
channelWithBindingsOnly.unsubscribe()
208+
})
209+
210+
test('should not enable presence when neither bindings exist nor config.presence.enabled is true', () => {
211+
const channelWithNeither = testSetup.socket.channel('test-presence-neither')
212+
assert.equal(
213+
channelWithNeither.bindings[REALTIME_LISTEN_TYPES.PRESENCE]?.length,
214+
undefined
215+
)
216+
channelWithNeither.subscribe()
217+
const joinPayload = channelWithNeither.joinPush.payload
218+
assert.equal(joinPayload.config.presence.enabled, false)
219+
channelWithNeither.unsubscribe()
220+
})
221+
222+
test('should allow using track() method when presence is enabled via config override', async () => {
223+
const channelWithPresenceEnabled = testSetup.socket.channel(
224+
'test-presence-track',
225+
{ config: { presence: { enabled: true } } }
226+
)
227+
setupJoinedChannelWithSocket(channelWithPresenceEnabled, testSetup.socket)
228+
const sendStub = vi
229+
.spyOn(channelWithPresenceEnabled, 'send')
230+
.mockResolvedValue('ok')
231+
await channelWithPresenceEnabled.track({ id: 123, name: 'Test User' })
232+
expect(sendStub).toHaveBeenCalledWith(
233+
{
234+
type: 'presence',
235+
event: 'track',
236+
payload: { id: 123, name: 'Test User' },
237+
},
238+
1000
239+
)
240+
channelWithPresenceEnabled.unsubscribe()
241+
})
242+
243+
test('should allow using untrack() method when presence is enabled via config override', async () => {
244+
const channelWithPresenceEnabled = testSetup.socket.channel(
245+
'test-presence-untrack',
246+
{ config: { presence: { enabled: true } } }
247+
)
248+
setupJoinedChannelWithSocket(channelWithPresenceEnabled, testSetup.socket)
249+
const sendStub = vi
250+
.spyOn(channelWithPresenceEnabled, 'send')
251+
.mockResolvedValue('ok')
252+
await channelWithPresenceEnabled.untrack()
253+
expect(sendStub).toHaveBeenCalledWith(
254+
{ type: 'presence', event: 'untrack' },
255+
{}
256+
)
257+
channelWithPresenceEnabled.unsubscribe()
258+
})
259+
})
260+
170261
describe('RealtimePresence static methods', () => {
171262
// Helper function to clone objects (from original RealtimePresence tests)
172263
const clone = (obj: any) => {

0 commit comments

Comments
 (0)