mirror of
https://github.com/standardnotes/mobile.git
synced 2026-04-19 05:39:20 -04:00
feature: e2e tests on Github Actions (#254)
This commit is contained in:
73
.github/workflows/e2e.yml
vendored
Normal file
73
.github/workflows/e2e.yml
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
name: e2e
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
pull_request:
|
||||
|
||||
|
||||
jobs:
|
||||
android:
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Download Android Emulator Image
|
||||
run: |
|
||||
$ANDROID_HOME/tools/bin/sdkmanager --install "system-images;android-28;default;x86_64"
|
||||
$ANDROID_HOME/tools/bin/avdmanager create avd -n pixel -d "Nexus 5X" --package "system-images;android-28;default;x86_64"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Add usesCleartextTraffic to release AndroidManifest.xml
|
||||
run: yarn replace-in-file '<application' '<application android:usesCleartextTraffic="true"' ./android/app/src/main/AndroidManifest.xml
|
||||
|
||||
- name: Build for detox
|
||||
env:
|
||||
DETOX_CI: true
|
||||
run: yarn detox build --configuration android.emu.release
|
||||
|
||||
- name: Setup Emulator
|
||||
timeout-minutes: 10
|
||||
run: |
|
||||
echo "Starting emulator"
|
||||
nohup $ANDROID_HOME/emulator/emulator -avd pixel -no-audio -no-snapshot -no-window -gpu swiftshader_indirect &
|
||||
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done; input keyevent 82'
|
||||
$ANDROID_HOME/platform-tools/adb shell settings put global window_animation_scale 0 &
|
||||
$ANDROID_HOME/platform-tools/adb shell settings put global transition_animation_scale 0 &
|
||||
$ANDROID_HOME/platform-tools/adb shell settings put global animator_duration_scale 0 &
|
||||
echo "Emulator started"
|
||||
- name: Run tests
|
||||
run: yarn detox test --configuration android.emu.release
|
||||
|
||||
ios:
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install Dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Install Pods
|
||||
run: npx pod-install ios
|
||||
|
||||
- name: Install detox dependencies
|
||||
run: brew tap wix/brew
|
||||
- name: Install simulator utils
|
||||
run: brew install applesimutils
|
||||
- name: Build for detox
|
||||
run: yarn detox build --configuration ios.sim.release
|
||||
- name: Run tests
|
||||
run: yarn detox test --configuration ios.sim.release
|
||||
5
.github/workflows/lint.yml
vendored
5
.github/workflows/lint.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: Lint & test
|
||||
name: lint & typescript check
|
||||
|
||||
on:
|
||||
push:
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'react-native';
|
||||
import React from 'react';
|
||||
import Index from '../index.android.js';
|
||||
import Index from '../index.android';
|
||||
|
||||
// Note: test renderer must be required after react-native.
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
@@ -154,7 +154,12 @@ android {
|
||||
keyPassword 'android'
|
||||
}
|
||||
release {
|
||||
if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
|
||||
if (System.getenv('DETOX_CI')) { // if running on e2e CI sign with a debug key
|
||||
storeFile file('debug.keystore')
|
||||
storePassword 'android'
|
||||
keyAlias 'androiddebugkey'
|
||||
keyPassword 'android'
|
||||
} else if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
|
||||
storeFile file(MYAPP_RELEASE_STORE_FILE)
|
||||
storePassword MYAPP_RELEASE_STORE_PASSWORD
|
||||
keyAlias MYAPP_RELEASE_KEY_ALIAS
|
||||
|
||||
@@ -37,6 +37,10 @@ allprojects {
|
||||
}
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
// All of Detox' artifacts are provided via the npm module
|
||||
url "$rootDir/../node_modules/detox/Detox-android"
|
||||
}
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,4 +28,7 @@ android.enableJetifier=true
|
||||
FLIPPER_VERSION=0.33.1
|
||||
|
||||
# Set AsyncStorage limit
|
||||
AsyncStorage_db_size_in_MB=50
|
||||
AsyncStorage_db_size_in_MB=50
|
||||
|
||||
org.gradle.daemon=true
|
||||
org.gradle.jvmargs=-Xmx2560m
|
||||
@@ -4,7 +4,4 @@ apply from: file("../node_modules/@react-native-community/cli-platform-android/n
|
||||
applyNativeModulesSettingsGradle(settings)
|
||||
|
||||
|
||||
include ':app'
|
||||
|
||||
include ':detox'
|
||||
project(':detox').projectDir = new File(rootProject.projectDir, '../node_modules/detox/android/detox')
|
||||
include ':app'
|
||||
7
e2e/config.json
Normal file
7
e2e/config.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"setupFilesAfterEnv": ["./init.js"],
|
||||
"testEnvironment": "detox/runners/jest/JestCircusEnvironment",
|
||||
"testRunner": "jest-circus/runner",
|
||||
"reporters": ["detox/runners/jest/streamlineReporter"],
|
||||
"verbose": true
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/* eslint-disable no-undef */
|
||||
const faker = require('faker');
|
||||
|
||||
module.exports.openSettingsScreen = async () => {
|
||||
await device.reloadReactNative();
|
||||
|
||||
// Opens the settings screen
|
||||
await waitFor(element(by.id('rootView')))
|
||||
.toBeVisible()
|
||||
.withTimeout(2000);
|
||||
await element(by.id('headerButton')).tap();
|
||||
await element(by.id('settingsButton')).tap();
|
||||
};
|
||||
|
||||
module.exports.openComposeNewNoteScreen = async () => {
|
||||
await device.reloadReactNative();
|
||||
|
||||
// Opens the screen to compose a new note
|
||||
await waitFor(element(by.id('rootView')))
|
||||
.toBeVisible()
|
||||
.withTimeout(2000);
|
||||
await element(by.id('newNoteButton')).tap();
|
||||
};
|
||||
|
||||
module.exports.randomCredentials = {
|
||||
email: faker.internet.exampleEmail(),
|
||||
password: faker.internet.password(),
|
||||
syncServerUrl: 'http://syncing-server.dev:3000',
|
||||
};
|
||||
62
e2e/helpers.ts
Normal file
62
e2e/helpers.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
const faker = require('faker');
|
||||
import { device, waitFor, element, by, expect } from 'detox';
|
||||
|
||||
export const expectToBeVisible = async (testedElement: Detox.DetoxAny) => {
|
||||
try {
|
||||
await expect(testedElement).toBeVisible();
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const checkAfterReinstall = async () => {
|
||||
if (device.getPlatform() === 'ios') {
|
||||
let alertVisible = await expectToBeVisible(
|
||||
element(
|
||||
by
|
||||
.label('Delete Local Data')
|
||||
.and(by.type('_UIAlertControllerActionView'))
|
||||
)
|
||||
);
|
||||
if (alertVisible) {
|
||||
await element(
|
||||
by
|
||||
.label('Delete Local Data')
|
||||
.and(by.type('_UIAlertControllerActionView'))
|
||||
).tap();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const openSettingsScreen = async () => {
|
||||
await checkAfterReinstall();
|
||||
await device.reloadReactNative();
|
||||
|
||||
// Opens the settings screen
|
||||
await waitFor(element(by.id('rootView')))
|
||||
.toBeVisible()
|
||||
.withTimeout(2000);
|
||||
await element(by.id('headerButton')).tap();
|
||||
await element(by.id('settingsButton')).tap();
|
||||
};
|
||||
|
||||
export const openComposeNewNoteScreen = async () => {
|
||||
await device.reloadReactNative();
|
||||
|
||||
// Opens the screen to compose a new note
|
||||
await waitFor(element(by.id('rootView')))
|
||||
.toBeVisible()
|
||||
.withTimeout(2000);
|
||||
await waitFor(element(by.id('newNoteButton')))
|
||||
.toBeVisible()
|
||||
.withTimeout(2000);
|
||||
console.log(element(by.id('newNoteButton')));
|
||||
await element(by.id('newNoteButton')).tap();
|
||||
};
|
||||
|
||||
export const randomCredentials = {
|
||||
email: faker.internet.exampleEmail(),
|
||||
password: faker.internet.password(),
|
||||
syncServerUrl: 'https://testapi.standardnotes.org/',
|
||||
};
|
||||
37
e2e/init.js
37
e2e/init.js
@@ -1,20 +1,37 @@
|
||||
/* eslint-disable no-undef */
|
||||
const detox = require('detox');
|
||||
const config = require('../package.json').detox;
|
||||
const adapter = require('detox/runners/mocha/adapter');
|
||||
const adapter = require('detox/runners/jest/adapter');
|
||||
const specReporter = require('detox/runners/jest/specReporter');
|
||||
const assignReporter = require('detox/runners/jest/assignReporter');
|
||||
|
||||
before(async () => {
|
||||
// Set the default timeout
|
||||
jest.setTimeout(120000);
|
||||
|
||||
detoxCircus.getEnv().addEventsListener(adapter);
|
||||
detoxCircus.getEnv().addEventsListener(specReporter);
|
||||
detoxCircus.getEnv().addEventsListener(assignReporter);
|
||||
|
||||
beforeAll(async () => {
|
||||
await detox.init(config);
|
||||
});
|
||||
}, 600000);
|
||||
|
||||
beforeEach(async function () {
|
||||
await adapter.beforeEach(this);
|
||||
});
|
||||
// beforeEach(async () => {
|
||||
// try {
|
||||
// await adapter.beforeEach();
|
||||
// } catch (err) {
|
||||
// // Workaround for the 'jest-jasmine' runner (default one): if 'beforeAll' hook above fails with a timeout,
|
||||
// // unfortunately, 'jest' might continue running other hooks and test suites. To prevent that behavior,
|
||||
// // adapter.beforeEach() will throw if detox.init() is still running; that allows us to run detox.cleanup()
|
||||
// // in that emergency case and disable calling 'device', 'element', 'expect', 'by' and other Detox globals.
|
||||
// // If you switch to 'jest-circus' runner, you can omit this try-catch workaround at all.
|
||||
|
||||
afterEach(async function () {
|
||||
await adapter.afterEach(this);
|
||||
});
|
||||
// await detox.cleanup();
|
||||
// throw err;
|
||||
// }
|
||||
// });
|
||||
|
||||
after(async () => {
|
||||
afterAll(async () => {
|
||||
await adapter.afterAll();
|
||||
await detox.cleanup();
|
||||
});
|
||||
|
||||
34
e2e/screens/notes/compose.disabledspec.ts
Normal file
34
e2e/screens/notes/compose.disabledspec.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
// import { openComposeNewNoteScreen } from '../../helpers';
|
||||
// import faker from 'faker';
|
||||
// import { element, by, expect, waitFor } from 'detox';
|
||||
|
||||
// describe('Compose', () => {
|
||||
// describe('Form', () => {
|
||||
// // beforeAll(async () => {
|
||||
// // await openComposeNewNoteScreen();
|
||||
// // });
|
||||
|
||||
// // it('should have the "Title" and "Content" fields visible', async () => {
|
||||
// // await expect(element(by.id('noteTitleField'))).toBeVisible();
|
||||
// // await expect(element(by.id('noteContentField'))).toBeVisible();
|
||||
// // });
|
||||
|
||||
// // it('should have the back button visible', async () => {
|
||||
// // await expect(element(by.id('headerButton'))).toBeVisible();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('New note', () => {
|
||||
// // it('should be created with only the "Title"', async () => {
|
||||
// // const noteTitle = faker.random.word();
|
||||
// // await element(by.id('noteTitleField')).typeText(noteTitle);
|
||||
// // await waitFor(element(by.id('noteTitleField')))
|
||||
// // .toHaveText(noteTitle)
|
||||
// // .withTimeout(2000);
|
||||
// // });
|
||||
|
||||
// // afterEach(async () => {
|
||||
// // await element(by.id('headerButton')).tap();
|
||||
// // });
|
||||
// // });
|
||||
// });
|
||||
@@ -1,34 +0,0 @@
|
||||
/* eslint-disable no-undef */
|
||||
const helpers = require('../../helpers');
|
||||
const faker = require('faker');
|
||||
|
||||
describe('Compose', () => {
|
||||
describe('Form', () => {
|
||||
before(async () => {
|
||||
await helpers.openComposeNewNoteScreen();
|
||||
});
|
||||
|
||||
it('should have the "Title" and "Content" fields visible', async () => {
|
||||
await expect(element(by.id('noteTitleField'))).toBeVisible();
|
||||
await expect(element(by.id('noteContentField'))).toBeVisible();
|
||||
});
|
||||
|
||||
it('should have the back button visible', async () => {
|
||||
await expect(element(by.id('headerButton'))).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
describe('New note', () => {
|
||||
it('should be created with only the "Title"', async () => {
|
||||
const noteTitle = faker.random.word();
|
||||
await element(by.id('noteTitleField')).typeText(noteTitle);
|
||||
await waitFor(element(by.id('noteTitleField')))
|
||||
.toHaveText(noteTitle)
|
||||
.withTimeout(2000);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await element(by.id('headerButton')).tap();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable no-undef */
|
||||
const helpers = require('../../helpers');
|
||||
import { element, by, device, expect, waitFor } from 'detox';
|
||||
|
||||
describe('Account section', () => {
|
||||
describe('Form', () => {
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
await helpers.openSettingsScreen();
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('Account section', () => {
|
||||
});
|
||||
|
||||
describe('Register', () => {
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
await helpers.openSettingsScreen();
|
||||
});
|
||||
|
||||
@@ -45,8 +45,12 @@ describe('Account section', () => {
|
||||
await element(by.id('otherOptionsButton')).tap();
|
||||
await element(by.id('syncServerField')).clearText();
|
||||
await element(by.id('syncServerField')).typeText(
|
||||
helpers.randomCredentials.syncServerUrl
|
||||
helpers.randomCredentials.syncServerUrl + '\n'
|
||||
);
|
||||
// wait for buttons to be visible after closing keyboard on smaller devices
|
||||
await waitFor(element(by.id('registerButton')))
|
||||
.toBeVisible()
|
||||
.withTimeout(1000);
|
||||
await element(by.id('registerButton')).tap();
|
||||
|
||||
// A confirmation screen is shown after we click the register button...
|
||||
@@ -60,7 +64,7 @@ describe('Account section', () => {
|
||||
await element(by.id('registerConfirmButton')).tap();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
afterAll(async () => {
|
||||
await helpers.openSettingsScreen();
|
||||
|
||||
// Account is created and we now proceed to sign out...
|
||||
@@ -68,13 +72,19 @@ describe('Account section', () => {
|
||||
await element(by.id('signOutButton')).tap();
|
||||
|
||||
// Confirmation button in the dialog...
|
||||
await expect(element(by.text('SIGN OUT'))).toBeVisible();
|
||||
await element(by.text('SIGN OUT')).tap();
|
||||
if (device.getPlatform() === 'ios') {
|
||||
await element(
|
||||
by.label('Sign Out').and(by.type('_UIAlertControllerActionView'))
|
||||
).tap();
|
||||
} else {
|
||||
await expect(element(by.text('SIGN OUT'))).toBeVisible();
|
||||
await element(by.text('SIGN OUT')).tap();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sign In', () => {
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
await helpers.openSettingsScreen();
|
||||
});
|
||||
|
||||
@@ -88,8 +98,12 @@ describe('Account section', () => {
|
||||
await element(by.id('otherOptionsButton')).tap();
|
||||
await element(by.id('syncServerField')).clearText();
|
||||
await element(by.id('syncServerField')).typeText(
|
||||
helpers.randomCredentials.syncServerUrl
|
||||
helpers.randomCredentials.syncServerUrl + '\n'
|
||||
);
|
||||
// wait for button to be visible after keyboard close
|
||||
await waitFor(element(by.id('signInButton')))
|
||||
.toBeVisible()
|
||||
.withTimeout(1000);
|
||||
await element(by.id('signInButton')).tap();
|
||||
});
|
||||
});
|
||||
@@ -1,8 +1,8 @@
|
||||
/* eslint-disable no-undef */
|
||||
const helpers = require('../../helpers');
|
||||
import { expect, element, by, device } from 'detox';
|
||||
|
||||
describe('Options section', () => {
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
await helpers.openSettingsScreen();
|
||||
});
|
||||
|
||||
@@ -16,21 +16,26 @@ describe('Options section', () => {
|
||||
|
||||
it('should restore to "Export Data" if dialog is dismissed', async () => {
|
||||
await expect(element(by.id('exportData-option-decrypted'))).toBeVisible();
|
||||
await element(by.id('exportData-option-decrypted')).tap();
|
||||
await device.pressBack();
|
||||
await expect(element(by.id('exportData-title'))).toHaveText(
|
||||
'Export Data'
|
||||
);
|
||||
if (device.getPlatform() === 'android') {
|
||||
await element(by.id('exportData-option-decrypted')).tap();
|
||||
await device.pressBack();
|
||||
await expect(element(by.id('exportData-title'))).toHaveText(
|
||||
'Export Data'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should export decrypted notes', async () => {
|
||||
await expect(element(by.id('exportData-option-decrypted'))).toBeVisible();
|
||||
await element(by.id('exportData-option-decrypted')).tap();
|
||||
await element(by.text('SAVE TO DISK')).tap();
|
||||
await element(by.text('DONE')).tap();
|
||||
await expect(element(by.id('exportData-title'))).toHaveText(
|
||||
'Export Data'
|
||||
);
|
||||
if (device.getPlatform() === 'android') {
|
||||
await element(by.id('exportData-option-decrypted')).tap();
|
||||
await element(by.text('SAVE TO DISK')).tap();
|
||||
|
||||
await element(by.text('DONE')).tap();
|
||||
await expect(element(by.id('exportData-title'))).toHaveText(
|
||||
'Export Data'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
34
package.json
34
package.json
@@ -62,24 +62,28 @@
|
||||
"@babel/core": "^7.9.6",
|
||||
"@babel/runtime": "^7.9.6",
|
||||
"@react-native-community/eslint-config": "^1.1.0",
|
||||
"@types/detox": "^14.5.2",
|
||||
"@types/faker": "^4.1.11",
|
||||
"@types/jest": "^25.2.1",
|
||||
"@types/lodash": "^4.14.150",
|
||||
"@types/react-native": "^0.62.7",
|
||||
"@types/react-native-vector-icons": "^6.4.5",
|
||||
"@typescript-eslint/eslint-plugin": "^2.31.0",
|
||||
"@typescript-eslint/parser": "^2.31.0",
|
||||
"babel-jest": "^24.9.0",
|
||||
"detox": "^16.2.1",
|
||||
"babel-jest": "^26.0.1",
|
||||
"concurrently": "^5.2.0",
|
||||
"detox": "^16.4.0",
|
||||
"eslint": "^6.8.0",
|
||||
"faker": "^4.1.0",
|
||||
"jest": "^24.9.0",
|
||||
"jest": "^26.0.1",
|
||||
"jest-circus": "^26.0.1",
|
||||
"metro-react-native-babel-preset": "^0.58.0",
|
||||
"mocha": "^7.1.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"patch-package": "^6.2.2",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"prettier": "^2.0.5",
|
||||
"react-test-renderer": "16.11.0",
|
||||
"replace-in-file": "^6.0.0",
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"jest": {
|
||||
@@ -87,12 +91,28 @@
|
||||
},
|
||||
"detox": {
|
||||
"configurations": {
|
||||
"ios.sim.debug": {
|
||||
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/StandardNotes.app",
|
||||
"build": "xcodebuild -workspace ios/StandardNotes.xcworkspace -scheme StandardNotes -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
|
||||
"type": "ios.simulator",
|
||||
"device": {
|
||||
"type": "iPhone SE (2nd generation)"
|
||||
}
|
||||
},
|
||||
"ios.sim.release": {
|
||||
"binaryPath": "ios/build/Build/Products/Release-iphonesimulator/StandardNotes.app",
|
||||
"build": "xcodebuild -workspace ios/StandardNotes.xcworkspace -scheme StandardNotes -configuration Release -sdk iphonesimulator -derivedDataPath ios/build",
|
||||
"type": "ios.simulator",
|
||||
"device": {
|
||||
"type": "iPhone 11 Pro"
|
||||
}
|
||||
},
|
||||
"android.emu.debug": {
|
||||
"binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
|
||||
"build": "cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
|
||||
"type": "android.emulator",
|
||||
"device": {
|
||||
"avdName": "Pixel_2_XL_API_28"
|
||||
"avdName": "pixel"
|
||||
}
|
||||
},
|
||||
"android.emu.release": {
|
||||
@@ -100,10 +120,10 @@
|
||||
"build": "cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
|
||||
"type": "android.emulator",
|
||||
"device": {
|
||||
"avdName": "Pixel_2_XL_API_28"
|
||||
"avdName": "pixel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"test-runner": "mocha"
|
||||
"test-runner": "jest"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,6 +333,7 @@ export default class MainSideMenu extends AbstractSideMenu<
|
||||
>
|
||||
<SideMenuHero
|
||||
outOfSync={this.state.outOfSync}
|
||||
testID="settingsButton"
|
||||
onPress={() => {
|
||||
this.presentSettings();
|
||||
}}
|
||||
@@ -357,10 +358,7 @@ export default class MainSideMenu extends AbstractSideMenu<
|
||||
size={29}
|
||||
paddingTop={ApplicationState.isIOS ? 2 : 0}
|
||||
iconTextComponent={
|
||||
<Icon
|
||||
testID="settingsButton"
|
||||
name={StyleKit.nameForIcon(ICON_SETTINGS)}
|
||||
/>
|
||||
<Icon name={StyleKit.nameForIcon(ICON_SETTINGS)} />
|
||||
}
|
||||
/>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
TouchableOpacity,
|
||||
ViewStyle,
|
||||
TextStyle,
|
||||
ViewProps,
|
||||
} from 'react-native';
|
||||
import Circle from '@Components/Circle';
|
||||
import ThemedComponent from '@Components/ThemedComponent';
|
||||
@@ -17,6 +18,7 @@ type Props = {
|
||||
onPress: () => void;
|
||||
outOfSync: boolean;
|
||||
onOutOfSyncPress: () => void;
|
||||
testID: ViewProps['testID'];
|
||||
};
|
||||
|
||||
export default class SideMenuHero extends ThemedComponent<Props> {
|
||||
@@ -48,7 +50,10 @@ export default class SideMenuHero extends ThemedComponent<Props> {
|
||||
const textData = this.getText();
|
||||
return (
|
||||
<View style={[this.styles.cell]}>
|
||||
<TouchableOpacity onPress={this.props.onPress}>
|
||||
<TouchableOpacity
|
||||
testID={this.props.testID}
|
||||
onPress={this.props.onPress}
|
||||
>
|
||||
<Text style={this.styles.title}>{textData.title}</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
|
||||
@@ -2,24 +2,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
"target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
|
||||
"module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
"lib": ["ESNext"], /* Specify library files to be included in the compilation. */
|
||||
"allowJs": true, /* Allow javascript files to be compiled. */
|
||||
"jsx": "react-native", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
"noEmit": true, /* Do not emit outputs. */
|
||||
"isolatedModules": false, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"target": "esnext",
|
||||
"module": "ESNext",
|
||||
"lib": ["ESNext"],
|
||||
"allowJs": true,
|
||||
"jsx": "react-native",
|
||||
"noEmit": true,
|
||||
"isolatedModules": false,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
// "noImplicitAny": false,
|
||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
"baseUrl": "src", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
"moduleResolution": "node",
|
||||
"baseUrl": "src",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"paths": {
|
||||
"@Components/*": ["./components/*"],
|
||||
"@Containers/*": ["./containers/*"],
|
||||
@@ -27,12 +23,12 @@
|
||||
"@Lib/*": ["./lib/*"],
|
||||
"@Screens/*": ["./screens/*"],
|
||||
"@Style/*": ["./style/*"],
|
||||
|
||||
},
|
||||
"typeRoots": ["./src/types/*"],
|
||||
"resolveJsonModule": true,
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules", "babel.config.js", "metro.config.js", "jest.config.js", "types"
|
||||
]
|
||||
}
|
||||
"types": ["jest"],
|
||||
"typeRoots": ["./src/types/*", "node_modules/@types"],
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules", "babel.config.js", "metro.config.js", "jest.config.js", "types"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user