Skip to content

Commit

Permalink
feature: addition of lint verification, unit testing and code analysi…
Browse files Browse the repository at this point in the history
…s in ci/cd

* fix: eslint and linting issues, addition of PR and testing workflows

* fix: failing test

* feat: sonar scan addition
  • Loading branch information
vanGalilea authored Sep 13, 2023
1 parent a38506e commit 54cdf36
Show file tree
Hide file tree
Showing 11 changed files with 595 additions and 477 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module.exports = {
root: true,
extends: '@react-native',
ignorePatterns: ['e2e/'],
};
13 changes: 13 additions & 0 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Check PR title

on:
pull_request:
types: [opened, edited, synchronize, reopened]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: aslafy-z/conventional-pr-title-action@v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43 changes: 43 additions & 0 deletions .github/workflows/unit-testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Unit Testing
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]

jobs:
install-lint-test-scan:
runs-on: ubuntu-latest
steps:
# Checkout the repository
- uses: actions/checkout@v3
with:
fetch-depth: 0
# Setup Node environment
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'yarn'
# Load previous cache
- name: ESLint Cache
uses: actions/cache@v3
with:
path: './.eslintcache'
key: ${{ runner.os }}-eslintcache-${{ github.ref_name }}-${{ hashFiles('.eslintcache') }}

- name: Install Dependencies
run: yarn install --immutable
# Verify linting
- name: Lint
run: yarn lint
# Run unit tests with coverage
- name: test
run: yarn test:unit:coverage
# Run Code Analysis Scan
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
4 changes: 2 additions & 2 deletions __mocks__/react-native-video.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import {View} from 'react-native'
import {View} from 'react-native';

export default View
export default View;
18 changes: 13 additions & 5 deletions __tests__/ListWithFetch.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import React from 'react';
import {cleanup, render, screen} from '@testing-library/react-native';
import {
cleanup,
render,
screen,
waitForElementToBeRemoved,
} from '@testing-library/react-native';
import ListWithFetch from '../src/components/ListWithFetch';
import {server} from '../src/test/mocks/server';
import {rest} from 'msw';
Expand All @@ -15,16 +20,19 @@ test('displays images from the server', async () => {

// Loader is initially visible
expect(screen.getByLabelText(/loader/i)).toBeOnTheScreen();

await waitForElementToBeRemoved(() => screen.getByLabelText(/loader/i), {
timeout: 1500,
});
// Verify that users are fetched and rendered
expect(await screen.findAllByLabelText(/user-container/i)).toHaveLength(10);

// Verifying that the loader is no longer visible
// There are 2 ways to verify that a component is not in the UI tree
// 1. Use getBy* methods and expect them to throw an error with a corresponding message
// 2. Use queryBy* methods and expect them to return null (See the next expect statement)
// 1. Use waitForElementToBeRemoved to wait for the element to be removed from the DOM
// 2. Use getBy* methods and expect them to throw an error with a corresponding message
// 3. Use queryBy* methods and expect them to return null (See the next expect statement)
expect(() => screen.getByLabelText(/loader/i)).toThrow(
'Unable to find an element with accessibilityLabel: /loader/i',
'Unable to find an element with accessibility label: /loader/i',
);

// Verifying that there are no errors
Expand Down
32 changes: 16 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,27 @@
"react-native-video": "6.0.0-alpha.7"
},
"devDependencies": {
"@babel/core": "^7.22.11",
"@babel/preset-env": "^7.22.14",
"@babel/runtime": "^7.22.11",
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native/eslint-config": "^0.72.2",
"@react-native/metro-config": "^0.72.11",
"@tsconfig/react-native": "^3.0.2",
"@testing-library/jest-native": "^5.4.2",
"@testing-library/react-native": "^12.2.2",
"@types/jest": "^29.5.4",
"@types/react": "^18.2.21",
"@types/react-native-video": "^5.0.15",
"@tsconfig/react-native": "^3.0.0",
"@types/react": "^18.0.24",
"@types/react-test-renderer": "^18.0.0",
"axios": "^1.5.0",
"babel-jest": "^29.6.4",
"eslint": "^8.48.0",
"jest": "^29.6.4",
"babel-jest": "^29.2.1",
"eslint": "^8.19.0",
"jest": "^29.2.1",
"metro-react-native-babel-preset": "0.76.8",
"msw": "^1.2.5",
"prettier": "^3.0.3",
"prettier": "^2.4.1",
"react-test-renderer": "18.2.0",
"typescript": "5.2.2"
"typescript": "4.8.4",
"@testing-library/jest-native": "^5.4.3",
"@testing-library/react-native": "^12.3.0",
"@types/jest": "^29.2.1",
"@types/react-native-video": "^5.0.15",
"axios": "^1.5.0",
"msw": "^1.3.0"
},
"engines": {
"node": ">=16"
Expand Down
10 changes: 10 additions & 0 deletions sonar-project.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
sonar.organization=vangalilea
sonar.projectKey=vanGalilea_react-native-testing
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.sources=src/
sonar.test.exclusions=**/__tests__/**
sonar.coverage.exclusions=**/__tests__/**, **/__mocks__/**
sonar.coverage.inclusions=**/src/**/**.ts|tsx
sonar.exclusions=**/android/**, **/ios/**
sonar.verbose=true
sonar.host.url=https://sonarcloud.io
7 changes: 5 additions & 2 deletions src/components/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ export default () => {
return (
<View style={styles.body}>
<StatusBar barStyle="dark-content" />
<SafeAreaView style={{flex: 1}}>
<SafeAreaView style={styles.flex1}>
<ScrollView
style={{flex: 1}}
style={styles.flex1}
contentInsetAdjustmentBehavior="automatic">
<View style={styles.innerScrollView}>
<Text>Go to component...</Text>
Expand Down Expand Up @@ -49,6 +49,9 @@ export default () => {
};

const styles = StyleSheet.create({
flex1: {
flex: 1,
},
body: {
backgroundColor: Colors.white,
...StyleSheet.absoluteFillObject,
Expand Down
5 changes: 4 additions & 1 deletion src/components/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default () => {
<View style={styles.modalView}>
<Text style={styles.modalText}>Hello World!</Text>
<Pressable
style={{...styles.openButton, backgroundColor: '#2196F3'}}
style={[styles.openButton, styles.specialBGColor]}
onPress={() => {
setModalVisible(!modalVisible);
}}>
Expand Down Expand Up @@ -74,4 +74,7 @@ const styles = StyleSheet.create({
marginBottom: 15,
textAlign: 'center',
},
specialBGColor: {
backgroundColor: '#2196F3',
},
});
16 changes: 8 additions & 8 deletions src/utils/theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ import React, {
SetStateAction,
useContext,
useState,
} from 'react'
} from 'react';

export type ThemeType = 'dark' | 'light';
type ThemeContextType = {
theme: ThemeType;
setTheme: Dispatch<SetStateAction<ThemeType>>;
};
const ThemeContext = createContext<ThemeContextType | null>(null)
const ThemeContext = createContext<ThemeContextType | null>(null);

const useTheme = () => {
const context = useContext(ThemeContext)
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme should be used within a ThemeProvider')
throw new Error('useTheme should be used within a ThemeProvider');
}
return context
return context;
};

const ThemeProvider = ({
Expand All @@ -28,8 +28,8 @@ const ThemeProvider = ({
initialTheme: ThemeType;
children: React.ReactNode;
}): JSX.Element => {
const [theme, setTheme] = useState(initialTheme)
return <ThemeContext.Provider value={{theme, setTheme}} {...props} />
const [theme, setTheme] = useState(initialTheme);
return <ThemeContext.Provider value={{theme, setTheme}} {...props} />;
};

export {useTheme, ThemeProvider}
export {useTheme, ThemeProvider};
Loading

0 comments on commit 54cdf36

Please sign in to comment.