diff --git a/.circleci/config.yml b/.circleci/config.yml index e17cde1b..2578dd30 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -94,7 +94,8 @@ commands: - run: name: "Generate access token" command: | - node test/scripts/gen-token.mjs $CLIENT_IDENTITY test/app/src/e2e-tests-token.ts + node test/scripts/gen-token.mjs $CLIENT_IDENTITY call test/app/src/e2e-tests-token.ts + node test/scripts/gen-token.mjs $CLIENT_IDENTITY preflightTest test/app/src/e2e-preflightTest-token.ts clear-detox-cache: description: "Clears detox framework cache" @@ -278,9 +279,11 @@ jobs: configuration: <> - detox-test: configuration: <> - artifacts: <> + artifacts: ./detox_artifacts + screenshots: all - store_artifacts: - path: ./test/app/test-report.html + when: always + path: ./test/app/detox_artifacts e2e-android: executor: @@ -448,4 +451,4 @@ workflows: jobs: - trigger-qe-tests: context: sdks-qe - name: Test QE Regression Tests Trigger + name: Test QE Regression Tests Trigger diff --git a/test/app/.gitignore b/test/app/.gitignore index 0b8637bd..5e757135 100644 --- a/test/app/.gitignore +++ b/test/app/.gitignore @@ -75,4 +75,7 @@ yarn-error.log # Secrets /src/e2e-tests-token.*.ts +/src/e2e-preflightTest-token.*.ts +/src/e2e-tests-token.ts +/src/e2e-preflightTest-token.ts /android/app/google-services.json diff --git a/test/app/e2e/jest.config.js b/test/app/e2e/jest.config.js index b0ae98a4..61d4ec43 100644 --- a/test/app/e2e/jest.config.js +++ b/test/app/e2e/jest.config.js @@ -1,7 +1,7 @@ /** @type {import('@jest/types').Config.InitialOptions} */ module.exports = { rootDir: '..', - testMatch: ['/e2e/**/*.test.ts'], + testMatch: ['/e2e/suites/**/*.test.ts'], testTimeout: 120000, maxWorkers: 1, globalSetup: 'detox/runners/jest/globalSetup', diff --git a/test/app/e2e/suites/call.test.ts b/test/app/e2e/suites/call.test.ts index 68fed92c..cb479bfc 100644 --- a/test/app/e2e/suites/call.test.ts +++ b/test/app/e2e/suites/call.test.ts @@ -25,6 +25,7 @@ describe('call', () => { beforeEach(async () => { await device.reloadReactNative(); + await element(by.text('CALL SUITE')).tap(); }); if (device.getPlatform() === 'ios') { @@ -77,7 +78,7 @@ describe('call', () => { log.includes('constant-audio-output-level'); return qualityWarningsChangedEvent && (constantAudioInputWarning || constantAudioOutputWarning); - } + }; // check the call quality warnings every 5 seconds // for a total of 30 seconds diff --git a/test/app/e2e/suites/callMessage.test.ts b/test/app/e2e/suites/callMessage.test.ts index 5d47ca06..2af80cdb 100644 --- a/test/app/e2e/suites/callMessage.test.ts +++ b/test/app/e2e/suites/callMessage.test.ts @@ -6,7 +6,7 @@ import { bootstrapTwilioClient } from '../common/twilioClient'; import { pollValidateLog, getRegExpMatch } from '../common/logParser'; const DEFAULT_TIMEOUT = 10000; -const RELAY_SERVER_URL = 'http://localhost:4040' +const RELAY_SERVER_URL = 'http://localhost:4040'; describe('call', () => { let twilioClient: ReturnType; @@ -187,6 +187,7 @@ describe('call', () => { beforeEach(async () => { await device.reloadReactNative(); + await element(by.text('CALL SUITE')).tap(); if (device.getPlatform() !== 'ios') { await toggleLogFormat(); diff --git a/test/app/e2e/suites/preflightTest.test.ts b/test/app/e2e/suites/preflightTest.test.ts new file mode 100644 index 00000000..d7c44740 --- /dev/null +++ b/test/app/e2e/suites/preflightTest.test.ts @@ -0,0 +1,683 @@ +import { device, element, by } from 'detox'; +import { expect as jestExpect } from 'expect'; +import { pollValidateLog, getRegExpMatch } from '../common/logParser'; +import type { PreflightTest } from '../../../../src/PreflightTest'; + +const expectNonEmptyString = (str: any) => { + jestExpect(typeof str).toEqual('string'); + jestExpect(str.length > 0).toEqual(true); +}; + +const expectIceCandidateStats = (stats: any) => { + expectNonEmptyString(stats.candidateType); + jestExpect(typeof stats.deleted).toEqual('boolean'); + expectNonEmptyString(stats.ip); + jestExpect(typeof stats.networkCost).toEqual('number'); + jestExpect(typeof stats.networkId).toEqual('number'); + expectNonEmptyString(stats.networkType); + jestExpect(typeof stats.port).toEqual('number'); + jestExpect(typeof stats.priority).toEqual('number'); + expectNonEmptyString(stats.protocol); + jestExpect(typeof stats.relatedAddress).toEqual('string'); + jestExpect(typeof stats.relatedPort).toEqual('number'); + jestExpect(typeof stats.tcpType).toEqual('string'); + expectNonEmptyString(stats.transportId); + jestExpect(typeof stats.url).toEqual('string'); +}; + +const expectTimeMeasurement = (timing: PreflightTest.TimeMeasurement) => { + jestExpect(typeof timing).toEqual('object'); + jestExpect(typeof timing.duration).toEqual('number'); + jestExpect(typeof timing.end).toEqual('number'); + jestExpect(typeof timing.start).toEqual('number'); +}; + +const expectSample = (sample: PreflightTest.RTCSample) => { + jestExpect(typeof sample.audioInputLevel).toEqual('number'); + jestExpect(typeof sample.audioOutputLevel).toEqual('number'); + jestExpect(typeof sample.bytesReceived).toEqual('number'); + jestExpect(typeof sample.bytesSent).toEqual('number'); + jestExpect(typeof sample.codec).toEqual('string'); + jestExpect(typeof sample.jitter).toEqual('number'); + jestExpect(typeof sample.mos).toEqual('number'); + jestExpect(typeof sample.packetsLost).toEqual('number'); + jestExpect(typeof sample.packetsLostFraction).toEqual('number'); + jestExpect(typeof sample.packetsReceived).toEqual('number'); + jestExpect(typeof sample.rtt).toEqual('number'); + jestExpect(typeof sample.timestamp).toEqual('number'); +}; + +const expectStats = (stats: PreflightTest.Stats) => { + jestExpect(typeof stats).toEqual('object'); + jestExpect(typeof stats.average).toEqual('number'); + jestExpect(typeof stats.max).toEqual('number'); + jestExpect(typeof stats.min).toEqual('number'); +}; + +const expectWarning = (warning: PreflightTest.Warning) => { + jestExpect(typeof warning).toEqual('object'); + expectNonEmptyString(warning.name); + expectNonEmptyString(warning.threshold); + jestExpect(typeof warning.timestamp).toEqual('number'); + expectNonEmptyString(warning.values); +}; + +const expectWarningCleared = (warningCleared: PreflightTest.WarningCleared) => { + jestExpect(typeof warningCleared).toEqual('object'); + expectNonEmptyString(warningCleared.name); + jestExpect(typeof warningCleared.timestamp).toEqual('number'); +}; + +const expectCallQuality = (callQuality: PreflightTest.CallQuality) => { + jestExpect( + (['degraded', 'excellent', 'fair', 'good', 'great'] as any) + .includes(callQuality) + ).toEqual(true); +}; + +const expectReport = (report: any) => { + expectCallQuality(report.callQuality); + + jestExpect(typeof report.callSid).toEqual('string'); + jestExpect(report.callSid.match(/^CA.*$/)).toBeTruthy(); + + expectNonEmptyString(report.edge); + + jestExpect(Array.isArray(report.iceCandidateStats)).toEqual(true); + jestExpect(report.iceCandidateStats.length > 0).toEqual(true); + for (const iceCandidate of report.iceCandidateStats) { + expectIceCandidateStats(iceCandidate); + } + + jestExpect(typeof report.isTurnRequired).toEqual('boolean'); + + expectTimeMeasurement(report.networkTiming.ice); + expectTimeMeasurement(report.networkTiming.peerConnection); + expectTimeMeasurement(report.networkTiming.signaling); + + jestExpect(Array.isArray(report.samples)).toEqual(true); + jestExpect(report.samples.length > 0).toEqual(true); + for (const sample of report.samples) { + expectSample(sample); + } + + expectNonEmptyString(report.selectedEdge); + + jestExpect(typeof report.selectedIceCandidatePairStats).toEqual('object'); + expectIceCandidateStats(report.selectedIceCandidatePairStats.localCandidate); + expectIceCandidateStats(report.selectedIceCandidatePairStats.remoteCandidate); + + jestExpect(typeof report.stats).toEqual('object'); + expectStats(report.stats.jitter); + expectStats(report.stats.mos); + expectStats(report.stats.rtt); + + expectTimeMeasurement(report.testTiming); + + jestExpect(Array.isArray(report.warnings)).toEqual(true); + for (const warning of report.warnings) { + expectWarning(warning); + } + + jestExpect(Array.isArray(report.warningsCleared)).toEqual(true); + for (const warningCleared of report.warningsCleared) { + expectWarningCleared(warningCleared); + } +}; + +const emptyRtcStats = { + candidateType: '', + deleted: false, + ip: '', + isRemote: false, + networkCost: 0, + networkId: 0, + networkType: '', + port: 0, + priority: 0, + protocol: '', + relatedAddress: '', + relatedPort: 0, + tcpType: '', + transportId: '', + url: '', +}; + +const emptyStats = { + average: 0, + max: 0, + min: 0, +}; + +const emptyTimeMeasurement = { + duration: 0, + end: 0, + start: 0, +}; + +const emptyReport = { + callSid: '', + callQuality: null, + edge: '', + iceCandidateStats: [], + isTurnRequired: null, + stats: { + jitter: emptyStats, + mos: emptyStats, + rtt: emptyStats, + }, + networkTiming: { + signaling: emptyTimeMeasurement, + peerConnection: emptyTimeMeasurement, + ice: emptyTimeMeasurement, + }, + testTiming: emptyTimeMeasurement, + samples: [], + selectedEdge: '', + selectedIceCandidatePairStats: { + localCandidate: emptyRtcStats, + remoteCandidate: emptyRtcStats, + }, + warnings: [], + warningsCleared: [], +}; + +describe('preflightTest', () => { + beforeAll(async () => { + await device.launchApp(); + }); + + if (device.getPlatform() === 'ios') { + it('should pass the dummy test', () => { + // by default jest does not pass a test suite if there are no tests + }); + } + + if (device.getPlatform() === 'android') { + describe('events', () => { + describe('successful flow', () => { + beforeAll(async () => { + await device.reloadReactNative(); + await element(by.text('PREFLIGHT TEST SUITE')).tap(); + await element(by.text('TOGGLE LOG FORMAT')).tap(); + + await element(by.text('START PREFLIGHT')).tap(); + + const didConnect = await pollValidateLog((logs) => logs.some((log) => + Boolean(log.content.match(/^preflight test connected/)) + )); + jestExpect(didConnect).toEqual(true); + + await element(by.text('GETSTATE')).tap(); + + const didComplete = await pollValidateLog((logs) => logs.some((log) => + Boolean(log.content.match(/^preflight test completed/)) + )); + + jestExpect(didComplete).toEqual(true); + }); + + it('should have received the connected event first', async () => { + const didReceiveConnectedEventFirst = await pollValidateLog((logs) => { + if (logs.length === 0) { return false; } + + const { content } = logs[0]; + const m = content.match(/^preflight test connected$/); + if (m === null) { return false; } + + return true; + }); + + jestExpect(didReceiveConnectedEventFirst).toEqual(true); + }); + + it('should have received connected when invoking getState', async () => { + const isStateConnected = await pollValidateLog((logs) => { + const getStateLogs = logs.reduce((reduction, { content }) => { + const m = content.match(/^preflight test getState "(.*)"$/); + if (m) { + return [...reduction, m]; + } else { + return reduction; + } + }, [] as RegExpMatchArray[]); + + if (getStateLogs.length !== 1) { return false; } + + const getStateLog = getStateLogs[0]; + const state = getStateLog[1]; + if (state !== 'connected') { return false; } + + return true; + }); + + jestExpect(isStateConnected).toEqual(true); + }); + + it('should have received samples', async () => { + const wasSampleReceieved = await pollValidateLog((logs) => { + const sampleEvents = + logs.filter((log) => log.content.match(/^preflight test sample/)); + + for (const sampleEvent of sampleEvents) { + const sampleRegExp = /^preflight test sample "(.*)"$/; + + const sampleMatch = sampleEvent.content.match(sampleRegExp); + jestExpect(sampleMatch).not.toEqual(null); + + const sampleStr = sampleMatch![1]; + expectNonEmptyString(sampleStr); + + const sample = JSON.parse(sampleStr!); + expectSample(sample); + } + + return true; + }); + + jestExpect(wasSampleReceieved).toEqual(true); + }); + + it('should have received quality warnings', async () => { + const wasQualityWarningsReceived = await pollValidateLog((logs) => { + const qualityWarningEvents = + logs.filter((log) => log.content.match(/^preflight test quality warnings/)); + + for (const qualityWarningEvent of qualityWarningEvents) { + const qualityWarningRegExp = /^preflight test quality warnings "(.*)"$/; + + const qualityWarningMatch = qualityWarningEvent.content.match(qualityWarningRegExp); + jestExpect(qualityWarningMatch).not.toEqual(null); + + const qualityWarningStr = qualityWarningMatch![1]; + expectNonEmptyString(qualityWarningStr); + + const { currentWarnings, previousWarnings } = JSON.parse(qualityWarningStr!); + jestExpect(Array.isArray(currentWarnings)).toEqual(true); + jestExpect(Array.isArray(previousWarnings)).toEqual(true); + + currentWarnings.forEach(expectNonEmptyString); + previousWarnings.forEach(expectNonEmptyString); + } + + return true; + }); + + jestExpect(wasQualityWarningsReceived).toEqual(true); + }); + + it('should have received the completed event last', async () => { + const didReceiveCompletedEventLast = await pollValidateLog((logs) => { + if (logs.length === 0) { return false; } + + const { content } = logs[logs.length - 1]; + const m = content.match(/^preflight test completed/); + if (m === null) { return false; } + + return true; + }); + + jestExpect(didReceiveCompletedEventLast).toEqual(true); + }); + }); + + describe('failure flow', () => { + beforeEach(async () => { + await device.reloadReactNative(); + await element(by.text('PREFLIGHT TEST SUITE')).tap(); + await element(by.text('TOGGLE LOG FORMAT')).tap(); + }); + + it('should disconnect an ongoing preflightTest and get a failure error', async () => { + await element(by.text('START PREFLIGHT')).tap(); + + await new Promise((r) => setTimeout(r, 5000)); + + const connectedEventRegExp = /^(preflight test connected)$/; + const connectedEvent = await getRegExpMatch(connectedEventRegExp); + jestExpect(connectedEvent).toEqual('preflight test connected'); + + await element(by.text('STOP PREFLIGHT')).tap(); + + const stoppedEventRegExp = /^(preflight test stopped)$/; + const stoppedEvent = await getRegExpMatch(stoppedEventRegExp); + jestExpect(stoppedEvent).toEqual('preflight test stopped'); + + const failedEventRegExp = /^preflight test failed "(.*)"$/; + const failedEvent = await getRegExpMatch(failedEventRegExp); + jestExpect(typeof failedEvent).toEqual('string'); + + const failureError = JSON.parse(failedEvent!); + jestExpect(failureError).toEqual({ + causes: [ + 'The incoming call was cancelled because it was not answered in ' + + 'time or it was accepted/rejected by another application ' + + 'instance registered with the same identity.', + ], + description: 'Call cancelled', + explanation: 'Unable to answer because the call has ended', + solutions: [], + code: 31008, + name: 'CallCancelledError', + }); + }); + + it('should fail if trying to start a preflight test with an invalid token', async () => { + await element(by.text('INVALID PREFLIGHT')).tap(); + + await pollValidateLog((logs) => { + if (logs.length === 0) { return false; } + + for (const { content } of logs) { + const match = content.match(/^preflight test failed "(.*)"$/); + jestExpect(match).not.toBeNull(); + + const group = match![1]; + expectNonEmptyString(group); + + const failureError = JSON.parse(group!); + jestExpect(failureError).toEqual({ + causes: [], + description: 'Invalid access token', + explanation: 'Twilio was unable to validate your Access Token', + solutions: [], + code: 20101, + name: 'AccessTokenInvalid', + }); + } + + return true; + }); + }); + + it('should fail if trying to start multiple preflightTests', async () => { + await element(by.text('START PREFLIGHT')).tap(); + + await new Promise((r) => setTimeout(r, 5000)); + + const connectedEventRegExp = /^(preflight test connected)$/; + const connectedEvent = await getRegExpMatch(connectedEventRegExp); + jestExpect(connectedEvent).toEqual('preflight test connected'); + + await element(by.text('START PREFLIGHT')).tap(); + + const errorEventRegExp = /^preflight test error "(.*)"$/; + const errorEventStr = await getRegExpMatch(errorEventRegExp); + jestExpect(typeof errorEventStr).toEqual('string'); + + const { error, message } = JSON.parse(errorEventStr!); + jestExpect(error).toEqual({ + causes: [], + description: 'Invalid state error.', + explanation: 'The SDK has entered an invalid state.', + solutions: [], + name: 'InvalidStateError', + }); + jestExpect(message).toEqual( + 'Cannot start a PreflightTest while one exists in-progress.' + ); + + await element(by.text('STOP PREFLIGHT')).tap(); + + await new Promise((r) => setTimeout(r, 5000)); + }); + }); + }); + + describe('public methods', () => { + describe('stopped preflightTest', () => { + beforeAll(async () => { + await device.reloadReactNative(); + await element(by.text('PREFLIGHT TEST SUITE')).tap(); + await element(by.text('TOGGLE LOG FORMAT')).tap(); + + await element(by.text('START PREFLIGHT')).tap(); + + await new Promise((r) => setTimeout(r, 5000)); + + const connectedEventRegExp = /^(preflight test connected)$/; + const connectedEvent = await getRegExpMatch(connectedEventRegExp); + jestExpect(connectedEvent).toEqual('preflight test connected'); + + await element(by.text('STOP PREFLIGHT')).tap(); + + const stoppedEventRegExp = /^(preflight test stopped)$/; + const stoppedEvent = await getRegExpMatch(stoppedEventRegExp); + jestExpect(stoppedEvent).toEqual('preflight test stopped'); + + const failedEventRegExp = /^preflight test failed "(.*)"$/; + const failedEvent = await getRegExpMatch(failedEventRegExp); + jestExpect(typeof failedEvent).toEqual('string'); + }); + + it('should get a failed state', async () => { + await element(by.text('GETSTATE')).tap(); + + const isStateFailed = await pollValidateLog((logs) => { + const getStateLogs = logs.reduce((reduction, { content }) => { + const m = content.match(/^preflight test getState "(.*)"$/); + if (m) { + return [...reduction, m]; + } else { + return reduction; + } + }, [] as RegExpMatchArray[]); + + if (getStateLogs.length !== 1) { + return false; + } + + const getStateLog = getStateLogs[0]; + const state = getStateLog[1]; + if (state !== 'failed') { + return false; + } + + return true; + }); + + jestExpect(isStateFailed).toEqual(true); + }); + + it('should get a valid sample', async () => { + await element(by.text('GETLATESTSAMPLE')).tap(); + + const getSampleRegExp = /^preflight test getLatestSample "(.*)"$/; + const getSampleStr = await getRegExpMatch(getSampleRegExp); + jestExpect(typeof getSampleStr).toEqual('string'); + + const sample = JSON.parse(getSampleStr!); + expectSample(sample); + }); + + it('should get an empty report', async () => { + await element(by.text('GETREPORT')).tap(); + + const getReportRegExp = /^preflight test getReport "(.*)"$/; + const getReportStr = await getRegExpMatch(getReportRegExp); + jestExpect(typeof getReportStr).toEqual('string'); + + const report = JSON.parse(getReportStr!); + jestExpect(report).toEqual(emptyReport); + }); + }); + + describe('invalid token preflightTest', () => { + beforeAll(async () => { + await device.reloadReactNative(); + await element(by.text('PREFLIGHT TEST SUITE')).tap(); + await element(by.text('TOGGLE LOG FORMAT')).tap(); + + await element(by.text('START PREFLIGHT')).tap(); + + await new Promise((r) => setTimeout(r, 5000)); + + const connectedEventRegExp = /^(preflight test connected)$/; + const connectedEvent = await getRegExpMatch(connectedEventRegExp); + jestExpect(connectedEvent).toEqual('preflight test connected'); + + await element(by.text('STOP PREFLIGHT')).tap(); + + const stoppedEventRegExp = /^(preflight test stopped)$/; + const stoppedEvent = await getRegExpMatch(stoppedEventRegExp); + jestExpect(stoppedEvent).toEqual('preflight test stopped'); + + const failedEventRegExp = /^preflight test failed "(.*)"$/; + const failedEvent = await getRegExpMatch(failedEventRegExp); + jestExpect(typeof failedEvent).toEqual('string'); + }); + + it('should get a failed state', async () => { + await element(by.text('GETSTATE')).tap(); + + const isStateFailed = await pollValidateLog((logs) => { + const getStateLogs = logs.reduce((reduction, { content }) => { + const m = content.match(/^preflight test getState "(.*)"$/); + if (m) { + return [...reduction, m]; + } else { + return reduction; + } + }, [] as RegExpMatchArray[]); + + if (getStateLogs.length !== 1) { + return false; + } + + const getStateLog = getStateLogs[0]; + const state = getStateLog[1]; + if (state !== 'failed') { + return false; + } + + return true; + }); + + jestExpect(isStateFailed).toEqual(true); + }); + + it('should get a valid sample', async () => { + await element(by.text('GETLATESTSAMPLE')).tap(); + + const getSampleRegExp = /^preflight test getLatestSample "(.*)"$/; + const getSampleStr = await getRegExpMatch(getSampleRegExp); + jestExpect(typeof getSampleStr).toEqual('string'); + + const sample = JSON.parse(getSampleStr!); + expectSample(sample); + }); + + it('should get an empty report', async () => { + await element(by.text('GETREPORT')).tap(); + + const getReportRegExp = /^preflight test getReport "(.*)"$/; + const getReportStr = await getRegExpMatch(getReportRegExp); + jestExpect(typeof getReportStr).toEqual('string'); + + const report = JSON.parse(getReportStr!); + jestExpect(report).toEqual(emptyReport); + }); + }); + + describe('completed preflightTest', () => { + let startTime: number; + let endTime: number; + + beforeAll(async () => { + await device.reloadReactNative(); + await element(by.text('PREFLIGHT TEST SUITE')).tap(); + await element(by.text('TOGGLE LOG FORMAT')).tap(); + + startTime = Date.now(); + + await element(by.text('START PREFLIGHT')).tap(); + + await new Promise((r) => setTimeout(r, 5000)); + + const connectedEventRegExp = /^(preflight test connected)$/; + const connectedEvent = await getRegExpMatch(connectedEventRegExp); + jestExpect(connectedEvent).toEqual('preflight test connected'); + + const didComplete = await pollValidateLog((logs) => logs.some((log) => + Boolean(log.content.match(/^preflight test completed/)) + )); + jestExpect(didComplete).toEqual(true); + + const completedEventRegExp = /^preflight test completed "(.*)"$/; + const completedEvent = await getRegExpMatch(completedEventRegExp); + jestExpect(typeof completedEvent).toEqual('string'); + + endTime = Date.now(); + }); + + it('should get a valid start time', async () => { + await element(by.text('GETSTARTTIME')).tap(); + + const getStartTimeRegExp = /^preflight test getStartTime "(.*)"$/; + const getStartTimeStr = await getRegExpMatch(getStartTimeRegExp); + jestExpect(typeof getStartTimeStr).toEqual('string'); + + const getStartTime = Number(getStartTimeStr); + jestExpect(getStartTime).not.toBeNaN(); + + const timeGap = Math.abs(startTime - getStartTime); + jestExpect(timeGap).toBeGreaterThan(0); + jestExpect(timeGap).toBeLessThan(10000); + }); + + it('should get a valid end time', async () => { + await element(by.text('GETENDTIME')).tap(); + + const getEndTimeRegExp = /^preflight test getEndTime "(.*)"$/; + const getEndTimeStr = await getRegExpMatch(getEndTimeRegExp); + jestExpect(typeof getEndTimeStr).toEqual('string'); + + const getEndTime = Number(getEndTimeStr); + jestExpect(getEndTime).not.toBeNaN(); + + const timeGap = Math.abs(endTime - getEndTime); + jestExpect(timeGap).toBeGreaterThan(0); + jestExpect(timeGap).toBeLessThan(10000); + }); + + it('should get a valid callsid', async () => { + await element(by.text('GETCALLSID')).tap(); + + const getCallSidRegExp = /^preflight test getCallSid "(CA.*)"$/; + const getCallSid = await getRegExpMatch(getCallSidRegExp); + expectNonEmptyString(getCallSid); + }); + + it('should get completed state', async () => { + await element(by.text('GETSTATE')).tap(); + + const getStateRegExp = /^preflight test getState "(.*)"$/; + const getState = await getRegExpMatch(getStateRegExp); + jestExpect(getState).toEqual('completed'); + }); + + it('should get a valid sample', async () => { + await element(by.text('GETLATESTSAMPLE')).tap(); + + const getSampleRegExp = /^preflight test getLatestSample "(.*)"$/; + const getSampleStr = await getRegExpMatch(getSampleRegExp); + jestExpect(typeof getSampleStr).toEqual('string'); + + const sample = JSON.parse(getSampleStr!); + expectSample(sample); + }); + + it('should get a valid report', async () => { + await element(by.text('GETREPORT')).tap(); + + const getReportRegExp = /^preflight test getReport "(.*)"$/; + const getReportStr = await getRegExpMatch(getReportRegExp); + jestExpect(typeof getReportStr).toEqual('string'); + + const report = JSON.parse(getReportStr!); + expectReport(report); + }); + }); + }); + } +}); diff --git a/test/app/e2e/suites/registration.test.ts b/test/app/e2e/suites/registration.test.ts index afe986c6..0b7a7127 100644 --- a/test/app/e2e/suites/registration.test.ts +++ b/test/app/e2e/suites/registration.test.ts @@ -16,6 +16,7 @@ describe('registration', () => { beforeEach(async () => { await device.reloadReactNative(); + await element(by.text('CALL SUITE')).tap(); }); it('should start unregistered', async () => { diff --git a/test/app/e2e/suites/voice.test.ts b/test/app/e2e/suites/voice.test.ts index 225a4678..12749f2c 100644 --- a/test/app/e2e/suites/voice.test.ts +++ b/test/app/e2e/suites/voice.test.ts @@ -8,6 +8,7 @@ describe('voice', () => { beforeEach(async () => { await device.reloadReactNative(); + await element(by.text('CALL SUITE')).tap(); }); it('should show a valid SDK version', async () => { diff --git a/test/app/src/App.tsx b/test/app/src/App.tsx index ace05055..9cbf291d 100644 --- a/test/app/src/App.tsx +++ b/test/app/src/App.tsx @@ -1,226 +1,34 @@ import * as React from 'react'; -import { - Button, - FlatList, - ListRenderItemInfo, - SafeAreaView, - StyleSheet, - Text, - View, -} from 'react-native'; -import { useVoice } from './hook'; -import type { EventLogItem } from './type'; -import Dialer from './Dialer'; -import Grid from './Grid'; +import { Button, SafeAreaView } from 'react-native'; +import { CallSuite } from './CallSuite'; +import { PreflightTestSuite } from './PreflightTestSuite'; -import { generateAccessToken } from './tokenUtility'; - -let token = ''; -if (!token.length) { - token = generateAccessToken(); -} +const defaultSuite = 'none'; export default function App() { - const { - registered, - sdkVersion, - events, - callInfo, - callMethod, - recentCallInvite, - connectHandler, - preflightTestHandler, - preflightTestMethods, - registerHandler, - unregisterHandler, - logAudioDevicesHandler, - selectAudioDeviceHandler, - getCallsHandler, - getCallInvitesHandler, - } = useVoice(token); - - const headerComponent = React.useMemo( - () => ( - SDK Version: {String(sdkVersion)}, - Registered: {String(registered)}, - ], - ]} - /> - ), - [sdkVersion, registered] - ); - - const callComponent = React.useMemo( - () => ( - Call From: {String(callInfo?.from)}, - Call To: {String(callInfo?.to)}, - ], - [Call State: {String(callInfo?.state)}], - [Call SID: {String(callInfo?.sid)}], - ]} - /> - ), - [callInfo] - ); - - const preflightTestButtons = React.useMemo(() => ( - , -