Skip to content
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

[VBLOCKS-1715] feat: outgoing call test #64

Merged
merged 10 commits into from
Jun 2, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
## App
* Upgrade React Native from `0.70.6` to `0.70.9`.
* Potentially fixes iOS builds for newer versions of Xcode.
* Added e2e tests for outgoing calls
82 changes: 82 additions & 0 deletions app/e2e/outgoingcall.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { by, element, expect, waitFor, device } from 'detox';

describe('Outgoing Call', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's continue the discussion here regarding the following test case, so we can separate the thread

  • check if call is disconnected after clicking end button

I tried to do test case disconnect after clicking end button , but the test would always fail. I believe this a detox issue, and the issue is still open wix/Detox#1185

Clicking the end button behaves the same as when the call is disconnected correct? Can we assert that the active call screen is not visible anymore and that it will transition to the next screen?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had tried various methods earlier in the week, so I didn't think this was possible. But I tried a few more methods yesterday and today, now I have one that works. I have now included a test for your test case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's continue the discussion here regarding the following test case, so we can separate the thread

  • check if call is disconnected after callee disconnects the call or times out

Can setup another number and use Twilio verb to end

I don't think we need to. We can just wait until the current call times out? It's only 10s long right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes call is only 10s. Ok will add a 12s timer to make sure call is no longer in progress

beforeAll(async () => {
await global.device.launchApp();
});

const navigateToDialer = async () => {
await element(by.id('login_button')).tap();
await element(by.id('dialer_button')).tap();
};

beforeEach(async () => {
await global.device.reloadReactNative();
await navigateToDialer();
});

it('should allow user to enter PTSN number', async () => {
await element(by.id('dialpad_button_1')).tap();
await element(by.id('dialpad_button_2')).tap();
await element(by.id('dialpad_button_3')).tap();
await element(by.id('dialpad_button_4')).tap();
await element(by.id('dialpad_button_4')).tap();
await element(by.id('dialpad_button_4')).tap();
await element(by.id('dialpad_button_8')).tap();
await element(by.id('dialpad_button_8')).tap();
await element(by.id('dialpad_button_8')).tap();
await element(by.id('dialpad_button_8')).tap();
await expect(element(by.id('formatted_number'))).toHaveText('+1234448888');
});

it('should allow user to enter Client-ID', async () => {
await element(by.text('Client')).tap();
await element(by.id('client_text_input')).typeText('Client-Web');
await expect(element(by.id('client_text_input'))).toHaveText('Client-Web');
});

if (device.getPlatform() === 'android') {
describe('Android: successful connect', () => {
it('should make a successful PTSN call', async () => {
await element(by.id('dialpad_button_3')).tap();
await element(by.id('dialpad_button_1')).tap();
await element(by.id('dialpad_button_5')).tap();
await element(by.id('dialpad_button_6')).tap();
await element(by.id('dialpad_button_7')).tap();
await element(by.id('dialpad_button_0')).tap();
await element(by.id('dialpad_button_3')).tap();
await element(by.id('dialpad_button_5')).tap();
await element(by.id('dialpad_button_4')).tap();
await element(by.id('dialpad_button_1')).tap();
await element(by.id('call_button')).tap();
await waitFor(element(by.id('active_call'))).toBeVisible();
await waitFor(element(by.id('call_status'))).toHaveText('ringing');
await waitFor(element(by.id('call_status'))).toHaveText('00:05');
charliesantos marked this conversation as resolved.
Show resolved Hide resolved
await waitFor(element(by.id('active_call'))).not.toBeVisible();
});
});

describe('disconnect', () => {
it('should disconnect if invalid number', async () => {
await element(by.id('dialpad_button_1')).tap();
await element(by.id('dialpad_button_2')).tap();
await element(by.id('dialpad_button_3')).tap();
await element(by.id('call_button')).tap();
await waitFor(element(by.id('active_call'))).toBeVisible();
await waitFor(element(by.id('call_status'))).toHaveText('ringing');
await waitFor(element(by.id('call_status'))).toHaveText('disconnected');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also check that the call gets disconnected right away? It should get disconnected in less than 3s, is that correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it can be right away, since the UI still needs time to switch Views. That is why I used a waitFor, since without it the test fails. Where does the 3s come from?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By "right away" I mean, it should not feel like it's stuck. 3s is just an estimate. What we want is, we need to fail this test if it exceeds a certain threshold. For example, if it does not disconnect after 20s, there must be something wrong. 20s is too extreme, so I estimated around 3s. How long do you think we should wait before we consider this as a fail?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get what you mean now. Okay will add another testcase. 3s sounds like a good time to me 👍

await waitFor(element(by.id('active_call'))).not.toBeVisible();
});

it('should disconnect if invalid Client-ID', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, we should check that the call has been disconnected right away.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disconnect still doesn't happen right away. We still have the user see texts ringinging -> disconnect -> back to dial screen.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as my response here #64 (comment)

await element(by.text('Client')).tap();
await element(by.id('client_text_input')).typeText('hi\n');
await element(by.id('call_button')).tap();
await waitFor(element(by.id('active_call'))).toBeVisible();
await waitFor(element(by.id('call_status'))).toHaveText('ringing');
await waitFor(element(by.id('call_status'))).toHaveText('disconnected');
await waitFor(element(by.id('active_call'))).not.toBeVisible();
});
});
}
});
6 changes: 4 additions & 2 deletions app/src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { StyleSheet, TouchableOpacity, View } from 'react-native';

type InheritedTouchableOpacityProps = Pick<
TouchableOpacity['props'],
'disabled' | 'onPress' | 'accessibilityLabel'
'disabled' | 'onPress' | 'accessibilityLabel' | 'testID'
>;

export type Props = React.PropsWithChildren<
Expand All @@ -18,6 +18,7 @@ const Button: React.FC<Props> = ({
disabled,
onPress,
size,
testID,
}) => {
const containerStyle = React.useMemo(
() => ({
Expand All @@ -35,7 +36,8 @@ const Button: React.FC<Props> = ({
<TouchableOpacity
disabled={disabled}
onPress={onPress}
accessibilityLabel={accessibilityLabel}>
accessibilityLabel={accessibilityLabel}
testID={testID}>
<View style={containerStyle}>{children}</View>
</TouchableOpacity>
);
Expand Down
3 changes: 2 additions & 1 deletion app/src/components/Call/MakeCallButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ const MakeOutgoingCallButton: React.FC<Props> = ({ disabled, onPress }) => (
accessibilityLabel="make call"
disabled={disabled}
size={96}
onPress={onPress}>
onPress={onPress}
testID="call_button">
<Image
source={MakeOutgoingCallSource}
resizeMode="contain"
Expand Down
4 changes: 3 additions & 1 deletion app/src/components/Call/RemoteParticipant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ const RemoteParticipant: React.FC<Props> = ({ title, subtitle }) => {
return (
<View style={styles.container} accessibilityLabel="remote participant">
<Text style={styles.title}>{title}</Text>
<Text style={styles.subtitle}>{subtitle}</Text>
<Text style={styles.subtitle} testID="call_status">
{subtitle}
</Text>
</View>
);
};
Expand Down
4 changes: 3 additions & 1 deletion app/src/components/Dialpad/DialpadButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ const DialpadButton: React.FC<Props> = ({
onPress,
}) => (
<Button disabled={disabled} size={96} onPress={onPress}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.title} testID={`dialpad_button_${title}`}>
{title}
</Text>
<Text style={styles.subtitle}>{subtitle}</Text>
</Button>
);
Expand Down
2 changes: 2 additions & 0 deletions app/src/hooks/activeCall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ export const useActiveCallTime = (
const animate = () => {
animationFrameId = requestAnimationFrame(() => {
if (
(activeCall?.status === 'fulfilled' &&
activeCall?.callInfo.state === 'disconnected') ||
activeCall?.status !== 'fulfilled' ||
typeof activeCall.initialConnectTimestamp === 'undefined'
) {
Expand Down
2 changes: 1 addition & 1 deletion app/src/screens/ActiveCall/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const ActiveCall: React.FC = () => {
}, [callStatus, navigation]);

return (
<View style={styles.container}>
<View style={styles.container} testID="active_call">
<View style={styles.containerSpacer} />
<View style={styles.remoteParticipant}>
<RemoteParticipant title={remoteParticipant} subtitle={callStatus} />
Expand Down
5 changes: 4 additions & 1 deletion app/src/screens/Dialer/OutgoingRemoteParticipant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ const OutgoingRemoteParticipant: React.FC<Props> = ({
defaultValue={outgoingIdentity}
onChangeText={setOutgoingIdentity}
style={styles.title}
testID="client_text_input"
/>
) : (
<Text style={numberStyle}>{formattedNumber}</Text>
<Text style={numberStyle} testID="formatted_number">
{formattedNumber}
</Text>
)}
<Text style={styles.subtitle} />
</View>
Expand Down