Skip to content

Commit 2ff1c89

Browse files
authored
feat: e2e - add helpers for entering passcode / biometrics (#742)
* refactor: add helper for entering biometrics or passcode * feat: add waitForAuthValidity to ':android:should save with AES_GCM storage - ' * test: replace expect with waitFor for credential save assertionss * test: match master changes * test: close keyboard if open after passcode * test: increase timeout for credential save RSA * test: increase timeout for credential load and save assertions
1 parent 39ae46f commit 2ff1c89

File tree

4 files changed

+82
-72
lines changed

4 files changed

+82
-72
lines changed

KeychainExample/e2e/testCases/acessControlTest.spec.js renamed to KeychainExample/e2e/testCases/accessControlTest.spec.js

Lines changed: 45 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,49 @@
11
import { by, device, element, expect, waitFor } from 'detox';
22
import { matchLoadInfo } from '../utils/matchLoadInfo';
3-
import cp from 'child_process';
3+
import {
4+
waitForAuthValidity,
5+
enterBiometrics,
6+
enterPasscode,
7+
} from '../utils/authHelpers';
48

59
describe('Access Control', () => {
610
beforeEach(async () => {
711
await device.launchApp({ newInstance: true });
812
});
913
['genericPassword', 'internetCredentials'].forEach((type) => {
14+
it(
15+
':android:should save and retrieve username and password with passcode - ' +
16+
type,
17+
async () => {
18+
await expect(element(by.text('Keychain Example'))).toExist();
19+
await element(by.id('usernameInput')).typeText('testUsernamePasscode');
20+
await element(by.id('passwordInput')).typeText('testPasswordPasscode');
21+
// Hide keyboard
22+
await element(by.text('Keychain Example')).tap();
23+
await element(by.text('Passcode')).tap();
24+
25+
await expect(element(by.text('Save'))).toBeVisible();
26+
27+
await element(by.text('Save')).tap();
28+
await enterPasscode();
29+
// Hide keyboard if open
30+
await element(by.text('Keychain Example')).tap();
31+
await waitFor(element(by.text(/^Credentials saved! .*$/)))
32+
.toExist()
33+
.withTimeout(4000);
34+
35+
await waitForAuthValidity();
36+
await element(by.text('Load')).tap();
37+
await enterPasscode();
38+
// Hide keyboard if open
39+
await element(by.text('Keychain Example')).tap();
40+
await matchLoadInfo(
41+
'testUsernamePasscode',
42+
'testPasswordPasscode',
43+
'KeystoreAESGCM'
44+
);
45+
}
46+
);
1047
it(
1148
' should save and retrieve username and password with biometrics - ' +
1249
type,
@@ -29,23 +66,17 @@ describe('Access Control', () => {
2966
}
3067

3168
await expect(element(by.text('Save'))).toBeVisible();
32-
if (device.getPlatform() === 'android') {
33-
setTimeout(() => {
34-
cp.spawnSync('adb', ['-e', 'emu', 'finger', 'touch', '1']);
35-
}, 1000);
36-
}
3769
await element(by.text('Save')).tap();
70+
await enterBiometrics();
71+
3872
await waitFor(element(by.text(/^Credentials saved! .*$/)))
3973
.toExist()
4074
.withTimeout(3000);
41-
// Biometric prompt is not available in the IOS simulator
42-
// https://github.com/oblador/react-native-keychain/issues/340
43-
if (device.getPlatform() === 'android') {
44-
setTimeout(() => {
45-
cp.spawnSync('adb', ['-e', 'emu', 'finger', 'touch', '1']);
46-
}, 1000);
47-
}
75+
76+
await waitForAuthValidity();
4877
await element(by.text('Load')).tap();
78+
await enterBiometrics();
79+
4980
await matchLoadInfo('testUsernameBiometrics', 'testPasswordBiometrics');
5081
}
5182
);
@@ -58,57 +89,12 @@ describe('Access Control', () => {
5889
await expect(
5990
element(by.text('hasGenericPassword: true'))
6091
).toBeVisible();
61-
// Biometric prompt is not available in the IOS simulator
62-
// https://github.com/oblador/react-native-keychain/issues/340
63-
if (device.getPlatform() === 'android') {
64-
setTimeout(() => {
65-
cp.spawnSync('adb', ['-e', 'emu', 'finger', 'touch', '1']);
66-
}, 1000);
67-
}
6892
await element(by.text('Load')).tap();
93+
await enterBiometrics();
6994
await matchLoadInfo('testUsernameBiometrics', 'testPasswordBiometrics');
7095
}
7196
);
7297

73-
it(
74-
':android:should save and retrieve username and password with passcode - ' +
75-
type,
76-
async () => {
77-
await expect(element(by.text('Keychain Example'))).toExist();
78-
await element(by.id('usernameInput')).typeText('testUsernamePasscode');
79-
await element(by.id('passwordInput')).typeText('testPasswordPasscode');
80-
// Hide keyboard
81-
await element(by.text('Keychain Example')).tap();
82-
await element(by.text('Passcode')).tap();
83-
84-
await expect(element(by.text('Save'))).toBeVisible();
85-
setTimeout(() => {
86-
cp.spawnSync('adb', ['shell', 'input', 'text', '1111']);
87-
}, 1500);
88-
setTimeout(() => {
89-
cp.spawnSync('adb', ['shell', 'input', 'keyevent', '66']);
90-
}, 3500);
91-
await element(by.text('Save')).tap();
92-
await waitFor(element(by.text(/^Credentials saved! .*$/)))
93-
.toExist()
94-
.withTimeout(4000);
95-
96-
setTimeout(() => {
97-
cp.spawnSync('adb', ['shell', 'input', 'text', '1111']);
98-
}, 1500);
99-
setTimeout(() => {
100-
cp.spawnSync('adb', ['shell', 'input', 'keyevent', '66']);
101-
}, 3000);
102-
103-
await element(by.text('Load')).tap();
104-
await matchLoadInfo(
105-
'testUsernamePasscode',
106-
'testPasswordPasscode',
107-
'KeystoreAESGCM'
108-
);
109-
}
110-
);
111-
11298
it(
11399
'should save and retrieve username and password without biometrics - ' +
114100
type,

KeychainExample/e2e/testCases/storageTypesTest.spec.js

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { by, device, element, expect, waitFor } from 'detox';
22
import { matchLoadInfo } from '../utils/matchLoadInfo';
3-
import cp from 'child_process';
3+
import { enterBiometrics, waitForAuthValidity } from '../utils/authHelpers';
44

55
describe(':android:Storage Types', () => {
66
beforeEach(async () => {
@@ -44,17 +44,14 @@ describe(':android:Storage Types', () => {
4444
await element(by.text('AES_GCM')).tap();
4545

4646
await expect(element(by.text('Save'))).toBeVisible();
47-
setTimeout(() => {
48-
cp.spawnSync('adb', ['-e', 'emu', 'finger', 'touch', '1']);
49-
}, 1000);
5047
await element(by.text('Save')).tap();
48+
await enterBiometrics();
5149
await waitFor(element(by.text(/^Credentials saved! .*$/)))
5250
.toExist()
5351
.withTimeout(3000);
54-
setTimeout(() => {
55-
cp.spawnSync('adb', ['-e', 'emu', 'finger', 'touch', '1']);
56-
}, 1000);
52+
await waitForAuthValidity();
5753
await element(by.text('Load')).tap();
54+
await enterBiometrics();
5855
await matchLoadInfo(
5956
'testUsernameAESGCM',
6057
'testPasswordAESGCM',
@@ -110,12 +107,12 @@ describe(':android:Storage Types', () => {
110107
await element(by.text('Save')).tap();
111108
await waitFor(element(by.text(/^Credentials saved! .*$/)))
112109
.toExist()
113-
.withTimeout(3000);
114-
setTimeout(() => {
115-
cp.spawnSync('adb', ['-e', 'emu', 'finger', 'touch', '1']);
116-
}, 1000);
110+
.withTimeout(5000);
117111
await element(by.text('Load')).tap();
118-
await expect(element(by.text(/^Credentials loaded! .*$/))).toExist();
112+
await enterBiometrics();
113+
await waitFor(element(by.text(/^Credentials loaded! .*$/)))
114+
.toExist()
115+
.withTimeout(5000);
119116
await matchLoadInfo(
120117
'testUsernameRSA',
121118
'testPasswordRSA',
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { device } from 'detox';
2+
import cp from 'child_process';
3+
4+
// Wait for 5 seconds to ensure auth validity period has expired
5+
export const waitForAuthValidity = async () => {
6+
await new Promise((resolve) => setTimeout(resolve, 5500)); // Added 500ms buffer
7+
};
8+
9+
export const enterBiometrics = async () => {
10+
// Biometric prompt is not available in the IOS simulator
11+
// https://github.com/oblador/react-native-keychain/issues/340
12+
if (device.getPlatform() === 'android') {
13+
await new Promise((resolve) => setTimeout(resolve, 1000));
14+
cp.spawnSync('adb', ['-e', 'emu', 'finger', 'touch', '1']);
15+
await new Promise((resolve) => setTimeout(resolve, 500));
16+
}
17+
};
18+
19+
export const enterPasscode = async () => {
20+
if (device.getPlatform() === 'android') {
21+
await new Promise((resolve) => setTimeout(resolve, 1500));
22+
cp.spawnSync('adb', ['shell', 'input', 'text', '1111']);
23+
await new Promise((resolve) => setTimeout(resolve, 2000));
24+
cp.spawnSync('adb', ['shell', 'input', 'keyevent', '66']);
25+
await new Promise((resolve) => setTimeout(resolve, 1500));
26+
}
27+
};

KeychainExample/e2e/utils/matchLoadInfo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ export const matchLoadInfo = async (
2121
regexPattern += '.*$';
2222
const regex = new RegExp(regexPattern);
2323
await waitFor(element(by.text(regex)))
24-
.toBeVisible()
24+
.toExist()
2525
.withTimeout(3000);
2626
};

0 commit comments

Comments
 (0)