1
0
Code Issues Pull Requests Packages Projects Releases Wiki Activity GitHub Gitee

feat: project init

This commit is contained in:
steven 2023-03-15 23:27:25 +08:00
parent 992b17d25a
commit 92675ed37c
43 changed files with 6448 additions and 546 deletions

232
.github/workflows/e2e_android.yml vendored Normal file
View File

@ -0,0 +1,232 @@
name: Testing E2E Android
on:
workflow_dispatch:
inputs:
clearCaches:
description: "Clear workflow caches where possible"
required: false
type: string
pull_request:
branches:
- '**'
paths-ignore:
- 'docs/**'
- 'website/**'
- '.spellcheck.dict.txt'
# - '**/*.md'
push:
branches:
- main
- v14-release
paths-ignore:
- 'docs/**'
- 'website/**'
- '.spellcheck.dict.txt'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
android:
name: Android
runs-on: macos-12
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
# Refactor to make these dynamic with a low/high bracket only on schedule, not push
# For now this is just the fastest combo (api/arch/target/snapshot-warm-time) based on testing
api-level: [30]
arch: [x86_64]
target: [google_apis]
first-boot-delay: [600]
# This is useful for benchmarking, do 0, 1, 2, etc (up to 256 max job-per-matrix limit) for averages
iteration: [0]
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
EMULATOR_COMMAND: "-avd TestingAVD -noaudio -gpu swiftshader_indirect -camera-back none -no-snapshot -no-window -no-boot-anim -nojni -memory 2048 -timezone 'Europe/London' -cores 2"
EMULATOR_EXECUTABLE: qemu-system-x86_64-headless
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 50
# Set up tool versions
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Configure JDK 1.11
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'
# Set path variables needed for caches
- name: Set workflow variables
id: workflow-variables
run: |
echo "metro-cache=$HOME/.metro" >> $GITHUB_OUTPUT
echo "yarn-cache-dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
echo "tempdir=$TMPDIR" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Yarn Cache
id: yarn-cache
with:
path: ${{ steps.workflow-variables.outputs.yarn-cache-dir }}
key: ${{ runner.os }}-yarn-v1-${{ hashFiles('yarn.lock') }}
restore-keys: ${{ runner.os }}-yarn-v1
- name: Yarn Install
uses: nick-invision/retry@v2
with:
timeout_minutes: 10
retry_wait_seconds: 60
max_attempts: 3
command: DETOX_DISABLE_POSTINSTALL=1 yarn --no-audit --prefer-offline
- name: Cache pushy Emulator
uses: actions/cache@v3
with:
path: ~/.cache/pushy/emulators
key: pushy-emulators-v1-${{ github.run_id }}
restore-keys: pushy-emulators-v1
- name: Start pushy Emulator
run: yarn tests:emulator:start-ci
- uses: actions/cache@v3
name: Gradle Cache
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-v1-${{ hashFiles('**/*.gradle*') }}
restore-keys: ${{ runner.os }}-gradle-v1
# This appears to be 'Cache Size: ~1230 MB (1290026823 B)' based on watching action logs
# Repo limit is 10GB; branch caches are independent; branches may read default branch cache.
# We don't want branches to evict main branch snapshot, so save on main, read-only all else
- name: AVD cache
uses: actions/cache@v3
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-${{ matrix.api-level }}-${{ matrix.arch }}-${{matrix.target}}-v1-${{ github.event.inputs.clearCaches }}
restore-keys: |
avd-${{ matrix.api-level }}-${{ matrix.arch }}-${{matrix.target}}-v1
- name: Clear Caches Optionally
if: "${{ github.event.inputs.clearCaches != '' }}"
shell: bash
run: |
du -sk ~/.gradle
du -sk ~/.android
rm -fr ~/.gradle
rm -fr ~/.android
du -sk ~/.gradle || echo ~/.gradle is gone
du -sk ~/.android || echo ~/.android is gone
- name: Build Android App
uses: nick-invision/retry@v2
with:
timeout_minutes: 25
retry_wait_seconds: 60
max_attempts: 3
command: yarn build:android-release
- name: Metro Bundler Cache
uses: actions/cache@v3
with:
path: ${{ steps.workflow-variables.outputs.metro-cache }}
key: ${{ runner.os }}-metro-v1-${{ github.run_id }}
restore-keys: ${{ runner.os }}-metro-v1
- name: Pre-fetch Javascript bundle
# Prebuild the bundle so that's fast when the app starts.
run: |
nohup yarn tests:packager:jet-ci &
printf 'Waiting for packager to come online'
until curl --output /dev/null --silent --head --fail http://localhost:8081/status; do
printf '.'
sleep 2
done
echo "Packager is online! Preparing javascript bundle..."
curl --output /dev/null --silent --head --fail "http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&inlineSourceMap=true"
echo "...javascript bundle ready."
- name: AVD Boot and Snapshot Creation
# Only generate a snapshot with a cache miss
# Comment the if out to generate snapshots on branch for performance testing
if: "${{ github.event.inputs.clearCaches != '' || (steps.avd-cache.outputs.cache-hit != 'true' && github.ref == 'refs/heads/main') }}"
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
avd-name: TestingAVD
force-avd-creation: false
target: ${{ matrix.target }}
arch: ${{ matrix.arch }}
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
sdcard-path-or-size: 100M
disable-animations: true
# Give the emulator a little time to run and do first boot stuff before taking snapshot
script: |
$ANDROID_HOME/platform-tools/adb logcat '*:D' -d > adb-snapshot-log.txt
$ANDROID_HOME/platform-tools/adb logcat --clear
echo "Generated AVD snapshot for caching."
# This step is separate so pure install time may be calculated as a step
- name: Emulator Snapshot After Firstboot Warmup
# Only generate a snapshot for saving with a cache miss
# Switch the if statements via comment if generating snapshots for performance testing
# if: matrix.first-boot-delay != '0'
if: "${{ github.event.inputs.clearCaches != '' || (steps.avd-cache.outputs.cache-hit != 'true' && github.ref == 'refs/heads/main') }}"
env:
FIRST_BOOT_DELAY: ${{ matrix.first-boot-delay }}
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
avd-name: TestingAVD
force-avd-creation: false
target: ${{ matrix.target }}
arch: ${{ matrix.arch }}
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
sdcard-path-or-size: 100M
disable-animations: true
# Give the emulator a little time to run and do first boot stuff before taking snapshot
# The zygote restart makes sure zygote has correct heap size as a workaround for android emulator init bug
script: |
$ANDROID_HOME/platform-tools/adb shell su root "setprop ctl.restart zygote"
sleep $FIRST_BOOT_DELAY
$ANDROID_HOME/platform-tools/adb logcat '*:D' -d > adb-warmup-log.txt
$ANDROID_HOME/platform-tools/adb logcat --clear
echo "First boot warmup completed."
- name: Test Tapper
# Run this outside the emulator runner so the emulator runner does not wait on it for cleanup
run: |
nohup sh -c "until false; do $ANDROID_HOME/platform-tools/adb shell input tap 100 800; sleep 0.2; done" &
shell: bash
- name: Detox Tests
uses: reactivecircus/android-emulator-runner@v2
timeout-minutes: 40
with:
api-level: ${{ matrix.api-level }}
avd-name: TestingAVD
force-avd-creation: false
target: ${{ matrix.target }}
arch: ${{ matrix.arch }}
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
sdcard-path-or-size: 100M
disable-animations: true
# Detox uses Espresso to choreograph steps in reaction to UI events, so we need to send a stream of taps.
script: |
$ANDROID_HOME/platform-tools/adb devices
nohup sh -c "$ANDROID_HOME/platform-tools/adb logcat '*:D' > adb-log.txt" &
yarn test:android-release

183
.github/workflows/e2e_ios.yml vendored Normal file
View File

@ -0,0 +1,183 @@
name: Testing E2E iOS
on:
pull_request:
branches:
- '**'
paths-ignore:
- 'docs/**'
- 'website/**'
- '.spellcheck.dict.txt'
- '**/*.md'
push:
branches:
- main
- v14-release
paths-ignore:
- 'docs/**'
- 'website/**'
- '.spellcheck.dict.txt'
# - '**/*.md'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
ios:
name: iOS
runs-on: macos-12
# TODO matrix across APIs, at least 11 and 15 (lowest to highest)
timeout-minutes: 60
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
steps:
# Set up tool versions
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Configure JDK 1.11
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: 'latest-stable'
- uses: actions/checkout@v3
with:
fetch-depth: 50
# Set path variables needed for caches
- name: Set workflow variables
id: workflow-variables
run: |
echo "metro-cache=$HOME/.metro" >> $GITHUB_OUTPUT
echo "xcode-version=$(xcodebuild -version|tail -1|cut -f3 -d' ')" >> $GITHUB_OUTPUT
echo "yarn-cache-dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Yarn Cache
id: yarn-cache
with:
path: ${{ steps.workflow-variables.outputs.yarn-cache-dir }}
key: ${{ runner.os }}-yarn-v1-${{ hashFiles('yarn.lock') }}
restore-keys: ${{ runner.os }}-yarn-v1
- uses: actions/cache@v3
name: Detox Framework Cache
id: detox-cache
with:
path: ~/Library/Detox/ios
key: ${{ runner.os }}-detox-framework-cache-${{ steps.workflow-variables.outputs.xcode-version }}
# Detox is compiled during yarn install, using Xcode, set up cache first
- uses: hendrikmuhs/ccache-action@v1.2
name: Xcode Compile Cache
with:
key: ${{ runner.os }}-v2 # makes a unique key w/related restore key internally
max-size: 1500M
- name: Yarn Install
uses: nick-invision/retry@v2
with:
timeout_minutes: 10
retry_wait_seconds: 60
max_attempts: 3
command: yarn --no-audit --prefer-offline
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3
- name: Update Ruby build tools
uses: nick-invision/retry@v2
with:
timeout_minutes: 2
retry_wait_seconds: 60
max_attempts: 3
command: gem update cocoapods xcodeproj
- uses: actions/cache@v3
name: Cache Pods
id: pods-cache
with:
path: tests/ios/Pods
key: ${{ runner.os }}-pods-v2-${{ hashFiles('tests/ios/Podfile.lock') }}
restore-keys: ${{ runner.os }}-pods-v2
- name: Pod Install
uses: nick-invision/retry@v2
with:
timeout_minutes: 10
retry_wait_seconds: 30
max_attempts: 3
command: yarn tests:ios:pod:install
- name: Cache Firestore Emulator
uses: actions/cache@v3
with:
path: ~/.cache/pushy/emulators
key: pushy-emulators-v1-${{ github.run_id }}
restore-keys: pushy-emulators-v1
- name: Start Firestore Emulator
run: yarn tests:emulator:start-ci
- name: Install brew utilities
uses: nick-invision/retry@v2
with:
timeout_minutes: 5
retry_wait_seconds: 60
max_attempts: 3
command: HOMEBREW_NO_AUTO_UPDATE=1 brew tap wix/brew && HOMEBREW_NO_AUTO_UPDATE=1 brew install applesimutils xcbeautify && applesimutils --list
- name: Build iOS App
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
export CCACHE_SLOPPINESS=clang_index_store,file_stat_matches,include_file_ctime,include_file_mtime,ivfsoverlay,pch_defines,modules,system_headers,time_macros
export CCACHE_FILECLONE=true
export CCACHE_DEPEND=true
export CCACHE_INODECACHE=true
export CCACHE_LIMIT_MULTIPLE=0.95
ccache -s
export SKIP_BUNDLING=1
export RCT_NO_LAUNCH_PACKAGER=1
set -o pipefail
yarn build:ios-release
ccache -s
shell: bash
- name: Metro Bundler Cache
uses: actions/cache@v3
with:
path: ${{ steps.workflow-variables.outputs.metro-cache }}
key: ${{ runner.os }}-metro-v1-${{ github.run_id }}
restore-keys: ${{ runner.os }}-metro-v1
- name: Pre-fetch Javascript bundle
run: |
nohup yarn tests:packager:jet-ci &
printf 'Waiting for packager to come online'
until curl --output /dev/null --silent --head --fail http://localhost:8081/status; do
printf '.'
sleep 2
done
echo "Packager is online! Preparing bundle..."
curl --output /dev/null --silent --head --fail "http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&inlineSourceMap=true"
echo "...javascript bundle ready"
- name: Create Simulator Log
# With a little delay so the detox test below has time to spawn it, missing the first part of boot is fine
# If you boot the simulator separately from detox, some other race fails and detox testee never sends ready to proxy
continue-on-error: true
run: nohup sh -c "sleep 30 && xcrun simctl spawn booted log stream --level debug --style compact > simulator.log 2>&1 &"
- name: Detox Test
timeout-minutes: 30
run: yarn test:ios-release

View File

@ -0,0 +1,10 @@
#!/bin/bash
echo "Running $1 on all running emulators..."
devices=`adb devices`
for device in $devices; do
if [[ "$device" =~ "emulator-" ]]; then
adb -s $device $1
fi
done
echo "All Done."

View File

@ -0,0 +1,13 @@
{
"rules": {
// Database in general is closed. Read/Write to anything but "tests/" will fail.
".read": false,
".write": false,
// ..."tests" node will succeed
"tests": {
".read": true,
".write": true,
}
}
}

39
.github/workflows/scripts/firebase.json vendored Normal file
View File

@ -0,0 +1,39 @@
{
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"functions": {
"predeploy": [
"yarn",
"yarn --prefix \"$RESOURCE_DIR\" build"
],
"source": "functions"
},
"database": {
"rules": "database.rules"
},
"storage": {
"rules": "storage.rules"
},
"emulators": {
"auth": {
"port": 9099
},
"database": {
"port": 9000
},
"firestore": {
"port": 8080
},
"functions": {
"port": 5001
},
"storage": {
"port": 9199
},
"ui": {
"enabled": true
}
}
}

View File

@ -0,0 +1,72 @@
{
"indexes": [
{
"collectionGroup": "firestore",
"queryScope": "COLLECTION",
"fields": [
{
"fieldPath": "a",
"order": "ASCENDING"
},
{
"fieldPath": "b",
"order": "ASCENDING"
}
]
}
],
"fieldOverrides": [
{
"collectionGroup": "collectionGroup",
"fieldPath": "value",
"indexes": [
{
"order": "ASCENDING",
"queryScope": "COLLECTION"
},
{
"order": "DESCENDING",
"queryScope": "COLLECTION"
},
{
"arrayConfig": "CONTAINS",
"queryScope": "COLLECTION"
},
{
"order": "ASCENDING",
"queryScope": "COLLECTION_GROUP"
},
{
"order": "DESCENDING",
"queryScope": "COLLECTION_GROUP"
}
]
},
{
"collectionGroup": "collectionGroup",
"fieldPath": "number",
"indexes": [
{
"order": "ASCENDING",
"queryScope": "COLLECTION"
},
{
"order": "DESCENDING",
"queryScope": "COLLECTION"
},
{
"arrayConfig": "CONTAINS",
"queryScope": "COLLECTION"
},
{
"order": "ASCENDING",
"queryScope": "COLLECTION_GROUP"
},
{
"order": "DESCENDING",
"queryScope": "COLLECTION_GROUP"
}
]
}
]
}

View File

@ -0,0 +1,17 @@
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
match /firestore-bundle-tests/{document=**} {
allow read, write: if true;
}
match /firestore/{document=**} {
allow read, write: if true;
}
match /{path=**}/collectionGroup/{documentId} {
allow read, write: if true;
}
}
}

View File

@ -0,0 +1,10 @@
# Compiled JavaScript files
lib/**/*.js
lib/**/*.js.map
# TypeScript v1 declaration files
typings/
# Node.js dependency directory
node_modules/
yarn.lock

View File

@ -0,0 +1,24 @@
{
"name": "functions",
"scripts": {
"build": "tsc",
"serve": "npm run build && firebase emulators:start --only functions",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "16"
},
"main": "lib/index.js",
"dependencies": {
"firebase-admin": "^11.3.0",
"firebase-functions": "^4.2.1"
},
"devDependencies": {
"firebase-functions-test": "^3.0.0",
"typescript": "^4.9.5"
},
"private": true
}

View File

@ -0,0 +1,13 @@
/*
*
* Testing tools for invertase/react-native-firebase use only.
*
* Copyright (C) 2018-present Invertase Limited <oss@invertase.io>
*
* See License file for more information.
*/
/* eslint-disable global-require */
module.exports = {
SAMPLE_DATA: require('./functions/sample-data'),
};

View File

@ -0,0 +1,12 @@
import * as functions from 'firebase-functions';
// // Start writing Firebase Functions
// // https://firebase.google.com/docs/functions/typescript
//
export const helloWorld = functions.https.onRequest((request, response) => {
functions.logger.info('Hello logs!', { structuredData: true });
response.send('{ "data": "Hello from Firebase!" }');
});
export { testFunctionCustomRegion } from './testFunctionCustomRegion';
export { testFunctionDefaultRegion } from './testFunctionDefaultRegion';

View File

@ -0,0 +1,80 @@
/*
* Testing tools for invertase/react-native-firebase use only.
*
* Copyright (C) 2018-present Invertase Limited <oss@invertase.io>
*
* See License file for more information.
*/
const SAMPLE_DATA: { [key: string]: any } = {
number: 1234,
string: 'acde',
boolean: true,
null: null,
object: {
number: 1234,
string: 'acde',
boolean: true,
null: null,
},
array: [1234, 'acde', true, null],
deepObject: {
array: [1234, 'acde', false, null],
object: {
number: 1234,
string: 'acde',
boolean: true,
null: null,
array: [1234, 'acde', true, null],
},
number: 1234,
string: 'acde',
boolean: true,
null: null,
},
deepArray: [
1234,
'acde',
true,
null,
[1234, 'acde', true, null],
{
number: 1234,
string: 'acde',
boolean: true,
null: null,
array: [1234, 'acde', true, null],
},
],
deepMap: {
number: 123,
string: 'foo',
booleanTrue: true,
booleanFalse: false,
null: null,
list: ['1', 2, true, false],
map: {
number: 123,
string: 'foo',
booleanTrue: true,
booleanFalse: false,
null: null,
},
},
deepList: [
'1',
2,
true,
false,
['1', 2, true, false],
{
number: 123,
string: 'foo',
booleanTrue: true,
booleanFalse: false,
null: null,
},
],
};
export default SAMPLE_DATA;

View File

@ -0,0 +1,14 @@
/*
*
* Testing tools for invertase/react-native-firebase use only.
*
* Copyright (C) 2018-present Invertase Limited <oss@invertase.io>
*
* See License file for more information.
*/
import * as functions from 'firebase-functions';
export const testFunctionCustomRegion = functions
.region('europe-west1')
.https.onCall(() => 'europe-west1');

View File

@ -0,0 +1,70 @@
/*
*
* Testing tools for invertase/react-native-firebase use only.
*
* Copyright (C) 2018-present Invertase Limited <oss@invertase.io>
*
* See License file for more information.
*/
import * as assert from 'assert';
import { FirebaseError } from 'firebase-admin';
import * as functions from 'firebase-functions';
import SAMPLE_DATA from './sample-data';
export const testFunctionDefaultRegion = functions.https.onCall(data => {
console.log(Date.now(), data);
if (typeof data === 'undefined') {
return 'undefined';
}
if (typeof data === 'string') {
return 'string';
}
if (typeof data === 'number') {
return 'number';
}
if (typeof data === 'boolean') {
return 'boolean';
}
if (data === null) {
return 'null';
}
if (Array.isArray(data)) {
return 'array';
}
const { type, asError, inputData } = data;
if (!Object.hasOwnProperty.call(SAMPLE_DATA, type)) {
throw new functions.https.HttpsError('invalid-argument', 'Invalid test requested.');
}
const outputData = SAMPLE_DATA[type];
try {
assert.deepEqual(outputData, inputData);
} catch (e) {
console.error(e);
throw new functions.https.HttpsError(
'invalid-argument',
'Input and Output types did not match.',
(e as FirebaseError).message,
);
}
// all good
if (asError) {
throw new functions.https.HttpsError(
'cancelled',
'Response data was requested to be sent as part of an Error payload, so here we are!',
outputData,
);
}
return outputData;
});

View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"module": "commonjs",
"noImplicitReturns": true,
"noUnusedLocals": true,
"outDir": "lib",
"sourceMap": true,
"skipLibCheck": true,
"strict": true,
"target": "es2017"
},
"compileOnSave": true,
"include": [
"src"
]
}

View File

@ -0,0 +1,6 @@
@REM this pushd is likely not needed, but just in case
pushd "%~dp0"
@REM this is just to see what our current directory is. Should be .github/workflow/scripts
echo %cd%
@REM strangely, unless you specify the config file as being right in the current directory, it won't find it, and everything fails
yarn firebase emulators:start --config %cd%\firebase.json --only auth,database,firestore,functions,storage --project react-native-firebase-testing

View File

@ -0,0 +1,44 @@
#!/bin/bash
if ! [ -x "$(command -v firebase)" ]; then
echo "❌ Firebase-tools CLI is missing. Run 'npm i -g firebase-tools' or the equivalent"
exit 1
fi
EMU_START_COMMAND="firebase emulators:start --only auth,database,firestore,functions,storage --project react-native-firebase-testing"
#EMU_START_COMMAND="sleep 120"
MAX_RETRIES=3
MAX_CHECKATTEMPTS=60
CHECKATTEMPTS_WAIT=1
# Make sure functions are ready to go
pushd "$(dirname "$0")/functions" && yarn && yarn build && popd
RETRIES=1
while [ $RETRIES -le $MAX_RETRIES ]; do
if [ "$1" == "--no-daemon" ]; then
echo "Starting Firebase Emulator Suite in foreground."
$EMU_START_COMMAND
exit 0
else
echo "Starting Firebase Emulator Suite in background."
$EMU_START_COMMAND &
CHECKATTEMPTS=1
while [ $CHECKATTEMPTS -le $MAX_CHECKATTEMPTS ]; do
sleep $CHECKATTEMPTS_WAIT
if curl --output /dev/null --silent --fail http://localhost:8080; then
echo "Firebase Emulator Suite is online!"
exit 0;
fi
echo "Waiting for Firebase Emulator Suite to come online, check $CHECKATTEMPTS of $MAX_CHECKATTEMPTS..."
((CHECKATTEMPTS = CHECKATTEMPTS + 1))
done
fi
echo "Firebase Emulator Suite did not come online in $MAX_CHECKATTEMPTS checks. Try $RETRIES of $MAX_RETRIES."
((RETRIES = RETRIES + 1))
done
echo "Firebase Emulator Suite did not come online after $MAX_RETRIES attempts."
exit 1

21
.github/workflows/scripts/storage.rules vendored Normal file
View File

@ -0,0 +1,21 @@
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{document=**} {
allow read, write: if false;
}
match /writeOnly.jpeg {
allow read: if false;
allow write: if true;
}
match /playground/{document=**} {
allow read, write: if true;
}
match /react-native-firebase-testing/{document=**} {
allow read, write: if true;
}
}
}

Binary file not shown.

View File

@ -0,0 +1,111 @@
/** @type {Detox.DetoxConfig} */
module.exports = {
logger: {
level: process.env.CI ? 'debug' : undefined,
},
testRunner: {
args: {
config: 'e2e/jest.config.js',
maxWorkers: process.env.CI ? 2 : undefined,
_: ['e2e'],
},
},
artifacts: {
plugins: {
log: process.env.CI ? 'failing' : undefined,
screenshot: process.env.CI ? 'failing' : undefined,
},
},
apps: {
'ios.release': {
type: 'ios.app',
binaryPath:
'ios/build/Build/Products/Release-iphonesimulator/testHotUpdate.app',
build:
'export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/testHotUpdate.xcworkspace -UseNewBuildSystem=NO -scheme testHotUpdate -configuration Release -sdk iphonesimulator -derivedDataPath ios/build -quiet',
},
'ios.debug': {
type: 'ios.app',
binaryPath:
'ios/build/Build/Products/Debug-iphonesimulator/testHotUpdate.app',
build:
'xcodebuild -workspace ios/testHotUpdate.xcworkspace -UseNewBuildSystem=NO -scheme testHotUpdate -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build',
start: 'scripts/start-rn.sh ios',
},
'android.debug': {
type: 'android.apk',
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
build:
'cd android ; ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug ; cd -',
start: 'scripts/start-rn.sh android',
},
'android.release': {
type: 'android.apk',
binaryPath: 'android/app/build/outputs/apk/release/app-release.apk',
build:
'cd android ; ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release ; cd -',
},
},
devices: {
simulator: {
type: 'ios.simulator',
headless: Boolean(process.env.CI),
device: {
type: 'iPhone 14',
},
},
emulator: {
type: 'android.emulator',
headless: Boolean(process.env.CI),
gpuMode: process.env.CI ? 'off' : undefined,
device: {
avdName: 'Pixel_3a_API_33_arm64-v8a',
},
utilBinaryPaths: ['./cache/test-butler-app.apk'],
},
'genymotion.emulator.uuid': {
type: 'android.genycloud',
device: {
recipeUUID: 'a50a71d6-da90-4c67-bdfa-5b602b0bbd15',
},
utilBinaryPaths: ['./cache/test-butler-app.apk'],
},
'genymotion.emulator.name': {
type: 'android.genycloud',
device: {
recipeName: 'Pixel_3a_API_33_arm64-v8a',
},
utilBinaryPaths: ['./cache/test-butler-app.apk'],
},
},
configurations: {
'ios.sim.release': {
device: 'simulator',
app: 'ios.release',
},
'ios.sim.debug': {
device: 'simulator',
app: 'ios.debug',
},
'ios.manual': {
type: 'ios.manual',
behavior: {
launchApp: 'manual',
},
artifacts: false,
session: {
autoStart: true,
server: 'ws://localhost:8099',
sessionId: 'com.wix.demo.react.native',
},
},
'android.emu.debug': {
device: 'emulator',
app: 'android.debug',
},
'android.emu.release': {
device: 'emulator',
app: 'android.release',
},
},
};

View File

@ -0,0 +1,89 @@
import {by, device, element, expect} from 'detox';
describe('测试Native模块的方法', () => {
beforeAll(async () => {
await device.launchApp();
});
it('setLocalHashInfo', async () => {
await element(by.id('testcase')).longPress();
await element(by.id('setLocalHashInfo')).tap();
await element(by.id('submit')).tap();
await expect(element(by.text('done'))).toBeVisible();
await element(by.text('OK')).tap();
});
it('getLocalHashInfo', async () => {
await element(by.id('getLocalHashInfo')).tap();
await element(by.id('submit')).tap();
await expect(element(by.text('done'))).toBeVisible();
await element(by.text('OK')).tap();
});
it('setUuid', async () => {
await element(by.id('setUuid')).tap();
await element(by.id('submit')).tap();
await expect(element(by.text('done'))).toBeVisible();
await element(by.text('OK')).tap();
});
it('setBlockUpdate', async () => {
await element(by.id('setBlockUpdate')).tap();
await element(by.id('submit')).tap();
await expect(element(by.text('done'))).toBeVisible();
await element(by.text('OK')).tap();
});
it('reloadUpdate', async () => {
await element(by.id('reloadUpdate')).tap();
await element(by.id('submit')).tap();
await expect(element(by.text('刚刚更新失败了,版本被回滚.'))).toBeVisible();
await element(by.text('OK')).tap();
});
it('setNeedUpdate', async () => {
await element(by.id('testcase')).longPress();
await element(by.id('setNeedUpdate')).tap();
await element(by.id('submit')).tap();
await expect(element(by.text('done'))).toBeVisible();
await element(by.text('OK')).tap();
});
it('markSuccess', async () => {
await element(by.id('markSuccess')).tap();
await element(by.id('submit')).tap();
await expect(element(by.text('done'))).toBeVisible();
await element(by.text('OK')).tap();
});
it('downloadPatchFromPpk', async () => {
await element(by.id('downloadPatchFromPpk')).tap();
await element(by.id('submit')).tap();
await expect(element(by.text('failed to open zip file'))).toBeVisible();
await element(by.text('OK')).tap();
});
it('downloadPatchFromPackage', async () => {
await element(by.id('downloadPatchFromPackage')).tap();
await element(by.id('submit')).tap();
await expect(element(by.text('failed to open zip file'))).toBeVisible();
await element(by.text('OK')).tap();
});
it('downloadFullUpdate', async () => {
await element(by.id('downloadFullUpdate')).tap();
await element(by.id('submit')).tap();
await expect(element(by.text('failed to open zip file'))).toBeVisible();
await element(by.text('OK')).tap();
});
if (device.getPlatform() === 'android') {
it('downloadAndInstallApk', async () => {
await element(by.id('testcase')).longPress();
await element(by.id('downloadAndInstallApk')).tap();
await element(by.id('submit')).tap();
await expect(element(by.text('failed to open zip file'))).toBeVisible();
await element(by.text('OK')).tap();
});
}
});

View File

@ -0,0 +1,29 @@
import { execSync } from 'child_process';
import { pathExists, ensureDir } from 'fs-extra';
import { resolveConfig } from 'detox/internals';
import { globalSetup } from 'detox/runners/jest';
export default async function customGlobalSetup() {
const config = await resolveConfig();
if (config.device.type === 'android.emulator') {
await downloadTestButlerAPK();
}
await globalSetup();
}
async function downloadTestButlerAPK() {
const version = '2.2.1';
const artifactUrl = `https://repo1.maven.org/maven2/com/linkedin/testbutler/test-butler-app/${version}/test-butler-app-${version}.apk`;
const filePath = `cache/test-butler-app.apk`;
await ensureDir('cache');
if (!(await pathExists(filePath))) {
console.log(`\nDownloading Test-Butler APK v${version}...`);
execSync(`curl -f -o ${filePath} ${artifactUrl}`);
}
}
module.exports = customGlobalSetup;

View File

@ -0,0 +1,16 @@
/** @type {import('jest').Config} */
module.exports = {
maxWorkers: 1,
globalSetup: './globalSetup.ts',
globalTeardown: 'detox/runners/jest/globalTeardown',
testEnvironment: 'detox/runners/jest/testEnvironment',
setupFilesAfterEnv: ['./setup.ts'],
testRunner: 'jest-circus/runner',
testTimeout: 120000,
testMatch: ['**/*.test.ts'],
transform: {
'\\.tsx?$': 'ts-jest',
},
reporters: ['detox/runners/jest/reporter'],
verbose: true,
};

View File

@ -0,0 +1,5 @@
import { device } from 'detox';
beforeAll(async () => {
await device.launchApp();
});

View File

@ -89,11 +89,6 @@ PODS:
- DoubleConversion
- fmt (~> 6.2.1)
- glog
- RCT-Folly/Fabric (2021.07.22.00):
- boost
- DoubleConversion
- fmt (~> 6.2.1)
- glog
- RCT-Folly/Futures (2021.07.22.00):
- boost
- DoubleConversion
@ -126,10 +121,8 @@ PODS:
- RCTRequired
- RCTTypeSafety
- React-Core
- React-graphics
- React-jsi
- React-jsiexecutor
- React-rncore
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- React-Core (0.71.1):
@ -278,326 +271,6 @@ PODS:
- React-logger (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-runtimeexecutor (= 0.71.1)
- React-Fabric (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-Fabric/animations (= 0.71.1)
- React-Fabric/attributedstring (= 0.71.1)
- React-Fabric/butter (= 0.71.1)
- React-Fabric/componentregistry (= 0.71.1)
- React-Fabric/componentregistrynative (= 0.71.1)
- React-Fabric/components (= 0.71.1)
- React-Fabric/config (= 0.71.1)
- React-Fabric/core (= 0.71.1)
- React-Fabric/debug_core (= 0.71.1)
- React-Fabric/debug_renderer (= 0.71.1)
- React-Fabric/imagemanager (= 0.71.1)
- React-Fabric/leakchecker (= 0.71.1)
- React-Fabric/mapbuffer (= 0.71.1)
- React-Fabric/mounting (= 0.71.1)
- React-Fabric/runtimescheduler (= 0.71.1)
- React-Fabric/scheduler (= 0.71.1)
- React-Fabric/telemetry (= 0.71.1)
- React-Fabric/templateprocessor (= 0.71.1)
- React-Fabric/textlayoutmanager (= 0.71.1)
- React-Fabric/uimanager (= 0.71.1)
- React-Fabric/utils (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/animations (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/attributedstring (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/butter (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/componentregistry (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/componentregistrynative (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-Fabric/components/activityindicator (= 0.71.1)
- React-Fabric/components/image (= 0.71.1)
- React-Fabric/components/inputaccessory (= 0.71.1)
- React-Fabric/components/legacyviewmanagerinterop (= 0.71.1)
- React-Fabric/components/modal (= 0.71.1)
- React-Fabric/components/root (= 0.71.1)
- React-Fabric/components/safeareaview (= 0.71.1)
- React-Fabric/components/scrollview (= 0.71.1)
- React-Fabric/components/slider (= 0.71.1)
- React-Fabric/components/text (= 0.71.1)
- React-Fabric/components/textinput (= 0.71.1)
- React-Fabric/components/unimplementedview (= 0.71.1)
- React-Fabric/components/view (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/activityindicator (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/image (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/inputaccessory (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/legacyviewmanagerinterop (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/modal (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/root (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/safeareaview (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/scrollview (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/slider (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/text (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/textinput (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/unimplementedview (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/components/view (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- Yoga
- React-Fabric/config (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/core (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/debug_core (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/debug_renderer (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/imagemanager (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-RCTImage (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/leakchecker (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/mapbuffer (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/mounting (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/runtimescheduler (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/scheduler (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/telemetry (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/templateprocessor (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/textlayoutmanager (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-Fabric/uimanager
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/uimanager (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-Fabric/utils (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-graphics (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-graphics (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- React-Core/Default (= 0.71.1)
- React-hermes (0.71.1):
- DoubleConversion
- glog
@ -624,34 +297,19 @@ PODS:
- React-jsinspector (0.71.1)
- React-logger (0.71.1):
- glog
- react-native-update (9.1.0):
- RCT-Folly (= 2021.07.22.00)
- RCTRequired
- RCTTypeSafety
- react-native-update (9.0.0):
- React
- React-Codegen
- React-Core
- react-native-update/HDiffPatch (= 9.1.0)
- react-native-update/RCTPushy (= 9.1.0)
- ReactCommon/turbomodule/core
- react-native-update/HDiffPatch (= 9.0.0)
- react-native-update/RCTPushy (= 9.0.0)
- SSZipArchive
- react-native-update/HDiffPatch (9.1.0):
- RCT-Folly (= 2021.07.22.00)
- RCTRequired
- RCTTypeSafety
- react-native-update/HDiffPatch (9.0.0):
- React
- React-Codegen
- React-Core
- ReactCommon/turbomodule/core
- SSZipArchive
- react-native-update/RCTPushy (9.1.0):
- RCT-Folly (= 2021.07.22.00)
- RCTRequired
- RCTTypeSafety
- react-native-update/RCTPushy (9.0.0):
- React
- React-Codegen
- React-Core
- ReactCommon/turbomodule/core
- SSZipArchive
- React-perflogger (0.71.1)
- React-RCTActionSheet (0.71.1):
@ -668,8 +326,6 @@ PODS:
- RCTRequired
- RCTTypeSafety
- React-Core
- React-graphics
- React-RCTFabric
- ReactCommon/turbomodule/core
- React-RCTBlob (0.71.1):
- RCT-Folly (= 2021.07.22.00)
@ -679,11 +335,6 @@ PODS:
- React-jsi (= 0.71.1)
- React-RCTNetwork (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-RCTFabric (0.71.1):
- RCT-Folly/Fabric (= 2021.07.22.00)
- React-Core (= 0.71.1)
- React-Fabric (= 0.71.1)
- React-RCTImage (= 0.71.1)
- React-RCTImage (0.71.1):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.1)
@ -719,7 +370,6 @@ PODS:
- React-Core/RCTVibrationHeaders (= 0.71.1)
- React-jsi (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-rncore (0.71.1)
- React-runtimeexecutor (0.71.1):
- React-jsi (= 0.71.1)
- ReactCommon/turbomodule/bridging (0.71.1):
@ -779,7 +429,6 @@ DEPENDENCIES:
- libevent (~> 2.1.12)
- OpenSSL-Universal (= 1.1.1100)
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../node_modules/react-native/`)
@ -790,8 +439,6 @@ DEPENDENCIES:
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
- React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
- React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
- React-Fabric (from `../node_modules/react-native/ReactCommon`)
- React-graphics (from `../node_modules/react-native/ReactCommon/react/renderer/graphics`)
- React-hermes (from `../node_modules/react-native/ReactCommon/hermes`)
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
@ -803,14 +450,12 @@ DEPENDENCIES:
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
- React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`)
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
- React-RCTFabric (from `../node_modules/react-native/React`)
- React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
- React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
- React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`)
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- React-rncore (from `../node_modules/react-native/ReactCommon`)
- React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
@ -865,10 +510,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/React/CoreModules"
React-cxxreact:
:path: "../node_modules/react-native/ReactCommon/cxxreact"
React-Fabric:
:path: "../node_modules/react-native/ReactCommon"
React-graphics:
:path: "../node_modules/react-native/ReactCommon/react/renderer/graphics"
React-hermes:
:path: "../node_modules/react-native/ReactCommon/hermes"
React-jsi:
@ -891,8 +532,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/Libraries/AppDelegate"
React-RCTBlob:
:path: "../node_modules/react-native/Libraries/Blob"
React-RCTFabric:
:path: "../node_modules/react-native/React"
React-RCTImage:
:path: "../node_modules/react-native/Libraries/Image"
React-RCTLinking:
@ -905,8 +544,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/Libraries/Text"
React-RCTVibration:
:path: "../node_modules/react-native/Libraries/Vibration"
React-rncore:
:path: "../node_modules/react-native/ReactCommon"
React-runtimeexecutor:
:path: "../node_modules/react-native/ReactCommon/runtimeexecutor"
ReactCommon:
@ -919,7 +556,7 @@ SPEC CHECKSUMS:
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
FBLazyVector: ad72713385db5289b19f1ead07e8e4aa26dcb01d
FBReactNativeSpec: 06fc2a521838dc240b499699d43467b071c66908
FBReactNativeSpec: df2602c11e33d310433496e28a48b4b2be652a61
Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30
@ -939,31 +576,27 @@ SPEC CHECKSUMS:
RCTTypeSafety: c276d85975bde3d8448907235c70bf0da257adfd
React: e481a67971af1ce9639c9f746b753dd0e84ca108
React-callinvoker: 1051c04a94fa9d243786b86380606bad701a3b31
React-Codegen: d4bc58865453b6a797405aa99f9fa1da3c9e58c6
React-Codegen: 14b1e716d361d5ad95e0ce1a338f3fa0733a98b5
React-Core: 698fc3baecb80d511d987475a16d036cec6d287f
React-CoreModules: 59245305f41ff0adfeac334acc0594dea4585a7c
React-cxxreact: 49accd2954b0f532805dbcd1918fa6962f32f247
React-Fabric: 30982dc19c7511bedf1751b0a0c21a5b816e2a3e
React-graphics: beabc29b026e7472ced1482557effedd15a09cf1
React-hermes: d068733294581a085e95b6024e8d951b005e26d3
React-jsi: 122b9bce14f4c6c7cb58f28f87912cfe091885fa
React-jsiexecutor: 60cf272aababc5212410e4249d17cea14fc36caa
React-jsinspector: ff56004b0c974b688a6548c156d5830ad751ae07
React-logger: 60a0b5f8bed667ecf9e24fecca1f30d125de6d75
react-native-update: b9d44d250953e61f1b42c22fa4d585a6bb5dd4d6
react-native-update: 2b5ef06bfeaa668614c8deb7ec4d20dcf56f9278
React-perflogger: ec8eef2a8f03ecfa6361c2c5fb9197ef4a29cc85
React-RCTActionSheet: a0c023b86cf4c862fa9c4eb0f6f91fbe878fb2de
React-RCTAnimation: 168d53718c74153947c0109f55900faa64d79439
React-RCTAppDelegate: 5f34addd2f9d8c542c129b562b7f3db0cc599a3d
React-RCTAppDelegate: a8efbab128b34aa07a9491c85a41401210b1bec5
React-RCTBlob: 9bcbfc893bfda9f6b2eb016329d38c0f6366d31a
React-RCTFabric: cec4e89720e8778aa132e5515be1af251d2e9b6a
React-RCTImage: 3fcd4570b4b0f1ac2f4b4b6308dba33ce66c5b50
React-RCTLinking: 1edb8e1bb3fc39bf9e13c63d6aaaa3f0c3d18683
React-RCTNetwork: 500a79e0e0f67678077df727fabba87a55c043e1
React-RCTSettings: cc4414eb84ad756d619076c3999fecbf12896d6f
React-RCTText: 2a34261f3da6e34f47a62154def657546ebfa5e1
React-RCTVibration: 49d531ec8498e0afa2c9b22c2205784372e3d4f3
React-rncore: b802bc9f6985c482127b066c869999a09d25edeb
React-runtimeexecutor: 311feb67600774723fe10eb8801d3138cae9ad67
ReactCommon: 03be76588338a27a88d103b35c3c44a3fd43d136
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608

View File

@ -211,7 +211,7 @@
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "testHotupdate" */;
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "testHotUpdate" */;
compatibilityVersion = "Xcode 12.0";
developmentRegion = en;
hasScannedForEncodings = 0;
@ -689,7 +689,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "testHotupdate" */ = {
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "testHotUpdate" */ = {
isa = XCConfigurationList;
buildConfigurations = (
83CBBA201A601CBA00E9B192 /* Debug */,

View File

@ -7,4 +7,7 @@
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
<FileRef
location = "group:testHotUpdate.xcodeproj">
</FileRef>
</Workspace>

View File

@ -24,17 +24,6 @@
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>UILaunchStoryboardName</key>
@ -51,5 +40,18 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>

View File

@ -25,11 +25,15 @@
"@types/react": "^18.0.24",
"@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.2.1",
"detox": "^20.5.0",
"eslint": "^8.19.0",
"fs-extra": "^11.1.0",
"jest": "^29.2.1",
"metro-react-native-babel-preset": "0.73.7",
"prettier": "^2.4.1",
"react-test-renderer": "18.2.0"
"react-test-renderer": "18.2.0",
"ts-jest": "^29.0.5",
"typescript": "^4.9.5"
},
"jest": {
"preset": "react-native"

View File

@ -1,6 +1,6 @@
/* eslint-disable react-native/no-inline-styles */
/* eslint-disable react/react-in-jsx-scope */
import {useState} from 'react';
import {useCallback, useMemo, useState} from 'react';
import {
ActivityIndicator,
Alert,
@ -10,14 +10,117 @@ import {
StyleSheet,
SafeAreaView,
Text,
ScrollView,
View,
TouchableOpacity,
} from 'react-native';
import {PushyModule} from 'react-native-update';
const Hash = '9D5CE6EBA420717BE7E7D308B11F8207681B066C951D68F3994D19828F342474';
const UUID = '00000000-0000-0000-0000-000000000000';
const DownloadUrl =
'http://cos.pgyer.com/697913e94d7441f20c686e2b0996a1aa.apk?sign=363b035b7ef52c199c268abfacee3712&t=1678603669&response-content-disposition=attachment%3Bfilename%3DtestHotupdate_1.0.apk';
export default function TestConsole({visible}) {
const [text, setText] = useState('');
const [running, setRunning] = useState(false);
const [options, setOptions] = useState();
const NativeTestMethod = useMemo(() => {
return [
{
name: 'setLocalHashInfo',
invoke: () => {
setText(
`setLocalHashInfo\n${Hash}\n{\"version\":\"1.0.0\",\"size\":\"19M\"}`,
);
},
},
{
name: 'getLocalHashInfo',
invoke: () => {
setText(`getLocalHashInfo\n${Hash}`);
},
},
{
name: 'setUuid',
invoke: () => {
setText(`setUuid\n${UUID}`);
},
},
{
name: 'setBlockUpdate',
invoke: () => {
setText('setBlockUpdate');
setOptions({reason: 'application has been block', until: 1673082950});
},
},
{
name: 'reloadUpdate',
invoke: () => {
setText('reloadUpdate');
setOptions({hash: Hash});
},
},
{
name: 'setNeedUpdate',
invoke: () => {
setText('setNeedUpdate');
setOptions({hash: Hash});
},
},
{
name: 'markSuccess',
invoke: () => {
setText('markSuccess');
},
},
{
name: 'downloadPatchFromPpk',
invoke: () => {
setText('downloadPatchFromPpk');
setOptions({updateUrl: DownloadUrl, hash: Hash, originHash: Hash});
},
},
{
name: 'downloadPatchFromPackage',
invoke: () => {
setText('downloadPatchFromPackage');
setOptions({updateUrl: DownloadUrl, hash: Hash});
},
},
{
name: 'downloadFullUpdate',
invoke: () => {
setText('downloadFullUpdate');
setOptions({updateUrl: DownloadUrl, hash: Hash});
},
},
{
name: 'downloadAndInstallApk',
invoke: () => {
setText('downloadAndInstallApk');
setOptions({url: DownloadUrl, target: Hash, hash: Hash});
},
},
];
}, []);
const renderTestView = useCallback(() => {
const views = [];
for (let i = 0; i < NativeTestMethod.length; i++) {
views.push(
<TouchableOpacity
key={i}
testID={NativeTestMethod[i].name}
onPress={() => {
NativeTestMethod[i].invoke();
}}
style={{width: 10, height: 10, backgroundColor: 'red'}}
/>,
);
}
return <View>{views}</View>;
}, [NativeTestMethod]);
return (
<Modal visible={visible}>
<SafeAreaView style={{flex: 1, padding: 10}}>
@ -42,25 +145,27 @@ export default function TestConsole({visible}) {
{running && <ActivityIndicator />}
<Button
title="执行"
testID="submit"
onPress={async () => {
setRunning(true);
try {
const inputs = text.split('\n');
const methodName = inputs[0];
let params;
let params = [];
if (inputs.length === 1) {
await PushyModule[methodName]();
if (options) {
await PushyModule[methodName](options);
} else {
await PushyModule[methodName]();
}
} else {
if (inputs.length === 2) {
params = inputs[1];
params = [inputs[1]];
} else {
params = {};
for (let i = 1; i < inputs.length; i += 2) {
params[inputs[i]] = inputs[i + 1];
}
params = [inputs[1], inputs[2]];
console.log({inputs, params});
}
await PushyModule[methodName](params);
await PushyModule[methodName](...params);
}
Alert.alert('done');
} catch (e) {
@ -69,9 +174,11 @@ export default function TestConsole({visible}) {
setRunning(false);
}}
/>
<View style={{marginTop: 15}}>
<ScrollView style={{marginTop: 15}}>
<Button title="重置" onPress={() => setText('')} />
</View>
{renderTestView()}
</ScrollView>
</SafeAreaView>
</Modal>
);

View File

@ -170,6 +170,7 @@ export default class App extends Component {
</TouchableOpacity>
<TouchableOpacity
testID="testcase"
style={{marginTop: 15}}
onLongPress={() => {
this.setState({showTestConsole: true});

File diff suppressed because it is too large Load Diff

View File

@ -58,6 +58,7 @@ repositories {
dependencies {
implementation 'com.facebook.react:react-native:+'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
}
if (isNewArchitectureEnabled()) {
react {

View File

@ -10,8 +10,19 @@ import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class UpdateModuleImpl {
public static final String NAME = "Pushy";
@ -72,27 +83,35 @@ public class UpdateModuleImpl {
}
public static void downloadPatchFromPpk(UpdateContext updateContext, ReadableMap options, Promise promise) {
String url = options.getString("updateUrl");
String hash = options.getString("hash");
try {
String url = options.getString("updateUrl");
String hash = options.getString("hash");
String originHash = options.getString("originHash");
String originHash = options.getString("originHash");
updateContext.downloadPatchFromPpk(url, hash, originHash, new UpdateContext.DownloadFileListener() {
@Override
public void onDownloadCompleted(DownloadTaskParams params) {
promise.resolve(null);
}
updateContext.downloadPatchFromPpk(url, hash, originHash, new UpdateContext.DownloadFileListener() {
@Override
public void onDownloadCompleted(DownloadTaskParams params) {
promise.resolve(null);
}
@Override
public void onDownloadFailed(Throwable error) {
promise.reject(error);
}
});
@Override
public void onDownloadFailed(Throwable error) {
promise.reject(error);
}
});
}catch (Exception e){
promise.reject("执行报错:"+e.getMessage());
}
}
public static void reloadUpdate(UpdateContext updateContext, ReactApplicationContext mContext, ReadableMap options) {
public static void reloadUpdate(UpdateContext updateContext, ReactApplicationContext mContext, ReadableMap options,Promise promise) {
final String hash = options.getString("hash");
if(hash==null || hash.isEmpty()){
promise.reject("hash不能为空");
return;
}
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
@ -112,6 +131,7 @@ public class UpdateModuleImpl {
loadField.setAccessible(true);
loadField.set(instanceManager, loader);
} catch (Throwable err) {
promise.reject("pushy:"+err.getMessage());
Field jsBundleField = instanceManager.getClass().getDeclaredField("mJSBundleFile");
jsBundleField.setAccessible(true);
jsBundleField.set(instanceManager, UpdateContext.getBundleUrl(application));
@ -119,11 +139,14 @@ public class UpdateModuleImpl {
try {
instanceManager.recreateReactContextInBackground();
promise.resolve(true);
} catch (Throwable err) {
promise.reject("pushy:"+err.getMessage());
activity.recreate();
}
} catch (Throwable err) {
promise.reject("pushy:switchVersion failed"+err.getMessage());
Log.e("pushy", "switchVersion failed", err);
}
}
@ -131,61 +154,112 @@ public class UpdateModuleImpl {
}
public static void setNeedUpdate(UpdateContext updateContext, ReadableMap options) {
final String hash = options.getString("hash");
public static void setNeedUpdate(UpdateContext updateContext, ReadableMap options,Promise promise) {
try {
final String hash = options.getString("hash");
if(hash==null || hash.isEmpty()){
promise.reject("hash不能为空");
return;
}
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
updateContext.switchVersion(hash);
promise.resolve(true);
} catch (Throwable err) {
promise.reject("switchVersionLater failed:"+err.getMessage());
Log.e("pushy", "switchVersionLater failed", err);
}
}
});
}catch (Exception e){
promise.reject("执行报错:"+e.getMessage());
}
}
public static void markSuccess(UpdateContext updateContext,Promise promise) {
try {
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.markSuccess();
promise.resolve(true);
}
});
}catch (Exception e){
promise.reject("执行报错:"+e.getMessage());
}
}
public static void setBlockUpdate(UpdateContext updateContext, ReadableMap options,Promise promise) {
try {
final int until = options.getInt("until");
final String reason = options.getString("reason");
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.setBlockUpdate(until, reason);
}
});
promise.resolve(true);
}catch (Exception e){
promise.reject("执行报错:"+e.getMessage());
}
}
public static void setUuid(UpdateContext updateContext, String uuid, Promise promise) {
try {
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.setKv("uuid", uuid);
promise.resolve(true);
}
});
}catch (Exception e){
promise.reject("执行报错:"+e.getMessage());
}
}
public static boolean check(String json) {
ObjectMapper mapper = new ObjectMapper();
try {
mapper.readValue(json, Map.class);
System.out.println("String can be converted to Map");
return true;
} catch (IOException e) {
System.out.println("String cannot be converted to Map");
return false;
}
}
public static void setLocalHashInfo(UpdateContext updateContext, final String hash, final String info, Promise promise) {
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
updateContext.switchVersion(hash);
} catch (Throwable err) {
Log.e("pushy", "switchVersionLater failed", err);
if(!check(info)){
updateContext.setKv("hash_" + hash, info);
promise.reject("校验报错:json字符串格式错误");
}else {
updateContext.setKv("hash_" + hash, info);
promise.resolve(true);
}
}
});
}
public static void markSuccess(UpdateContext updateContext) {
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.markSuccess();
}
});
}
public static void setBlockUpdate(UpdateContext updateContext, ReadableMap options) {
final int until = options.getInt("until");
final String reason = options.getString("reason");
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.setBlockUpdate(until, reason);
}
});
}
public static void setUuid(UpdateContext updateContext, String uuid) {
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.setKv("uuid", uuid);
}
});
}
public static void setLocalHashInfo(UpdateContext updateContext, final String hash, final String info) {
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.setKv("hash_" + hash, info);
}
});
}
public static void getLocalHashInfo(UpdateContext updateContext, final String hash, Promise promise) {
promise.resolve(updateContext.getKv("hash_" + hash));
String value = updateContext.getKv("hash_" + hash);
if(check(value)){
promise.resolve(value);
}else {
promise.reject("校验报错:json字符串格式错误");
}
}
}

View File

@ -94,33 +94,33 @@ public class UpdateModule extends NativeUpdateSpec {
}
@Override
public void reloadUpdate(ReadableMap options) {
UpdateModuleImpl.reloadUpdate(updateContext, mContext, options);
public void reloadUpdate(ReadableMap options,Promise promise) {
UpdateModuleImpl.reloadUpdate(updateContext, mContext, options,promise);
}
@Override
public void setNeedUpdate(ReadableMap options) {
UpdateModuleImpl.setNeedUpdate(updateContext, options);
public void setNeedUpdate(ReadableMap options,Promise promise) {
UpdateModuleImpl.setNeedUpdate(updateContext, options,promise);
}
@Override
public void markSuccess() {
UpdateModuleImpl.markSuccess(updateContext);
public void markSuccess(Promise promise) {
UpdateModuleImpl.markSuccess(updateContext,promise);
}
@Override
public void setBlockUpdate(ReadableMap options) {
UpdateModuleImpl.setBlockUpdate(updateContext,options);
public void setBlockUpdate(ReadableMap options,Promise promise) {
UpdateModuleImpl.setBlockUpdate(updateContext,options,promise);
}
@Override
public void setUuid(final String uuid) {
UpdateModuleImpl.setUuid(updateContext,uuid);
public void setUuid(final String uuid, Promise promise) {
UpdateModuleImpl.setUuid(updateContext,uuid,promise);
}
@Override
public void setLocalHashInfo(final String hash, final String info) {
UpdateModuleImpl.setLocalHashInfo(updateContext,hash,info);
public void setLocalHashInfo(final String hash, final String info, final Promise promise) {
UpdateModuleImpl.setLocalHashInfo(updateContext,hash,info,promise);
}
@Override

View File

@ -192,29 +192,53 @@ RCT_EXPORT_MODULE(RCTPushy);
return self;
}
RCT_EXPORT_METHOD(setBlockUpdate:(NSDictionary *)options)
RCT_EXPORT_METHOD(setBlockUpdate:(NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
// NSMutableDictionary *blockUpdateInfo = [NSMutableDictionary new];
// blockUpdateInfo[@"reason"] = options[@"reason"];
// blockUpdateInfo[@"until"] = options[@"until"];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:options forKey:keyBlockUpdate];
[defaults synchronize];
@try {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:options forKey:keyBlockUpdate];
[defaults synchronize];
resolve(@true);
}
@catch (NSException *exception) {
reject(@"执行报错", nil, nil);
}
}
RCT_EXPORT_METHOD(setUuid:(NSString *)uuid)
RCT_EXPORT_METHOD(setUuid:(NSString *)uuid resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:uuid forKey:keyUuid];
[defaults synchronize];
@try {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:uuid forKey:keyUuid];
[defaults synchronize];
resolve(@true);
}
@catch (NSException *exception) {
reject(@"json格式校验报错", nil, nil);
}
}
RCT_EXPORT_METHOD(setLocalHashInfo:(NSString *)hash
value:(NSString *)value)
value:(NSString *)value resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:value forKey:[keyHashInfo stringByAppendingString:hash]];
[defaults synchronize];
NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
id object = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (object && [object isKindOfClass:[NSDictionary class]]) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:value forKey:[keyHashInfo stringByAppendingString:hash]];
[defaults synchronize];
resolve(@true);
} else {
reject(@"json格式校验报错", nil, nil);
}
}
@ -269,7 +293,9 @@ RCT_EXPORT_METHOD(downloadPatchFromPpk:(NSDictionary *)options
}];
}
RCT_EXPORT_METHOD(setNeedUpdate:(NSDictionary *)options)
RCT_EXPORT_METHOD(setNeedUpdate:(NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSString *hash = options[@"hash"];
@ -290,45 +316,66 @@ RCT_EXPORT_METHOD(setNeedUpdate:(NSDictionary *)options)
[defaults setObject:newInfo forKey:keyPushyInfo];
[defaults synchronize];
resolve(@true);
}else{
reject(@"执行报错", nil, nil);
}
}
RCT_EXPORT_METHOD(reloadUpdate:(NSDictionary *)options)
RCT_EXPORT_METHOD(reloadUpdate:(NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSString *hash = options[@"hash"];
if (hash.length) {
[self setNeedUpdate:options];
// reload 0.62+
// RCTReloadCommandSetBundleURL([[self class] bundleURL]);
// RCTTriggerReloadCommandListeners(@"pushy reload");
dispatch_async(dispatch_get_main_queue(), ^{
[self.bridge setValue:[[self class] bundleURL] forKey:@"bundleURL"];
[self.bridge reload];
});
@try {
NSString *hash = options[@"hash"];
if (hash.length) {
[self setNeedUpdate:options resolver:resolve rejecter:reject];
// reload 0.62+
// RCTReloadCommandSetBundleURL([[self class] bundleURL]);
// RCTTriggerReloadCommandListeners(@"pushy reload");
dispatch_async(dispatch_get_main_queue(), ^{
[self.bridge setValue:[[self class] bundleURL] forKey:@"bundleURL"];
[self.bridge reload];
});
resolve(@true);
}else{
reject(@"执行报错", nil, nil);
}
}
@catch (NSException *exception) {
reject(@"执行报错", nil, nil);
}
}
RCT_EXPORT_METHOD(markSuccess)
RCT_EXPORT_METHOD(markSuccess:
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
// up package info
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *pushyInfo = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:keyPushyInfo]];
[pushyInfo setObject:@(NO) forKey:paramIsFirstTime];
[pushyInfo setObject:@(YES) forKey:paramIsFirstLoadOk];
NSString *lastVersion = pushyInfo[paramLastVersion];
NSString *curVersion = pushyInfo[paramCurrentVersion];
if (lastVersion != nil && ![lastVersion isEqualToString:curVersion]) {
[pushyInfo removeObjectForKey:[keyHashInfo stringByAppendingString:lastVersion]];
@try {
// up package info
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *pushyInfo = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:keyPushyInfo]];
[pushyInfo setObject:@(NO) forKey:paramIsFirstTime];
[pushyInfo setObject:@(YES) forKey:paramIsFirstLoadOk];
NSString *lastVersion = pushyInfo[paramLastVersion];
NSString *curVersion = pushyInfo[paramCurrentVersion];
if (lastVersion != nil && ![lastVersion isEqualToString:curVersion]) {
[pushyInfo removeObjectForKey:[keyHashInfo stringByAppendingString:lastVersion]];
}
[defaults setObject:pushyInfo forKey:keyPushyInfo];
[defaults synchronize];
// clear other package dir
[self clearInvalidFiles];
resolve(@true);
}
@catch (NSException *exception) {
reject(@"执行报错", nil, nil);
}
[defaults setObject:pushyInfo forKey:keyPushyInfo];
[defaults synchronize];
// clear other package dir
[self clearInvalidFiles];
}
@ -354,6 +401,19 @@ RCT_EXPORT_METHOD(markSuccess)
// Remove upstream listeners, stop unnecessary background tasks
}
- (BOOL) isBlankString:(NSString *)string {
if (string == nil || string == NULL) {
return YES;
}
if ([string isKindOfClass:[NSNull class]]) {
return YES;
}
if ([[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length]==0) {
return YES;
}
return NO;
}
- (void)doPushy:(PushyType)type options:(NSDictionary *)options callback:(void (^)(NSError *error))callback
{
@ -365,7 +425,7 @@ RCT_EXPORT_METHOD(markSuccess)
return;
}
NSString *originHash = [RCTConvert NSString:options[@"originHash"]];
if (type == PushyTypePatchFromPpk && originHash <= 0) {
if (type == PushyTypePatchFromPpk && [self isBlankString:originHash]) {
callback([self errorWithMessage:ERROR_OPTIONS]);
return;
}
@ -570,7 +630,7 @@ RCT_EXPORT_METHOD(markSuccess)
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return std::make_shared<facebook::react::NativeCalculatorSpecJSI>(params);
return std::make_shared<facebook::react::NativeUpdateSpecJSI>(params);
}
#endif

View File

@ -1 +1 @@
1676798137
1678849794

View File

@ -19,10 +19,13 @@ export interface Spec extends TurboModule {
uuid: string,
isUsingBundleUrl: boolean,
};
setLocalHashInfo(hash: string, info: string): void;
setLocalHashInfo(hash: string, info: string): Promise<void>;
getLocalHashInfo(hash: string): Promise<string>;
setUuid(uuid: string): void;
setBlockUpdate(options: { reason: string, until: number }): void;
setUuid(uuid: string): Promise<void>;
setBlockUpdate(options: { reason: string, until: number }): Promise<void>;
reloadUpdate(options: { hash: string }): Promise<void>;
setNeedUpdate(options: { hash: string }): Promise<void>;
markSuccess(): Promise<void>;
downloadPatchFromPpk(options: {
updateUrl: string,
hash: string,
@ -36,9 +39,6 @@ export interface Spec extends TurboModule {
updateUrl: string,
hash: string,
}): Promise<void>;
reloadUpdate(options: { hash: string }): void;
setNeedUpdate(options: { hash: string }): void;
markSuccess(): void;
downloadAndInstallApk(options: {
url: string,
target: string,

View File

@ -7,7 +7,23 @@
"prepublish": "yarn submodule",
"submodule": "git submodule update --init --recursive",
"test": "echo \"Error: no test specified\" && exit 1",
"build-lib": "yarn submodule && $ANDROID_HOME/ndk/20.1.5948944/ndk-build NDK_PROJECT_PATH=android APP_BUILD_SCRIPT=android/jni/Android.mk NDK_APPLICATION_MK=android/jni/Application.mk NDK_LIBS_OUT=android/lib"
"build-lib": "yarn submodule && $ANDROID_HOME/ndk/20.1.5948944/ndk-build NDK_PROJECT_PATH=android APP_BUILD_SCRIPT=android/jni/Android.mk NDK_APPLICATION_MK=android/jni/Application.mk NDK_LIBS_OUT=android/lib",
"build:ios": "cd Example/testHotUpdate && detox build --configuration ios.sim.debug",
"build:ios-debug": "cd Example/testHotUpdate && detox build --configuration ios.sim.debug",
"build:ios-release": "cd Example/testHotUpdate && detox build --configuration ios.sim.release",
"build:android-debug": "cd Example/testHotUpdate && detox build --configuration android.emu.debug",
"build:android-release": "cd Example/testHotUpdate && detox build --configuration android.emu.release",
"test:ios": "cd Example/testHotUpdate && detox test --configuration ios.sim.debug",
"test:ios-debug": "cd Example/testHotUpdate && detox test --configuration ios.sim.debug",
"test:ios-release": "cd Example/testHotUpdate && detox test --configuration ios.sim.release",
"test:android-debug": "cd Example/testHotUpdate && detox test --configuration android.emu.debug",
"test:android-release": "cd Example/testHotUpdate && ./node_modules/.bin/nyc yarn detox test --configuration android.emu.debug",
"e2e:ios": "npm run build:ios-release && npm run test:ios-release",
"e2e:android": "npm run build:android-release && npm run test:android-release",
"tests:emulator:prepare": "cd .github/workflows/scripts/functions && yarn && yarn build",
"tests:emulator:start-ci": "yarn tests:emulator:prepare && cd ./.github/workflows/scripts && ./start-firebase-emulator.sh",
"tests:packager:jet-ci": "cd Example/testHotUpdate && cross-env TMPDIR=$HOME/.metro REACT_DEBUGGER=\"echo nope\" node_modules/.bin/react-native start --no-interactive",
"tests:ios:pod:install": "rm -rf ios/RCTPushy.xcworkspace && yarn pod-install"
},
"repository": {
"type": "git",
@ -33,11 +49,14 @@
},
"codegenConfig": {
"libraries": [
{
{
"name": "RCTPushySpec",
"type": "modules",
"jsSrcsDir": "lib"
}
}
]
}
},
"devDependencies": {
"firebase-tools": "^11.24.1"
}
}

4331
yarn.lock

File diff suppressed because it is too large Load Diff