Compare commits
10 Commits
Author | SHA1 | Date |
---|---|---|
Oksana Stepanenko | a7e3f8a836 | 11 months ago |
Oksana Stepanenko | 93a5162f4c | 1 year ago |
Oksana Stepanenko | 6449f3d78a | 1 year ago |
Oksana Stepanenko | b21cefce6e | 1 year ago |
Oksana Stepanenko | 441fb20759 | 1 year ago |
Oksana Stepanenko | 386e7d051e | 1 year ago |
Oksana Stepanenko | 3325bdea11 | 1 year ago |
Oksana Stepanenko | c2c6089ab0 | 1 year ago |
Oksana Stepanenko | 6c166fb4cc | 1 year ago |
Oksana Stepanenko | d1c026ca81 | 1 year ago |
73 changed files with 33876 additions and 31131 deletions
@ -0,0 +1,83 @@
@@ -0,0 +1,83 @@
|
||||
/** @type {Detox.DetoxConfig} */ |
||||
module.exports = { |
||||
testRunner: { |
||||
args: { |
||||
$0: 'jest', |
||||
config: 'e2e/jest.config.js', |
||||
}, |
||||
jest: { |
||||
setupTimeout: 120000, |
||||
}, |
||||
}, |
||||
apps: { |
||||
'ios.debug': { |
||||
type: 'ios.app', |
||||
binaryPath: |
||||
'ios/build/Build/Products/Debug-iphonesimulator/taskme2.app', |
||||
build: 'xcodebuild -workspace ios/taskme2.xcworkspace -scheme taskme2 -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build', |
||||
}, |
||||
'ios.release': { |
||||
type: 'ios.app', |
||||
binaryPath: |
||||
'ios/build/Build/Products/Release-iphonesimulator/taskme2.app', |
||||
build: 'xcodebuild -workspace ios/taskme2.xcworkspace -scheme taskme2 -configuration Release -sdk iphonesimulator -derivedDataPath ios/build', |
||||
}, |
||||
'android.debug': { |
||||
type: 'android.apk', |
||||
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk', |
||||
build: 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', |
||||
reversePorts: [8081], |
||||
}, |
||||
'android.release': { |
||||
type: 'android.apk', |
||||
binaryPath: 'android/app/build/outputs/apk/release/app-release.apk', |
||||
build: 'cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release', |
||||
}, |
||||
}, |
||||
devices: { |
||||
simulator: { |
||||
type: 'ios.simulator', |
||||
device: { |
||||
type: 'iPhone 14', |
||||
}, |
||||
}, |
||||
attached: { |
||||
type: 'android.attached', |
||||
device: { |
||||
adbName: '.*', |
||||
}, |
||||
}, |
||||
emulator: { |
||||
type: 'android.emulator', |
||||
device: { |
||||
avdName: 'Pixel_2_API_28', |
||||
}, |
||||
}, |
||||
}, |
||||
configurations: { |
||||
'ios.sim.debug': { |
||||
device: 'simulator', |
||||
app: 'ios.debug', |
||||
}, |
||||
'ios.sim.release': { |
||||
device: 'simulator', |
||||
app: 'ios.release', |
||||
}, |
||||
'android.att.debug': { |
||||
device: 'attached', |
||||
app: 'android.debug', |
||||
}, |
||||
'android.att.release': { |
||||
device: 'attached', |
||||
app: 'android.release', |
||||
}, |
||||
'android.emu.debug': { |
||||
device: 'emulator', |
||||
app: 'android.debug', |
||||
}, |
||||
'android.emu.release': { |
||||
device: 'emulator', |
||||
app: 'android.release', |
||||
}, |
||||
}, |
||||
} |
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
package com.app.task_me; |
||||
|
||||
import com.wix.detox.Detox; |
||||
import com.wix.detox.config.DetoxConfig; |
||||
|
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4; |
||||
import androidx.test.filters.LargeTest; |
||||
import androidx.test.rule.ActivityTestRule; |
||||
|
||||
@RunWith(AndroidJUnit4.class) |
||||
@LargeTest |
||||
public class DetoxTest { |
||||
@Rule // (2)
|
||||
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false); |
||||
|
||||
@Test |
||||
public void runDetoxTests() { |
||||
DetoxConfig detoxConfig = new DetoxConfig(); |
||||
detoxConfig.idlePolicyConfig.masterTimeoutSec = 90; |
||||
detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60; |
||||
detoxConfig.rnContextLoadTimeoutSec = (BuildConfig.DEBUG ? 180 : 60); |
||||
|
||||
Detox.runTests(mActivityRule, detoxConfig); |
||||
} |
||||
} |
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<network-security-config> |
||||
<domain-config cleartextTrafficPermitted="true"> |
||||
<domain includeSubdomains="true">10.0.2.2</domain> |
||||
<domain includeSubdomains="true">localhost</domain> |
||||
</domain-config> |
||||
</network-security-config> |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
describe('Start', () => { |
||||
beforeAll(async () => { |
||||
await device.launchApp({ |
||||
newInstance: true, |
||||
delete: true, |
||||
}) |
||||
}) |
||||
|
||||
it('Should have sign in screen when app is started', async () => { |
||||
await expect(element(by.id('signInScreen'))).toBeVisible() |
||||
}) |
||||
}) |
@ -0,0 +1,122 @@
@@ -0,0 +1,122 @@
|
||||
/* eslint-disable no-undef */ |
||||
const { config } = require('@/config') |
||||
const { insertPhoneAndEnter, goToProfileScreen } = require('./snippets') |
||||
const { newPhoneNumber } = require('./config') |
||||
|
||||
describe('Change work phone number', () => { |
||||
beforeAll(async () => { |
||||
await device.launchApp({ |
||||
permissions: { |
||||
notifications: 'YES', |
||||
}, |
||||
newInstance: true, |
||||
delete: true, |
||||
}) |
||||
}) |
||||
|
||||
it('Should success sign in the app with default phone number (need no confirmation)', async () => { |
||||
await insertPhoneAndEnter(config.defaultPhoneNumber) |
||||
|
||||
await expect(element(by.id('homeScreen'))).toExist() |
||||
}) |
||||
|
||||
it('Should navigate to profile screen', async () => { |
||||
await goToProfileScreen() |
||||
|
||||
await expect(element(by.text('Налаштування профілю'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should open work phone number input modal', async () => { |
||||
await waitFor(element(by.id(`workPhone`))) |
||||
.toBeVisible() |
||||
.whileElement(by.id('profileScreenScroll')) |
||||
.scroll(250, 'down') |
||||
|
||||
await element(by.id(`workPhone`)).tap() |
||||
|
||||
await expect(element(by.id('workPhoneInput'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should not go further and show error message when trying to go ahead with empty phone number field', async () => { |
||||
await element(by.id('changeWorkPhoneBtn')).tap() |
||||
|
||||
await expect(element(by.id('workPhoneInputError'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should not go further and show error message when trying to go ahead with phone number existing in DB', async () => { |
||||
await element(by.id('workPhoneInput')).typeText( |
||||
config.defaultPhoneNumber, |
||||
) |
||||
await element(by.id('changeWorkPhoneBtn')).tap() |
||||
|
||||
await expect(element(by.id('workPhoneInputError'))).toBeVisible() |
||||
await expect(element(by.id('workPhoneInputError'))).toHaveText( |
||||
'Номер зайнятий', |
||||
) |
||||
}) |
||||
|
||||
it('Should show OTP input field when trying to go ahead with valid phone number that is not in DB', async () => { |
||||
await element(by.id('workPhoneInput')).clearText() |
||||
await element(by.id('workPhoneInput')).typeText(newPhoneNumber) |
||||
await element(by.id('changeWorkPhoneBtn')).tap() |
||||
|
||||
await expect(element(by.id('confirmCodeInput'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should not go further and show error message when trying to confirm change work phone number with empty OTP', async () => { |
||||
await element(by.id('confirmCodeBtn')).tap() |
||||
|
||||
await expect( |
||||
element( |
||||
by.id('confirmCodeInputError').and(by.text("Обов'язкове поле")), |
||||
), |
||||
).toBeVisible() |
||||
}) |
||||
|
||||
it('Should not go further and show error message when trying to confirm change work phone number with invalid OTP', async () => { |
||||
await element(by.id('confirmCodeInput')).typeText('0000') |
||||
await element(by.id('confirmCodeBtn')).tap() |
||||
|
||||
await expect(element(by.text('Не дійсний код'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should "Надіслати код повторно" interactive text be displayed after timer expires, press that text restarts the timer', async () => { |
||||
await waitFor(element(by.text('Надіслати код повторно'))) |
||||
.toBeVisible() |
||||
.withTimeout(config.initialTimerCount * 1000) |
||||
await element(by.text('Надіслати код повторно')).tap() |
||||
|
||||
await expect(element(by.id('timer'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should success change work phone number with correct OTP', async () => { |
||||
await element(by.id('confirmCodeInput')).clearText() |
||||
await element(by.id('confirmCodeInput')).typeText('1234') |
||||
|
||||
await expect(element(by.id('signInScreen'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should success sign in with new work phone number', async () => { |
||||
await insertPhoneAndEnter(newPhoneNumber) |
||||
await element(by.id('signInCodeInput')).typeText('1234') |
||||
|
||||
await expect(element(by.id('homeScreen'))).toExist() |
||||
}) |
||||
|
||||
it('Should change back work phone number to initial', async () => { |
||||
await goToProfileScreen() |
||||
await waitFor(element(by.id(`workPhone`))) |
||||
.toBeVisible() |
||||
.whileElement(by.id('profileScreenScroll')) |
||||
.scroll(250, 'down') |
||||
|
||||
await element(by.id(`workPhone`)).tap() |
||||
await element(by.id('workPhoneInput')).typeText( |
||||
config.defaultPhoneNumber, |
||||
) |
||||
await element(by.id('changeWorkPhoneBtn')).tap() |
||||
await element(by.id('confirmCodeInput')).typeText('1234') |
||||
|
||||
await expect(element(by.id('signInScreen'))).toBeVisible() |
||||
}) |
||||
}) |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
import { makeRandomString } from '@/shared/helpers/random-string.helper' |
||||
|
||||
export const fieldErrors = { |
||||
profileDOBInput: 'Вам має бути не менше 18 років', |
||||
profileNameInput: 'Мінімальна довжина становить 2 символи', |
||||
profileLastNameInput: 'Мінімальна довжина становить 2 символи', |
||||
profileMiddleNameInput: 'Мінімальна довжина становить 2 символи', |
||||
profilePositionInput: 'Мінімальна довжина становить 2 символи', |
||||
profilePersonalPhoneInput: 'Номер не вірний', |
||||
profileInnerPhoneInput: 'Мінімальна довжина становить 4 цифри', |
||||
profileEmailInput: 'Введений email не вірний', |
||||
} |
||||
|
||||
export const validProfileData = { |
||||
DOB: '2000-02-06T05:10:00-08:00', |
||||
Name: 'First', |
||||
LastName: 'Last', |
||||
MiddleName: 'Middle', |
||||
Position: 'Position', |
||||
PersonalPhone: `38099${makeRandomString(7, 'num')}`, |
||||
InnerPhone: '12345', |
||||
Email: `${makeRandomString()}@mail.com`, |
||||
} |
||||
|
||||
export const newPhoneNumber = makeRandomString(12, 'num') |
||||
|
||||
export const personalReason = 'Персональна ' + makeRandomString(5) |
||||
export const commonReason = 'Загальна ' + makeRandomString(5) |
@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
|
||||
/* eslint-disable no-undef */ |
||||
const { config } = require('@/config') |
||||
const { insertPhoneAndEnter, goToCreateTaskScreen } = require('./snippets') |
||||
const { personalReason, commonReason } = require('./config') |
||||
|
||||
const openAddReasonModal = async () => { |
||||
await waitFor(element(by.id('addReasonInput'))) |
||||
.toBeVisible() |
||||
.whileElement(by.id('editTaskScreenScroll')) |
||||
.scroll(50, 'down') |
||||
|
||||
await element(by.id('addReasonInput')).tap() |
||||
} |
||||
|
||||
describe('Create task reason', () => { |
||||
beforeAll(async () => { |
||||
await device.launchApp({ |
||||
permissions: { |
||||
notifications: 'YES', |
||||
}, |
||||
newInstance: true, |
||||
delete: true, |
||||
}) |
||||
}) |
||||
|
||||
it('Should success sign in the app with default phone number (need no confirmation)', async () => { |
||||
await insertPhoneAndEnter(config.defaultPhoneNumber) |
||||
|
||||
await expect(element(by.id('homeScreen'))).toExist() |
||||
}) |
||||
|
||||
it('Should navigate to create task screen', async () => { |
||||
await goToCreateTaskScreen() |
||||
|
||||
await expect(element(by.text('Нова задача'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should open add new reason modal', async () => { |
||||
await openAddReasonModal() |
||||
|
||||
await expect(element(by.id('reasonInput'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should close add reason modal and show error when trying to save personal reason with empty reason name field', async () => { |
||||
await element(by.id('savePersonalReasonBtn')).tap() |
||||
|
||||
await expect(element(by.id('reasonInput'))).not.toBeVisible() |
||||
await expect( |
||||
element(by.id('infoModalTxt').and(by.text('Виникла помилка'))), |
||||
).toBeVisible() |
||||
|
||||
await element(by.id('infoModalBtn')).tap() |
||||
}) |
||||
|
||||
it('Should close add reason modal and show error when trying to save common reason with empty reason name field', async () => { |
||||
await openAddReasonModal() |
||||
await element(by.id('saveCommonReasonBtn')).tap() |
||||
|
||||
await expect(element(by.id('reasonInput'))).not.toBeVisible() |
||||
await expect( |
||||
element(by.id('infoModalTxt').and(by.text('Виникла помилка'))), |
||||
).toBeVisible() |
||||
|
||||
await element(by.id('infoModalBtn')).tap() |
||||
}) |
||||
|
||||
it('Should successful add new personal reason and automatically set new reason as task reason', async () => { |
||||
await openAddReasonModal() |
||||
if (device.getPlatform() === 'android') |
||||
await element(by.id('reasonInput')).replaceText(personalReason) |
||||
else await element(by.id('reasonInput')).typeText(personalReason) |
||||
|
||||
await element(by.id('savePersonalReasonBtn')).tap() |
||||
|
||||
await expect(element(by.id('reasonInput'))).not.toBeVisible() |
||||
await expect(element(by.id('reasonSelectValue'))).toHaveText( |
||||
personalReason, |
||||
) |
||||
}) |
||||
|
||||
it('Should successful add new common reason and automatically set new reason as task reason', async () => { |
||||
await openAddReasonModal() |
||||
if (device.getPlatform() === 'android') |
||||
await element(by.id('reasonInput')).replaceText(commonReason) |
||||
else await element(by.id('reasonInput')).typeText(commonReason) |
||||
await element(by.id('saveCommonReasonBtn')).tap() |
||||
|
||||
await expect(element(by.id('reasonInput'))).not.toBeVisible() |
||||
await expect(element(by.id('reasonSelectValue'))).toHaveText( |
||||
commonReason, |
||||
) |
||||
}) |
||||
}) |
@ -0,0 +1,85 @@
@@ -0,0 +1,85 @@
|
||||
const { config } = require('@/config') |
||||
const { insertPhoneAndEnter, goToCreateTaskScreen } = require('./snippets') |
||||
|
||||
const requiredFields = ['executor', 'initiator', 'description', 'title'] |
||||
const currentDate = new Date() |
||||
|
||||
const checkFieldHasError = async fieldName => { |
||||
const field = `task${fieldName.charAt(0).toUpperCase()}${fieldName.slice( |
||||
1, |
||||
)}` |
||||
await waitFor( |
||||
element( |
||||
by |
||||
.id(`${field}Error`) |
||||
.and(by.text("Поле обов'язкове до заповнення")), |
||||
), |
||||
) |
||||
.toBeVisible() |
||||
.whileElement(by.id('editTaskScreenScroll')) |
||||
.scroll(250, 'up') |
||||
} |
||||
|
||||
describe('Create task reason', () => { |
||||
beforeAll(async () => { |
||||
await device.launchApp({ |
||||
permissions: { |
||||
notifications: 'YES', |
||||
}, |
||||
newInstance: true, |
||||
delete: true, |
||||
}) |
||||
}) |
||||
|
||||
it('Should success sign in the app with default phone number (need no confirmation)', async () => { |
||||
await insertPhoneAndEnter(config.defaultPhoneNumber) |
||||
await expect(element(by.id('homeScreen'))).toExist() |
||||
}) |
||||
|
||||
it('Should navigate to create task screen', async () => { |
||||
await goToCreateTaskScreen() |
||||
await expect(element(by.text('Нова задача'))).toBeVisible() |
||||
}) |
||||
|
||||
// ***** TEST SAVE EMPTY FIELDS *****
|
||||
|
||||
it('Should show errors under required fields when trying to create task without filling in those fields', async () => { |
||||
await element(by.id('editTaskScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('saveTaskBtn')).tap() |
||||
|
||||
for await (const field of requiredFields) { |
||||
await checkFieldHasError(field) |
||||
} |
||||
}) |
||||
|
||||
it("Should show error when trying to create task with end date that doesn't meet requirements", async () => { |
||||
await element(by.id('editTaskScreenScroll')).scrollTo('top') |
||||
await element(by.id('taskStartDate')).tap() |
||||
if (device.getPlatform() === 'android') { |
||||
const date = currentDate.getDate() |
||||
await element(by.text(date.toString())).swipe('up', 'slow') |
||||
await element(by.id('datePickerView')).tap({ x: 30, y: -200 }) |
||||
} else { |
||||
const datePicker = element(by.id('datePicker')) |
||||
await datePicker.setDatePickerDate( |
||||
new Date( |
||||
currentDate.setDate(currentDate.getDate() + 2), |
||||
).toISOString(), |
||||
'ISO8601', |
||||
) |
||||
await element(by.type('RCTRootContentView')).tap({ x: 0, y: 0 }) |
||||
} |
||||
|
||||
await element(by.id('editTaskScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('saveTaskBtn')).tap() |
||||
|
||||
await waitFor(element(by.id('taskDateError'))) |
||||
.toBeVisible() |
||||
.whileElement(by.id('editTaskScreenScroll')) |
||||
.scroll(250, 'up') |
||||
await expect(element(by.id('taskDateError'))).toBeVisible() |
||||
await expect(element(by.id('taskDateError'))).toHaveText( |
||||
'Кінцева дата не може бути меншою, ніж початкова', |
||||
) |
||||
}) |
||||
}) |
@ -0,0 +1,273 @@
@@ -0,0 +1,273 @@
|
||||
/* eslint-disable no-undef */ |
||||
const { config } = require('@/config') |
||||
const { insertPhoneAndEnter, goToProfileScreen } = require('./snippets') |
||||
const { fieldErrors, validProfileData } = require('./config') |
||||
|
||||
const testSaveClearedField = async fieldName => { |
||||
await element(by.id(`profile${fieldName}Input`)).clearText() |
||||
await element(by.id('profileScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('profileSaveBtn')).tap() |
||||
await waitFor( |
||||
element( |
||||
by |
||||
.id(`profile${fieldName}InputError`) |
||||
.and(by.text('Поле обовязкове до заповнення')), |
||||
), |
||||
) |
||||
.toBeVisible() |
||||
.whileElement(by.id('profileScreenScroll')) |
||||
.scroll(250, 'up') |
||||
} |
||||
|
||||
const testSaveInvalidData = async (fieldName, newValue) => { |
||||
const field = `profile${fieldName}Input` |
||||
await element(by.id(field)).replaceText(newValue) |
||||
|
||||
await element(by.id('profileScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('profileSaveBtn')).tap() |
||||
await waitFor( |
||||
element( |
||||
by |
||||
.id(`profile${fieldName}InputError`) |
||||
.and(by.text(fieldErrors[field])), |
||||
), |
||||
) |
||||
.toBeVisible() |
||||
.whileElement(by.id('profileScreenScroll')) |
||||
.scroll(250, 'up') |
||||
} |
||||
|
||||
const doBeforeActions = async () => { |
||||
if (device.getPlatform() === 'android') |
||||
await device.launchApp({ newInstance: true }) |
||||
else await device.reloadReactNative() |
||||
await goToProfileScreen() |
||||
} |
||||
|
||||
const insertNewValue = async (fieldName, newValue) => { |
||||
const field = `profile${fieldName}Input` |
||||
|
||||
if (fieldName === 'DOB') { |
||||
await element(by.id('profileDOBInput')).tap() |
||||
const datePicker = element(by.id('datePicker')) |
||||
await datePicker.setDatePickerDate(newValue, 'ISO8601') |
||||
await element(by.id('selectDateBtn')).tap() |
||||
} else { |
||||
await element(by.id(field)).clearText() |
||||
await element(by.id(field)).typeText(newValue) |
||||
} |
||||
} |
||||
|
||||
const fillFields = async (fieldName, newValue) => { |
||||
if (device.getPlatform() === 'android' && fieldName === 'DOB') return |
||||
|
||||
const field = `profile${fieldName}Input` |
||||
|
||||
try { |
||||
await insertNewValue(fieldName, newValue) |
||||
} catch (e) { |
||||
await waitFor(element(by.id(field))) |
||||
.toBeVisible() |
||||
.whileElement(by.id('profileScreenScroll')) |
||||
.scroll(250, 'down') |
||||
|
||||
await insertNewValue(fieldName, newValue) |
||||
} |
||||
} |
||||
|
||||
describe('Edit profile', () => { |
||||
beforeAll(async () => { |
||||
await device.launchApp({ |
||||
permissions: { |
||||
camera: 'YES', |
||||
notifications: 'YES', |
||||
medialibrary: 'YES', |
||||
photos: 'YES', |
||||
}, |
||||
newInstance: true, |
||||
delete: true, |
||||
}) |
||||
}) |
||||
|
||||
it('Should success sign in the app with default phone number (need no confirmation)', async () => { |
||||
await insertPhoneAndEnter(config.defaultPhoneNumber) |
||||
|
||||
await expect(element(by.id('homeScreen'))).toExist() |
||||
}) |
||||
|
||||
it('Should navigate to profile screen', async () => { |
||||
await goToProfileScreen() |
||||
|
||||
await expect(element(by.text('Налаштування профілю'))).toBeVisible() |
||||
}) |
||||
|
||||
// ***** TEST SAVE EMPTY FIELDS *****
|
||||
|
||||
it('Should show error when save account data with empty name', async () => { |
||||
await testSaveClearedField('Name') |
||||
}) |
||||
|
||||
it('Should show error when save account data with empty last name', async () => { |
||||
await testSaveClearedField('LastName') |
||||
}) |
||||
|
||||
it('Should show error when save account data with empty middle name', async () => { |
||||
await testSaveClearedField('MiddleName') |
||||
}) |
||||
|
||||
it('Should show error when save account data with empty position', async () => { |
||||
await testSaveClearedField('Position') |
||||
}) |
||||
|
||||
// ***** TEST SAVE INVALID FIELDS *****
|
||||
|
||||
it('Should show error when save account data with invalid date of birth (only IOS)', async () => { |
||||
await doBeforeActions() |
||||
if (device.getPlatform() !== 'android') { |
||||
await element(by.id('profileDOBInput')).tap() |
||||
const datePicker = element(by.id('datePicker')) |
||||
await datePicker.setDatePickerDate( |
||||
new Date().toISOString(), |
||||
'ISO8601', |
||||
) |
||||
await element(by.id('selectDateBtn')).tap() |
||||
await element(by.id('profileScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('profileSaveBtn')).tap() |
||||
await waitFor( |
||||
element( |
||||
by |
||||
.id('profileDOBInputError') |
||||
.and(by.text(fieldErrors.profileDOBInput)), |
||||
), |
||||
) |
||||
.toBeVisible() |
||||
.whileElement(by.id('profileScreenScroll')) |
||||
.scroll(250, 'up') |
||||
} else { |
||||
await element(by.id('profileDOBInput')).tap() |
||||
await element(by.id('selectDateBtn')).tap() |
||||
await waitFor(element(by.id('profileDOBInput'))) |
||||
.toBeVisible() |
||||
.whileElement(by.id('profileScreenScroll')) |
||||
.scroll(250, 'up') |
||||
} |
||||
}) |
||||
|
||||
it('Should show error when save account data with invalid first name', async () => { |
||||
await testSaveInvalidData('Name', 'A') |
||||
}) |
||||
|
||||
it('Should show error when save account data with invalid last name', async () => { |
||||
await testSaveInvalidData('LastName', 'A') |
||||
}) |
||||
|
||||
it('Should show error when save account data with invalid middle name', async () => { |
||||
await testSaveInvalidData('MiddleName', 'A') |
||||
}) |
||||
|
||||
it('Should show error when save account data with invalid position', async () => { |
||||
await testSaveInvalidData('Position', 'A') |
||||
}) |
||||
|
||||
it('Should show error when save account data with invalid personal phone number', async () => { |
||||
await testSaveInvalidData('PersonalPhone', '1') |
||||
}) |
||||
|
||||
it('Should show error when save account data with invalid inner phone number', async () => { |
||||
await testSaveInvalidData('InnerPhone', '1') |
||||
}) |
||||
|
||||
it('Should show error when save account data with invalid email', async () => { |
||||
await testSaveInvalidData('Email', 'invalid email') |
||||
}) |
||||
|
||||
it('Should update profile and show success message when all fields filled properly', async () => { |
||||
await doBeforeActions() |
||||
|
||||
for await (const field of Object.keys(validProfileData)) { |
||||
await fillFields(field, validProfileData[field]) |
||||
} |
||||
|
||||
await element(by.id('profileSaveBtn')).tap() |
||||
await element(by.id('confirmModalOkBtn')).tap() |
||||
|
||||
await expect( |
||||
element(by.id('infoModalTxt').and(by.text('Дані оновлені.'))), |
||||
).toBeVisible() |
||||
|
||||
await element(by.id('infoModalBtn')).tap() |
||||
}) |
||||
|
||||
it('Should change back email to initial', async () => { |
||||
await doBeforeActions() |
||||
await element(by.id('profileScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('profileEmailInput')).clearText() |
||||
await element(by.id('profileEmailInput')).typeText( |
||||
`${config.defaultPhoneNumber}@admin.com`, |
||||
) |
||||
await element(by.id('profileScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('profileSaveBtn')).tap() |
||||
await element(by.id('confirmModalOkBtn')).tap() |
||||
|
||||
await expect( |
||||
element(by.id('infoModalTxt').and(by.text('Дані оновлені.'))), |
||||
).toBeVisible() |
||||
|
||||
await element(by.id('infoModalBtn')).tap() |
||||
}) |
||||
|
||||
it('Should show message that email exists when save account data with email already existing in DB', async () => { |
||||
await doBeforeActions() |
||||
await element(by.id('profileScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('profileEmailInput')).clearText() |
||||
await element(by.id('profileEmailInput')).typeText(config.existEmail) |
||||
await element(by.id('profileScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('profileSaveBtn')).tap() |
||||
await element(by.id('confirmModalOkBtn')).tap() |
||||
|
||||
await expect( |
||||
element( |
||||
by |
||||
.id('infoModalTxt') |
||||
.and(by.text('Вказана електронна пошта вже зайнята')), |
||||
), |
||||
).toBeVisible() |
||||
|
||||
await element(by.id('infoModalBtn')).tap() |
||||
}) |
||||
|
||||
it('Should update profile avatar and show success message', async () => { |
||||
await doBeforeActions() |
||||
await element(by.id('profileAvatar')).tap() |
||||
await element(by.text('Фото з галереї')).tap() |
||||
|
||||
await expect(element(by.id('clearAvatarBtn'))).toBeVisible() |
||||
|
||||
await element(by.id('profileScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('profileSaveBtn')).tap() |
||||
await element(by.id('confirmModalOkBtn')).tap() |
||||
|
||||
await expect( |
||||
element(by.id('infoModalTxt').and(by.text('Дані оновлені.'))), |
||||
).toBeVisible() |
||||
|
||||
await element(by.id('infoModalBtn')).tap() |
||||
}) |
||||
|
||||
it('Should clear profile avatar and show success message', async () => { |
||||
await doBeforeActions() |
||||
await element(by.id('clearAvatarBtn')).tap() |
||||
|
||||
await expect(element(by.id('clearAvatarBtn'))).not.toBeVisible() |
||||
|
||||
await element(by.id('profileScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('profileSaveBtn')).tap() |
||||
await element(by.id('confirmModalOkBtn')).tap() |
||||
|
||||
await expect( |
||||
element(by.id('infoModalTxt').and(by.text('Дані оновлені.'))), |
||||
).toBeVisible() |
||||
|
||||
await element(by.id('infoModalBtn')).tap() |
||||
}) |
||||
}) |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
/** @type {import('@jest/types').Config.InitialOptions} */ |
||||
module.exports = { |
||||
rootDir: '..', |
||||
testMatch: ['<rootDir>/e2e/**/*.test.js'], |
||||
testTimeout: 120000, |
||||
maxWorkers: 1, |
||||
globalSetup: 'detox/runners/jest/globalSetup', |
||||
globalTeardown: 'detox/runners/jest/globalTeardown', |
||||
reporters: ['detox/runners/jest/reporter'], |
||||
testEnvironment: 'detox/runners/jest/testEnvironment', |
||||
verbose: true, |
||||
}; |
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
/* eslint-disable no-undef */ |
||||
const { config } = require('@/config') |
||||
const { insertPhoneAndEnter, goToProfileScreen } = require('./snippets') |
||||
|
||||
describe('Logout', () => { |
||||
beforeAll(async () => { |
||||
await device.launchApp({ |
||||
permissions: { camera: 'YES', notifications: 'YES' }, |
||||
newInstance: true, |
||||
delete: true, |
||||
}) |
||||
}) |
||||
|
||||
it('Should success sign in the app with default phone number (need no confirmation)', async () => { |
||||
await insertPhoneAndEnter(config.defaultPhoneNumber) |
||||
|
||||
await expect(element(by.id('homeScreen'))).toExist() |
||||
}) |
||||
|
||||
it('Should navigate to profile screen', async () => { |
||||
await goToProfileScreen() |
||||
|
||||
await expect(element(by.text('Налаштування профілю'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should stay on profile screen', async () => { |
||||
await element(by.id('profileScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('profileLogoutBtn')).tap() |
||||
await element(by.id('confirmModalCancelBtn')).tap() |
||||
|
||||
await expect(element(by.text('Налаштування профілю'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should success logout', async () => { |
||||
await element(by.id('profileScreenScroll')).scrollTo('bottom') |
||||
await element(by.id('profileLogoutBtn')).tap() |
||||
await element(by.id('confirmModalOkBtn')).tap() |
||||
|
||||
await expect(element(by.id('signInScreen'))).toBeVisible() |
||||
}) |
||||
}) |
@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
|
||||
/* eslint-disable no-undef */ |
||||
const { config } = require('@/config') |
||||
const { insertPhoneAndEnter } = require('./snippets') |
||||
|
||||
describe('Sign in', () => { |
||||
beforeAll(async () => { |
||||
await device.launchApp({ |
||||
permissions: { camera: 'YES', notifications: 'YES' }, |
||||
newInstance: true, |
||||
delete: true, |
||||
}) |
||||
}) |
||||
|
||||
beforeEach(async () => { |
||||
if (device.getPlatform() === 'android') |
||||
await device.launchApp({ newInstance: true, delete: true }) |
||||
else await device.reloadReactNative() |
||||
}) |
||||
|
||||
it('Should not go further and show error message when trying to sign with empty phone number', async () => { |
||||
await element(by.id('signInBtn')).tap() |
||||
|
||||
await expect(element(by.text("Обов'язкове поле"))).toExist() |
||||
}) |
||||
|
||||
it('Should not go further and show error message when trying to sign in with empty OTP', async () => { |
||||
await insertPhoneAndEnter(config.phoneNumber) |
||||
await element(by.id('sendCodeBtn')).tap() |
||||
|
||||
await expect(element(by.text("Обов'язкове поле"))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should not go further and show error message when trying to sign in with invalid OTP', async () => { |
||||
await insertPhoneAndEnter(config.phoneNumber) |
||||
await element(by.id('signInCodeInput')).typeText('0000') |
||||
await element(by.text('Вхід')).tap() |
||||
await element(by.id('sendCodeBtn')).tap() |
||||
|
||||
await expect(element(by.text('Не дійсний код'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should "Надіслати код повторно" interactive text be displayed after timer expires, press that text restarts the timer', async () => { |
||||
await insertPhoneAndEnter(config.phoneNumber) |
||||
await waitFor(element(by.text('Надіслати код повторно'))) |
||||
.toBeVisible() |
||||
.withTimeout(config.initialTimerCount * 1000) |
||||
await element(by.text('Надіслати код повторно')).tap() |
||||
|
||||
await expect(element(by.id('timer'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should not go further and show error message when trying to sign in with invalid phone number (not exists in DB))', async () => { |
||||
await insertPhoneAndEnter('123456789012') |
||||
|
||||
await expect(element(by.text('Невірно введені дані'))).toBeVisible() |
||||
}) |
||||
|
||||
it('Should success sign in the app with default phone number (need no confirmation)', async () => { |
||||
await insertPhoneAndEnter(config.defaultPhoneNumber) |
||||
|
||||
await expect(element(by.id('homeScreen'))).toExist() |
||||
}) |
||||
|
||||
it('Should success sign in the app with phone number and correct OTP', async () => { |
||||
if (device.getPlatform() === 'ios') |
||||
await device.launchApp({ |
||||
permissions: { camera: 'YES', notifications: 'YES' }, |
||||
newInstance: true, |
||||
delete: true, |
||||
}) |
||||
|
||||
await insertPhoneAndEnter(config.phoneNumber) |
||||
await element(by.id('signInCodeInput')).typeText('1234') |
||||
|
||||
await expect(element(by.id('homeScreen'))).toExist() |
||||
}) |
||||
}) |
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
export const insertPhoneAndEnter = async (phoneNumber: string) => { |
||||
await element(by.id('signInInput')).typeText(phoneNumber) |
||||
await element(by.text('Вхід')).tap() |
||||
await element(by.id('signInBtn')).tap() |
||||
} |
||||
|
||||
export const goToProfileScreen = async () => { |
||||
try { |
||||
await element(by.id('confirmModalCancelBtn')).tap() |
||||
} catch (e) {} |
||||
|
||||
await element(by.id('settingsNavBtn')).tap() |
||||
await element(by.text('Профіль користувача')).tap() |
||||
} |
||||
|
||||
export const goToCreateTaskScreen = async () => { |
||||
try { |
||||
await element(by.id('confirmModalCancelBtn')).tap() |
||||
} catch (e) {} |
||||
|
||||
await element(by.id('addTaskNavBtn')).tap() |
||||
} |
@ -1,130 +1,135 @@
@@ -1,130 +1,135 @@
|
||||
{ |
||||
"name": "taskme2", |
||||
"version": "0.0.1", |
||||
"private": true, |
||||
"scripts": { |
||||
"android": "react-native run-android", |
||||
"ios": "react-native run-ios", |
||||
"lint": "eslint .", |
||||
"start": "react-native start", |
||||
"test": "jest", |
||||
"postinstall": "react-native setup-ios-permissions && pod-install", |
||||
"build:ios": "react-native bundle --entry-file='index.js' --bundle-output='./ios/main.jsbundle' --dev=false --platform='ios'", |
||||
"build:android": "cd android && ./gradlew assembleRelease", |
||||
"build:android:prod": "cd android && ./gradlew bundleRelease" |
||||
}, |
||||
"dependencies": { |
||||
"@bitalikrty/redux-create-reducer": "^1.0.0", |
||||
"@gorhom/bottom-sheet": "^4.4.7", |
||||
"@miblanchard/react-native-slider": "^2.1.0", |
||||
"@notifee/react-native": "^7.8.0", |
||||
"@react-native-async-storage/async-storage": "^1.18.2", |
||||
"@react-native-camera-roll/camera-roll": "^5.7.2", |
||||
"@react-native-community/clipboard": "^1.5.1", |
||||
"@react-native-community/netinfo": "^9.3.10", |
||||
"@react-native-picker/picker": "^2.4.10", |
||||
"@react-navigation/bottom-tabs": "^6.5.7", |
||||
"@react-navigation/native": "^6.1.6", |
||||
"@react-navigation/native-stack": "^6.9.12", |
||||
"@react-navigation/stack": "^6.3.16", |
||||
"@tomlangan/react-native-sound-level": "^1.2.1", |
||||
"axios": "^1.4.0", |
||||
"buffer": "^6.0.3", |
||||
"cachios": "^4.0.0", |
||||
"deprecated-react-native-prop-types": "^4.1.0", |
||||
"events": "^3.3.0", |
||||
"ffmpeg-kit-react-native": "^5.1.0", |
||||
"jet-tools": "^1.3.0", |
||||
"lodash": "^4.17.21", |
||||
"mime": "^3.0.0", |
||||
"moment": "^2.29.4", |
||||
"path": "^0.12.7", |
||||
"react": "18.2.0", |
||||
"react-native": "0.71.9", |
||||
"react-native-app-badge": "^0.1.5", |
||||
"react-native-audio-recorder-player": "^3.5.3", |
||||
"react-native-autolink": "^4.1.0", |
||||
"react-native-calendars": "^1.1298.0", |
||||
"react-native-controlled-mentions": "^2.2.5", |
||||
"react-native-date-picker": "^4.2.13", |
||||
"react-native-device-info": "^10.6.0", |
||||
"react-native-document-picker": "^9.0.1", |
||||
"react-native-draggable-switch": "^1.1.1", |
||||
"react-native-drawer": "^2.5.1", |
||||
"react-native-exception-handler": "^2.10.10", |
||||
"react-native-expire-storage": "^0.0.3", |
||||
"react-native-fast-image": "^8.6.3", |
||||
"react-native-fs": "^2.20.0", |
||||
"react-native-gesture-handler": "^2.11.0", |
||||
"react-native-gifted-chat": "^2.4.0", |
||||
"react-native-html-to-pdf": "^0.12.0", |
||||
"react-native-image-crop-picker": "^0.40.0", |
||||
"react-native-image-picker": "^5.6.0", |
||||
"react-native-keyboard-aware-scroll-view": "^0.9.5", |
||||
"react-native-masked-text": "^1.13.0", |
||||
"react-native-modal": "^13.0.1", |
||||
"react-native-onesignal": "^4.5.1", |
||||
"react-native-pager-view": "^6.2.0", |
||||
"react-native-permissions": "^3.8.4", |
||||
"react-native-raw-bottom-sheet": "^2.2.0", |
||||
"react-native-reanimated": "^3.4.1", |
||||
"react-native-restart": "^0.0.27", |
||||
"react-native-safe-area-context": "^4.7.1", |
||||
"react-native-screens": "^3.23.0", |
||||
"react-native-shadow-2": "^7.0.8", |
||||
"react-native-share": "^9.2.3", |
||||
"react-native-splash-screen": "^3.3.0", |
||||
"react-native-sqlite-storage": "^6.0.1", |
||||
"react-native-svg": "^13.10.0", |
||||
"react-native-swiper": "^1.6.0", |
||||
"react-native-tab-view": "^3.5.2", |
||||
"react-native-vector-icons": "^10.0.0", |
||||
"react-native-video": "^5.2.1", |
||||
"react-native-video-controls": "^2.8.1", |
||||
"react-native-webrtc": "^111.0.1", |
||||
"react-native-wheel-pick": "^1.2.2", |
||||
"react-redux": "^8.1.2", |
||||
"redux": "^4.2.1", |
||||
"rn-fetch-blob": "^0.12.0", |
||||
"socket.io-client": "^4.5.0", |
||||
"validate.js": "^0.13.1" |
||||
}, |
||||
"devDependencies": { |
||||
"@babel/core": "^7.20.0", |
||||
"@babel/preset-env": "^7.20.0", |
||||
"@babel/runtime": "^7.20.0", |
||||
"@react-native-community/eslint-config": "^3.2.0", |
||||
"@tsconfig/react-native": "^2.0.2", |
||||
"@types/jest": "^29.2.1", |
||||
"@types/react": "^18.0.24", |
||||
"@types/react-native-drawer": "^2.5.5", |
||||
"@types/react-native-sqlite-storage": "^6.0.0", |
||||
"@types/react-native-video": "^5.0.14", |
||||
"@types/react-test-renderer": "^18.0.0", |
||||
"babel-jest": "^29.2.1", |
||||
"babel-plugin-inline-import": "^3.0.0", |
||||
"babel-plugin-module-resolver": "^5.0.0", |
||||
"babel-plugin-transform-remove-console": "^6.9.4", |
||||
"eslint": "^8.19.0", |
||||
"jest": "^29.2.1", |
||||
"metro-react-native-babel-preset": "0.73.9", |
||||
"pod-install": "^0.1.38", |
||||
"prettier": "^2.4.1", |
||||
"react-native-make": "^1.0.1", |
||||
"react-test-renderer": "18.2.0", |
||||
"reactotron-react-native": "^5.0.3", |
||||
"reactotron-redux": "^3.1.3", |
||||
"typescript": "4.8.4" |
||||
}, |
||||
"jest": { |
||||
"preset": "react-native" |
||||
}, |
||||
"reactNativePermissionsIOS": [ |
||||
"Camera", |
||||
"Contacts", |
||||
"MediaLibrary", |
||||
"Microphone", |
||||
"Notifications", |
||||
"PhotoLibrary" |
||||
] |
||||
"name": "taskme2", |
||||
"version": "0.0.1", |
||||
"private": true, |
||||
"scripts": { |
||||
"android": "react-native run-android", |
||||
"ios": "react-native run-ios", |
||||
"lint": "eslint .", |
||||
"start": "react-native start", |
||||
"postinstall": "react-native setup-ios-permissions && pod-install", |
||||
"build:ios": "react-native bundle --entry-file='index.js' --bundle-output='./ios/main.jsbundle' --dev=false --platform='ios'", |
||||
"build:android": "cd android && ./gradlew assembleRelease", |
||||
"build:android:prod": "cd android && ./gradlew bundleRelease", |
||||
"start:test": "RN_SRC_EXT=e2e.js react-native start", |
||||
"test:debug:ios": "detox test --configuration ios.sim.debug", |
||||
"test:release:ios": "detox test --configuration ios.sim.release", |
||||
"test:debug:android": "detox test --configuration android.emu.debug", |
||||
"test:release:android": "detox test --configuration android.emu.release" |
||||
}, |
||||
"dependencies": { |
||||
"@bitalikrty/redux-create-reducer": "^1.0.0", |
||||
"@gorhom/bottom-sheet": "^4.4.7", |
||||
"@miblanchard/react-native-slider": "^2.1.0", |
||||
"@notifee/react-native": "^7.8.0", |
||||
"@react-native-async-storage/async-storage": "^1.18.2", |
||||
"@react-native-camera-roll/camera-roll": "^5.7.2", |
||||
"@react-native-community/clipboard": "^1.5.1", |
||||
"@react-native-community/netinfo": "^9.3.10", |
||||
"@react-native-picker/picker": "^2.4.10", |
||||
"@react-navigation/bottom-tabs": "^6.5.7", |
||||
"@react-navigation/native": "^6.1.6", |
||||
"@react-navigation/native-stack": "^6.9.12", |
||||
"@react-navigation/stack": "^6.3.16", |
||||
"@tomlangan/react-native-sound-level": "^1.2.1", |
||||
"axios": "^1.4.0", |
||||
"buffer": "^6.0.3", |
||||
"cachios": "^4.0.0", |
||||
"deprecated-react-native-prop-types": "^4.1.0", |
||||
"events": "^3.3.0", |
||||
"ffmpeg-kit-react-native": "^5.1.0", |
||||
"jet-tools": "^1.3.0", |
||||
"lodash": "^4.17.21", |
||||
"mime": "^3.0.0", |
||||
"moment": "^2.29.4", |
||||
"path": "^0.12.7", |
||||
"react": "18.2.0", |
||||
"react-native": "0.71.9", |
||||
"react-native-app-badge": "^0.1.5", |
||||
"react-native-audio-recorder-player": "^3.5.3", |
||||
"react-native-autolink": "^4.1.0", |
||||
"react-native-calendars": "^1.1298.0", |
||||
"react-native-controlled-mentions": "^2.2.5", |
||||
"react-native-date-picker": "^4.2.13", |
||||
"react-native-device-info": "^10.6.0", |
||||
"react-native-document-picker": "^9.0.1", |
||||
"react-native-draggable-switch": "^1.1.1", |
||||
"react-native-drawer": "^2.5.1", |
||||
"react-native-exception-handler": "^2.10.10", |
||||
"react-native-expire-storage": "^0.0.3", |
||||
"react-native-fast-image": "^8.6.3", |
||||
"react-native-fs": "^2.20.0", |
||||
"react-native-gesture-handler": "^2.11.0", |
||||
"react-native-gifted-chat": "^2.4.0", |
||||
"react-native-html-to-pdf": "^0.12.0", |
||||
"react-native-image-crop-picker": "^0.40.0", |
||||
"react-native-image-picker": "^5.6.0", |
||||
"react-native-keyboard-aware-scroll-view": "^0.9.5", |
||||
"react-native-masked-text": "^1.13.0", |
||||
"react-native-modal": "^13.0.1", |
||||
"react-native-onesignal": "^4.5.1", |
||||
"react-native-pager-view": "^6.2.0", |
||||
"react-native-permissions": "^3.8.4", |
||||
"react-native-raw-bottom-sheet": "^2.2.0", |
||||
"react-native-reanimated": "^3.4.1", |
||||
"react-native-restart": "^0.0.27", |
||||
"react-native-safe-area-context": "^4.7.1", |
||||
"react-native-screens": "^3.23.0", |
||||
"react-native-shadow-2": "^7.0.8", |
||||
"react-native-share": "^9.2.3", |
||||
"react-native-splash-screen": "^3.3.0", |
||||
"react-native-sqlite-storage": "^6.0.1", |
||||
"react-native-svg": "^13.10.0", |
||||
"react-native-swiper": "^1.6.0", |
||||
"react-native-tab-view": "^3.5.2", |
||||
"react-native-vector-icons": "^10.0.0", |
||||
"react-native-video": "^5.2.1", |
||||
"react-native-video-controls": "^2.8.1", |
||||
"react-native-webrtc": "^111.0.1", |
||||
"react-native-wheel-pick": "^1.2.2", |
||||
"react-redux": "^8.1.2", |
||||
"redux": "^4.2.1", |
||||
"rn-fetch-blob": "^0.12.0", |
||||
"socket.io-client": "^4.5.0", |
||||
"validate.js": "^0.13.1" |
||||
}, |
||||
"devDependencies": { |
||||
"@babel/core": "^7.20.0", |
||||
"@babel/preset-env": "^7.20.0", |
||||
"@babel/runtime": "^7.20.0", |
||||
"@react-native-community/eslint-config": "^3.2.0", |
||||
"@tsconfig/react-native": "^2.0.2", |
||||
"@types/jest": "^29.2.1", |
||||
"@types/react": "^18.0.24", |
||||
"@types/react-native-drawer": "^2.5.5", |
||||
"@types/react-native-sqlite-storage": "^6.0.0", |
||||
"@types/react-native-video": "^5.0.14", |
||||
"@types/react-test-renderer": "^18.0.0", |
||||
"babel-jest": "^29.2.1", |
||||
"babel-plugin-inline-import": "^3.0.0", |
||||
"babel-plugin-module-resolver": "^5.0.0", |
||||
"babel-plugin-transform-remove-console": "^6.9.4", |
||||
"detox": "^20.11.4", |
||||
"eslint": "^8.19.0", |
||||
"jest": "^29.2.1", |
||||
"metro-react-native-babel-preset": "0.73.9", |
||||
"pod-install": "^0.1.38", |
||||
"prettier": "^2.4.1", |
||||
"react-native-make": "^1.0.1", |
||||
"react-test-renderer": "18.2.0", |
||||
"reactotron-react-native": "^5.0.3", |
||||
"reactotron-redux": "^3.1.3", |
||||
"typescript": "4.8.4" |
||||
}, |
||||
"jest": { |
||||
"preset": "react-native" |
||||
}, |
||||
"reactNativePermissionsIOS": [ |
||||
"Camera", |
||||
"Contacts", |
||||
"MediaLibrary", |
||||
"Microphone", |
||||
"Notifications", |
||||
"PhotoLibrary" |
||||
] |
||||
} |
||||
|
After Width: | Height: | Size: 313 KiB |
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
import { fsService } from '@/services/system' |
||||
import { base64AvatarString } from '@/shared/consts/base64Avatar' |
||||
import { base64BGString } from '@/shared/consts/base64Background' |
||||
import { Platform } from 'react-native' |
||||
|
||||
async function openPicker(options) { |
||||
const filePath = await fsService.writeFile( |
||||
{ |
||||
content: Platform.select({ |
||||
ios: base64AvatarString, |
||||
default: base64BGString, |
||||
}), |
||||
fileName: 'Test_avatar.png', |
||||
}, |
||||
'base64', |
||||
) |
||||
const path = 'file://' + filePath |
||||
|
||||
return { |
||||
filename: 'Test_avatar.png', |
||||
path, |
||||
height: 200, |
||||
width: 200, |
||||
mime: 'image/png', |
||||
size: 500, |
||||
} |
||||
} |
||||
|
||||
export default openPicker |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
import { openPicker } from 'react-native-image-crop-picker' |
||||
export default openPicker |
@ -1,15 +1,28 @@
@@ -1,15 +1,28 @@
|
||||
import AsyncStorageLib from '@react-native-async-storage/async-storage' |
||||
import AsyncStorage from '@react-native-async-storage/async-storage' |
||||
import { NativeModules } from 'react-native' |
||||
import Reactotron from 'reactotron-react-native' |
||||
|
||||
Reactotron.setAsyncStorageHandler(AsyncStorageLib) |
||||
.configure() |
||||
.useReactNative() |
||||
import { reactotronRedux } from 'reactotron-redux' |
||||
const scriptURL = NativeModules.SourceCode.scriptURL |
||||
const packagerHostname = scriptURL.split('://')[1].split(':')[0] |
||||
Reactotron.setAsyncStorageHandler(AsyncStorage) // AsyncStorage would either come from `react-native` or `@react-native-community/async-storage` depending on where you get it from
|
||||
.configure({ |
||||
name: 'React Native Demo', |
||||
host: packagerHostname, |
||||
}) |
||||
.useReactNative({ |
||||
asyncStorage: false, // there are more options to the async storage.
|
||||
networking: { |
||||
// optionally, you can turn it off with false.
|
||||
ignoreUrls: /symbolicate/, |
||||
}, |
||||
editor: false, // there are more options to editor
|
||||
errors: { veto: () => false }, // or turn it off with false
|
||||
overlay: false, // just turning off overlay
|
||||
}) |
||||
.use(reactotronRedux()) |
||||
.connect() |
||||
|
||||
const yeOldeConsoleLog = console.log |
||||
|
||||
|
||||
console.log = (...props) => { |
||||
yeOldeConsoleLog(...props) |
||||
Reactotron.log(...props) |
||||
} |
||||
} |
||||
|
@ -1,45 +1,43 @@
@@ -1,45 +1,43 @@
|
||||
import React from 'react'; |
||||
import { createIconSetFromFontello } from 'react-native-vector-icons'; |
||||
import { fontelloConfig } from '@/config/fontello.config'; |
||||
import { StyleSheet, TouchableOpacity, ViewStyle } from 'react-native'; |
||||
const Icon = createIconSetFromFontello(fontelloConfig); |
||||
import React from 'react' |
||||
import { createIconSetFromFontello } from 'react-native-vector-icons' |
||||
import { fontelloConfig } from '@/config/fontello.config' |
||||
import { TouchableOpacity, ViewStyle } from 'react-native' |
||||
const Icon = createIconSetFromFontello(fontelloConfig) |
||||
|
||||
interface IProps { |
||||
name: string |
||||
size: number |
||||
color?: string |
||||
style?: any |
||||
onPress?: () => void |
||||
btnStyle?: ViewStyle |
||||
name: string |
||||
size: number |
||||
color?: string |
||||
style?: any |
||||
onPress?: () => void |
||||
btnStyle?: ViewStyle |
||||
testID?: string |
||||
} |
||||
|
||||
export const IconComponent = ({ |
||||
onPress, |
||||
...props |
||||
}: IProps) => { |
||||
|
||||
if (onPress) { |
||||
return ( |
||||
<TouchableOpacity onPress={onPress} style={[ props.btnStyle]}> |
||||
<Icon |
||||
name={props.name} |
||||
size={props.size} |
||||
color={props.color} |
||||
style={props.style}
|
||||
/> |
||||
</TouchableOpacity> |
||||
) |
||||
} else { |
||||
return ( |
||||
<Icon |
||||
name={props.name} |
||||
size={props.size} |
||||
color={props.color} |
||||
style={props.style}
|
||||
/> |
||||
) |
||||
} |
||||
export const IconComponent = ({ onPress, ...props }: IProps) => { |
||||
if (onPress) { |
||||
return ( |
||||
<TouchableOpacity |
||||
testID={props.testID} |
||||
onPress={onPress} |
||||
style={[props.btnStyle]}> |
||||
<Icon |
||||
name={props.name} |
||||
size={props.size} |
||||
color={props.color} |
||||
style={props.style} |
||||
/> |
||||
</TouchableOpacity> |
||||
) |
||||
} else { |
||||
return ( |
||||
<Icon |
||||
testID={props.testID} |
||||
name={props.name} |
||||
size={props.size} |
||||
color={props.color} |
||||
style={props.style} |
||||
/> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const styles = StyleSheet.create({ |
||||
}) |
File diff suppressed because one or more lines are too long
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export const base64BGString = |
||||
'iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAADMElEQVR4nOzVwQnAIBQFQYXff81RUkQCOyDj1YOPnbXWPmeTRef+/3O/OyBjzh3CD95BfqICMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMO0TAAD//2Anhf4QtqobAAAAAElFTkSuQmCC' |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
const numbers = '0123456789' |
||||
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' |
||||
|
||||
export const makeRandomString = (length = 6, mod) => { |
||||
let result = '' |
||||
let characters |
||||
switch (mod) { |
||||
case 'num': |
||||
characters = numbers |
||||
break |
||||
case 'str': |
||||
characters = letters |
||||
break |
||||
default: |
||||
characters = letters + numbers |
||||
break |
||||
} |
||||
|
||||
const charactersLength = characters.length |
||||
let counter = 0 |
||||
while (counter < length) { |
||||
result += characters.charAt( |
||||
Math.floor(Math.random() * charactersLength), |
||||
) |
||||
counter += 1 |
||||
} |
||||
return result |
||||
} |
Loading…
Reference in new issue