Update dependencies

This commit is contained in:
Alex Demchenko
2020-05-17 10:42:28 +02:00
parent 21a60cb6d8
commit ab9541a5a6
21 changed files with 377 additions and 751 deletions

View File

@@ -40,7 +40,9 @@ jobs:
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
chmod +x ./cc-test-reporter
- run: yarn run check
- run: yarn lint
- run: yarn type-coverage
- run:
name: Run tests

View File

@@ -2,10 +2,18 @@
"env": {
"jest": true
},
"extends": ["@react-native-community", "./node_modules/gts"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jest/all",
"@react-native-community",
"plugin:prettier/recommended"
],
"plugins": ["simple-import-sort"],
"root": true,
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/member-delimiter-style": [
"error",
{
@@ -18,6 +26,13 @@
}
}
],
"@typescript-eslint/no-use-before-define": "off",
"jest/no-hooks": [
"error",
{
"allow": ["beforeEach"]
}
],
"simple-import-sort/sort": [
"error",
{ "groups": [["^\\u0000", "^@?\\w", "^[^.]", "^\\."]] }

View File

@@ -1,6 +1,5 @@
module.exports = {
...require('gts/.prettierrc.json'),
bracketSpacing: true,
jsxSingleQuote: true,
semi: false,
singleQuote: true,
}

View File

@@ -2,5 +2,4 @@
[![CircleCI](https://circleci.com/gh/flyerhq/react-native-chat-ui.svg?style=shield)](https://circleci.com/gh/flyerhq/react-native-chat-ui)
[![Test Coverage](https://api.codeclimate.com/v1/badges/cc0aeb07c6674ce204ec/test_coverage)](https://codeclimate.com/github/flyerhq/react-native-chat-ui/test_coverage)
[![type-coverage](https://img.shields.io/badge/dynamic/json.svg?label=type-coverage&suffix=%&query=$.typeCoverage.atLeast&uri=https%3A%2F%2Fraw.githubusercontent.com%2Fflyerhq%2Freact-native-chat-ui%2Fdevelop%2Fpackage.json)](https://github.com/plantain-00/type-coverage)
[![Code Style: Google](https://img.shields.io/badge/code%20style-google-blueviolet.svg)](https://github.com/google/gts)
[![type-coverage](https://img.shields.io/badge/dynamic/json.svg?label=type-coverage&suffix=%&query=$.typeCoverage.is&uri=https%3A%2F%2Fraw.githubusercontent.com%2Fflyerhq%2Freact-native-chat-ui%2Fdevelop%2Fpackage.json)](https://github.com/plantain-00/type-coverage)

View File

@@ -1,4 +1,4 @@
export const useSafeArea = jest.fn(() => ({
export const useSafeAreaInsets = jest.fn(() => ({
top: 0,
right: 0,
bottom: 0,

View File

@@ -11,36 +11,36 @@
"lib"
],
"scripts": {
"check": "gts check",
"clean": "gts clean",
"compile": "tsc -p . && copyup src/assets/*.png lib",
"fix": "gts fix",
"lint": "yarn eslint .",
"prepare": "yarn run compile",
"test": "jest",
"type-coverage": "type-coverage --at-least 100 --strict --ignore-catch --cache"
"type-coverage": "type-coverage --is 100 --strict --ignore-catch --cache"
},
"devDependencies": {
"@babel/core": "^7.9.6",
"@babel/runtime": "^7.9.6",
"@react-native-community/eslint-config": "^1.1.0",
"@testing-library/react-hooks": "^3.2.1",
"@types/jest": "^25.2.1",
"@types/react-native": "^0.62.7",
"@types/jest": "^25.2.2",
"@types/react-native": "^0.62.10",
"@types/react-test-renderer": "^16.9.2",
"@typescript-eslint/eslint-plugin": "^2.33.0",
"babel-jest": "^26.0.1",
"copyfiles": "^2.2.0",
"eslint": "^6.8.0",
"eslint": "^7.0.0",
"eslint-plugin-jest": "^23.13.0",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-simple-import-sort": "^5.0.3",
"gts": "^2.0.0",
"jest": "^26.0.1",
"metro-react-native-babel-preset": "^0.59.0",
"react": "^16.13.1",
"react-native": "^0.62.2",
"react-native-safe-area-context": "^0.7.3",
"react-native-safe-area-context": "^1.0.0",
"react-native-testing-library": "^1.13.2",
"react-test-renderer": "^16.13.1",
"type-coverage": "^2.5.0",
"typescript": "^3.8.3"
"type-coverage": "^2.6.1",
"typescript": "^3.9.2"
},
"peerDependencies": {
"react": "*",
@@ -78,6 +78,6 @@
]
},
"typeCoverage": {
"atLeast": 100
"is": 100
}
}

View File

@@ -3,14 +3,16 @@ import { fireEvent, render } from 'react-native-testing-library'
import { message, user } from '../../../utils/fixtures'
import { Chat } from '../Chat'
test('it calls onSendPress', () => {
const messages = [message]
const onSendPress = jest.fn()
const { getByA11yLabel } = render(
<Chat messages={messages} onSendPress={onSendPress} user={user} />
)
const button = getByA11yLabel('Send a message')
fireEvent.press(button)
expect(onSendPress).toHaveBeenCalledWith({ ...message, text: '' })
describe('chat', () => {
it('calls onSendPress', () => {
expect.assertions(1)
const messages = [message]
const onSendPress = jest.fn()
const { getByA11yLabel } = render(
<Chat messages={messages} onSendPress={onSendPress} user={user} />
)
const button = getByA11yLabel('Send a message')
fireEvent.press(button)
expect(onSendPress).toHaveBeenCalledWith({ ...message, text: '' })
})
})

View File

@@ -21,6 +21,8 @@ export const Input = ({ onSendPress, textInputProps, user }: InputProps) => {
// Use `defaultValue` if provided
const [text, setText] = React.useState(textInputProps?.defaultValue ?? '')
const value = textInputProps?.value ?? text
const handleChangeText = (newText: string) => {
// Track local state in case `onChangeText` is provided and `value` is not
setText(newText)
@@ -37,8 +39,6 @@ export const Input = ({ onSendPress, textInputProps, user }: InputProps) => {
setText('')
}
const value = textInputProps?.value ?? text
const renderInput = () => (
// Wrap container in a `View` with a background color set to
// chat background to mimic `borderRadius` on an `InputAccessoryView`

View File

@@ -6,7 +6,8 @@ import { message, user } from '../../../utils/fixtures'
import { Input } from '../Input'
describe('input', () => {
test('it sends a correct message', () => {
it('sends a correct message', () => {
expect.assertions(2)
const onSendPress = jest.fn()
const { getByPlaceholder, getByA11yLabel } = render(
<Input onSendPress={onSendPress} user={user} />
@@ -16,13 +17,14 @@ describe('input', () => {
fireEvent.changeText(textInput, 'text')
fireEvent.press(button)
expect(onSendPress).toHaveBeenCalledWith(message)
expect(textInput.props.value).toEqual('')
expect(textInput.props.value).toStrictEqual('')
})
test('it sends a correct message if onChangeText and value are provided', () => {
it('sends a correct message if onChangeText and value are provided', () => {
expect.assertions(2)
const onSendPress = jest.fn()
const value = 'value'
const onChangeText = jest.fn(newValue => {
const onChangeText = jest.fn((newValue) => {
rerender(
<Input
onSendPress={onSendPress}
@@ -43,10 +45,11 @@ describe('input', () => {
fireEvent.changeText(textInput, 'text')
fireEvent.press(button)
expect(onSendPress).toHaveBeenCalledWith(message)
expect(textInput.props.value).toEqual('text')
expect(textInput.props.value).toStrictEqual('text')
})
test('it sends a correct message if onChangeText is provided', () => {
it('sends a correct message if onChangeText is provided', () => {
expect.assertions(2)
const onSendPress = jest.fn()
const onChangeText = jest.fn()
const { getByPlaceholder, getByA11yLabel } = render(
@@ -61,10 +64,11 @@ describe('input', () => {
fireEvent.changeText(textInput, 'text')
fireEvent.press(button)
expect(onSendPress).toHaveBeenCalledWith(message)
expect(textInput.props.value).toEqual('')
expect(textInput.props.value).toStrictEqual('')
})
test('it sends a correct message if value is provided', () => {
it('sends a correct message if value is provided', () => {
expect.assertions(2)
const onSendPress = jest.fn()
const value = 'value'
const { getByPlaceholder, getByA11yLabel } = render(
@@ -75,10 +79,11 @@ describe('input', () => {
fireEvent.changeText(textInput, 'text')
fireEvent.press(button)
expect(onSendPress).toHaveBeenCalledWith({ ...message, text: value })
expect(textInput.props.value).toEqual(value)
expect(textInput.props.value).toStrictEqual(value)
})
test('it sends a correct message if defaultValue is provided', () => {
it('sends a correct message if defaultValue is provided', () => {
expect.assertions(2)
const onSendPress = jest.fn()
const defaultValue = 'defaultValue'
const { getByPlaceholder, getByA11yLabel } = render(
@@ -92,16 +97,17 @@ describe('input', () => {
const button = getByA11yLabel('Send a message')
fireEvent.press(button)
expect(onSendPress).toHaveBeenCalledWith({ ...message, text: defaultValue })
expect(textInput.props.value).toEqual('')
expect(textInput.props.value).toStrictEqual('')
})
})
describe('input per platform', () => {
beforeEach(() => {
jest.dontMock('react-native/Libraries/Utilities/Platform').resetModules()
jest.resetModules()
})
test('it renders InputAccessoryView for iOS', () => {
it('renders InputAccessoryView for iOS', () => {
expect.assertions(1)
jest.mock('react-native/Libraries/Utilities/Platform', () => ({
OS: 'ios',
select: jest.fn(),
@@ -112,12 +118,13 @@ describe('input per platform', () => {
<Input onSendPress={onSendPress} user={user} />
)
const container = getByA11yRole('toolbar')
expect((container.children[0] as ReactTestInstance).type).toEqual(
expect((container.children[0] as ReactTestInstance).type).toStrictEqual(
InputAccessoryView
)
})
test('it does not render InputAccessoryView for Android', () => {
it('does not render InputAccessoryView for Android', () => {
expect.assertions(1)
jest.mock('react-native/Libraries/Utilities/Platform', () => ({
OS: 'android',
select: jest.fn(),
@@ -128,6 +135,8 @@ describe('input per platform', () => {
<Input onSendPress={onSendPress} user={user} />
)
const container = getByA11yRole('toolbar')
expect((container.children[0] as ReactTestInstance).type).toEqual(View)
expect((container.children[0] as ReactTestInstance).type).toStrictEqual(
View
)
})
})

View File

@@ -31,6 +31,7 @@ export const SendButton = ({
{...touchableOpacityProps}
onPress={handlePress}
>
{/* type-coverage:ignore-next-line */}
<Image source={require('../../assets/icon-send.png')} />
</TouchableOpacity>
)

View File

@@ -2,10 +2,13 @@ import * as React from 'react'
import { fireEvent, render } from 'react-native-testing-library'
import { SendButton } from '../SendButton'
test('it sends an event', () => {
const onPress = jest.fn()
const { getByA11yLabel } = render(<SendButton onPress={onPress} />)
const button = getByA11yLabel('Send a message')
fireEvent.press(button)
expect(onPress).toHaveBeenCalled()
describe('send button', () => {
it('sends an event', () => {
expect.assertions(1)
const onPress = jest.fn()
const { getByA11yLabel } = render(<SendButton onPress={onPress} />)
const button = getByA11yLabel('Send a message')
fireEvent.press(button)
expect(onPress).toHaveBeenCalledWith()
})
})

View File

@@ -3,11 +3,14 @@ import { render } from 'react-native-testing-library'
import { message, size, user } from '../../../utils/fixtures'
import { TextMessage } from '../TextMessage'
test('it renders text', () => {
const text = 'text'
const { getByText } = render(
<TextMessage message={message} parentComponentSize={size} user={user} />
)
const textComponent = getByText(text)
expect(textComponent).toBeDefined()
describe('text message', () => {
it('renders text', () => {
expect.assertions(1)
const text = 'text'
const { getByText } = render(
<TextMessage message={message} parentComponentSize={size} user={user} />
)
const textComponent = getByText(text)
expect(textComponent).toBeDefined()
})
})

View File

@@ -1,23 +1,14 @@
import { act, renderHook } from '@testing-library/react-hooks'
import { onLayoutEvent, size } from '../../utils/fixtures'
import { useComponentSize } from '../useComponentSize'
test('returns correct component size', () => {
const size = {
height: 100,
width: 100,
}
const event = {
nativeEvent: {
layout: { x: 0, y: 0, ...size },
},
}
const { result } = renderHook(() => useComponentSize())
act(() => {
result.current.onLayout(event)
describe('useComponentSize', () => {
it('returns correct size', () => {
expect.assertions(1)
const { result } = renderHook(() => useComponentSize())
act(() => {
result.current.onLayout(onLayoutEvent)
})
expect(result.current.size).toStrictEqual(size)
})
expect(result.current.size).toEqual(size)
})

View File

@@ -1,88 +1,57 @@
import { act, renderHook } from '@testing-library/react-hooks'
import { KeyboardEvent } from 'react-native'
import { keyboardEvent } from '../../utils/fixtures'
import { useKeyboardBottomInset } from '../useKeyboardBottomInset'
jest.mock('react-native/Libraries/LayoutAnimation/LayoutAnimation', () => ({
...jest.requireActual(
'react-native/Libraries/LayoutAnimation/LayoutAnimation'
),
configureNext: jest.fn(),
}))
jest.mock('react-native/Libraries/Utilities/Dimensions', () => ({
get: jest.fn(() => ({ height: 812 })),
}))
const eventMock = {
duration: 10,
easing: 'keyboard',
endCoordinates: {
screenX: 0,
screenY: 333,
width: 0,
height: 0,
},
startCoordinates: {
screenX: 0,
screenY: 0,
width: 0,
height: 0,
},
isEventFromThisApp: true,
} as KeyboardEvent
test('returns correct bottom inset', () => {
const event = { ...eventMock }
const { result } = renderHook(() => useKeyboardBottomInset())
act(() => {
result.current.updateBottomInset(event)
describe('useKeyboardBottomInset', () => {
it('returns correct bottom inset', () => {
expect.assertions(1)
const event = { ...keyboardEvent }
const { result } = renderHook(() => useKeyboardBottomInset())
act(() => {
result.current.updateBottomInset(event)
})
expect(result.current.bottomInset).toStrictEqual(346)
})
expect(result.current.bottomInset).toEqual(479)
})
test('returns correct bottom inset with longer animation time', () => {
const event = {
...eventMock,
duration: 20,
}
const { result } = renderHook(() => useKeyboardBottomInset())
act(() => {
result.current.updateBottomInset(event)
it('returns correct bottom inset with longer animation time', () => {
expect.assertions(1)
const event = {
...keyboardEvent,
duration: 20,
}
const { result } = renderHook(() => useKeyboardBottomInset())
act(() => {
result.current.updateBottomInset(event)
})
expect(result.current.bottomInset).toStrictEqual(346)
})
expect(result.current.bottomInset).toEqual(479)
})
test('does not configure animation if keyboard event duration is 0', () => {
const event = {
...eventMock,
duration: 0,
}
const { result } = renderHook(() => useKeyboardBottomInset())
act(() => {
result.current.updateBottomInset(event)
it('does not configure animation if keyboard event duration is 0', () => {
expect.assertions(1)
const event = {
...keyboardEvent,
duration: 0,
}
const { result } = renderHook(() => useKeyboardBottomInset())
act(() => {
result.current.updateBottomInset(event)
})
expect(result.current.bottomInset).toStrictEqual(346)
})
expect(result.current.bottomInset).toEqual(479)
})
test('skips setBottomInset if bottom inset does not change', () => {
const event = {
...eventMock,
endCoordinates: {
...eventMock.endCoordinates,
screenY: 812,
},
}
const { result } = renderHook(() => useKeyboardBottomInset())
act(() => {
result.current.updateBottomInset(event)
it('skips setBottomInset if bottom inset does not change', () => {
expect.assertions(1)
const event = {
...keyboardEvent,
endCoordinates: {
...keyboardEvent.endCoordinates,
screenY: 896,
},
}
const { result } = renderHook(() => useKeyboardBottomInset())
act(() => {
result.current.updateBottomInset(event)
})
expect(result.current.bottomInset).toStrictEqual(0)
})
expect(result.current.bottomInset).toEqual(0)
})

View File

@@ -5,11 +5,11 @@ import {
KeyboardEvent,
LayoutAnimation,
} from 'react-native'
import { useSafeArea } from 'react-native-safe-area-context'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
export const useKeyboardBottomInset = () => {
const [bottomInset, setBottomInset] = React.useState(0)
const { bottom: safeAreaBottomInset } = useSafeArea()
const { bottom: safeAreaBottomInset } = useSafeAreaInsets()
React.useEffect(() => {
Keyboard.addListener('keyboardWillChangeFrame', updateBottomInset)

View File

@@ -1,14 +1,16 @@
import { getTextSizeInBytes, uuidv4 } from '../'
describe('Text size in bytes', () => {
test('it calculates the size for a simple text', () => {
describe('text size in bytes', () => {
it('calculates the size for a simple text', () => {
expect.assertions(1)
const text = 'text'
expect(getTextSizeInBytes(text)).toEqual(4)
expect(getTextSizeInBytes(text)).toStrictEqual(4)
})
test('it calculates the size for an emoji text', () => {
it('calculates the size for an emoji text', () => {
expect.assertions(1)
const text = '🤔 🤓'
expect(getTextSizeInBytes(text)).toEqual(9)
expect(getTextSizeInBytes(text)).toStrictEqual(9)
})
})
@@ -18,7 +20,8 @@ describe('uuiv4', () => {
jest.spyOn(Math, 'random').mockReturnValue(0)
})
test('it generates specific id when Math.random returns only 0', () => {
expect(uuidv4()).toEqual('00000000-0000-4000-8000-000000000000')
it('generates specific id when Math.random returns only 0', () => {
expect.assertions(1)
expect(uuidv4()).toStrictEqual('00000000-0000-4000-8000-000000000000')
})
})

View File

@@ -1,5 +1,24 @@
import { KeyboardEvent, LayoutChangeEvent } from 'react-native'
import { Message, Size, User } from '../types'
export const keyboardEvent: KeyboardEvent = {
duration: 10,
easing: 'keyboard',
endCoordinates: {
height: 346,
screenX: 0,
screenY: 550,
width: 414,
},
isEventFromThisApp: true,
startCoordinates: {
height: 243,
screenX: 0,
screenY: 896,
width: 414,
},
}
export const message: Message = {
authorId: 'userId',
id: 'uuidv4',
@@ -8,8 +27,14 @@ export const message: Message = {
}
export const size: Size = {
height: 0,
width: 0,
height: 896,
width: 414,
}
export const onLayoutEvent: LayoutChangeEvent = {
nativeEvent: {
layout: { x: 0, y: 0, ...size },
},
}
export const user: User = {

View File

@@ -1,4 +1,11 @@
import { Dimensions, LayoutAnimation } from 'react-native'
import * as utils from './utils'
jest.spyOn(utils, 'uuidv4').mockReturnValue('uuidv4')
jest.spyOn(Date, 'now').mockReturnValue(0)
jest
.spyOn(Dimensions, 'get')
.mockImplementation(
jest.fn(() => ({ width: 414, height: 896, scale: 2, fontScale: 1 }))
)
jest.spyOn(LayoutAnimation, 'configureNext').mockImplementation()
jest.spyOn(utils, 'uuidv4').mockReturnValue('uuidv4')

View File

@@ -5,7 +5,7 @@ export const getTextSizeInBytes = (text: string) => {
}
export const uuidv4 = () => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = Math.floor(Math.random() * 16)
const v = c === 'x' ? r : (r % 4) + 8
return v.toString(16)

View File

@@ -1,13 +1,17 @@
{
"extends": "./node_modules/gts/tsconfig-google.json",
"compilerOptions": {
"declaration": true,
"esModuleInterop": true,
"jsx": "react-native",
"noUnusedLocals": true,
"noUnusedParameters": true,
"lib": ["ESNext"],
"module": "CommonJS",
"noEmitOnError": true,
"outDir": "./lib",
"removeComments": true
"removeComments": true,
"sourceMap": true,
"strict": true,
"target": "ESNext"
},
"include": ["src"],
"exclude": ["**/__tests__/*"]
"exclude": ["node_modules", "**/__tests__/*"]
}

750
yarn.lock

File diff suppressed because it is too large Load Diff