💬 Question and Help
Hi, I'm trying to integrate fast-check with cypress and it gives me the error shown on the attached screen.
My code looks like:
import React from 'react';
import TextField from '../../src/components/textField';
import fc from 'fast-check';
const textFieldSelector = '[data-testid="city-input"]';
class TypeText {
constructor(text) {
this.text = text;
}
check = m => true;
run(m, r) {
await cy.get(textFieldSelector).type(this.text));
m.text += this.text;
}
}
describe('TextField.cy.js', () => {
it.only('should call onChange', async () => {
await cy.mount(<TextField title="city" />);
const allCommands = [
fc.string({minLength: 1}).map(t => new TypeText(t)),
];
await fc.assert(
fc.asyncProperty(fc.commands(allCommands, {size: '+1'}), async cmds => {
const s = () => ({model: {text: ''}, real: {}});
await fc.asyncModelRun(s, cmds);
})
);
});
});
https://user-images.githubusercontent.com/2463068/201888964-5671ba89-b90a-418b-ab44-5eecfead208b.png
Related
I'm having trouble writing test code for this application. Specifically, the code works in the app - it works exactly the way I want it to. But with all the crazy mocks and abstractions in the codebase I have to work with, for some reason the tests never seem to pass.
Here is the code I'm hoping to test:
// useLandAccessHistory.ts
import { RequestState, RootState, useAppDispatch, useAppSelector } from '../../../../../store';
import { fetchLandAccessHistory } from '../../../../../extension/LandAccess/module/thunks';
import { LAND_ACCESS_SLICE_NAME } from '../../../../../extension/LandAccess/module/constants';
import { models } from '#h2know-how/moata-land-management-sdk';
import { useEffect, useMemo } from 'react';
const landAccessHistoryTableSelector = (state: RootState) => {
const landAccess = state[LAND_ACCESS_SLICE_NAME];
const byTitleId = (titleId: number): models.LandAccessHistory[] | undefined =>
landAccess.landAccessHistories[titleId];
const { status, error } = landAccess.apiStatus.fetchLandAccessHistory;
return { status, error, byTitleId };
};
export const useLandAccessHistory = ({
projectId,
titleId,
}: {
projectId: number;
titleId: number;
}): { historyData?: models.LandAccessHistory[] } & RequestState => {
const dispatch = useAppDispatch();
const { status, error, byTitleId } = useAppSelector(landAccessHistoryTableSelector);
useEffect(() => {
const req = dispatch(
fetchLandAccessHistory({
projectId,
titleId,
})
);
return req.abort;
}, [dispatch, projectId, titleId]);
const historyData = useMemo(() => byTitleId(titleId), [byTitleId, titleId]);
return { historyData, status, error };
};
And here is the test suite that is failing.
// useLandAccessHistory.spec.ts
import React from 'react';
import { Provider } from 'react-redux';
import { configureStore } from '#reduxjs/toolkit';
import { renderHook } from '#testing-library/react-hooks';
import { waitFor } from '#testing-library/react';
import { useLocation } from 'react-router-dom';
import { useLandAccessHistory } from './useLandAccessHistory';
import { useAppDispatch, useAppSelector } from '../../../../../store';
import { landAccessSlice } from '../../../../../extension/LandAccess/module/landAccess';
import { LAND_ACCESS_SLICE_NAME } from '../../../../../extension/LandAccess/module/constants';
import { useUrlParams, useUrlSearchParams } from '../../../../../utils/hooks';
import { mockLandAccessHistories } from '../../../../../extension/LandAccess/test-helpers/factories';
import { landAccessAPI } from '../../../../../extension/LandAccess/api/landAccessAPI';
const mockHistories = mockLandAccessHistories({ projectId: 123, titleId: 345 }, 2);
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: jest.fn(),
}));
jest.mock('../../../../../store');
jest.mock('../../../../../utils/hooks', () => ({
...jest.requireActual('../../../../../utils/hooks'),
useUrlParams: jest.fn(),
useUrlSearchParams: jest.fn(),
}));
jest.mock('../../../../../extension/LandAccess/api/landAccessAPI', () => ({
getLandAccessHistory: jest.fn(() => Promise.resolve(mockHistories)),
}));
describe('useLandAccessHistory', () => {
let dispatch: jest.Mock;
let abort: jest.Mock;
let store: any;
let wrapper: any;
beforeEach(() => {
(useLocation as jest.Mock).mockReturnValue({ pathname: '/route/123' });
store = configureStore({
reducer: { [LAND_ACCESS_SLICE_NAME]: landAccessSlice.reducer },
middleware: (getDefaultMiddleware) => getDefaultMiddleware(),
});
dispatch = jest.fn((...params) => store.dispatch(...params));
abort = jest.fn();
dispatch.mockReturnValue({ abort });
wrapper = ({ children }) => <Provider store={store}>{children}</Provider>;
(useUrlParams as jest.Mock).mockReturnValue({ projectId: 123 });
(useUrlSearchParams as jest.Mock).mockReturnValue({ feature_id: 345 });
(useAppDispatch as jest.Mock).mockReturnValue(dispatch);
(useAppSelector as jest.Mock).mockImplementation((selectorFunction) => selectorFunction(store.getState()));
});
it('fetches the land access history', async () => {
const { result, rerender } = renderHook(() => useLandAccessHistory({ projectId: 123, titleId: 345 }), { wrapper });
await waitFor(() => {
expect(dispatch).toHaveBeenCalled(); // this line runs.
});
rerender();
await waitFor(() => {
expect(store.getState().landAccessHistories).toEqual(mockHistories); // the store does not contain the data
expect(result.current).toEqual({ error: null, status: 'fulfilled', historyData: mockHistories }); // the status is idle and history data is undefined
});
});
});
Here's what I've tried so far: so far as I can tell, dispatch runs, but it doesn't seem to actually call the mocked (or real) API function. It could be that dispatch has been called but has not yet resolved, but then wouldn't await waitFor and rerender() help solve that problem?
This is my codes but it doesn't work when I post please help me. When I say employee.value in Axios, it updates the data, but I need to update the image, so I say newData but it doesn't send it at that time. I don't found solve this code please help me.
If can someone give me a working composition api code.
<script>
import useEmployee from '../composables/employee'
import { onMounted } from 'vue'
import { ref } from "vue";
import axios from "axios";
import { useRouter } from "vue-router";
export default {
props : {
id : {
require : true,
type : String
}
},
setup(props) {
const employee = ref({
'id' : '',
'img' : '',
'name' : '',
'email' : '',
'description' : '',
'position' : '',
'salary' : ''
})
const errors = ref("")
const router = useRouter()
const getEmployee = async (id) => {
let response = await axios.get(`/api/employees/${id}`)
employee.value = response.data.data;
}
onMounted(getEmployee(props.id))
const saveEmployee = async () => {
await updateEmployee(props.id)
}
const updateEmployee = async (id) => {
try {
const formdata = new FormData();
formdata.append('img',employee.value.img)
formdata.append('name',employee.value.name)
formdata.append('email',employee.value.email)
formdata.append('description',employee.value.description)
formdata.append('position',employee.value.position)
formdata.append('salary',employee.value.salary)
let config =
{ headers: {
'Content-Type': "multipart/form-data; charset=utf-8; boundary=" + Math.random().toString().substr(2),
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
} }
// await axios.put(`/api/employees/${id}`,employee.value) it updates the data
// await axios.put(`/api/employees/${id}`,formdata,config) it's not working and get error
await router.push({ name : 'employee.index' })
} catch (error) {
if(error.response.status === 422) {
errors.value = error.response.data.errors;
}
}
}
const onFileChange = async (e) => {
employee.value = e.target.files[0]
}
return {
saveEmployee,
employee,
onFileChange,
errors,
router,
updateEmployee,
}
}
}
</script>
I'm trying to practise createAsyncThunk with reduxjs/tookit. When I first fetch the data from the api it works and I can render the data. However, when I refresh the page I get "TypeError: Cannot read properties of undefined (reading 'memes')" error and can't get it worked anymore. I looked up for some info and thought passing dispatch as useEffect dependency would help but it didn't. When I open Redux Devtools extension => diff = I can clearly see that I fetched the data, promise fulfilled and everything is fine. I try to log allMemes to console and it shows an empty object.
store.js
import { configureStore } from "#reduxjs/toolkit";
import memeSlice from "./features/getAllMemes/displayAllMemesSlice";
const store = configureStore({
reducer:{
memes:memeSlice
}
});
export default store;
DisplaySlice.js
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
export const loadAllMemes = createAsyncThunk("getMemes/loadAllMemes", async () => {
try {
const response = await fetch("https://api.imgflip.com/get_memes");
const data = await response.json();
return data;
}
catch (error) {
console.log(error)
}
})
export const memeSlice = createSlice({
name:"getMemes",
initialState: {
isLoading:false,
hasError:false,
allMemes:{},
},
extraReducers: {
[loadAllMemes.pending]:(state, action) => {
state.isLoading = true;
},
[loadAllMemes.fulfilled]:(state, action) => {
state.allMemes = action.payload;
state.isLoading = false;
},
[loadAllMemes.rejected]:(state, action) => {
state.hasError = true;
}
}
})
export default memeSlice.reducer;
export const selectAllMemes = state => state.memes.allMemes;
displayAllMemes.js
import React , {useEffect} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { loadAllMemes, selectAllMemes } from './displayAllMemesSlice';
export default function DisplayAllMemes() {
const allMemes = useSelector(selectAllMemes)
const dispatch = useDispatch()
useEffect(() => {
dispatch(loadAllMemes())
console.log(allMemes)
}, [dispatch])
return (
<div>
{allMemes.data.memes.map(item => (
<h1>{item.id}</h1>
))}
</div>
)
}
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
export const loadAllMemes = createAsyncThunk("memes/getAllMemes", async () => {
try {
const response = await fetch("https://api.imgflip.com/get_memes");
const data = await response.json();
return data;
} catch (error) {
console.log(error);
}
});
export const memeSlice = createSlice({
name: "memes",
initialState: {
isLoading: false,
hasError: false,
allMemes: {},
},
extraReducers: {
[loadAllMemes.pending]: (state, action) => {
state.isLoading = true;
},
[loadAllMemes.fulfilled]: (state, action) => {
const { data, success } = action.payload;
state.allMemes = data;
state.isLoading = false;
},
[loadAllMemes.rejected]: (state, action) => {
state.hasError = true;
},
},
});
export const {} = memeSlice.actions;
export default memeSlice.reducer;
-- root reducer
import { combineReducers } from "redux";
import memes_slice from "./memes_slice";
const reducer = combineReducers({
memes: memes_slice,
});
export default reducer;
-- Component
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { loadAllMemes } from "../../app/memes_slice";
function Memes() {
const { allMemes } = useSelector((state) => state.memes);
const dispatch = useDispatch();
useEffect(() => {
dispatch(loadAllMemes());
}, [dispatch]);
console.log(allMemes.memes);
return <div>Memes</div>;
}
export default Memes;
I would like to add a progress indicator to Tesseract.js logging.
The example in docs works just fine, until setting a state hook into logger:
const worker = createWorker({
logger: (m) => {
setProgress(m) //new
}});
...
const [ocr, setOcr] = useState('Recognizing...');
const [progress, setProgress] = useState(null); //new
...
return (
<div className="App">
<p>
<LogComponent progress={progress}/> //new
</p>
</div>);
This causes the browser to slow down significantly (probably due React's way to re-render on each state update). Is there a way to get around this? Using React.memo perhaps?
You could design it into a hook so your entire component doesn't re-render. Here is a useTesseract hook you can use that I created: https://gist.github.com/KevinDanikowski/25cdcdda2ef4750bcf443f2027cc375a
Copy and Pasted:
import { useState, useEffect } from 'react'
import { createWorker } from 'tesseract.js'
export default function useTesseract({ tesseractLanguage = 'eng', log = false }) {
const [tesseractWorker, setTesseractWorker] = useState(null)
const [loadingModel, setLoadingModel] = useState(true)
const [modelError, setModelError] = useState(false)
const [imgResults, setImgResults] = useState({})
const [processing, setProcessing] = useState(false)
const [progress, setProgress] = useState(0)
const extractTextFromImage = (imageUrl) => {
const recognize = async () => {
const {
data: {
hocr: htmlOutput,
text,
// tsv, box, unlv
},
} = await tesseractWorker.recognize(imageUrl)
setProcessing(false)
setImgResults({ html: htmlOutput, text })
}
if (loadingModel) {
try {
setTimeout(recognize, 400)
} catch (e) {
console.error('Timeout Error:', e.message)
setImgResults({ error: true })
}
} else {
try {
setProcessing(true)
recognize()
} catch (e) {
console.error('Tesseract Error:', e.message)
setProcessing(false)
setImgResults({ error: true })
}
}
}
const logger = (m) => {
setProgress(m.progress)
if (log) {
console.info(m)
}
}
useEffect(() => {
const loadTesseract = async () => {
if (tesseractWorker) {
await tesseractWorker.loadLanguage(tesseractLanguage)
await tesseractWorker.initialize(tesseractLanguage)
console.info(`INFO: loaded ${tesseractLanguage} tesseract model`)
} else {
const tesseractWorker = createWorker({
logger,
// specify paths because sometimes the free CDN goes down
// corePath: '/static/tesseract-core.wasm.2.2.0.js',
// workerPath: '/static/tesseract-worker.v2.1.4.min.js',
})
setTesseractWorker(tesseractWorker)
await tesseractWorker.load()
await tesseractWorker.loadLanguage(tesseractLanguage)
await tesseractWorker.initialize(tesseractLanguage)
console.info(`INFO: loaded ${tesseractLanguage} tesseract model`)
setLoadingModel(false)
setModelError(true)
setLoadingModel(false)
}
}
loadTesseract().catch((e) => {
console.error(`ERROR: Failed to load tesseract model`, e.message)
setModelError(true)
setLoadingModel(false)
})
// TODO: Have to add a ref to reference the latest tesseractWorker in order to terminate
// return () => tesseractWorker.terminate()
}, [tesseractLanguage])
return {
imgResults,
loadingModel,
processing,
modelError,
progress,
extractTextFromImage,
}
}
I managed to implement and render the progress of the tesseract worker by putting my application render inside a class and using the setState method:
class App extends React.Component {
constructor(props){
super(props)
this.state = {
file: null
}
this.handleChange = this.handleChange.bind(this)
}
setProgress(m) {
if (m.progress !== 0 && m.progress !== 0.5 && m.progress !== 1){
var prog = "Progress: " + Math.round(m.progress*100) + "%"
this.setState({progress: prog})
}
}
worker = createWorker({
logger: m => this.setProgress(m),
});
doOCR = async () => {
await this.worker.load();
await this.worker.loadLanguage('eng');
await this.worker.initialize('eng');
const { data: { text } } = await this.worker.recognize(this.state.file);
this.setState({text: extractTotal(text),
progress: ""});
};
handleChange(event) {
this.setState({text: placeholder});
this.setState({
file: URL.createObjectURL(event.target.files[0]),
})
this.doOCR()
}
setText(input){
if (!input) {
return "Please select a receipt"
}
else {
return input
}
}
render() {
console.log("Text: " + this.state.text)
return (
<div className="container">
<p>{this.setText(this.state.text)}</p>
<p>{this.state.progress}</p>
<input type="file" onChange={this.handleChange}/>
<img src={this.state.file} className='logo' alt=""/>
</div>
);
}
}
new to Jest and having trouble with getting up and running. When calling Dispatcher.register, all I get is undefined.
I'm running Node.js 5.7.0 and Jest 0.9.2.
The following test fails:
BookedServicesStore-test.js:
jest.unmock("../BookedServicesStore");
jest.unmock("object-assign");
describe("BookedServicesStore", () => {
let BookedServicesStore;
let AppDispatcher;
beforeEach(() => {
BookedServicesStore = require("../BookedServicesStore");
AppDispatcher = require("../../dispatcher/AppDispatcher");
});
it("has a dispatch token", () => {
expect(BookedServicesStore.dispatchToken).not.toBeUndefined();
});
});
BookedServicesStore.js:
import AppDispatcher from "../dispatcher/AppDispatcher";
import { EventEmitter } from "events";
import assign from "object-assign";
const CHANGE_EVENT = "change";
const BookedServicesStore = assign({}, EventEmitter.prototype, {
emitChange() {
this.emit(CHANGE_EVENT);
},
addChangeListener(callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
// ...
});
BookedServicesStore.dispatchToken = AppDispatcher.register(action => {
// ...
});
module.exports = BookedServicesStore;
AppDispatcher.js:
import { Dispatcher } from "flux";
module.exports = new Dispatcher();
Not sure what's going on here. What is it that I'm missing?