Skip to content

[Vblocks-4671] Cypress Tests #337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jun 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const { defineConfig } = require('cypress');

module.exports = defineConfig({
e2e: {
defaultCommandTimeout: 10000,
supportFile: false,
setupNodeEvents(on, config) {
on('before:browser:launch', (browser, launchOptions) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import * as assert from 'assert';
import axios from 'axios';
import Device from '../../../lib/twilio/device';
import type Call from '../../../lib/twilio/call';
import { generateAccessToken } from '../../lib/token';
import { expectEvent } from '../../lib/util';
const env = require('../../env');
import { generateAccessToken } from '../../../tests/lib/token';
import { expectEvent } from '../../../tests/lib/util';

const RELAY_SERVER_URL = 'http://localhost:3030';

Expand All @@ -13,17 +12,19 @@ function waitFor(n: number, reject?: boolean) {
}

describe('userDefinedMessage', function() {
this.timeout(1000 * 60 * 10); // 10 minute timeout for the whole suite
const MAX_TIMEOUT = 1000 * 60 * 10; // 10 minute timeout for the whole suite
this.timeout(MAX_TIMEOUT);
Cypress.config('defaultCommandTimeout', MAX_TIMEOUT);

const createCallTest = async () => {
const tokenTtl = 60 * 3; // 3 minute TTL

const aliceId = `client-id-call-message-tests-alice-${Date.now()}`;
const aliceToken = generateAccessToken(aliceId, tokenTtl, env.appSid);
const aliceToken = generateAccessToken(aliceId, tokenTtl);
const aliceDevice = new Device(aliceToken);

const bobId = `client-id-call-message-tests-bob-${Date.now()}`;
const bobToken = generateAccessToken(bobId, tokenTtl, env.appSid);
const bobToken = generateAccessToken(bobId, tokenTtl);
const bobDevice = new Device(bobToken);

await bobDevice.register();
Expand Down Expand Up @@ -74,8 +75,7 @@ describe('userDefinedMessage', function() {
performTeardown();
});

it(
'should successfully send a message to the customer server',
it('should successfully send a message to the customer server',
async function() {
const { call } = alice;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import * as assert from 'assert';
import * as sinon from 'sinon';
import Device from '../../lib/twilio/device';
import Call from '../../lib/twilio/call';
import { generateAccessToken } from '../lib/token';
import { expectEvent } from '../lib/util';
import { generateAccessToken } from '../../tests/lib/token';
import { expectEvent } from '../../tests/lib/util';

describe('connectToken', function() {
this.timeout(10000);
Expand Down
191 changes: 169 additions & 22 deletions cypress/e2e/device.cy.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import Device from '../../lib/twilio/device';
import * as assert from 'assert';
import Call from '../../lib/twilio/call';
import Device from '../../lib/twilio/device';
import { generateAccessToken } from '../../tests/lib/token';

describe('Device', function () {
this.timeout(10000);
// (rrowland) The TwiML expected by these tests can be found in the README.md

describe('Device', function() {
let device1: Device;
let device2: Device;
let identity1: string;
Expand All @@ -17,11 +18,13 @@ describe('Device', function () {
identity2 = 'id2-' + Date.now();
token1 = generateAccessToken(identity1);
token2 = generateAccessToken(identity2);

device1 = new Device(token1);
device2 = new Device(token2);

return Promise.all([device1.register(), device2.register()]);
return Promise.all([
device1.register(),
device2.register(),
]);
});

after(() => {
Expand All @@ -36,27 +39,75 @@ describe('Device', function () {
}
});

describe('tokenWillExpire event', () => {
const setupDevice = (tokenTtl: number, tokenRefreshMs: number) => {
const accessToken = generateAccessToken(`device-tokenWillExpire-${Date.now()}`, tokenTtl);
const device = new Device(accessToken, { tokenRefreshMs });
device.on(Device.EventName.Error, () => { /* no-op */ });
return device;
};

it('should emit a "tokenWillExpire" event', (done) => {
const device = setupDevice(5, 1000);
device.on(Device.EventName.TokenWillExpire, () => {
done();
});
device.register();
});

it('should not emit a "tokenWillExpire" event early', async () => {
const device = setupDevice(10, 1000);
await new Promise<void>(async (resolve, reject) => {
let successTimeout: any = null;
device.on(Device.EventName.TokenWillExpire, () => {
if (successTimeout) {
clearTimeout(successTimeout);
}
reject();
});
await device.register();
// NOTE(csantos): Expected time before tokenWillExpire event is raised is about 9s
// for a ttl: 10s and tokenRefreshMS: 1000, meaning we should resolve the test at less than 9s.
// However, the ttl returned from signaling, which we use to calculate the timeout,
// sometimes returns 1s less from the original ttl we provided. So let's account for that.
// Instead of resolving the test before 9s, we should do it before 8s instead.
successTimeout = setTimeout(resolve, 7500);
});
});

it('should emit a "tokenWillExpire" event as soon as possible if the option is smaller than the ttl', async () => {
const device = setupDevice(5, 10000);

const eventPromises = Promise.all([
Device.EventName.TokenWillExpire,
Device.EventName.Registering,
].map((eventName: string) => new Promise<number>(res => {
device.on(eventName, () => res(Date.now()));
})));

device.register();

const [expireTime, registeringTime] = await eventPromises;
const diff = Math.abs(expireTime - registeringTime);
// ttl returned from signaling, which we use to calculate the timeout,
// sometimes returns 1s less from the original ttl we provided. So let's account for that.
assert(diff < 2000, `event time occurred too late; diff: ${diff}`);
});
});

describe('device 1 calls device 2', () => {
let call1: Call;
let call2: Call;

before(
() =>
new Promise<void>(async (resolve) => {
device2.once(Device.EventName.Incoming, (call: Call) => {
call2 = call;
resolve();
});
call1 = await (device1['connect'] as any)({
params: {
To: identity2,
Custom1: 'foo + bar',
Custom2: undefined,
Custom3: '我不吃蛋',
},
});
})
);
before(() => new Promise<void>(async resolve => {
device2.once(Device.EventName.Incoming, (call: Call) => {
call2 = call;
resolve();
});
call1 = await (device1['connect'] as any)({
params: { To: identity2, Custom1: 'foo + bar', Custom2: undefined, Custom3: '我不吃蛋' }
});
}));

describe('and device 2 accepts', () => {
beforeEach(() => {
Expand All @@ -69,6 +120,102 @@ describe('Device', function () {
call2.once('accept', () => done());
call2.accept();
});

it('should stay open 3 seconds', (done) => {
function fail() {
call1.removeListener('disconnect', fail);
call2.removeListener('disconnect', fail);
done(new Error('Expected the call to stay open for 3 seconds'));
}

call1.once('disconnect', fail);
call2.once('disconnect', fail);

setTimeout(() => {
call1.removeListener('disconnect', fail);
call2.removeListener('disconnect', fail);
done();
}, 3000);
});

it('should set callerInfo to null on both calls', () => {
assert.equal(call1!.callerInfo, null);
assert.equal(call2!.callerInfo, null);
});

it('should be using the PCMU codec for both calls', (done) => {
let codec1: string | null | undefined = null;

function setCodec(sample: any) {
if (codec1 === null) {
codec1 = sample.codecName;
} else {
if (codec1 === 'opus' || sample.codecName === 'opus') {
done(new Error('Codec is opus'));
} else {
done();
}
}
}

const monitor1: any = call1['_monitor'];
const monitor2: any = call2['_monitor'];
if (!monitor1 || !monitor2) {
done(new Error('Missing monitor'));
}
monitor1.once('sample', setCodec);
monitor2.once('sample', setCodec);
});

it('should update network priority to high if supported', () => {
if (!call2 || !call2['_mediaHandler'] || !call2['_mediaHandler']._sender) {
throw new Error('Expected sender to be present');
}
const sender = call2['_mediaHandler']._sender;
const params = sender.getParameters();
const encoding = params.encodings && params.encodings[0];

if (!params.priority && !encoding) {
// Not supported by browser.
return;
}

assert(params.priority === 'high'
|| (encoding && encoding.priority === 'high')
|| (encoding && encoding.networkPriority === 'high'));
});

it('should receive the correct custom parameters from the TwiML app', () => {
const comparator =
([customParamKeyA]: string[], [customParamKeyB]: string[]) => {
return customParamKeyA.localeCompare(customParamKeyB);
};
assert.deepEqual(
Array.from(call2.customParameters.entries()).sort(comparator),
[
['custom + param', '我不吃蛋'],
['custom1', 'foo + bar'],
['custom2', 'undefined'],
['custom3', '我不吃蛋'],
['duplicate', '123456'],
['foobar', 'some + value'],
],
);
});

it('should post metrics', (done) => {
const publisher = call1['_publisher'];
(publisher as any)['_request'] = { post: (params: any, err: Function) => {
if (/EndpointMetrics$/.test(params.url)) {
done();
}
}};
});

it('should hang up', (done) => {
call1.once('disconnect', () => done());
call2.disconnect();
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Device from '../../lib/twilio/device';
import { generateAccessToken } from '../lib/token';
import { expectEvent } from '../lib/util';
import { generateAccessToken } from '../../tests/lib/token';
import { expectEvent } from '../../tests/lib/util';

describe('DeviceOptions', function() {
this.timeout(10000);
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as assert from 'assert';
import Call from '../../lib/twilio/call';
import Device from '../../lib/twilio/device';
import { generateAccessToken } from '../lib/token';
import { expectEvent, isFirefox } from '../lib/util';
import { generateAccessToken } from '../../tests/lib/token';
import { expectEvent, isFirefox } from '../../tests/lib/util';

const CONNECTION_DELAY_THRESHOLD = 1000;
const SUITE_TIMEOUT = 20000;
Expand All @@ -11,6 +11,7 @@ const MAX_TIMEOUT = 300000;
const maybeSkip = isFirefox() ? describe.skip : describe;

maybeSkip('ICE Nomination', function() {
Cypress.config('defaultCommandTimeout', MAX_TIMEOUT);
this.timeout(MAX_TIMEOUT);

let device1: Device;
Expand Down Expand Up @@ -144,16 +145,16 @@ maybeSkip('ICE Nomination', function() {
assert(duration < CONNECTION_DELAY_THRESHOLD);
});
});

// These two tests are flaky. Disable for now. We need to re-run/update them once media server deploys a fix.
it.skip('should have a significant dtls handshake delay when using high-delay region and aggressive nomination is false', async () => {

it('should have a significant dtls handshake delay when using high-delay region and aggressive nomination is false', async () => {
deviceOptions.edge = highDelayEdge;
await setupDevices();
await getCallDuration(direction).then(duration => {
assert(duration > CONNECTION_DELAY_THRESHOLD);
});
});
it.skip('should not have a significant dtls handshake delay when using high-delay region and aggressive nomination is true', async () => {

it('should not have a significant dtls handshake delay when using high-delay region and aggressive nomination is true', async () => {
deviceOptions.edge = highDelayEdge;
deviceOptions.forceAggressiveIceNomination = true;
await setupDevices();
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as assert from 'assert';
import * as sinon from 'sinon';
import Call from '../../lib/twilio/call';
import Device from '../../lib/twilio/device';
import { generateAccessToken } from '../lib/token';
import { generateAccessToken } from '../../tests/lib/token';

function waitFor(n: number, reject?: boolean) {
return new Promise((res, rej) => setTimeout(reject ? rej : res, n));
Expand Down Expand Up @@ -73,6 +73,7 @@ describe('mutable options', function() {
});

context('ongoing calls', function() {
Cypress.config('defaultCommandTimeout', 30000);
this.timeout(30000);

let caller: Device;
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/opus.ts → cypress/e2e/opus.cy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as assert from 'assert';
import Call from '../../lib/twilio/call';
import Device from '../../lib/twilio/device';
import { generateAccessToken } from '../lib/token';
import { generateAccessToken } from '../../tests/lib/token';

// (rrowland) The TwiML expected by these tests can be found in the README.md

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Device from '../../lib/twilio/device';
import { generateAccessToken } from '../lib/token';
import { generateAccessToken } from '../../tests/lib/token';
import * as assert from 'assert';
import { EventEmitter } from 'events';
import { PreflightTest } from '../../lib/twilio/preflight/preflight';
Expand All @@ -12,6 +12,7 @@ const MAX_TIMEOUT = 300000;

describe('Preflight Test', function() {
this.timeout(MAX_TIMEOUT);
Cypress.config('defaultCommandTimeout', MAX_TIMEOUT);

let callerIdentity: string;
let callerToken: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import * as assert from 'assert';
import { EventEmitter } from 'events';
import Call from '../../lib/twilio/call';
import Device from '../../lib/twilio/device';
import * as env from '../env';
import { generateAccessToken } from '../lib/token';
import * as env from '../../tests/env';
import { generateAccessToken } from '../../tests/lib/token';

describe('SHAKEN/STIR', function() {
this.timeout(10000);
Expand Down
Loading