I have an app built with React-Native, Amplify, AppSync and Cognito and when it loads I would like to save the USER ID and USER TYPE as a global state that can be accessed on every screen.
The user id and user type (Teacher or Student) will never change as these are created on signup.
import React, { useEffect, useState, useReducer } from 'react';
import {AppRegistry} from 'react-native';
import {name as appName} from './app.json';
import App from './src/AppNavigation';
import Amplify, { API, graphqlOperation, Auth } from 'aws-amplify';
import awsmobile from './aws-exports';
import { getUser } from './src/graphql/queries';
Amplify.configure(awsmobile);
export const UserContext = React.createContext()
function MyApp() {
const [userContext, setUserContext] = useState({})
const getUserIdAndType = async () => {
try {
// get User data
const currentUser = await Auth.currentAuthenticatedUser();
const userId = await currentUser.signInUserSession.accessToken.payload.sub;
// get user data from AppSync
const userData = await API.graphql(graphqlOperation(getUser, { id: userId }));
setUserContext({ userId: userId, userType: userData.data.getUser.userType })
} catch (err) {
console.log('error', err);
}
}
useEffect(() => {
getUserIdAndType()
}, [])
return (
<UserContext.Provider value={userContext}>
<App />
</UserContext.Provider>
);
}
AppRegistry.registerComponent(appName, () => MyApp);
Then when I want to use the context state I do as follows:
import { useContext } from 'react';
import { UserContext } from '../../../index';
function Loading ({ navigation }) {
const userContext = useContext(UserContext)
if (userContext.userId != '') {
navigation.navigate('AppTabs');
} else {
navigation.navigate('Auth');
}
}
export default Loading;
Or to get which screen to show (Teacher or Student)...
import { useContext } from 'react';
import { UserContext } from '../../../index';
function LoadingProfile ({ navigation }) {
const userContext = useContext(UserContext)
if (userContext.userType === 'Teacher') {
navigation.navigate('TeacherScreen');
} else if (userContext.userType === 'Student') {
navigation.navigate('StudentScreen');
}
}
export default LoadingProfile;
When the app loads it says the userContext.userId and userContext.userType are empty so it is not saving the state when I set it in the getUserIdAndType() function.
-
****** If I rewrite the App file (INSTEAD OF USING THE HOOKS useState, useEffect) I just declare the values then it works... so I am obviously not using the hooks or async getUserIdAndType() correctly. ******
import React, { useEffect, useState, useReducer } from 'react';
import {AppRegistry} from 'react-native';
import {name as appName} from './app.json';
import App from './src/AppNavigation';
import Amplify, { API, graphqlOperation, Auth } from 'aws-amplify';
import awsmobile from './aws-exports';
import { getUser } from './src/graphql/queries';
Amplify.configure(awsmobile);
export const UserContext = React.createContext()
function MyApp() {
const userContext = {
userId: '123456789', // add the user id
userType: 'Teacher', // add the user type
}
return (
<UserContext.Provider value={userContext}>
<App />
</UserContext.Provider>
);
}
AppRegistry.registerComponent(appName, () => MyApp);
change this :
<UserContext.Provider value={{userContext}}>
<App />
</UserContext.Provider>
to this :
<UserContext.Provider value={userContext}>
<App />
</UserContext.Provider>
you've added an extra curly bracket " { "
Related
I have written many test cases in the react testing library, I got stuck in the accessing condition based DOM element. I have tried many ways using import { renderHook } from '#testing-library/react-hooks'. But didn't work from me. Here is the code. Great appreciate
import axios from "axios";
import { withTranslation } from "react-i18next";
function MDStaticContent({ t }) {
const [promoArrayIsValid, setPromoArrayIsValid] = useState(false);
const loadRecent = async () => {
API Calls Here
const recents = await onRecentMedia(accounts[0], instance);
if (Some condition) {
if (Here Some condition) {
setPromoArrayIsValid(true);
}
dispatch(Here Will discpatch data);
}
};
return (
<>
{ promoArrayIsValid && (
<div data-testid="mdStaticContent">
Hello
</div>
</>
)}
}
import React from "react";
import { render, cleanup } from "#testing-library/react";
import { useEffect, useState } from "react";
import { Provider } from 'react-redux';
import "#testing-library/jest-dom/extend-expect";
import { MemoryRouter } from 'react-router-dom';
import configureMockStore from 'redux-mock-store';
import { ThemeProvider } from 'styled-components';
import thunk from 'redux-thunk';
import MDStaticContent from "./MDStaticContent";
import { act } from 'react-dom/test-utils';
import MockAPIData from '../resources/locales/en/MockAPIData.json';
import { renderHook } from '#testing-library/react-hooks'
const mockStore = configureMockStore([thunk]);
let data = {
mediaArrayIsValid: true,
promoArrayIsValid: true
}
const store = mockStore(data);
it("should take a snapshot and match of MDStaticContent Page", () => {
const { asFragment, getAllByTestId, getByTestId
} = render(
<ThemeProvider theme={theme}>
<Provider store={store}>
<MemoryRouter>
<MDStaticContent/>
</MemoryRouter>
</Provider>
</ThemeProvider>
);
expect(asFragment()).toMatchSnapshot();
expect(getAllByTestId("mdStaticContent")).toHaveLength(1);
expect(getByTestId("mdStaticContent")).toBeVisible();
});
I have created a Django and Angular application to upload files. It was working without errors until I integrated a login page. I have not been able to upload files since integration. I get 401 - "Unauthorized" error. What could have possibly gone wrong?
Auth-interceptor:
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest,HttpErrorResponse } from "#angular/common/http";
import { Injectable } from "#angular/core";
import { catchError, Observable, throwError } from "rxjs";
import { LoginService } from "src/services/login.service";
#Injectable()
export class AuthInterceptorService implements HttpInterceptor {
constructor(private authService: LoginService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.authService.isLoggedIn()) {
const token = this.authService.getAuthToken();
console.log("intercept",token)
// If we have a token, we set it to the header
request = request.clone({
setHeaders: {Authorization: `Token ${token}`}
});
}
return next.handle(request)
}
}
fileupload.component.ts:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup } from '#angular/forms';
import { LoginService } from 'src/services/login.service';
import { FileUploader, FileLikeObject } from 'ng2-file-upload';
import { concat, Observable } from 'rxjs';
import { HttpEvent, HttpEventType } from '#angular/common/http';
#Component({
selector: 'app-fileupload',
templateUrl: './fileupload.component.html',
styleUrls: ['./fileupload.component.scss']
})
export class FileuploadComponent {
DJANGO_SERVER = 'http://127.0.0.1:8081'
public uploader: FileUploader = new FileUploader({});
public hasBaseDropZoneOver: boolean = false;
constructor(private formBuilder: FormBuilder, private uploadService: LoginService) { }
fileOverBase(event): void {
this.hasBaseDropZoneOver = event;
}
getFiles(): FileLikeObject[] {
return this.uploader.queue.map((fileItem) => {
return fileItem.file;
});
}
upload() {
let files = this.getFiles();
console.log(files);
let requests= [];
files.forEach((file) => {
let formData = new FormData();
formData.append('file' , file.rawFile, file.name);
requests.push(this.uploadService.upload(formData));
console.log(requests,file)
});
concat(...requests).subscribe(
(res) => {
console.log(res);
},
}
);
}}
console.log(err);
}
);
}}
service:
public upload(formData) {
let token= localStorage.getItem('token');
return this.http.post<any>(`${this.DJANGO_SERVER}/upload/`, formData).pipe(map((res) => {
console.log(res)
})
)
}
Thank you
I resolved the issue. It was because I was usign interceptor and I was using third party API for authentication. So instead of Django token, the third party APIs token was sent in header of POST request.
How I resolved it?
I used Httpbackend to process POST requests to Django DB so that the request is not intercepted and then I added custom header (with Django token to the reuest). I used the code snippet on this website: https://levelup.gitconnected.com/the-correct-way-to-make-api-requests-in-an-angular-application-22a079fe8413
im trying to to convert below code snippet to JEST unit testing but its throwing error please help me out in resolving it
import React from 'react'
import { BrowserRouter as Router, Switch, Route} from 'react-router-dom';
import IdleTimer from 'react-idle-timer';
import { ApolloProvider } from '#apollo/react-hooks';
import Dashboard from '../pages/Dashboard';
import Loader from '../components/Loader';
import Alert from '../components/Alert';
import NoAccess from '../pages/NoAccess';
import { GQL } from '../services/GQL';
import { IdleTimeOutModal } from './IdleTimeoutModal';
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.min.css';
import '../App.scss';
import '../assets/css/loader.scss';
import AuthenticatedRoute from '../guards/AuthenticatedRoute';
import auth from '../services/Auth';
import { connect } from 'react-redux';
class Layout extends React.Component {
constructor(props){
super(props);
this.state = {
timeout:1000 * 900 * 1,
showModal: false,
userLoggedIn: window.localStorage.getItem('loginUser'),
isTimedOut: false,
}
this.idleTimer = null
this.onAction = this._onAction.bind(this)
this.onActive = this._onActive.bind(this)
this.onIdle = this._onIdle.bind(this)
this.handleClose = this.handleClose.bind(this)
this.handleLogout = this.handleLogout.bind(this)
}
_onAction(e) {
// console.log('user did something', e)
this.setState({isTimedOut: false})
}
_onActive(e) {
// console.log('user is active', e)
this.setState({isTimedOut: false})
}
_onIdle(e) {
// console.log('user is idle', e)
const isTimedOut = this.state.isTimedOut
if (isTimedOut) {
this.setState({showModal: false})
window.localStorage.setItem('loginUser', 'false');
} else {
this.setState({showModal: true})
this.idleTimer.reset();
this.setState({isTimedOut: true})
}
}
handleClose() {
this.setState({showModal: false})
}
handleLogout() {
this.setState({showModal: false})
auth.signout();
}
render(){
// console.log(window.location)
const {match} = window.location.href;
// const { match } = this.location
return(
<>
<IdleTimer
ref={ref => { this.idleTimer = ref }}
element={document}
onActive={this.onActive}
onIdle={this.onIdle}
onAction={this.onAction}
debounce={250}
timeout={this.state.timeout} />
<div className="">
<ApolloProvider client={GQL}>
<Router>
<Switch>
{/* <Route component={NoAccess} path="/no-access" exact={true} /> */}
<AuthenticatedRoute path={`${window.location.pathname}`} component={Dashboard} />
</Switch>
</Router>
{/* <Loader isOpen={loader.isLoading} /> */}
{/* <Alert /> */}
</ApolloProvider>
<IdleTimeOutModal
showModal={this.state.showModal}
handleClose={this.handleClose}
handleLogout={this.handleLogout}
/>
</div>
</>
)
}
}
***JEST throwing error while converting to JEST - ShallowWrapper::state() can only be called on class components
jest help***
export default connect((props) =>({
match: props.uiel.isRequired,
history: props.uiel.isRequired
}))(Layout);
import React from 'react';
import { BrowserRouter as Router, Switch, Route} from 'react-router-dom';
import { render, cleanup, fireEvent } from '#testing-library/react';
import '#testing-library/jest-dom/extend-expect';
import Enzyme, { shallow, mount } from 'enzyme';
import Layout from './Layout';
import Adapter from 'enzyme-adapter-react-16';
import { Provider } from "react-redux";
import configureMockStore from "redux-mock-store";
import { IdleTimeOutModal } from './IdleTimeoutModal';
import { wrap } from 'module';
Enzyme.configure({ adapter: new Adapter() })
const mockStore = configureMockStore();
let handleLogout:any;
let showModal: any;
let handleClose:any;
let remainingTime:any;
let _onAction:any;
let _onIdle:any;
let _onActive:any;
let idleTimer:any;
describe("Render Layout Component", ()=>{
let store;
let wrapperlayout:any;
beforeEach(()=>{
store = mockStore({
loader: false
});
wrapperlayout = shallow(
<Provider store={store}>
<Layout />
</Provider>
);
});
it('should render the value of color', () => {
wrapperlayout.setProps({ timeout:1000 * 900 * 1 });
wrapperlayout.setProps({ showModal: false });
wrapperlayout.setProps({ userLoggedIn: window.localStorage.getItem('loginUser') });
wrapperlayout.setProps({ isTimedOut: false });
//expect(wrapper.state('color')).toEqual('transparent');
});
it("should increment index - function test", () => {
//const app = shallow(<Normal />);
expect(wrapperlayout.state("isTimedOut")).toEqual('false');
wrapperlayout.instance()._onAction();
expect(wrapperlayout.state("isTimedOut")).toEqual('false');
});
test("Render Modal Layout", ()=>{
expect(wrapperlayout.exists()).toBe(true);
});
});
I want to test a TheLogin.vue component that has a child BaseInput.vue component. I tried the code below and also shallowMount but I keep getting the error below.
TheLogin.vue
<template>
<section>
<legend>
Hello Login
</legend>
<BaseInput id="userName"></BaseInput>
</section>
</template>
export default {
name: 'TheLogin',
data() {
return {
userName: null
}
}
}
TheLogin.spec.js
import TheLogin from '#/pages/login/TheLogin.vue';
import BaseInput from '#/components/ui/BaseInput.vue';
import { createLocalVue, mount } from '#vue/test-utils';
describe('TheLogin.vue', () => {
const localVue = createLocalVue();
localVue.use(BaseInput); // no luck
it('renders the title', () => {
const wrapper = mount(TheLogin, {
localVue,
// stubs: {BaseInput: true // no luck either
// stubs: ['base-input'] // no luck again
});
expect(wrapper.find('legend').text()).toEqual(
'Hello Login'
);
});
I import my base components in a separate file which I import into my main.js
import Vue from 'vue';
const components = {
BaseInput: () => import('#/components/ui/BaseInput.vue'),
BaseButton: () => import('#/components/ui/BaseButton.vue'),
//et cetera
};
Object.entries(components).forEach(([name, component]) =>
Vue.component(name, component)
);
The error I'm getting is:
TypeError: Cannot read property 'userName' of undefined
UPDATE
Turned out it was Vuelidate causing the error (the code above was not complete). I also had in my script:
validations: {
userName: {
required,
minLength: minLength(4)
},
password: {
required,
minLength: minLength(4)
}
}
I solved it by adding in my test:
import Vuelidate from 'vuelidate';
import Vue from 'vue';
Vue.use(Vuelidate);
Have you tried to shallow mount the component without using localVue and setting BaseInput as a stub?
Something like:
import TheLogin from '#/pages/login/TheLogin.vue';
import { shallowMount } from '#vue/test-utils';
describe('TheLogin.vue', () => {
it('renders the title', () => {
const wrapper = shallowMount(TheLogin, {
stubs: { BaseInput: true }
});
expect(wrapper.find('legend').text()).toEqual(
'Hello Login'
);
});
});
I'm trying to to do integration tests, by mounting a smart connected component.
The fetch action that is within componentDidMount of my smart components dispatches just fine, and it's taken by my Saga. Although it is supposed to put a success action, it doesn't .
Here is my testing code :
import React from 'react'
import { Provider } from 'react-redux'
import configureMockStore from 'redux-mock-store'
import MockAdapter from 'axios-mock-adapter'
import Enzyme,{ mount,render } from 'enzyme'
import Tasks from '../containers/tasks.jsx'
import createSagaMiddleware from 'redux-saga'
import axios from "axios";
import Adapter from 'enzyme-adapter-react-16';
import mySaga from '../actions/tasksSaga'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import reducer from '../reducers'
Enzyme.configure({ adapter: new Adapter() });
describe('App', () => {
// create the saga middleware
const sagaMiddleware = createSagaMiddleware()
const mock = new MockAdapter(axios)
const state = {
tasksReducer:{
tasks:[]
},
uiReducer :{
}
};
const todos = [
{
id: 1,
title: 'todo1',
description: 'nice'
},
{
id: 2,
title: 'todo2',
description: 'nice'
}
]
beforeAll(() => {
mock.onGet('http://localhost:9001/tasks').reply(200, {tasks:todos})
})
it('renders an App container', () => {
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware),
)
sagaMiddleware.run(mySaga)
const wrapper = mount(
<Provider store={store}>
<Tasks />
</Provider>
)
wrapper.instance().forceUpdate()
expect(wrapper.find('Task')).toHaveLength(3)
})
})
My success action is never called although data is good.