Error when adding a document on firestore - reactjs

react_devtools_backend.js:4026 Error adding document:
SyntheticBaseEvent {_reactName: 'onSubmit', _targetInst: null, type: 'submit', nativeEvent: SubmitEvent, target: form, …}
This is todo app and what im trying to do is to send object of 'title' and 'completed' in firestore in collection todos
Something goes wrong :(
import React, {useState} from 'react'
import { db } from '../firebase/firebase'
import { collection, addDoc } from 'firebase/firestore'
const AddTodo = () => {
const [title, setTitle] = useState('')
const submitHandler = async e => {
e.preventDefault()
try {
await addDoc(collection(db, 'todos'), {
title,
completed: false
})
} catch (error) {
console.error('Error adding document: ', e)
}
}
return (
<form onSubmit={submitHandler}>
<input
type='text'
placeholder='Enter todo...'
value={title}
onChange={e => setTitle(e.target.value)}
/>
<button type='submit'>Add</button>
</form>
)
}
export default AddTodo
Firebase db
import { initializeApp } from 'firebase/app'
import { getFirestore } from 'firebase/firestore'
const firebaseConfig = {
//pasted from project settings
}
const app = initializeApp(firebaseConfig)
const db = getFirestore(app)
export { db }

Related

How to implement pagination and search in React

Good evening! I am creating simple React app using TS + React Query + Recoil. App is about 'online library'. I would like to create pagination and search input (to find specific author or title).
My idea was, when app starts I am fetching data from page 1. Then when I'll click 2nd button on my pagination bar I'll fetch data from page 2 etc. Code looks like this:
Main component
import { useGetBooks } from '../../hooks/useGetBooks';
import { BookType } from '../../types/Book';
import { SingleBook } from './SingleBook';
import styled from 'styled-components';
import { Navbar } from './Navbar';
import { Loader } from '../utilities/Loader';
import { Error } from '../utilities/Error';
import { useState } from 'react';
import { useRecoilState } from 'recoil';
import { Books } from '../../recoil/globalState';
type bookType = BookType;
export const BookList = () => {
const [pageNumber, setPageNumber] = useState(1);
const [books, setBooks] = useRecoilState(Books);
const { isLoading, isError } = useGetBooks(pageNumber, setBooks);
if (isLoading) {
return <Loader isLoading={isLoading} />
}
if (isError) {
return <Error />
}
const displayBooks = books.data.map((book: bookType) => {
return (
<SingleBook key={book.id} book={book} />
)
})
return (
<BookContainer>
<div className='test'>
<button onClick={() => setPageNumber((page) => page - 1)} disabled={pageNumber == 1}>Prev page</button>
<p>{books.metadata.page}</p>
<button onClick={() => setPageNumber((page) => page + 1)} disabled={books.metadata.records_per_page * books.metadata.page > books.metadata.total_records}>Next page</button>
</div>
<Navbar />
<BookContent>
{displayBooks}
</BookContent>
</BookContainer>
)
}
React query:
import { useQuery } from "react-query";
import axios from 'axios';
const fetchBooks = async (pageNumber: number) => {
const res = await axios.get(`http://localhost:3001/api/book?page=${pageNumber}`);
return res.data
}
export const useGetBooks = (pageNumber: number, setBooks: any) => {
return useQuery(['books', pageNumber], () => fetchBooks(pageNumber),
{
onSuccess: (data) => setBooks(data),
keepPreviousData: true
})
}
Recoil:
import { atom } from 'recoil';
export const Books = atom({
key: 'book',
default: [] as any
})
And books response:
books: {
data: [
{
author: 'Some crazy',
title: 'Some crazy'
},
{
author: 'Some crazy1',
title: 'Some crazy1'
},
],
metadata: {
page: 1,
records_per_page: 10,
total_records: 17
}
}
Search Input Implementation:
import React, { useState } from "react"
import { useGetFilteredBooks } from "../../hooks/useGetFilteredBooks"
import { useRecoilState } from "recoil"
import { Books } from "../../recoil/globalState"
export const SearchBook = () => {
const [text, setText] = useState('')
const [books, setBooks] = useRecoilState(Books);
const { data, refetch } = useGetFilteredBooks(text, setBooks);
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setText(event.target.value)
}
const handleOnSubmit = (e: any) => {
e.preventDefault();
refetch();
}
return (
<>
<form onSubmit={handleOnSubmit}>
<Input value={text} onChange={handleOnChange} placeholder="Enter the name of the book or author" />
<button type="submit">Show</button>
</form>
<button onClick={() => console.log(books)}>plasda</button>
</>
)
}
React query:
import { useQuery } from "react-query";
import axios from 'axios';
const fetchFilteredsBooks = async (searchText: string) => {
const res = await axios.get(`http://localhost:3001/api/book?search=${searchText}`);
return res.data
}
export const useGetFilteredBooks = (searchText: string, setBooks: any) => {
return useQuery(['filteredBooks', searchText], () => fetchFilteredsBooks(searchText),
{
onSuccess: (data) => setBooks(data),
enabled: false
})
}
We can only display 10 items per 1 page.
PROBLEM:
When we search something and we get data back, we can have scenario, when data will need to be display not on 1 page. So when we have filtered data, and we click 2nd button on pagination, the filtered data will disapeared and we see not filtered data from page 2

React jest login form test

Hi i am new developer testing platform. I have a problem but I did not find a solution or work it with correct way. I am trying to login component test with to parameter by Inputs. Firstly I filled these are userEvent.type. After I am clicking my button. And when I was waiting my method that call by onSubmitForTest in one time , I am facing an error like fallowing image.
What is the reason of this ? How can I solve my problem ? Thanks for your helps.
My Login.tsx component:
import React, { FC, useState } from "react";
import { useTranslation } from "react-i18next";
import Input from "../../components/Input";
import InputPassword from "../../components/Input/InputPassword";
import ButtonLoading from "../../components/Button/ButtonLoading";
import { GetLoginInfo, ILoginRequest } from "../../store/actions/loginActions";
interface ILoginState {
emailorUsername: string;
password: string;
}
const initialState = {
emailorUsername: "",
password: "",
};
interface IProps {
onSubmitForTest: (items: any) => void
}
const Login: FC<IProps> = ({ onSubmitForTest }) => {
const { t } = useTranslation();
const [state, setstate] = useState<ILoginState>(initialState);
const onChange = (key: string, value: string | number) => {
setstate({ ...state, [key]: value });
};
const handleLogin = async () => {
const loginRequest: ILoginRequest = {
emailOrUsername: state.emailorUsername,
password: state.password,
returnUrl: "",
};
const response = await GetLoginInfo(loginRequest);
if (response.isSucceed) { } else { }
};
const renderLoginPart = () => {
return (
<div className="flex">
<Input
name="emailorUsername"
label={t("emailorUsername")}
value={state.emailorUsername}
onChange={(val: any) => onChange("emailorUsername", val)}
/>
<InputPassword
name="password"
label={t("password")}
value={state.password}
onChange={(val: any) => onChange("password", val)}
/>
<ButtonLoading
text={t("login")}
variant="contained"
onClick={() => {
if (onSubmitForTest) {
const loginRequestItemForTest = {
emailOrUsername: "testUsername",
password: "testPassword",
};
onSubmitForTest(loginRequestItemForTest)
}
handleLogin()
}}
dataTestid={"login-button-element"}
/>
</div>
);
};
return <div className="">{renderLoginPart()}</div>;
};
export default Login;
My index.test.js :
import React from 'react'
import { render, screen, waitFor } from "#testing-library/react"
import LoginPage from "../index"
import userEvent from "#testing-library/user-event"
const onSubmit = jest.fn()
beforeEach(()=>{
const { } = render(<LoginPage />)
onSubmit.mockClear()
})
test('Login form parametre olarak doğru data gönderme testi', async () => {
const eMail = screen.getByTestId('text-input-element')
const password = screen.getByTestId('password-input-element')
userEvent.type(eMail, "fillWithTestUsername")
userEvent.type(password, "fillWithTestPassword")
userEvent.click(screen.getByTestId('login-button-element'))
await waitFor(()=>{
expect(onSubmit).toHaveBeenCalledTimes(1)
})
})
beforeEach(()=>{
render(<LoginPage onSubmitForTest={onSubmit} />)
})
Please try doing this in beforeEach. If this still doesn't work you can try replacing toHaveBeenCalledTimes with toBeCalledTimes like below
await waitFor(()=>{
expect(onSubmit).toBeCalledTimes(1)
})

Mocking axios.post response triggered by button in react component

I would like to test (with RTL and jest) the api.post (with axios) triggered by a button in my react component bellow. It's about a login display with 2 inputs fields (email and password) and 2 buttons (enter and register). Both the enter and the register buttons called asynchronous functions.
// src > pages > Login
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { saveToken } from '../app/slices/tokenSlice';
import { saveUserData } from '../app/slices/userSlice';
import api from '../services/api';
function Login() {
const [loginState, setLoginState] = useState({
email: '',
password: '',
});
const navigate = useNavigate();
const dispatch = useDispatch();
const handleChange = ({ target: { name, value } }) => {
setLoginState({
...loginState,
[name]: value,
});
};
const enterUser = async () => {
await api.post('/login', { ...loginState })
.then((response) => {
dispatch(saveToken(response.data.token));
dispatch(saveUserData(loginState));
navigate('/home');
})
.catch((error) => {
alert(error.message);
});
};
const registerUser = async () => {
api.post('/user', loginState)
.then(() => {
alert('Usuário cadastrado com sucesso!');
})
.catch((error) => {
alert(error.message);
});
};
return (
<div>
<label htmlFor="email">
Email:
<input
id="email"
name="email"
onChange={handleChange}
placeholder="Email"
type="text"
value={loginState.email}
/>
</label>
<label htmlFor="password">
Senha:
<input
id="password"
name="password"
onChange={handleChange}
type="password"
value={loginState.password}
/>
</label>
<button
name="btnEnterUser"
type="button"
onClick={() => enterUser()}
>
Entrar
</button>
<button
name="btnRegisterUser"
type="button"
onClick={() => registerUser()}
>
Cadastrar
</button>
</div>
);
}
export default Login;
// src > services > api.js
import axios from 'axios';
const { BASE_URL } = process.env;
const api = axios.create({
baseURL: BASE_URL,
});
api.defaults.headers.post['Content-Type'] = 'application/json;charset=utf-8';
api.defaults.headers.post['Access-Control-Allow-Origin'] = '*';
export default api;
How can i develop a test with mock data related to "registerUser" and "enterUser" functions. I have tried many options with jest.fn(), jest.spyOn() and userEvent.click(btnRegister), where btnRegister is the element which i getByRole as you can check in my test file bellow:
// src > tests > login.test.js
import React from 'react';
import '#testing-library/jest-dom';
import { screen } from '#testing-library/react';
import userEvent from '#testing-library/user-event';
import Login from '../pages/Login';
import renderWithRouter from './renderWithRouter';
import api from '../services/api';
describe('Componente Login', () => {
let inputEmail;
let inputPassword;
let btnRegister;
let btnEnter;
const userEmail = 'mariana#gmail.com';
const userPassword = '123456';
const tokenMock = {
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjA4MWYyYzBhMWFhNzFmYjhjZjU2NjAiLCJlbWFpbCI6Im1hcmlhbmFAZ21haWwuY29tIiwiaWF0IjoxNjQ1MzIyMjU1LCJleHAiOjE2NDUzNDAyNTV9.TIgJFIzg1W0bisvJ3CfRsVCZr3kbKn13_NBN-Ah1U1w',
};
const userRegisteredResponseMock = {
user:
{
email: 'vitao#gmail.com',
id: '6211a1d3eb25fc2418dec05a',
},
};
beforeAll(() => {
renderWithRouter(<Login />);
inputEmail = screen.getByLabelText(/email/i);
inputPassword = screen.getByLabelText(/senha/i);
btnRegister = screen.getByRole('button', { name: /cadastrar/i });
btnEnter = screen.getByRole('button', { name: /entrar/i });
userEvent.type(inputEmail, userEmail);
userEvent.type(inputPassword, userPassword);
});
it('Registro de usuário com sucesso', async () => {
// ************** DOESNT WORK **********************
// api.post = jest.fn().mockImplementation(() => {
// Promise.resolve(userRegisteredResponseMock);
// });
// api.post = jest.fn(() => Promise.resolve(userRegisteredResponseMock));
// api.post = jest.fn().mockResolvedValue(userRegisteredResponseMock);
// jest.spyOn(Login, 'enterUser');
// ****************************************************
userEvent.click(btnRegister);
expect(<"REGISTER USER" FUNCTION>).toBeCalledTimes(1);
});
});
I also have tried created "mocks" folder as jest documentation has mentioned in this link: https://jestjs.io/docs/manual-mocks , but without success.
in order to force jest using the mocked module, the jest.mock() function should be called.
import api from '../services/api';
...
jest.mock('../services/api');
...
api.post.mockResolvedValue(userRegisteredResponseMock);
it's also possible to mock axios module itself. furthermore, there's a jest-mock-axios npm module designed to achieve the behaviour.

How to test amplify auth signup in react using Jest + Enzyme?

I need to test the register method along with the amplify auth signup which is written in my action file.
import history from "../utils/history.helper";
import { alertInfo } from "../utils/common.helper";
import { Auth } from "aws-amplify";
export const AuthAction = {
register,
};
function register(signUpData) {
return async (dispatch) => {
const {
username,
password,
company_name,
country_name,
designation,
} = signUpData;
try {
const signUpResponse = await Auth.signUp({
username,
password,
attributes: {
"custom:company_name": company_name,
"custom:country_name": country_name,
"custom:designation": designation,
},
});
alertInfo("success", "Please verify the mail,for successfull login");
history.push("/login");
} catch (error) {
alertInfo("error", error.message);
}
};
}]
This action file I'm calling from my signup component
import React from "react";
import { useForm } from "react-hook-form";
import { useDispatch } from "react-redux";
import { yupResolver } from "#hookform/resolvers/yup";
import content from "./content";
import { AuthInputField } from "../../common/FieldComponent";
import { AuthAction } from "../../../actions/auth.action";
import { signupSchema } from "../../common/Validation";
const SignupForm = () => {
const dispatch = useDispatch();
const { register, handleSubmit, errors } = useForm({
resolver: yupResolver(signupSchema),
});
const [buttonDisable, setButtonDisable] = React.useState(false);
function onSubmit(signUpData) {
setButtonDisable(true);
dispatch(AuthAction.register(signUpData));
}
return (
<div className="container" data-test="signUpContainer">
<h4>Welcome</h4>
<p>Please resigter to your account.</p>
<form data-testid="submit" onSubmit={handleSubmit(onSubmit)}>
{content.inputs.map((input, index) => {
return (
<AuthInputField
key={index}
name={input.name}
label={input.label}
placeholder={input.placeholder}
type={input.type}
register={register}
error={(errors && errors[input.name] && errors[input.name]) || {}}
/>
);
})}
<input
type="submit"
className="btn btn-primary button"
name="submit"
value={`Submit`}
role="submit"
disabled={buttonDisable}
/>
</form>
</div>
);
};
export default SignupForm;
I'm trying to find a way for testing the "Auth.signup" but didn't find any specific solution.
After spending so many hours, finally wrote these test cases for the above question.
import { register } from "./auth.action";
import mockData from "./__mocks__/mockData";
import { Auth } from "./__mocks__/aws-amplify";
import history from "../utils/history.helper";
jest.mock("./auth.action", () => {
return { register: jest.fn(() => mockPromise) };
});
jest.mock("aws-amplify");
describe("Signup action", () => {
beforeEach(() => {
register.mockClear();
});
test("Check register function have been called or not", async () => {
register();
expect(register).toHaveBeenCalled();
expect(register).toMatchSnapshot();
});
test("Check args passed in function are valid or not", () => {
expect(mockData.signupData).not.toBeNull();
expect(mockData.signupData).toMatchObject({
username: "Abhinav02#getnada.com",
});
expect(mockData.signupData).toHaveProperty("company_name", "Ces");
expect(mockData.signupData).toHaveProperty("country_name", "India");
expect(mockData.signupData).toHaveProperty("designation", "SD");
expect(mockData.signupData).toHaveProperty("password", "Password#123");
expect(mockData.signupData).toMatchSnapshot();
});
test("Amplify auth is called or not", () => {
Auth.signUp(mockData.signupData);
expect(Auth.signUp).toHaveBeenCalled();
expect(Auth.signUp).toMatchSnapshot();
});
test("history is pushed", () => {
const pushSpy = jest.spyOn(history, "push");
pushSpy("/login");
expect(pushSpy).toHaveBeenCalled();
expect(pushSpy.mock.calls[0][0]).toEqual("/login");
});
});
I have written the amplify auth test case in mock file.
// in __mocks__/aws-amplify.js
export const Auth = {
currentSession: jest.fn(() => Promise.resolve()),
signUp: jest.fn(() => Promise.resolve()),
signIn: jest.fn(() => Promise.resolve()),
};
Hope it helps others as well who are looking for the same.

What is the alternative for Compose in react-Apollo?

I have been trying to use multiple graphql() enhancers at once, but compose seems not to be working. I have tried a load of different imports from different libraries, still nothing. Does any know any fix? The data is not being passed from graphl() to my component props, so I'm getting an error that reads Cannot destructure property 'loading' of 'data' as it is undefined.
Here's my component:
import React,{useState} from 'react'
// import {compose} from 'redux'
import { flowRight as compose } from 'lodash'
import { graphql} from 'react-apollo'
import {getAuthorsQuery, addBookMutation} from './queries/query'
const AddBook = (props) => {
const [formData, setFormData] = useState({
name:'',
genre:'',
authorId:''
})
const {name, genre, authorId} = formData
const onChange = e => setFormData({ ...formData, [e.target.name]: e.target.value})
const displayAuthors = () => {
let data = props.data
const {loading} = data
if(loading){
return (<option>Loading Authors</option>)
} else{
return data.authors.map(author => {
return (<option key={author.id} value={author.id}>{author.name}</option>)
})
}
}
const submitForm = e => {
e.preventDefault();
console.log(formData);
}
return (
<form id="add-book" onSubmit={e => submitForm(e)}>
<div className="field">
<label>Book name:</label>
<input
type="text"
onChange={e => onChange(e)}
name="name"
value={name}
/>
</div>
<div className="field">
<label>Genre:</label>
<input
name="genre"
value={genre}
type="text"
onChange={e => onChange(e)}
/>
</div>
<div className="field">
<label>Author:</label>
<select
name="authorId"
value={authorId}
onChange={e => onChange(e)}
>
<option>Select Author</option>
{displayAuthors()}
</select>
</div>
<button>+</button>
</form>
)
}
// export default graphql(getAuthorsQuery)(AddBook)
export default compose(
graphql(getAuthorsQuery, {name: "getAuthorsQuery"}),
graphql(addBookMutation, {name:"addBookMutation"})
)(AddBook)
And my query:
import { gql } from 'apollo-boost'
const getBooksQuery = gql`
{
books{
name,
id
}
}
`
const getAuthorsQuery = gql`
{
authors{
name,
id
}
}
`
const addBookMutation = gql`
mutation{
addBook(name:"", genre:"", authorId:""){
name,
id
}
}
`
export {getAuthorsQuery, getBooksQuery, addBookMutation}
You're providing a name option for both your HOCs, so the data will be available under those prop names:
props.getAuthorsQuery.loading
That said, the HOCs are deprecated -- you should probably be using the newer hooks API.
You can make use of useQuery hook from apollo and execute the queries like
import { useQuery } from '#apollo/react-hooks';
import React,{useState} from 'react'
// import {compose} from 'redux'
import { flowRight as compose } from 'lodash'
import {getAuthorsQuery, addBookMutation} from './queries/query'
const AddBook = (props) => {
const [formData, setFormData] = useState({
name:'',
genre:'',
authorId:''
})
const authors = useQuery(getAuthorsQuery):
const bookMutation = useQuery(addBookMutation);
... rest of code here
}
export default AddBook;
I was dealing with the same issue. I got useQuery and useMutation methods to work together and did not have to use compose. I am still new to gql so if you spot any bad practice or a tip, I would love to hear:
const authorsData = useQuery(authorsQuery)
//addBook takes args and send data to db when triggered in submitHandler
const [addBook] = useMutation(addBookMutation);
const submitHandler = (e) => {
e.preventDefault();
console.log(name, genre, author)
addBook({
variables: {
name: name,
genre: genre,
authorId: author
},
refetchQueries: [{ query: bookList }]
})
}

Resources