Skip to content

Commit

Permalink
ci: fix e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
DorianMazur committed Aug 26, 2024
1 parent a6cc5e4 commit b3d0ed9
Show file tree
Hide file tree
Showing 15 changed files with 256 additions and 68 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/e2e_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ jobs:
fail-fast: false
matrix:
api-level:
- 24
- 26
- 29
- 32
- 33
- 34

steps:
- name: Enable KVM group perms
Expand Down
2 changes: 1 addition & 1 deletion KeychainExample/.detoxrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ module.exports = {
type: 'ios.simulator',
headless: Boolean(process.env.CI),
device: {
type: 'iPhone 15',
type: 'iPhone 15 Pro',
},
},
attached: {
Expand Down
8 changes: 1 addition & 7 deletions KeychainExample/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,7 @@ export default class KeychainExample extends Component {
securityLevel: this.state.securityLevel,
storage: this.state.storage,
}
)
.catch((err) => {
console.log(err);
})
.then((result) => {
console.log(result);
});
);

const end = new Date();

Expand Down
2 changes: 1 addition & 1 deletion KeychainExample/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,4 @@ RUBY VERSION
ruby 3.3.0p0

BUNDLED WITH
2.5.4
2.5.17
104 changes: 104 additions & 0 deletions KeychainExample/e2e/testCases/biometricsAccessControlTest.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { by, device, element, expect } from 'detox';
import path from 'path';
import cp from 'child_process';

const enrollBiometric = async () => {
if (device.getPlatform() === 'android') {
const script = path.resolve(
__dirname,
'../utils/enrollFingerprintAndroid.sh'
);
const result = cp.spawnSync('sh', [script], {
stdio: 'inherit',
});

// Check for errors
if (result.error) {
console.error('Error executing script:', result.error);
}
} else {
await device.setBiometricEnrollment(true);
}
};

describe('Biometrics Access Control', () => {
beforeAll(async () => {
await enrollBiometric();
});

beforeEach(async () => {
await device.launchApp({ newInstance: true });
});

it('should save and retrieve username and password', async () => {
await expect(element(by.text('Keychain Example'))).toExist();
await element(by.id('usernameInput')).typeText('testUsername');
await element(by.id('passwordInput')).typeText('testPassword');
// Hide keyboard
await element(by.text('Keychain Example')).tap();

if (device.getPlatform() === 'android') {
await element(by.text('Fingerprint')).tap();
await element(by.text('Software')).tap();
} else {
await element(by.text('FaceID')).tap();
}

await expect(element(by.text('Save'))).toBeVisible();
await element(by.text('Save')).tap();
await expect(element(by.text(/^Credentials saved! .*$/))).toBeVisible();
// Biometric prompt is not available in the IOS simulator
// https://github.com/oblador/react-native-keychain/issues/340
if (device.getPlatform() === 'android') {
setTimeout(() => {
cp.spawnSync('adb', ['-e', 'emu', 'finger', 'touch', '1']);
}, 1000);
}
await element(by.text('Load')).tap();
await expect(element(by.text('Credentials loaded!'))).toBeVisible();
await expect(element(by.id('usernameInput'))).toHaveText('testUsername');
await expect(element(by.id('passwordInput'))).toHaveText('testPassword');
});

it('should retrieve username and password after app launch', async () => {
await expect(element(by.text('Keychain Example'))).toExist();
// Biometric prompt is not available in the IOS simulator
// https://github.com/oblador/react-native-keychain/issues/340
if (device.getPlatform() === 'android') {
setTimeout(() => {
cp.spawnSync('adb', ['-e', 'emu', 'finger', 'touch', '1']);
}, 1000);
}
await element(by.text('Load')).tap();
await expect(element(by.text('Credentials loaded!'))).toBeVisible();
await expect(element(by.id('usernameInput'))).toHaveText('testUsername');
await expect(element(by.id('passwordInput'))).toHaveText('testPassword');
});

it(':android:should save and retrieve username and password for hardware security level', async () => {
await expect(element(by.text('Keychain Example'))).toExist();
await element(by.id('usernameInput')).typeText('testUsernameHardware');
await element(by.id('passwordInput')).typeText('testPasswordHardware');
// Hide keyboard
await element(by.text('Keychain Example')).tap();
await element(by.text('Fingerprint')).tap();
await element(by.text('Hardware')).tap();

await expect(element(by.text('Save'))).toBeVisible();
await element(by.text('Save')).tap();
await expect(element(by.text(/^Credentials saved! .*$/))).toBeVisible();

setTimeout(() => {
cp.spawnSync('adb', ['-e', 'emu', 'finger', 'touch', '1']);
}, 1000);

await element(by.text('Load')).tap();
await expect(element(by.text('Credentials loaded!'))).toBeVisible();
await expect(element(by.id('usernameInput'))).toHaveText(
'testUsernameHardware'
);
await expect(element(by.id('passwordInput'))).toHaveText(
'testPasswordHardware'
);
});
});
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { by, device, element, expect } from 'detox';
import cp from 'child_process';

describe('FingerprintTest', () => {
beforeAll(async () => {
await device.launchApp();
describe('None Access Control', () => {
beforeEach(async () => {
await device.launchApp({ newInstance: true });
});

it('should save and retrieve username and password', async () => {
Expand All @@ -12,15 +11,15 @@ describe('FingerprintTest', () => {
await element(by.id('passwordInput')).typeText('testPassword');
// Hide keyboard
await element(by.text('Keychain Example')).tap();
await element(by.text('None')).tap();

if (device.getPlatform() === 'android') {
await element(by.text('Software')).tap();
}

await element(by.text('Fingerprint')).tap();
await element(by.text('Software')).tap();
await expect(element(by.text('Save'))).toBeVisible();
await element(by.text('Save')).tap();
await expect(element(by.text(/^Credentials saved! .*$/))).toBeVisible();
setTimeout(() => {
cp.spawnSync('adb', ['-e', 'emu', 'finger', 'touch', '1']);
}, 1000);
await element(by.text('Load')).tap();
await expect(element(by.text('Credentials loaded!'))).toBeVisible();
await expect(element(by.id('usernameInput'))).toHaveText('testUsername');
Expand All @@ -30,9 +29,6 @@ describe('FingerprintTest', () => {
it('should retrieve username and password after app launch', async () => {
await device.launchApp({ newInstance: true });
await expect(element(by.text('Keychain Example'))).toExist();
setTimeout(() => {
cp.spawnSync('adb', ['-e', 'emu', 'finger', 'touch', '1']);
}, 1000);
await element(by.text('Load')).tap();
await expect(element(by.text('Credentials loaded!'))).toBeVisible();
await expect(element(by.id('usernameInput'))).toHaveText('testUsername');
Expand Down
32 changes: 32 additions & 0 deletions KeychainExample/e2e/utils/enrollFingerprintAndroid.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env bash

DELAY=10
adb shell locksettings set-pin 1111
adb shell am start -a android.settings.SECURITY_SETTINGS
sleep $DELAY
adb shell input tap 274 2300
sleep $DELAY
adb shell input text 1111
adb shell input keyevent 66
sleep $DELAY
adb shell input tap 1100 2700
sleep $DELAY
adb shell input tap 1100 2700
sleep $DELAY
adb -e emu finger touch 1
sleep $DELAY
adb -e emu finger touch 1
sleep $DELAY
adb -e emu finger touch 1
sleep $DELAY
adb shell input keyevent 4
sleep $DELAY
adb shell input keyevent 4
sleep $DELAY
adb shell input keyevent 4
sleep $DELAY
adb shell input keyevent 4
sleep $DELAY
adb shell input keyevent 4
sleep $DELAY
adb shell input keyevent 4
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
31 changes: 25 additions & 6 deletions KeychainExample/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@ PODS:
- React-logger (= 0.74.5)
- React-perflogger (= 0.74.5)
- React-utils (= 0.74.5)
- ReactNativeHost (0.4.11):
- ReactNativeHost (0.4.12):
- DoubleConversion
- glog
- RCT-Folly (= 2024.01.01.00)
Expand All @@ -1141,12 +1141,31 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- ReactTestApp-DevSupport (3.9.0):
- ReactTestApp-DevSupport (3.9.7):
- React-Core
- React-jsi
- ReactTestApp-Resources (1.0.0-dev)
- RNKeychain (8.2.0):
- DoubleConversion
- glog
- RCT-Folly (= 2024.01.01.00)
- RCTRequired
- RCTTypeSafety
- React-Codegen
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
- React-utils
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- SocketRocket (0.7.0)
- Yoga (0.0.0)

Expand Down Expand Up @@ -1380,13 +1399,13 @@ SPEC CHECKSUMS:
React-runtimescheduler: 057a40b536cab47481d2a2b4f5e93d7eb0b285de
React-utils: b4b4a8bdd58632a9ec314071fbd4a642d984718e
ReactCommon: bfd464c8f774a0acb93cf2a45afa80b763431f1b
ReactNativeHost: 4983b4bfdef9f1a67f523b633909df57a9c1dae9
ReactTestApp-DevSupport: f781365c3ae6377dcfa2affe319ae66e0fb20777
ReactNativeHost: 2bc85a4cc8f2e7e7fef5e551d4adb9c90757859f
ReactTestApp-DevSupport: 74676edd899013becce4eaecc5eabba1fc51e26e
ReactTestApp-Resources: 857244f3a23f2b3157b364fa06cf3e8866deff9c
RNKeychain: bfef0ca723289eb7693835308b5b93432eb7ea5d
RNKeychain: 70a32d9f845cf928ab367ad9c0a9050eb3d5206c
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
Yoga: 950bbfd7e6f04790fdb51149ed51df41f329fcc8

PODFILE CHECKSUM: 8daf094af2120901c020904ed4ae6b82bfbd5642

COCOAPODS: 1.14.3
COCOAPODS: 1.15.2
18 changes: 9 additions & 9 deletions KeychainExample/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios --simulator 'iPhone 15 Pro'",
"build:android": "npm run mkdist && react-native bundle --entry-file index.js --platform android --dev true --bundle-output dist/main.android.jsbundle --assets-dest dist/res",
"build:ios": "npm run mkdist && react-native bundle --entry-file index.js --platform ios --dev true --bundle-output dist/main.ios.jsbundle --assets-dest dist",
"ios": "react-native run-ios --simulator 'iPhone 15 Pro' --mode Release",
"build:android": "npm run mkdist && react-native bundle --entry-file index.js --platform android --dev false --bundle-output dist/main.android.jsbundle --assets-dest dist/res",
"build:ios": "npm run mkdist && react-native bundle --entry-file index.js --platform ios --dev false --bundle-output dist/main.ios.jsbundle --assets-dest dist/assets",
"mkdist": "node -e \"require('node:fs').mkdirSync('dist', { recursive: true, mode: 0o755 })\"",
"start": "react-native start",
"test:android": "yarn test:android:build && yarn test:android:run",
Expand All @@ -18,23 +18,23 @@
},
"dependencies": {
"react": "18.2.0",
"react-native": "0.74.5",
"react-native": "^0.74.5",
"react-native-segmented-control-tab": "^4.0.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native/babel-preset": "^0.74.0",
"@react-native/metro-config": "^0.74.0",
"@react-native/typescript-config": "^0.74.0",
"@react-native/babel-preset": "^0.74.5",
"@react-native/metro-config": "^0.74.5",
"@react-native/typescript-config": "^0.74.5",
"@rnx-kit/polyfills": "^0.1.1",
"@types/react": "^18.2.0",
"babel-plugin-module-resolver": "^5.0.2",
"detox": "^20.25.4",
"detox": "^20.25.6",
"jest": "^29.7.0",
"jest-junit": "^16.0.0",
"react-native-test-app": "^3.9.0"
"react-native-test-app": "^3.9.7"
},
"jest": {
"preset": "react-native"
Expand Down
25 changes: 24 additions & 1 deletion RNKeychain.podspec
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require 'json'

version = JSON.parse(File.read('package.json'))["version"]
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'

Pod::Spec.new do |s|

Expand All @@ -16,6 +18,27 @@ Pod::Spec.new do |s|
s.source = { :git => "https://github.com/oblador/react-native-keychain.git", :tag => "v#{s.version}" }
s.source_files = 'ios/RNKeychainManager/**/*.{h,m}'
s.preserve_paths = "**/*.js"
s.dependency 'React-Core'

if respond_to?(:install_modules_dependencies, true)
# React Native Core dependency
install_modules_dependencies(s)
else
s.dependency "React-Core"

# Don't install the dependencies when we run `pod install` in the old architecture.
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
s.pod_target_xcconfig = {
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
}
s.dependency "React-Codegen"
s.dependency "RCT-Folly"
s.dependency "RCTRequired"
s.dependency "RCTTypeSafety"
s.dependency "ReactCommon/turbomodule/core"
end
end

end
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ dependencies {
if (isNewArchitectureEnabled()) {
react {
jsRootDir = file("../src/")
libraryName = "Keychain"
libraryName = "RNKeychain"
codegenJavaPackageName = "com.oblador.keychain"
}
}
Expand Down
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@
"engines": {
"node": ">=18"
},
"codegenConfig": {
"name": "RNKeychainSpec",
"type": "modules",
"jsSrcsDir": "src",
"android": {
"javaPackageName": "com.oblador.keychain"
}
},
"react-native-builder-bob": {
"source": "src",
"output": "lib",
Expand Down
Loading

0 comments on commit b3d0ed9

Please sign in to comment.