Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/26-storing-session-forms-sqlite #28

Merged
merged 36 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9033a4a
[#26] Install & setup axios
wayangalihpratama Jun 28, 2023
ed34b2e
[#26] Add login functionality
wayangalihpratama Jun 28, 2023
b20ff0b
[#26] Create session table & store session and forms
wayangalihpratama Jun 28, 2023
6e9deeb
[#26] Fetch & save form detail
wayangalihpratama Jun 28, 2023
0b295c4
[#26] Update AuthForm page test
wayangalihpratama Jun 28, 2023
2d0abd5
[#26] Change saving token logic
wayangalihpratama Jun 28, 2023
f04644a
[#26] Check session when start app on get started page
wayangalihpratama Jun 28, 2023
17ac87a
[#26] Fix prettier
wayangalihpratama Jun 28, 2023
ad59ed8
[#27] Create initial UserProfilePage
ifirmawan Jun 28, 2023
1252cc0
[#27] Add new route: UserProfile
ifirmawan Jun 28, 2023
96591a1
[#27] Add conditional hasUser from DB
ifirmawan Jun 28, 2023
bed0be8
[#26] Move session query to database/crud
wayangalihpratama Jun 29, 2023
1448e40
[#26] Move saving form fn into crudForms
wayangalihpratama Jun 29, 2023
edfad41
[#26] Refine check session & set initialRouteName
wayangalihpratama Jun 29, 2023
6a70587
[#26] Store passcode to sessions table & load to auth state
wayangalihpratama Jun 29, 2023
9a2e89e
[#26] Init prevent go back to get started
wayangalihpratama Jun 29, 2023
d684d78
[#26] Prevent hardwareBackPress when has session on home page
wayangalihpratama Jun 29, 2023
13d7377
[#26] Create test for crud-forms.js
wayangalihpratama Jun 29, 2023
d7601fa
[#26] Create test for crud-sessions.js
wayangalihpratama Jun 29, 2023
e5df9db
[#27] Restore HomePage and remove UserProfile
ifirmawan Jun 29, 2023
07e8827
[#27] Create AddUserPage
ifirmawan Jun 29, 2023
47af2ee
[#27] Add password in UserState
ifirmawan Jun 29, 2023
aaaa60f
[#27] Create new snapshot: AddUserPage
ifirmawan Jun 29, 2023
02cd7df
Merge branch 'feature/26-storing-session-forms-sqlite' into feature/2…
ifirmawan Jun 30, 2023
131ccc2
[#26] Create table first before checking session
ifirmawan Jun 30, 2023
02100c0
[#26] Load serverURL from build.json
ifirmawan Jun 30, 2023
88ce2ce
Merge pull request #29 from akvo/feature/27-add-user-page
wayangalihpratama Jun 30, 2023
30106ed
[#26] Implement workflow when session/user defined
wayangalihpratama Jun 30, 2023
78ad035
[#27] Remove initial value test for serverURL
ifirmawan Jun 30, 2023
7313b4a
[#27] Create test mocks: expo-crypto
ifirmawan Jun 30, 2023
06114c0
[#27] Add testID and conditional platform OS
ifirmawan Jun 30, 2023
b2305b4
[#27] Add functionality testing: AddUser page
ifirmawan Jun 30, 2023
e6b6b45
Merge branch 'feature/26-storing-session-forms-sqlite' into feature/2…
ifirmawan Jun 30, 2023
e8a687e
Merge pull request #30 from akvo/feature/27-add-user-page
wayangalihpratama Jun 30, 2023
d275917
[#26] Check connection before login
wayangalihpratama Jun 30, 2023
01ded09
[#26] Merge branch 'main' into feature/26-storing-session-forms-sqlite
wayangalihpratama Jul 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions app/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,56 @@ import NetInfo from '@react-native-community/netinfo';

import Navigation from './src/navigation';
import { conn, query, tables } from './src/database';
import { UIState } from './src/store';
import { UIState, AuthState, UserState } from './src/store';
import { crudSessions, crudUsers } from './src/database/crud';

const db = conn.init;

const App = () => {
const handleCheckSession = () => {
dedenbangkit marked this conversation as resolved.
Show resolved Hide resolved
crudSessions.selectLastSession().then((session) => {
if (!session) {
return session;
}
console.info('Session =>', session);
// check users exist
crudUsers
.selectUsers({ count: false })
.then((users) => {
console.info('Users =>', users);
let page = null;
if (session && users?.length) {
page = 'Home';
}
if (session && !users?.length) {
page = 'AddUser';
}
return { user: users?.[users?.length - 1], page };
})
.then(({ user, page }) => {
UserState.update((s) => {
s.id = user.id;
s.name = user.name;
s.password = user.password;
});
AuthState.update((s) => {
s.token = session.token;
s.authenticationCode = session.passcode;
});
UIState.update((s) => {
s.currentPage = page ? page : s.currentPage;
});
});
});
};

React.useEffect(() => {
const queries = tables.map((t) => query.initialQuery(t.name, t.fields)).join(' ');
conn.tx(db, queries).then((res) => {
console.log('results', res);
const queries = tables.map((t) => {
const queryString = query.initialQuery(t.name, t.fields);
return conn.tx(db, queryString);
});
Promise.all(queries).then(() => {
handleCheckSession();
});
}, []);

Expand All @@ -27,6 +68,7 @@ const App = () => {
unsubscribe();
};
}, []);

return (
<SafeAreaProvider>
<Navigation testID="navigation-element" />
Expand Down
14 changes: 14 additions & 0 deletions app/__mocks__/expo-crypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const mockDigestStringAsync = jest.fn();

export const Crypto = {
CryptoDigestAlgorithm: {
SHA1: 'SHA1',
},
digestStringAsync: mockDigestStringAsync,
};

export const digestStringAsync = (algorithm, data) => {
return Promise.resolve(`Mocked${data}-${algorithm}`);
};

export const CryptoDigestAlgorithm = Crypto.CryptoDigestAlgorithm;
26 changes: 26 additions & 0 deletions app/__tests__/pages/AuthForm.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import { AuthFormPage } from '../../src/pages';

describe('AuthFormPage', () => {
test('renders correctly', () => {
const tree = renderer.create(<AuthFormPage />).toJSON();
expect(tree).toMatchSnapshot();
});

test('auth page field render correctly', () => {
const { getByTestId } = render(<AuthFormPage />);
const inputField = getByTestId('auth-password-field');
const toggleEye = getByTestId('auth-toggle-eye-button');
const checkbox = getByTestId('auth-checkbox-field');
const loginBtn = getByTestId('auth-login-button');

expect(inputField).toBeDefined();
expect(toggleEye).toBeDefined();
expect(checkbox).toBeDefined();
expect(loginBtn).toBeDefined();
});

test('enables the login button when the form is filled correctly', () => {
const { getByTestId } = render(<AuthFormPage />);
const inputField = getByTestId('auth-password-field');
const checkbox = getByTestId('auth-checkbox-field');
const loginButton = getByTestId('auth-login-button');

expect(loginButton.props.accessibilityState.disabled).toBe(true);
fireEvent.changeText(inputField, '123456');
fireEvent.press(checkbox);
expect(loginButton.props.accessibilityState.disabled).toBe(false);
});
});
22 changes: 18 additions & 4 deletions app/__tests__/pages/__snapshots__/AuthForm.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ exports[`AuthFormPage renders correctly 1`] = `
<TextInput
autoFocus={true}
editable={true}
onChangeText={[Function]}
placeholder="Enumerator ID"
placeholderTextColor="#86939e"
secureTextEntry={true}
Expand All @@ -119,6 +120,7 @@ exports[`AuthFormPage renders correctly 1`] = `
}
testID="auth-password-field"
underlineColorAndroid="transparent"
value={null}
/>
<View
style={
Expand Down Expand Up @@ -180,7 +182,7 @@ exports[`AuthFormPage renders correctly 1`] = `
"opacity": 1,
}
}
testID="auth-toggle-eye-buton"
testID="auth-toggle-eye-button"
>
<View
style={
Expand Down Expand Up @@ -304,7 +306,7 @@ exports[`AuthFormPage renders correctly 1`] = `
{
"busy": false,
"checked": undefined,
"disabled": false,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
Expand Down Expand Up @@ -338,7 +340,7 @@ exports[`AuthFormPage renders correctly 1`] = `
style={
{
"alignItems": "center",
"backgroundColor": "#2089dc",
"backgroundColor": "hsl(208, 8%, 90%)",
"borderColor": "#2089dc",
"borderRadius": 2,
"borderWidth": 0,
Expand All @@ -352,7 +354,7 @@ exports[`AuthFormPage renders correctly 1`] = `
<Text
style={
{
"color": "white",
"color": "hsl(208, 8%, 63%)",
"fontSize": 18,
"paddingVertical": 1,
"textAlign": "center",
Expand All @@ -364,5 +366,17 @@ exports[`AuthFormPage renders correctly 1`] = `
</View>
</View>
</View>
<Modal
hardwareAccelerated={false}
onRequestClose={[Function]}
style={
{
"flex": 1,
}
}
testID="Internal__Overlay"
transparent={true}
visible={false}
/>
</View>
`;
51 changes: 50 additions & 1 deletion app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,20 @@
"prettier-write": "prettier --write ."
},
"dependencies": {
"@react-native-community/netinfo": "^9.3.10",
"@react-native-community/datetimepicker": "6.7.3",
"@react-native-community/netinfo": "^9.3.10",
"@react-navigation/native": "^6.1.6",
"@react-navigation/native-stack": "^6.9.12",
"@rneui/base": "^4.0.0-rc.7",
"@rneui/themed": "^4.0.0-rc.7",
"axios": "^1.4.0",
"expo": "~48.0.18",
"expo-crypto": "^12.2.2",
"expo-image-picker": "~14.1.1",
"expo-sqlite": "^11.1.1",
"expo-status-bar": "~1.4.4",
"pullstate": "^1.25.0",
"formik": "^2.4.2",
"pullstate": "^1.25.0",
"react": "18.2.0",
"react-native": "0.71.8",
"react-native-element-dropdown": "^2.9.0",
Expand Down
51 changes: 51 additions & 0 deletions app/src/database/crud/__tests__/crud-forms.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import crudForms from '../crud-forms';
jest.mock('expo-sqlite');

describe('crudForms function', () => {
beforeEach(() => {
jest.clearAllMocks();
});

describe('addFormsIfNotExist', () => {
it('should insert the form if it does not exist or has a different version', async () => {
const formId = 1;
const version = 1;
const formJSON = { id: 1, version: 1, name: 'Form 1' };
const result = await crudForms.addFormsIfNotExist({ id: formId, version, formJSON });
expect(result).toEqual({ rowsAffected: 1 });
});

it('should update the form if it exists and has the same version', async () => {
const formId = 1;
const version = 1;
const formJSON = { id: 1, version: 1, name: 'Form 1' };
const result = await crudForms.addFormsIfNotExist({ id: formId, version, formJSON });
expect(result).toEqual({ rowsAffected: 1 });
});
});

describe('selectFormByIdAndVersion', () => {
it('should return false if the form does not exist', async () => {
const result = await crudForms.selectFormByIdAndVersion({ id: 1, version: '1.0.0' });
expect(result).toBe(false);
});

it('should return the form if it exists', async () => {
// Mock the result set for select
const formData = [
{
id: 1,
formId: 123,
name: 'Form Test',
latest: 1,
version: '1.0.0',
json: { id: 1, version: 1, name: 'Form 1' },
},
];
const mockSelectSql = jest.fn(() => formData);
crudForms.selectFormByIdAndVersion = mockSelectSql;
const result = await crudForms.selectFormByIdAndVersion({ id: 1, version: '1.0.0' });
expect(result).toEqual(formData);
});
});
});
40 changes: 40 additions & 0 deletions app/src/database/crud/__tests__/crud-sessions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import crudSessions from '../crud-sessions';
jest.mock('expo-sqlite');

describe('crudSessions function', () => {
beforeEach(() => {
jest.clearAllMocks();
});

describe('addSession', () => {
it('should insert the session', async () => {
const result = await crudSessions.addSession({ token: 'Bearer abcefg', passcode: '123456' });
expect(result).toEqual({ rowsAffected: 1 });
});
});

describe('selectLastSession', () => {
it('should return false if session does not exist', async () => {
const result = await crudSessions.selectLastSession();
expect(result).toBe(false);
});

it('should return last session', async () => {
// Mock the result set for select
const sessions = [
{
token: 'Bearer 1',
passcode: '123',
},
{
token: 'Bearer 2',
passcode: '321',
},
];
const mockSelectSql = jest.fn(() => sessions[1]);
crudSessions.selectLastSession = mockSelectSql;
const result = await crudSessions.selectLastSession();
expect(result).toEqual(sessions[1]);
});
});
});
Loading