diff --git a/.detoxrc.json b/.detoxrc.json
new file mode 100644
index 00000000..217717a7
--- /dev/null
+++ b/.detoxrc.json
@@ -0,0 +1,41 @@
+{
+ "testRunner": "jest",
+ "runnerConfig": "e2e/config.json",
+ "skipLegacyWorkersInjection": true,
+ "apps": {
+ "ios": {
+ "type": "ios.app",
+ "binaryPath": "ios/build/Build/Products/Release-iphonesimulator/monsuivipsy.app",
+ "build": "xcodebuild -workspace ios/monsuivipsy.xcworkspace -configuration Release -sdk iphonesimulator -scheme monsuivipsy -derivedDataPath ios/build",
+ "bundleId": "org.reactjs.native.example.monsuivipsy"
+ },
+ "android": {
+ "type": "android.apk",
+ "binaryPath": "SPECIFY_PATH_TO_YOUR_APP_BINARY"
+ }
+ },
+ "devices": {
+ "simulator": {
+ "type": "ios.simulator",
+ "device": {
+ "type": "iPhone 13"
+ }
+ },
+ "emulator": {
+ "type": "android.emulator",
+ "device": {
+ "avdName": "Pixel_3a_API_30_x86"
+ }
+ }
+ },
+ "configurations": {
+ "ios": {
+ "device": "simulator",
+ "app": "ios"
+ },
+ "android": {
+ "device": "emulator",
+ "app": "android"
+ }
+ }
+}
\ No newline at end of file
diff --git a/App.js b/App.js
index 426fd082..8cf7f0f4 100644
--- a/App.js
+++ b/App.js
@@ -8,8 +8,10 @@ import {DiaryNotesProvider} from './src/context/diaryNotes';
import NPS from './src/services/NPS/NPS';
import VersionChecker from './src/services/versionChecker';
import {Sentry} from 'react-native-sentry';
+import { LogBox } from 'react-native';
if (!__DEV__) {
+ LogBox.ignoreAllLogs();
Sentry.config(
'https://9f0bd8f8af8444eea9f470d00a1bb411@sentry.fabrique.social.gouv.fr/54',
).install();
diff --git a/e2e/config.json b/e2e/config.json
new file mode 100644
index 00000000..1f9b30c4
--- /dev/null
+++ b/e2e/config.json
@@ -0,0 +1,9 @@
+{
+ "maxWorkers": 1,
+ "testEnvironment": "./environment",
+ "testRunner": "jest-circus/runner",
+ "testTimeout": 120000,
+ "testRegex": "\\.e2e\\.js$",
+ "reporters": ["detox/runners/jest/streamlineReporter"],
+ "verbose": true
+}
diff --git a/e2e/environment.js b/e2e/environment.js
new file mode 100644
index 00000000..7f4fc942
--- /dev/null
+++ b/e2e/environment.js
@@ -0,0 +1,23 @@
+const {
+ DetoxCircusEnvironment,
+ SpecReporter,
+ WorkerAssignReporter,
+} = require('detox/runners/jest-circus');
+
+class CustomDetoxEnvironment extends DetoxCircusEnvironment {
+ constructor(config, context) {
+ super(config, context);
+
+ // Can be safely removed, if you are content with the default value (=300000ms)
+ this.initTimeout = 300000;
+
+ // This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level.
+ // This is strictly optional.
+ this.registerListeners({
+ SpecReporter,
+ WorkerAssignReporter,
+ });
+ }
+}
+
+module.exports = CustomDetoxEnvironment;
diff --git a/e2e/onboarding.e2e.js b/e2e/onboarding.e2e.js
new file mode 100644
index 00000000..15fef8e6
--- /dev/null
+++ b/e2e/onboarding.e2e.js
@@ -0,0 +1,98 @@
+describe('Onboarding', () => {
+ beforeAll(async () => {
+ await device.launchApp({ permissions: { notifications: 'YES' } });
+ });
+
+ beforeEach(async () => {
+ await device.reloadReactNative();
+ });
+
+ it('Presentation', async () => {
+ await expect(element(by.id('screen-0'))).toBeVisible();
+
+ await expect(element(by.id('scroll-view'))).toBeVisible();
+
+ await element(by.id('scroll-view')).swipe('left', 'fast');
+ await expect(element(by.id('screen-1'))).toBeVisible();
+ await element(by.id('scroll-view')).swipe('left', 'fast');
+ await expect(element(by.id('screen-2'))).toBeVisible();
+ await element(by.id('scroll-view')).swipe('left', 'fast');
+ await expect(element(by.id('screen-3'))).toBeVisible();
+
+ await element(by.id('scroll-view')).swipe('right', 'fast');
+ await expect(element(by.id('screen-2'))).toBeVisible();
+ await element(by.id('scroll-view')).swipe('right', 'fast');
+ await expect(element(by.id('screen-1'))).toBeVisible();
+
+ await element(by.id('next-button')).tap();
+ await expect(element(by.id('screen-2'))).toBeVisible();
+ await element(by.id('next-button')).tap();
+ await expect(element(by.id('screen-3'))).toBeVisible();
+
+ try { // check button disabled
+ await element(by.id('start-button')).tap();
+ await expect(element(by.id('__unknown'))).toBeVisible();
+ } catch(e) {}
+
+ await element(by.id('check-box')).tap();
+
+ await element(by.id('start-button')).tap();
+ });
+
+ it('Supported', async () => {
+ await element(by.id('not-followed-button')).tap();
+ });
+
+ it('Symptoms', async () => {
+ await element(by.id('scroll-view')).scrollTo('bottom');
+
+ try { // check button disabled
+ await element(by.id('validate-button')).tap();
+ await expect(element(by.id('__unknown'))).toBeVisible();
+ } catch(e) {}
+
+ await element(by.id('scroll-view')).scrollTo('top');
+
+ await element(by.id('check-box-mood')).tap();
+ await element(by.id('check-box-anxiety')).tap();
+
+ await element(by.id('custom-input')).typeText('Custom');
+ await element(by.id('custom-add-button')).tap();
+ await expect(element(by.text('Custom'))).toBeVisible();
+
+ await element(by.id('scroll-view')).scrollTo('bottom');
+
+ await element(by.id('validate-button')).tap();
+ });
+
+ it('Drugs', async () => {
+ await element(by.id('add-drugs-button')).tap();
+
+ await waitFor(element(by.text('Champix'))).toBeVisible().whileElement(by.id('scroll-view')).scroll(300, 'down', 0.5, 0.7);
+
+ await element(by.id('check-box-champix')).tap();
+
+ await waitFor(element(by.text('Deroxat'))).toBeVisible().whileElement(by.id('scroll-view')).scroll(300, 'down', 0.5, 0.7);
+
+ await element(by.id('check-box-deroxat')).tap();
+
+ await element(by.id('validate-button')).tap();
+
+ await expect(element(by.id('drug-item-deroxat'))).toBeVisible();
+ await expect(element(by.id('drug-item-champix'))).toBeVisible();
+
+ await element(by.id('continue-button')).tap();
+ });
+
+ it('Reminder', async () => {
+ await element(by.id('later-button')).tap();
+ });
+
+ it('Final check', async () => {
+ await element(by.id('main-button')).tap();
+ await element(by.id('day-button-0')).tap();
+ await expect(element(by.text('Humeur'))).toBeVisible();
+ await expect(element(by.text('Anxiété'))).toBeVisible();
+ await expect(element(by.text('Custom'))).toBeVisible();
+ });
+});
diff --git a/package.json b/package.json
index aba5fe51..8f366a7f 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"ios:12promax": "react-native run-ios --simulator=\"iPhone 12 Pro Max\"",
"start": "react-native start --reset-cache",
"test": "jest",
+ "detox:ios": "detox build --configuration ios && detox test --configuration ios",
"lint": "eslint .",
"android:build-aab": "cd android && ./gradlew clean && ./gradlew bundleRelease && cd ../",
"postinstall": "pod install --project-directory=ios/ --repo-update && npx jetify -r",
@@ -63,8 +64,10 @@
"@babel/runtime": "^7.12.5",
"@react-native-community/eslint-config": "^2.0.0",
"babel-jest": "^26.6.3",
+ "detox": "^19.4.2",
"eslint": "7.32.0",
"jest": "^26.6.3",
+ "jest-circus": "^27.4.6",
"metro-react-native-babel-preset": "^0.66.0",
"react-native-codegen": "^0.0.12",
"react-native-svg-transformer": "^0.14.3",
diff --git a/src/components/AddElemToList.js b/src/components/AddElemToList.js
index 4eaa512d..15c797d5 100644
--- a/src/components/AddElemToList.js
+++ b/src/components/AddElemToList.js
@@ -14,6 +14,7 @@ export default ({
placeholder = 'Ajouter...',
styleContainer,
onChangeText = console.log,
+ testID
}) => {
const [value, setValue] = useState();
@@ -29,6 +30,7 @@ export default ({
placeholder={placeholder}
placeholderTextColor="lightgrey"
style={styles.text}
+ testID={testID ? testID+'-input' : undefined}
/>
@@ -39,7 +41,8 @@ export default ({
onChange(value);
setValue('');
onChangeText('');
- }}>
+ }}
+ testID={testID ? testID+'-add-button' : undefined}>
{
const color = disabled ? 'lightgrey' : buttonColor;
return (
@@ -53,7 +54,8 @@ const Button = ({
buttonStyle,
]}
onPress={onPress}
- disabled={disabled}>
+ disabled={disabled}
+ testID={testID}>
{title}
diff --git a/src/scenes/diary/index.js b/src/scenes/diary/index.js
index 9fdab57c..1e3a44a0 100644
--- a/src/scenes/diary/index.js
+++ b/src/scenes/diary/index.js
@@ -53,8 +53,13 @@ const Diary = ({navigation}) => {
else {
const isFirstAppLaunch = await localStorage.getIsFirstAppLaunch();
if (isFirstAppLaunch !== 'false') {
- navigation.navigate('onboarding', {
- screen: onboardingStep || 'OnboardingPresentation',
+ navigation.reset({
+ routes: [
+ {
+ name: 'onboarding',
+ params: {screen: onboardingStep || 'OnboardingPresentation'},
+ },
+ ],
});
}
}
diff --git a/src/scenes/onboarding/onboarding.js b/src/scenes/onboarding/onboarding.js
index d75e6c59..6350e797 100644
--- a/src/scenes/onboarding/onboarding.js
+++ b/src/scenes/onboarding/onboarding.js
@@ -51,7 +51,7 @@ const Onboarding = ({navigation}) => {
Mon Suivi Psy m'accompagne entre mes consultations
-
+
{
// dirty hack because of this issue
@@ -84,6 +84,7 @@ const Onboarding = ({navigation}) => {
style={styles.checkbox}
value={isCguChecked}
onValueChange={(newValue) => setIsCguChecked(newValue)}
+ testID='check-box'
/>
En cochant cette case, vous acceptez nos{' '}
@@ -106,18 +107,19 @@ const Onboarding = ({navigation}) => {
>
) : (
-
+
)
) : (
-
+
)}
diff --git a/src/scenes/onboarding/onboardingDrugs/drug-item.js b/src/scenes/onboarding/onboardingDrugs/drug-item.js
index 95c5609a..50fa29ce 100644
--- a/src/scenes/onboarding/onboardingDrugs/drug-item.js
+++ b/src/scenes/onboarding/onboardingDrugs/drug-item.js
@@ -11,7 +11,7 @@ import {colors} from '../../../utils/colors';
import RNPickerSelect from 'react-native-picker-select';
import Icon from '../../../components/Icon';
-export default ({drug, onChange, showPosology, onClose}) => {
+export default ({drug, onChange, showPosology, onClose, testID}) => {
const [showFreeText, setShowFreeText] = useState(false);
const [freeText, setFreeText] = useState('');
@@ -28,7 +28,7 @@ export default ({drug, onChange, showPosology, onClose}) => {
const render = () => {
return (
-
+
diff --git a/src/scenes/onboarding/onboardingDrugs/index.js b/src/scenes/onboarding/onboardingDrugs/index.js
index 9eab987e..36a5c6cd 100644
--- a/src/scenes/onboarding/onboardingDrugs/index.js
+++ b/src/scenes/onboarding/onboardingDrugs/index.js
@@ -135,6 +135,7 @@ const Drugs = ({navigation, route}) => {
onChange={handleDrugChange}
showPosology={false}
onClose={() => handleDelete(e)}
+ testID={e.name1 ? 'drug-item-'+e.name1.trim().toLowerCase() : undefined}
/>
))}
@@ -143,7 +144,8 @@ const Drugs = ({navigation, route}) => {
navigation.navigate('reminder', {onboarding: true})}
- style={styles.setupButton}>
+ style={styles.setupButton}
+ testID='continue-button'>
Continuer
diff --git a/src/scenes/onboarding/onboardingDrugs/list.js b/src/scenes/onboarding/onboardingDrugs/list.js
index 11992f03..7ee773c8 100644
--- a/src/scenes/onboarding/onboardingDrugs/list.js
+++ b/src/scenes/onboarding/onboardingDrugs/list.js
@@ -160,7 +160,8 @@ const Drugs = ({navigation, route}) => {
ref={scrollRef}
style={styles.container}
contentContainerStyle={styles.scrollContainer}
- keyboardShouldPersistTaps="handled">
+ keyboardShouldPersistTaps="handled"
+ testID="scroll-view">
{!filteredList ? (
Chargement
) : (
@@ -198,6 +199,7 @@ const Drugs = ({navigation, route}) => {
style={styles.checkbox}
value={!!treatment.find((x) => x.id === e.id)}
onValueChange={(newValue) => setToogleCheckbox(e, newValue)}
+ testID={e.name1 ? "check-box-"+e.name1.trim().toLowerCase() : undefined}
/>
))}
@@ -205,7 +207,9 @@ const Drugs = ({navigation, route}) => {
)}
-
+
);
diff --git a/src/scenes/onboarding/onboardingDrugs/no-data.js b/src/scenes/onboarding/onboardingDrugs/no-data.js
index 624f5e32..6a1df1fd 100644
--- a/src/scenes/onboarding/onboardingDrugs/no-data.js
+++ b/src/scenes/onboarding/onboardingDrugs/no-data.js
@@ -28,6 +28,7 @@ export default ({navigation}) => {
title="Ajouter un traitement"
buttonStyle={styles.button}
textStyle={{fontSize: 14, fontWeight: 'bold'}}
+ testID='add-drugs-button'
/>
Informations sur les traitements
diff --git a/src/scenes/onboarding/onboardingSupported/index.js b/src/scenes/onboarding/onboardingSupported/index.js
index 6e7b673c..8d0600ec 100644
--- a/src/scenes/onboarding/onboardingSupported/index.js
+++ b/src/scenes/onboarding/onboardingSupported/index.js
@@ -38,7 +38,8 @@ const Supported = ({navigation}) => {
+ contentContainerStyle={styles.scrollContainer}
+ testID="scroll-view">
Bienvenue sur{'\n'}Mon Suivi Psy
@@ -63,6 +64,7 @@ const Supported = ({navigation}) => {
title="Je ne suis pas suivi"
color="#F4FCFD"
handleClick={() => handleClick('NO')}
+ testID="not-followed-button"
/>
{
);
};
-const Card = ({title, handleClick}) => {
+const Card = ({title, handleClick, testID}) => {
return (
-
+
{title}
diff --git a/src/scenes/onboarding/onboardingSymptoms/index.js b/src/scenes/onboarding/onboardingSymptoms/index.js
index c826554b..0e0d35fe 100644
--- a/src/scenes/onboarding/onboardingSymptoms/index.js
+++ b/src/scenes/onboarding/onboardingSymptoms/index.js
@@ -103,7 +103,8 @@ const SymptomScreen = ({navigation, route}) => {
+ contentContainerStyle={styles.scrollContainer}
+ testID="scroll-view">
@@ -121,6 +122,7 @@ const SymptomScreen = ({navigation, route}) => {
Je peux aussi en sélectionner parmi ces exemples :
@@ -145,6 +147,7 @@ const SymptomScreen = ({navigation, route}) => {
onTintColor={colors.LIGHT_BLUE}
onAnimationType="bounce"
offAnimationType="bounce"
+ testID={'check-box-'+cat.toLowerCase()}
/>
))}
@@ -163,6 +166,7 @@ const SymptomScreen = ({navigation, route}) => {
title="Valider"
onPress={nextOnboardingScreen}
disabled={noneSelected()}
+ testID="validate-button"
/>
diff --git a/src/scenes/onboarding/screens.js b/src/scenes/onboarding/screens.js
index 6e2a21c4..12f14ac9 100644
--- a/src/scenes/onboarding/screens.js
+++ b/src/scenes/onboarding/screens.js
@@ -22,7 +22,7 @@ const size =
screenHeight * (Dimensions.get('window').height > 700 ? 0.15 : 0.1);
export const Screen0 = () => (
-
+
@@ -41,7 +41,7 @@ export const Screen0 = () => (
);
export const Screen1 = () => (
-
+
(
);
export const Screen2 = () => (
-
+
Courbes de mes ressentis
@@ -72,7 +72,7 @@ export const Screen2 = () => (
);
export const Screen3 = () => (
-
+
Arriver préparé
diff --git a/src/scenes/reminder/reminder.js b/src/scenes/reminder/reminder.js
index 21b5bec7..f2193f7e 100644
--- a/src/scenes/reminder/reminder.js
+++ b/src/scenes/reminder/reminder.js
@@ -235,7 +235,8 @@ class Reminder extends React.Component {
+ style={[styles.laterContainer]}
+ testID='later-button'>
{reminder ? "Modifier l'heure du rappel" : 'Plus tard'}
diff --git a/src/scenes/status/index.js b/src/scenes/status/index.js
index b6aab7bd..cc4d5b8d 100644
--- a/src/scenes/status/index.js
+++ b/src/scenes/status/index.js
@@ -42,8 +42,13 @@ const Status = ({navigation}) => {
} else {
const isFirstAppLaunch = await localStorage.getIsFirstAppLaunch();
if (isFirstAppLaunch !== 'false') {
- navigation.navigate('onboarding', {
- screen: onboardingStep || 'OnboardingPresentation',
+ navigation.reset({
+ routes: [
+ {
+ name: 'onboarding',
+ params: {screen: onboardingStep || 'OnboardingPresentation'},
+ },
+ ],
});
}
}
@@ -89,7 +94,7 @@ const Status = ({navigation}) => {
setBannerProNPSVisible(false)} />
) : (
<>
-
+
Comment s'est passée ma journée
diff --git a/src/scenes/survey/selectDay.js b/src/scenes/survey/selectDay.js
index df32ec97..e9e2b4ab 100644
--- a/src/scenes/survey/selectDay.js
+++ b/src/scenes/survey/selectDay.js
@@ -55,7 +55,7 @@ const SurveyScreen = ({navigation}) => {
const dayIsDone = Object.keys(filtered).length !== 0;
return (
- startSurvey(i)}>
+ startSurvey(i)} testID={'day-button-'+i}>