使用 React Native 应用 运行 Expo 是否真的可以通过 Detox/Jest 测试?
Is it actually possible to make Detox/Jest tests pass with a React Native app running with Expo?
我正在尝试使用 Expo 为我的 React Native 应用程序创建自动化 UI 测试套件。我到处寻找好的教程,但是当我进入实际的测试写作部分时,我的测试甚至从未 运行 因为环境问题,例如 "Unexpected Identifier/Token" on import Icon from...
或其他我认为的愚蠢问题找不到任何关于如何修复它们的教程。我真的花了一个星期试图解决这些问题。
我是 React Native 的新手,也是 Jest/Detox/Expo
的新手
这是我的 package.json
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"test": "node_modules/.bin/jest test/**/*.spec.js",
"eject": "expo eject"
},
"jest": {
"verbose": true,
"preset": "jest-expo"
},
"dependencies": {
"apsl-react-native-button": "^3.1.1",
"react": "16.5.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
"react-native-camera": "git+https://git@github.com/react-native-community/react-native-camera.git",
"react-native-camera-roll-picker": "^1.2.3",
"react-native-elements": "^1.0.0",
"react-native-fontawesome": "^6.0.1",
"react-native-is-iphonex": "^1.0.1",
"react-native-vector-icons": "^6.2.0",
"react-navigation": "^3.1.5"
},
"devDependencies": {
"babel-preset-expo": "^5.0.0",
"bunyan-debug-stream": "^2.0.0",
"detox": "^10.0.9",
"detox-expo-helpers": "^0.6.0",
"expo-detox-hook": "^1.0.10",
"jest-expo": "^32.0.0",
"react-native-testing-library": "^1.5.0",
"react-test-renderer": "^16.8.2",
"babel-jest": "^24.1.0",
"enzyme": "^3.9.0",
"@babel/core": "^7.3.3",
"@expo/vector-icons": "^9.0.0",
"expo": "^32.0.0",
"jest": "^24.1.0"
},
"private": true,
"detox": {
"test-runner": "jest",
"configurations": {
"ios.sim": {
"binaryPath": "bin/Exponent.app",
"type": "ios.simulator",
"name": "iPhone X"
}
}
}
}
这是我遇到的错误
ip-10-101-32-118:KitchenProject bob.dole$ detox test --loglevel trace
configuration="ios.sim" loglevel="trace" artifactsLocation="artifacts/ios.sim.2019-02-21 21-54-14Z" node_modules/.bin/jest "e2e" --config=e2e/config.json --maxWorkers=1 '--testNamePattern=^((?!:android:).)*$'
● Deprecation Warning:
Option "setupTestFrameworkScriptFile" was replaced by configuration "setupFilesAfterEnv", which supports multiple paths.
Please update your configuration.
Configuration Documentation:
https://jestjs.io/docs/configuration.html
FAIL e2e/RoomLayout.spec.js
● Test suite failed to run
/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/@expo/vector-icons/FontAwesome.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import glyphMap from './vendor/react-native-vector-icons/glyphmaps/FontAwesome.json';
^^^^^^^^
SyntaxError: Unexpected identifier
> 1 | import FontAwesomeI from 'react-native-vector-icons/FontAwesome'
| ^
2 | import React from 'react'
3 |
4 | export const FontAwesome = props => (
at ScriptTransformer._transformAndBuildScript (../node_modules/jest/node_modules/jest-runtime/build/ScriptTransformer.js:440:17)
at Object.<anonymous> (../Components/icons.js:1:1)
FAIL e2e/tests/components/RoomLayoutDetox.spec.js
● Test suite failed to run
/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/@expo/vector-icons/FontAwesome.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import glyphMap from './vendor/react-native-vector-icons/glyphmaps/FontAwesome.json';
^^^^^^^^
SyntaxError: Unexpected identifier
> 1 | import FontAwesomeI from 'react-native-vector-icons/FontAwesome'
| ^
2 | import React from 'react'
3 |
4 | export const FontAwesome = props => (
at ScriptTransformer._transformAndBuildScript (../node_modules/jest/node_modules/jest-runtime/build/ScriptTransformer.js:440:17)
at Object.<anonymous> (../Components/icons.js:1:1)
Test Suites: 2 failed, 2 total
Tests: 0 total
Snapshots: 0 total
Time: 0.827s
Ran all test suites matching /e2e/i with tests matching "^((?!:android:).)*$".
child_process.js:677
throw err;
^
Error: Command failed: node_modules/.bin/jest "e2e" --config=e2e/config.json --maxWorkers=1 '--testNamePattern=^((?!:android:).)*$'
at checkExecSyncError (child_process.js:637:11)
at Object.execSync (child_process.js:674:13)
at runJest (/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/detox/local-cli/detox-test.js:166:6)
at run (/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/detox/local-cli/detox-test.js:86:7)
at Object.<anonymous> (/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/detox/local-cli/detox-test.js:229:1)
at Module._compile (internal/modules/cjs/loader.js:738:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:749:10)
at Module.load (internal/modules/cjs/loader.js:630:32)
at tryModuleLoad (internal/modules/cjs/loader.js:570:12)
at Function.Module._load (internal/modules/cjs/loader.js:562:3)
这是我的组件文件
RoomLayout.js
import React, { Component } from 'react';
import { StyleSheet, View, Text, Button } from 'react-native';
import { LayoutButtons } from './LayoutButtons';
import { CameraLauncher } from './CameraLauncher';
import { CommentsLauncher } from './CommentsLauncher';
export class RoomLayout extends Component {
render() {
return (
<View>
<Text testID='roomLayoutText' style={styles.room}>
Room Layout{"\n"}
</Text>
<Text testID='infoText' style={styles.infoText}>
Take photos from opposite corners of the room{"\n"}
</Text>
<LayoutButtons />
</View>
);
}
}
const styles = StyleSheet.create({
view: {
marginTop: 80,
textAlign: 'center',
alignItems: 'center',
justifyContent: 'center'
},
infoText: {
marginTop: -10,
fontWeight: 'normal',
textAlign: 'center',
fontSize: 12,
justifyContent: 'center',
alignSelf: 'center',
color: 'grey'
},
room: {
marginTop: 15,
fontWeight: 'bold',
textAlign: 'center',
lineHeight: 14,
fontSize: 15
}
});
这是我的 RoomLayout.spec.js 文件
import React from 'react';
// import { RoomLayout } from '../Components/RoomLayout';
import { render } from 'react-native-testing-library';
describe('RoomLayout', () => {
// *** EDIT - I have removed this code ***
// beforeEach(async () => {
// const tree = render(<RoomLayout />);
//
// });
test('should have header and info text', async () => {
await element(by.text('Room Layout'));
await element(by.id('infoText'));
await element(by.id('infoText').and(by.text(' Take photos from opposite corners of the room')));
await expect(element(by.id('layoutButtonsReference'))).toBeVisible();
});
});
使用 Expo 应用设置 Detox。您可能最适合从尚未使用过的干净应用程序入手。您需要确保已遵循基本设置 (step 1) 以使排毒在您的机器上正常工作
#安装以下devDependencies
npm i -D detox detox-expo-helpers expo-detox-hook jest
#更新package.json
将以下内容添加到您的 package.json
文件中,这将配置排毒。您可以选择您想要的iPhone类型。
"detox": {
"configurations": {
"ios.sim": {
"binaryPath": "bin/Exponent.app",
"type": "ios.simulator",
"name": "iPhone X"
}
},
"test-runner": "jest"
}
在脚本部分添加以下内容:
"scripts": {
"e2e": "detox test --configuration ios.sim"
}
这将使我们能够 运行 排毒测试,但使用 npm run e2e
#设置你的第一个测试
运行 下面设置你的第一个测试
detox init -r jest
这将在您的项目中添加一个名为 e2e
的文件夹。你会在里面找到三个文件
config.json
firstTest.spec.js
init.js
firstTest.spec.js
是一个样本测试。您需要对其进行以下更改。
const { reloadApp } = require('detox-expo-helpers');
您还需要更改以下行
await device.reloadReactNative();
到
await reloadApp();
#将 Expo Client 添加到您的项目
- 从 Expo.io/tools 下载 Expo Client iOS 应用程序。
- 解压缩 iOS IPA 并将文件夹重命名为
Exponent.app
。它会有一个文件图标,但仍然是一个文件夹。
- 创建
bin
文件夹并将 Exponent.app
放入其中,使其与上面设置的 binaryPath 匹配。
或者您可以使用以下脚本,在您的项目根目录中创建一个文件并将其命名为 setup.sh
,复制内容然后 运行 它(您可能需要给它获得 运行 的许可,您可以先通过 运行ning chmod +x setup.sh
完成,然后您可以 运行 使用 ./setup.sh
).
#!/bin/bash -e
# query expo.io to find most recent ipaUrl
IPA_URL=`curl https://expo.io/--/api/v2/versions | python -c 'import sys, json; print json.load(sys.stdin)["iosUrl"]'`
# download tar.gz
TMP_PATH=/tmp/exponent.tar.gz
wget -O $TMP_PATH $IPA_URL
# recursively make app dir
APP_PATH=bin/Exponent.app
mkdir -p $APP_PATH
# unzip tar.gz into APP_PATH
tar -C $APP_PATH -xzf $TMP_PATH
此脚本与上述步骤相同。
#运行你的第一次测试
使用 expo start -c
启动打包程序
启动您计划用于测试的模拟器(因此,如果您选择 iPhone X,请启动 iPhone X 等)。
然后在您的终端中您可以 运行 npm run e2e
如果您添加了脚本或者您可以 运行 detox test
.
您会发现您的测试会失败。没关系,这是意料之中的。
现在,如果你想让你的测试通过,你需要实施 firstTest.spec.js
中存在的所有测试用例,或者你可以抓取这些并编写你自己的。
#链接
#让你的测试通过
如果你想让你的测试通过,你可以更新以下文件,你应该得到 3 个通过的测试。
firstTest.spec.js
const { reloadApp } = require('detox-expo-helpers');
describe('Example', () => {
beforeEach(async () => {
await reloadApp();
});
it('should have welcome screen', async () => {
await expect(element(by.id('welcome'))).toBeVisible();
});
it('should show hello screen after tap', async () => {
await element(by.id('hello_button')).tap();
await expect(element(by.label('Hello!!!'))).toBeVisible();
});
it('should show world screen after tap', async () => {
await element(by.id('world_button')).tap();
await expect(element(by.label('World!!!'))).toBeVisible();
});
});
App.js
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
export default class App extends Component {
state = {
greeting: undefined
};
render () {
if (this.state.greeting) return this.renderAfterButton();
return (
<View
testID="welcome"
style={{
flex: 1,
paddingTop: 20,
justifyContent: 'center',
alignItems: 'center'
}}>
<Text style={{ fontSize: 25, marginBottom: 30 }}>Welcome</Text>
<TouchableOpacity
testID="hello_button"
onPress={this.onButtonPress.bind(this, 'Hello')}>
<Text style={{ color: 'blue', marginBottom: 20 }}>Say Hello</Text>
</TouchableOpacity>
<TouchableOpacity
testID="world_button"
onPress={this.onButtonPress.bind(this, 'World')}>
<Text style={{ color: 'blue', marginBottom: 20 }}>Say World</Text>
</TouchableOpacity>
</View>
);
}
renderAfterButton () {
return (
<View
style={{
flex: 1,
paddingTop: 20,
justifyContent: 'center',
alignItems: 'center'
}}>
<Text style={{ fontSize: 25 }}>{this.state.greeting}!!!</Text>
</View>
);
}
onButtonPress (greeting) {
this.setState({
greeting: greeting
});
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center'
}
});
我正在尝试使用 Expo 为我的 React Native 应用程序创建自动化 UI 测试套件。我到处寻找好的教程,但是当我进入实际的测试写作部分时,我的测试甚至从未 运行 因为环境问题,例如 "Unexpected Identifier/Token" on import Icon from...
或其他我认为的愚蠢问题找不到任何关于如何修复它们的教程。我真的花了一个星期试图解决这些问题。
我是 React Native 的新手,也是 Jest/Detox/Expo
的新手这是我的 package.json
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"test": "node_modules/.bin/jest test/**/*.spec.js",
"eject": "expo eject"
},
"jest": {
"verbose": true,
"preset": "jest-expo"
},
"dependencies": {
"apsl-react-native-button": "^3.1.1",
"react": "16.5.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
"react-native-camera": "git+https://git@github.com/react-native-community/react-native-camera.git",
"react-native-camera-roll-picker": "^1.2.3",
"react-native-elements": "^1.0.0",
"react-native-fontawesome": "^6.0.1",
"react-native-is-iphonex": "^1.0.1",
"react-native-vector-icons": "^6.2.0",
"react-navigation": "^3.1.5"
},
"devDependencies": {
"babel-preset-expo": "^5.0.0",
"bunyan-debug-stream": "^2.0.0",
"detox": "^10.0.9",
"detox-expo-helpers": "^0.6.0",
"expo-detox-hook": "^1.0.10",
"jest-expo": "^32.0.0",
"react-native-testing-library": "^1.5.0",
"react-test-renderer": "^16.8.2",
"babel-jest": "^24.1.0",
"enzyme": "^3.9.0",
"@babel/core": "^7.3.3",
"@expo/vector-icons": "^9.0.0",
"expo": "^32.0.0",
"jest": "^24.1.0"
},
"private": true,
"detox": {
"test-runner": "jest",
"configurations": {
"ios.sim": {
"binaryPath": "bin/Exponent.app",
"type": "ios.simulator",
"name": "iPhone X"
}
}
}
}
这是我遇到的错误
ip-10-101-32-118:KitchenProject bob.dole$ detox test --loglevel trace
configuration="ios.sim" loglevel="trace" artifactsLocation="artifacts/ios.sim.2019-02-21 21-54-14Z" node_modules/.bin/jest "e2e" --config=e2e/config.json --maxWorkers=1 '--testNamePattern=^((?!:android:).)*$'
● Deprecation Warning:
Option "setupTestFrameworkScriptFile" was replaced by configuration "setupFilesAfterEnv", which supports multiple paths.
Please update your configuration.
Configuration Documentation:
https://jestjs.io/docs/configuration.html
FAIL e2e/RoomLayout.spec.js
● Test suite failed to run
/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/@expo/vector-icons/FontAwesome.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import glyphMap from './vendor/react-native-vector-icons/glyphmaps/FontAwesome.json';
^^^^^^^^
SyntaxError: Unexpected identifier
> 1 | import FontAwesomeI from 'react-native-vector-icons/FontAwesome'
| ^
2 | import React from 'react'
3 |
4 | export const FontAwesome = props => (
at ScriptTransformer._transformAndBuildScript (../node_modules/jest/node_modules/jest-runtime/build/ScriptTransformer.js:440:17)
at Object.<anonymous> (../Components/icons.js:1:1)
FAIL e2e/tests/components/RoomLayoutDetox.spec.js
● Test suite failed to run
/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/@expo/vector-icons/FontAwesome.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import glyphMap from './vendor/react-native-vector-icons/glyphmaps/FontAwesome.json';
^^^^^^^^
SyntaxError: Unexpected identifier
> 1 | import FontAwesomeI from 'react-native-vector-icons/FontAwesome'
| ^
2 | import React from 'react'
3 |
4 | export const FontAwesome = props => (
at ScriptTransformer._transformAndBuildScript (../node_modules/jest/node_modules/jest-runtime/build/ScriptTransformer.js:440:17)
at Object.<anonymous> (../Components/icons.js:1:1)
Test Suites: 2 failed, 2 total
Tests: 0 total
Snapshots: 0 total
Time: 0.827s
Ran all test suites matching /e2e/i with tests matching "^((?!:android:).)*$".
child_process.js:677
throw err;
^
Error: Command failed: node_modules/.bin/jest "e2e" --config=e2e/config.json --maxWorkers=1 '--testNamePattern=^((?!:android:).)*$'
at checkExecSyncError (child_process.js:637:11)
at Object.execSync (child_process.js:674:13)
at runJest (/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/detox/local-cli/detox-test.js:166:6)
at run (/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/detox/local-cli/detox-test.js:86:7)
at Object.<anonymous> (/Users/bob.dole/KitchenDetail/KitchenProject/node_modules/detox/local-cli/detox-test.js:229:1)
at Module._compile (internal/modules/cjs/loader.js:738:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:749:10)
at Module.load (internal/modules/cjs/loader.js:630:32)
at tryModuleLoad (internal/modules/cjs/loader.js:570:12)
at Function.Module._load (internal/modules/cjs/loader.js:562:3)
这是我的组件文件 RoomLayout.js
import React, { Component } from 'react';
import { StyleSheet, View, Text, Button } from 'react-native';
import { LayoutButtons } from './LayoutButtons';
import { CameraLauncher } from './CameraLauncher';
import { CommentsLauncher } from './CommentsLauncher';
export class RoomLayout extends Component {
render() {
return (
<View>
<Text testID='roomLayoutText' style={styles.room}>
Room Layout{"\n"}
</Text>
<Text testID='infoText' style={styles.infoText}>
Take photos from opposite corners of the room{"\n"}
</Text>
<LayoutButtons />
</View>
);
}
}
const styles = StyleSheet.create({
view: {
marginTop: 80,
textAlign: 'center',
alignItems: 'center',
justifyContent: 'center'
},
infoText: {
marginTop: -10,
fontWeight: 'normal',
textAlign: 'center',
fontSize: 12,
justifyContent: 'center',
alignSelf: 'center',
color: 'grey'
},
room: {
marginTop: 15,
fontWeight: 'bold',
textAlign: 'center',
lineHeight: 14,
fontSize: 15
}
});
这是我的 RoomLayout.spec.js 文件
import React from 'react';
// import { RoomLayout } from '../Components/RoomLayout';
import { render } from 'react-native-testing-library';
describe('RoomLayout', () => {
// *** EDIT - I have removed this code ***
// beforeEach(async () => {
// const tree = render(<RoomLayout />);
//
// });
test('should have header and info text', async () => {
await element(by.text('Room Layout'));
await element(by.id('infoText'));
await element(by.id('infoText').and(by.text(' Take photos from opposite corners of the room')));
await expect(element(by.id('layoutButtonsReference'))).toBeVisible();
});
});
使用 Expo 应用设置 Detox。您可能最适合从尚未使用过的干净应用程序入手。您需要确保已遵循基本设置 (step 1) 以使排毒在您的机器上正常工作
#安装以下devDependencies
npm i -D detox detox-expo-helpers expo-detox-hook jest
#更新package.json
将以下内容添加到您的 package.json
文件中,这将配置排毒。您可以选择您想要的iPhone类型。
"detox": {
"configurations": {
"ios.sim": {
"binaryPath": "bin/Exponent.app",
"type": "ios.simulator",
"name": "iPhone X"
}
},
"test-runner": "jest"
}
在脚本部分添加以下内容:
"scripts": {
"e2e": "detox test --configuration ios.sim"
}
这将使我们能够 运行 排毒测试,但使用 npm run e2e
#设置你的第一个测试
运行 下面设置你的第一个测试
detox init -r jest
这将在您的项目中添加一个名为 e2e
的文件夹。你会在里面找到三个文件
config.json
firstTest.spec.js
init.js
firstTest.spec.js
是一个样本测试。您需要对其进行以下更改。
const { reloadApp } = require('detox-expo-helpers');
您还需要更改以下行
await device.reloadReactNative();
到
await reloadApp();
#将 Expo Client 添加到您的项目
- 从 Expo.io/tools 下载 Expo Client iOS 应用程序。
- 解压缩 iOS IPA 并将文件夹重命名为
Exponent.app
。它会有一个文件图标,但仍然是一个文件夹。 - 创建
bin
文件夹并将Exponent.app
放入其中,使其与上面设置的 binaryPath 匹配。
或者您可以使用以下脚本,在您的项目根目录中创建一个文件并将其命名为 setup.sh
,复制内容然后 运行 它(您可能需要给它获得 运行 的许可,您可以先通过 运行ning chmod +x setup.sh
完成,然后您可以 运行 使用 ./setup.sh
).
#!/bin/bash -e
# query expo.io to find most recent ipaUrl
IPA_URL=`curl https://expo.io/--/api/v2/versions | python -c 'import sys, json; print json.load(sys.stdin)["iosUrl"]'`
# download tar.gz
TMP_PATH=/tmp/exponent.tar.gz
wget -O $TMP_PATH $IPA_URL
# recursively make app dir
APP_PATH=bin/Exponent.app
mkdir -p $APP_PATH
# unzip tar.gz into APP_PATH
tar -C $APP_PATH -xzf $TMP_PATH
此脚本与上述步骤相同。
#运行你的第一次测试
使用 expo start -c
启动您计划用于测试的模拟器(因此,如果您选择 iPhone X,请启动 iPhone X 等)。
然后在您的终端中您可以 运行 npm run e2e
如果您添加了脚本或者您可以 运行 detox test
.
您会发现您的测试会失败。没关系,这是意料之中的。
现在,如果你想让你的测试通过,你需要实施 firstTest.spec.js
中存在的所有测试用例,或者你可以抓取这些并编写你自己的。
#链接
#让你的测试通过
如果你想让你的测试通过,你可以更新以下文件,你应该得到 3 个通过的测试。
firstTest.spec.js
const { reloadApp } = require('detox-expo-helpers');
describe('Example', () => {
beforeEach(async () => {
await reloadApp();
});
it('should have welcome screen', async () => {
await expect(element(by.id('welcome'))).toBeVisible();
});
it('should show hello screen after tap', async () => {
await element(by.id('hello_button')).tap();
await expect(element(by.label('Hello!!!'))).toBeVisible();
});
it('should show world screen after tap', async () => {
await element(by.id('world_button')).tap();
await expect(element(by.label('World!!!'))).toBeVisible();
});
});
App.js
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
export default class App extends Component {
state = {
greeting: undefined
};
render () {
if (this.state.greeting) return this.renderAfterButton();
return (
<View
testID="welcome"
style={{
flex: 1,
paddingTop: 20,
justifyContent: 'center',
alignItems: 'center'
}}>
<Text style={{ fontSize: 25, marginBottom: 30 }}>Welcome</Text>
<TouchableOpacity
testID="hello_button"
onPress={this.onButtonPress.bind(this, 'Hello')}>
<Text style={{ color: 'blue', marginBottom: 20 }}>Say Hello</Text>
</TouchableOpacity>
<TouchableOpacity
testID="world_button"
onPress={this.onButtonPress.bind(this, 'World')}>
<Text style={{ color: 'blue', marginBottom: 20 }}>Say World</Text>
</TouchableOpacity>
</View>
);
}
renderAfterButton () {
return (
<View
style={{
flex: 1,
paddingTop: 20,
justifyContent: 'center',
alignItems: 'center'
}}>
<Text style={{ fontSize: 25 }}>{this.state.greeting}!!!</Text>
</View>
);
}
onButtonPress (greeting) {
this.setState({
greeting: greeting
});
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center'
}
});