diff --git a/.github/workflows/e2e-android-self.yml b/.github/workflows/e2e-android-self.yml
new file mode 100644
index 0000000000..113d6e1d6f
--- /dev/null
+++ b/.github/workflows/e2e-android-self.yml
@@ -0,0 +1,44 @@
+name: Detox E2E Android (self-hosted)
+
+on:
+ push:
+ paths:
+ - packages/mobile/**
+ - packages/backend/**
+ - packages/state-manager/**
+ - .github/workflows/e2e-android-self.yml
+
+jobs:
+ detox-android-self-hosted:
+ timeout-minutes: 10
+ runs-on: [self-hosted, macOS, ARM64, android]
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install dependencies
+ run: |
+ npm i
+ npm run lerna bootstrap --scope @quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/mobile,backend-bundle
+
+ - name: Pull binaries
+ run: |
+ git lfs install --force
+ git lfs pull
+
+ - name: Pass local config
+ run : |
+ cat << EOF >> packages/mobile/android/local.properties
+ ndk.path=/Users/quiet/Library/Android/sdk/ndk/25.1.8937393
+ EOF
+
+ - name: Build Detox
+ run: |
+ export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home
+ cd packages/mobile
+ detox build -c android.emu.debug.ci
+
+ - name: Run basic tests
+ run: |
+ cd packages/mobile
+ detox test starter -c android.emu.debug.ci
\ No newline at end of file
diff --git a/.github/workflows/e2e-android.yml b/.github/workflows/e2e-android.yml
index 4481bdc54d..08a9ee19de 100644
--- a/.github/workflows/e2e-android.yml
+++ b/.github/workflows/e2e-android.yml
@@ -1,40 +1,70 @@
-name: E2E Android
+name: Detox E2E Android
-on:
- push:
- paths:
- - packages/mobile/**
- - packages/backend/**
- - packages/state-manager/**
- - .github/workflows/e2e-android.yml
+on: workflow_dispatch
jobs:
detox-android:
- timeout-minutes: 10
- runs-on: [self-hosted, macOS, ARM64, android]
+ timeout-minutes: 25
+ runs-on: [macos-latest-xlarge]
steps:
- uses: actions/checkout@v4
+
+ - uses: actions/setup-node@master
+ with:
+ node-version: 18.12.1
- name: Install dependencies
run: |
- npm i
- npm run lerna bootstrap --scope @quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/mobile,backend-bundle
+ npm ci
+ npm run lerna bootstrap -- --scope=\'{@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/mobile,backend-bundle}\'
- name: Pull binaries
run: |
- git lfs install
+ git lfs install --force
git lfs pull
- - name: Pass local config
- run : |
- cat << EOF >> packages/mobile/android/local.properties
- ndk.path=/Users/quiet/Library/Android/sdk/ndk/25.1.8937393
- EOF
+ # see: https://stackoverflow.com/questions/67264212/android-emulator-crash-when-start-hvf-error-hv-error
+ - name: Create qemu entitlements
+ run: |
+ {
+ echo ''
+ echo ''
+ echo ''
+ echo ''
+ echo ' com.apple.security.hypervisor'
+ echo ' '
+ echo ''
+ echo ''
+ } >> $ANDROID_HOME/emulator/qemu/darwin-aarch64/entitlements.xml
+
+ - name: Re-sign qemu binary
+ run: |
+ cd $ANDROID_HOME/emulator/qemu/darwin-aarch64
+ codesign -s - --entitlements entitlements.xml --force qemu-system-aarch64 --verbose
+
+ - name: Install SDK image
+ run: yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --install 'system-images;android-34;google_apis;arm64-v8a'
+
+ - name: Create AVD
+ run: $ANDROID_HOME/cmdline-tools/latest/bin/avdmanager create avd -n emulator_ci -k 'system-images;android-34;google_apis;arm64-v8a' -d 'pixel_7'
+
+ - name: Boot AVD
+ run: $ANDROID_HOME/emulator/emulator -avd emulator_ci
+
+ - name: Install pm2
+ run: npm install pm2@latest -g
+
+ - name: Start metro
+ run: |
+ cd packages/mobile
+ pm2 --name METRO start npm -- start
+
+ - name: Install Detox CLI
+ run: npm install detox-cli --global
- name: Build Detox
run: |
- export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home
cd packages/mobile
detox build -c android.emu.debug.ci
@@ -42,3 +72,17 @@ jobs:
run: |
cd packages/mobile
detox test starter -c android.emu.debug.ci
+
+ - name: Stop metro
+ run: pm2 stop METRO
+
+ - name: Take screenshot
+ if: always()
+ run: | # TODO
+
+ - name: Upload screenshot
+ if: always()
+ uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
+ with:
+ name: screenshot.png
+ path: screenshot.png
diff --git a/.github/workflows/e2e-crossplatform.yml b/.github/workflows/e2e-crossplatform.yml
index b45d5caddb..e5e629a763 100644
--- a/.github/workflows/e2e-crossplatform.yml
+++ b/.github/workflows/e2e-crossplatform.yml
@@ -8,6 +8,7 @@ on:
- packages/state-manager/**
- packages/identity/**
- packages/common/**
+ - packages/e2e-tests/**
jobs:
mac:
diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml
index 34ad277d79..40e4a43dc7 100644
--- a/.github/workflows/e2e-ios.yml
+++ b/.github/workflows/e2e-ios.yml
@@ -1,28 +1,27 @@
-name: E2E iOS
+name: Detox E2E iOS
-on:
- push:
- paths:
- - packages/mobile/**
- - packages/backend/**
- - packages/state-manager/**
+on: workflow_dispatch
jobs:
detox-ios:
- timeout-minutes: 10
- runs-on: [self-hosted, macOS, ARM64, iOS]
+ timeout-minutes: 25
+ runs-on: [macos-latest-xlarge]
steps:
- uses: actions/checkout@v4
+
+ - uses: actions/setup-node@master
+ with:
+ node-version: 18.12.1
- name: Install dependencies
run: |
- npm i
- npm run lerna bootstrap --scope @quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/mobile,backend-bundle
+ npm ci
+ npm run lerna bootstrap -- --scope=\'{@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/mobile,backend-bundle}\'
- name: Pull binaries
run: |
- git lfs install
+ git lfs install --force
git lfs pull
- name: Install pods
@@ -30,6 +29,30 @@ jobs:
cd packages/mobile/ios
pod install
+ - name: List simulator devices
+ run: xcrun simctl list devices
+
+ - name: Boot simulator
+ run: |
+ UDID=$(xcrun simctl list devices | grep 'iPhone 15 (' | awk -F '[()]' '{print $2}' | awk 'NR==2')
+ xcrun simctl boot "$UDID"
+
+ - name: Install pm2
+ run: npm install pm2@latest -g
+
+ - name: Start metro
+ run: |
+ cd packages/mobile
+ pm2 --name METRO start npm -- start
+
+ - name: Install Detox CLI
+ run: npm install detox-cli --global
+
+ - name: Install applesimutils
+ run: |
+ brew tap wix/brew
+ brew install applesimutils
+
- name: Build Detox
run: |
cd packages/mobile
@@ -39,3 +62,18 @@ jobs:
run: |
cd packages/mobile
detox test starter -c ios.sim.debug.ci
+
+ - name: Stop metro
+ run: pm2 stop METRO
+
+ - name: Take screenshot
+ if: always()
+ run: |
+ /usr/bin/xcrun simctl io booted screenshot screenshot.png
+
+ - name: Upload screenshot
+ if: always()
+ uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
+ with:
+ name: screenshot.png
+ path: screenshot.png
diff --git a/.github/workflows/e2e-win.yml b/.github/workflows/e2e-win.yml
index 3c6b32f20e..541f73edb7 100644
--- a/.github/workflows/e2e-win.yml
+++ b/.github/workflows/e2e-win.yml
@@ -92,15 +92,7 @@ jobs:
max_attempts: 3
shell: bash
command: cd packages/e2e-tests && npm run test oneClient.test.ts
-
- - name: Run multiple clients test
- uses: nick-fields/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2.9.0
- with:
- timeout_minutes: 30
- max_attempts: 3
- shell: bash
- command: cd packages/e2e-tests && npm run test multipleClients.test.ts
-
+
- name: Run user profile test
uses: nick-fields/retry@v2
with:
@@ -108,11 +100,20 @@ jobs:
max_attempts: 3
shell: bash
command: cd packages/e2e-tests && npm run test userProfile.test.ts
-
+
- name: Run invitation link test - Includes 2 separate application clients
uses: nick-fields/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2.9.0
with:
- timeout_minutes: 25
+ timeout_minutes: 10
max_attempts: 3
shell: bash
command: cd packages/e2e-tests && npm run test invitationLink.test.ts
+
+
+ - name: Run multiple clients test
+ uses: nick-fields/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2.9.0
+ with:
+ timeout_minutes: 30
+ max_attempts: 3
+ shell: bash
+ command: cd packages/e2e-tests && npm run test multipleClients.test.ts
\ No newline at end of file
diff --git a/packages/e2e-tests/src/selectors.ts b/packages/e2e-tests/src/selectors.ts
index ff1325c8f4..bd865d4f41 100644
--- a/packages/e2e-tests/src/selectors.ts
+++ b/packages/e2e-tests/src/selectors.ts
@@ -51,12 +51,12 @@ export class App {
console.log('App closed', this.buildSetup.dataDir)
}
- async cleanup() {
+ async cleanup(force: boolean = false) {
console.log(`Performing app cleanup`, this.buildSetup.dataDir)
if (this.isOpened) {
throw new Error(`App with dataDir ${this.buildSetup.dataDir} is still open, close before cleaning up!`)
}
- this.buildSetup.clearDataDir()
+ this.buildSetup.clearDataDir(force)
}
get saveStateButton() {
diff --git a/packages/e2e-tests/src/tests/invitationLink.test.ts b/packages/e2e-tests/src/tests/invitationLink.test.ts
index 472780f041..321bc77489 100644
--- a/packages/e2e-tests/src/tests/invitationLink.test.ts
+++ b/packages/e2e-tests/src/tests/invitationLink.test.ts
@@ -26,6 +26,9 @@ describe('New user joins using invitation link while having app opened', () => {
beforeAll(async () => {
ownerApp = new App()
guestApp = new App({ defaultDataDir: true })
+ if (process.platform === 'win32') {
+ await guestApp.cleanup(true)
+ }
})
beforeEach(async () => {
@@ -143,7 +146,7 @@ describe('New user joins using invitation link while having app opened', () => {
const copiedCode = url.hash.substring(1)
expect(() => parseInvitationCode(copiedCode)).not.toThrow()
const data = parseInvitationCode(copiedCode)
- const commandFull = `${command[process.platform as SupportedPlatformDesktop]} "${composeInvitationDeepUrl(data)}"`
+ const commandFull = `${command[process.platform as SupportedPlatformDesktop]} ${process.platform === 'win32' ? '""' : ''} "${composeInvitationDeepUrl(data)}"`
console.log(`Calling ${commandFull}`)
execSync(commandFull)
console.log('Guest opened invitation link')
diff --git a/packages/e2e-tests/src/utils.ts b/packages/e2e-tests/src/utils.ts
index 363f639d8b..c167c02a3c 100644
--- a/packages/e2e-tests/src/utils.ts
+++ b/packages/e2e-tests/src/utils.ts
@@ -54,9 +54,9 @@ export class BuildSetup {
}
private getBinaryLocation() {
- console.log('filename', this.fileName)
switch (process.platform) {
case 'linux':
+ console.log('filename', this.fileName)
return `${__dirname}/../Quiet/${this.fileName ? this.fileName : BuildSetup.getEnvFileName()}`
case 'win32':
return `${process.env.LOCALAPPDATA}\\Programs\\@quietdesktop\\Quiet.exe`
@@ -234,8 +234,8 @@ export class BuildSetup {
await this.driver?.close()
}
- public clearDataDir() {
- if (process.env.IS_CI === 'true') {
+ public clearDataDir(force: boolean = false) {
+ if (process.env.IS_CI === 'true' && !force) {
console.warn('Not deleting data directory because we are running in CI')
return
}
diff --git a/packages/mobile/.detoxrc.js b/packages/mobile/.detoxrc.js
index a03e86f3a9..8ac7d9d0ce 100644
--- a/packages/mobile/.detoxrc.js
+++ b/packages/mobile/.detoxrc.js
@@ -77,7 +77,7 @@ module.exports = {
emulator_ci: {
type: 'android.emulator',
device: {
- avdName: 'Pixel_7_API_31',
+ avdName: 'emulator_ci',
},
},
},
diff --git a/packages/mobile/e2e/utils/consts/timeouts.js b/packages/mobile/e2e/utils/consts/timeouts.js
index f4bcf26440..9bdfda887e 100644
--- a/packages/mobile/e2e/utils/consts/timeouts.js
+++ b/packages/mobile/e2e/utils/consts/timeouts.js
@@ -1,3 +1,3 @@
export const BASIC = 5000
export const LONG = 20000
-export const STARTUP = 90000
+export const STARTUP = 120000
diff --git a/packages/mobile/ios/Podfile b/packages/mobile/ios/Podfile
index 43c87c263b..de8795aada 100644
--- a/packages/mobile/ios/Podfile
+++ b/packages/mobile/ios/Podfile
@@ -1,6 +1,6 @@
require_relative '../node_modules/react-native/scripts/react_native_pods'
-platform :ios, '17.4'
+platform :ios, '17.1'
target 'Quiet' do
config = use_native_modules!
diff --git a/packages/mobile/ios/Podfile.lock b/packages/mobile/ios/Podfile.lock
index e550809b7d..01c21f974e 100644
--- a/packages/mobile/ios/Podfile.lock
+++ b/packages/mobile/ios/Podfile.lock
@@ -1393,6 +1393,6 @@ SPEC CHECKSUMS:
Tor: 39dc71bf048312e202608eb499ca5c74e841b503
Yoga: 13c8ef87792450193e117976337b8527b49e8c03
-PODFILE CHECKSUM: 811e75c5d23ebd5b5f3e16c6dd4bae230db730d0
+PODFILE CHECKSUM: eed49772dde039b0723324c813c83dd4c1af35f7
-COCOAPODS: 1.14.3
+COCOAPODS: 1.15.2
diff --git a/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj b/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj
index 489dc64d1f..c101495008 100644
--- a/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj
+++ b/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj
@@ -5300,7 +5300,7 @@
INFOPLIST_FILE = Quiet/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Quiet;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
- IPHONEOS_DEPLOYMENT_TARGET = 17.4;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -5396,7 +5396,7 @@
INFOPLIST_FILE = Quiet/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Quiet;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
- IPHONEOS_DEPLOYMENT_TARGET = 17.4;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
diff --git a/packages/mobile/ios/Quiet/AppDelegate.m b/packages/mobile/ios/Quiet/AppDelegate.m
index cb92136270..ee2c2c74ac 100644
--- a/packages/mobile/ios/Quiet/AppDelegate.m
+++ b/packages/mobile/ios/Quiet/AppDelegate.m
@@ -51,11 +51,14 @@ - (void) initWebsocketConnection {
* Delay used below can't cause any race condition as websocket won't connect until data server starts listening anyway.
*/
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
- NSTimeInterval delayInSeconds = 5;
- dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
- dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
- [[self.bridge moduleForName:@"CommunicationModule"] sendDataPortWithPort:self.dataPort socketIOSecret:self.socketIOSecret];
- });
+ NSArray *intervals = @[@5, @15, @30, @60, @90];
+ for (NSNumber *interval in intervals) {
+ NSTimeInterval delayInSeconds = [interval doubleValue];
+ dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
+ dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
+ [[self.bridge moduleForName:@"CommunicationModule"] sendDataPortWithPort:self.dataPort socketIOSecret:self.socketIOSecret];
+ });
+ }
});
}
diff --git a/packages/mobile/ios/Quiet/Info.plist b/packages/mobile/ios/Quiet/Info.plist
index 51846dee44..1ba9e97f4a 100644
--- a/packages/mobile/ios/Quiet/Info.plist
+++ b/packages/mobile/ios/Quiet/Info.plist
@@ -36,26 +36,26 @@
CFBundleVersion
371
ITSAppUsesNonExemptEncryption
-
+
LSRequiresIPhoneOS
-
+
NSAppTransportSecurity
NSAllowsArbitraryLoads
-
+
NSAllowsLocalNetworking
-
+
NSExceptionDomains
localhost
NSExceptionAllowsInsecureHTTPLoads
-
+
NSLocationWhenInUseUsageDescription
-
+
UIAppFonts
Rubik-Black.ttf
@@ -74,7 +74,7 @@
Rubik-SemiBoldItalic.ttf
UIBackgroundModes
-
+
UILaunchStoryboardName
LaunchScreen
UIRequiredDeviceCapabilities
@@ -88,6 +88,6 @@
UIInterfaceOrientationLandscapeRight
UIViewControllerBasedStatusBarAppearance
-
+
diff --git a/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.ts b/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.ts
index 7b7195b2be..3a7c001360 100644
--- a/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.ts
+++ b/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.ts
@@ -2,7 +2,7 @@ import { delay, put, select } from 'typed-redux-saga'
import { initSelectors } from '../../init.selectors'
import { initActions } from '../../init.slice'
-const WEBSOCKET_CONNECTION_DELAY = 5000
+const WEBSOCKET_CONNECTION_DELAY = 15000
export function* restoreConnectionSaga(): Generator {
// Give the worker time to init websocket connection
diff --git a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts
index 5a4d63ce1b..6cf1f49ef3 100644
--- a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts
+++ b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts
@@ -10,6 +10,9 @@ import { eventChannel } from 'redux-saga'
export function* startConnectionSaga(
action: PayloadAction['payload']>
): Generator {
+ const isAlreadyConnected = yield* select(initSelectors.isWebsocketConnected)
+ if (isAlreadyConnected) return
+
const { dataPort, socketIOSecret } = action.payload
console.log('WEBSOCKET', 'Entered start connection saga', dataPort)