softphone: People panel with presence, click-to-call
ci / images (push) Canceled after 0s
ci / check (push) Canceled after 4s

People lists every extension with its status dot and a search box: a
right-hand column on wide screens, /me/people on small ones. Call uses the
browser softphone when it's on, else POST /api/me/click-to-call, which
originates to the user's desk phone first (409 if none is registered).
During a call: Transfer (blind), Talk first (consult), or drag the call
card onto a person; a held call card offers Complete transfer whenever
another call is active. useSoftphone moved to its own module.

e2e m5-t3: the dot changes ~30 ms after Asterisk's hint; click-to-call
rings 174 before 172. cdp click() takes a scope (People also has
Call/Transfer buttons). trunk-test ignores the RTCP-at-teardown EPERM
error seen once.

Task: M5-T3
This commit is contained in:
iamdoubz committed 2026-10-18 10:06:28 -05:00
1 parent c7fd4df77c
commit 609965bba5
16 files changed
+452 -33

No files matched your search

+19 -1
View File
@@ -77,7 +77,7 @@ describe.skipIf(!hasTestDb)('/api/me/voicemail and greetings', () => {
// Asterisk's side of VoicemailMove/VoicemailRemove, on disk (one message, msg0000).
t.ami.action = async function (f: Record<string, string>) {
this.actions.push(f);
const from = join(box(), f.Folder!, 'msg0000');
const from = join(box(), f.Folder ?? '', 'msg0000');
if (f.Action === 'VoicemailMove') {
mkdirSync(join(box(), f.ToFolder!), { recursive: true });
for (const x of ['wav', 'txt'])
@@ -144,6 +144,24 @@ describe.skipIf(!hasTestDb)('/api/me/voicemail and greetings', () => {
t.ami.connected = true;
});
it('click-to-call: rings my desk phone first, then dials the number', async () => {
expect((await dan('POST', '/api/me/click-to-call', { number: '172' })).statusCode).toBe(409);
t.live.deskOnline = (n) => n === '101';
const r = await dan('POST', '/api/me/click-to-call', { number: '(312) 555-1234' });
expect(r.statusCode).toBe(200);
expect(t.ami.actions.at(-1)).toEqual({
Action: 'Originate',
Channel: 'PJSIP/101',
Context: 'sp-from-ext',
Exten: '3125551234',
Priority: '1',
CallerID: '"3125551234" <101>',
Async: 'true',
});
expect((await dan('POST', '/api/me/click-to-call', { number: 'abc' })).statusCode).toBe(400);
expect((await eve('POST', '/api/me/click-to-call', { number: '172' })).statusCode).toBe(404);
});
it('webrtc: creds for <ext>-web with ICE servers; 404 without an extension', async () => {
await setSetting(t.db, 'network.stunServer', 'stun.example.org:3478');
const r = await dan('GET', '/api/me/webrtc');