Passing usetranslation as a parameter to function from jest - javascript

I wanted to pass use-translation as a parameter to util function I created, I'm not able get the desired result, how can I pass a function?
import { useTranslation } from "react-i18next";
import { submissionTypes } from "../../Submissions/Details/constants";
import { getFormName } from "./helpers";
// jest.mock("react-i18next", () => ({
// useTranslation: () => ({ t: key => key }),
// }));
// jest.mock('react-i18next', () => ({
// useTranslation: () => ({
// i18n: { changeLanguage: jest.fn() },
// t: key => key,
// }),
// }));
describe("utils/helpers", () => {
const { t } = useTranslation();
describe("getFormName()", () => {
it("should return withdrawal", () => {
const result = getFormName("withdrawal", true, t ); //last parameter is 't' which is a translation function
expect(result).toEqual("WITHDRAWAL");
});
});
});
Below if function definiton for getFormName:
export function getFormName(type, uppercase, t) {
switch (type) {
case "withdrawal":
return uppercase
? _.upperCase(t("mission.withdrawal"))
: t("mission.withdrawal");
default:
return uppercase ? _.upperCase(type) : toTitleCase(type);
}
}

Related

react - mockImplementation not working using jest

App.tsx
import { StackNavigationProp } from '#react-navigation/stack';
...
export const App = () => {
const rootNavigation = useNavigation<StackNavigationProp<ParamList>>();
const { params } =
useRoute<RouteProp<ParamList, 'myScreen'>>();
const navigateToHomePage = (): void => {
if (params.isVerified) {
rootNavigation.reset({
index: 0,
routes: [
{
name: 'HomePage',
},
],
});
}
};
return (...)
}
App.test.tsx
import React from 'react';
import { render, fireEvent, waitFor, RenderAPI } from '#testing-library/react-native';
import { MigrationFlowContext } from 'src/contexts/migration/MigrationFlowContext';
import App from './App.tsx';
const mockedNavigate = jest.fn();
const mockedReset = jest.fn();
const mockSpy = jest.fn().mockImplementation(() => ({
params: {
isVerified: true,
},
}));
jest.mock('#react-navigation/native', () => {
const actualNav = jest.requireActual('#react-navigation/native');
return {
...actualNav,
useNavigation: () => ({
navigate: mockedNavigate,
reset: mockedReset,
}),
useRoute: async () => mockSpy(),
};
});
describe('App', () => {
beforeEach(() => {
mockSpy.mockReset();
});
describe('isVerified = true', () => {...})
describe('isVerified = false', () => {
mockSpy.mockImplementation(() => {
return {
params: {
isVerified: false,
},
};
});
test('Click Yes', async () => {
const { getByTestId } = renderHomeScreen();
...
});
test('Click No', () => {
const { getByTestId } = renderHomeScreen();
...
});
});
}
It didn't work with below error
TypeError: Cannot read properties of undefined (reading 'isVerified')
How to fix it?

Redux is able to set characters to an array however the remove action does not seem to reach the reducer

I am using redux to update an array of characters as a user types or erases it, so that when the user correctly types the entire phrase I can set a success flag.
So far when typing in characters the redux type SET_INPUT fires off and updates my state but unfortunately my REMOVE_INPUT doesn't seem to fire off but it does however reach the action.
My Reducer:
import { GET_PHRASE, SET_LOADING, SET_INPUT, REMOVE_INPUT } from "../types";
const initialState = {
level: 1,
phrase: null,
scrambledPhrase: null,
words: [],
input: [],
goal: [],
success: false,
loading: false,
};
export const phraseReducer = (state = initialState, action) => {
switch (action.type) {
case GET_PHRASE:
return {
...state,
phrase: action.payload.sentence,
scrambledPhrase: action.payload.scrambledPhrase,
words: action.payload.phrase.split(" "),
goal: action.payload.phrase.split(""),
loading: false,
};
case SET_INPUT:
console.log("setting input");
return {
...state,
input: [...state.input, action.payload],
};
case REMOVE_INPUT:
console.log("removing input");
return {
...state,
input: [...state.input.slice(0, -1)],
};
case SET_LOADING:
return {
...state,
loading: true,
};
default:
return state;
}
};
My Actions:
import { GET_PHRASE, SET_LOADING, SET_INPUT, REMOVE_INPUT } from "../types";
import axios from "axios";
export const getPhrase = (level) => async (dispatch) => {
try {
setLoading();
await axios
.get(`MY ROUTE`)
.then((res) => {
// console.log(res);
const sentence = res.data.data.phrase;
const scrambledSentence = scramblePhrase(
res.data.data.phrase
);
dispatch({
type: GET_PHRASE,
payload: {
phrase: phrase.toLowerCase(),
scrambledPhrase: scrambledPhrase.toLowerCase(),
},
});
});
} catch (err) {
console.error(err);
}
};
// SET INPUT
export const setInput = (input) => async (dispatch) => {
try {
dispatch({
type: SET_INPUT,
payload: input,
});
} catch (err) {
console.error(err);
}
};
// REMOVE INPUT
export const removeInput = () => {
try {
console.log("remove reached in actions");
return {
type: REMOVE_INPUT,
};
} catch (err) {
console.error(err);
}
};
// SET LOADING
export const setLoading = () => {
console.log("Loading...");
return {
type: SET_LOADING,
};
};
My Component to input a character:
import React, { useState } from "react";
// redux imports
import { connect } from "react-redux";
import { setInput, removeInput } from "../redux/actions/phraseActions";
import PropTypes from "prop-types";
const Character = ({ character, hasSpace, setInput }) => {
const [success, setSuccess] = useState();
const handleChange = (e) => {
if (e.target.value === character) {
// console.log("Success");
setSuccess(true);
} else {
setSuccess(false);
}
};
const keyedDown = (e) => {
// check for space or a letter
if (e.keyCode === 32 || (e.keyCode > 64 && e.keyCode < 91)) {
setInput(String.fromCharCode(e.keyCode).toLowerCase());
}
// check for backspace
else if (e.keyCode === 8) {
removeInput();
}
};
return (
<div
className={`character ${
success ? "success" : hasSpace ? "space" : ""
}`}
>
<input
type="text"
name="name"
required
maxLength="1"
size="1"
onChange={handleChange}
onKeyDown={keyedDown}
className="input"
autoComplete="off"
></input>
</div>
);
};
Character.propTypes = {
setInput: PropTypes.func.isRequired,
removeInput: PropTypes.func.isRequired,
};
const mapStateToProps = (state) => ({
// define state
phrase: state.phrase,
});
export default connect(mapStateToProps, { setInput, removeInput })(Character);
In my console you can see which points I am reaching:
In your event handler you are not calling removeInput that was provided by connect (props.removeInput) but the imported removeInput that doesn't dispatch anything and just returns an action object, so I suggest changing the component definition to:
const Character = ({ character, hasSpace, setInput, removeInput }) => {
In your reducer you can do: input: state.input.slice(0, -1), because slice already returns a shallow copy of the array so no need to copy it with [...]

Update tvWidget in charting component using hooks

I am adding the tradingview charting library into my project and am having troubles getting the chart to re-render when I change the selected symbol.
When the chart loads initially it was calling a componentDidMount to submit parameters to their chart component which returns the chart. This is the charting component and I have a list of securities beside it that update redux state for symbol when clicked.
what I want to do is force the chart to update when the state changes so the correct symbol is displayed.
It is the same issue mentioned in this question, but I'm using hooks instead of class based components and when I try to use useEffect as componentDidUpdate I am getting symbol undefined.
Update:: in other question they said to use something like this in componentDidUpdate
this.tvWidget.chart().setSymbol('BINANCE:' + this.props.selectedSymbol.name)
but I cannot figure out how to do something similar with hooks
charting.js
export function TVChartContainer(props) {
const [symbol, setSymbol] = useState(props.symbol);
const tvWidget = null;
useEffect(() => {
setSymbol(props.symbol)
}, [props.symbol])
const componentDidMount = () => {
// setSymbol(props.symbol)
const widgetOptions = {
symbol: symbol,
//symbol: 'BTC/USDT',
//symbol: 'BTC/USD', //getUrlVars()["symbol"],
datafeed: Datafeed,
container_id: 'tv_chart_container',
library_path: '/charting_library/',
locale: getLanguageFromURL() || 'en',
disabled_features: ['use_localstorage_for_settings'],
enabled_features: ['study_templates'],
charts_storage_url: props.chartsStorageUrl,
charts_storage_api_version: props.chartsStorageApiVersion,
fullscreen: false,
autosize: true,
width: '100%',
timezone: 'America/New_York',
client_id: 'Hubcap',
user_id: 'public_user_id',
auto_save_delay: 10,
theme: 'Light',
loading_screen: { backgroundColor: '#222222', foregroundColor: '#229712' },
custom_indicators_getter: indicators,
};
const tvWidget = new widget(widgetOptions);
// tvWidget = tvWidget;
const thisComponent = props;
tvWidget.onChartReady(() => {
tvWidget.headerReady().then(() => {
const button = tvWidget.createButton();
button.setAttribute('title', 'Click to show a notification popup');
button.classList.add('apply-common-tooltip');
button.addEventListener('click', () =>
tvWidget.showNoticeDialog({
title: 'Notification',
body: 'TradingView Charting Library API works correctly',
callback: () => {
console.log('Noticed!');
},
})
);
button.innerHTML = '';
// thisComponent.getPattern(); //might need to uncomment later
tvWidget
.chart()
.onIntervalChanged()
.subscribe(null, function (interval, obj) {
console.log('On interval change');
thisComponent.getPattern();
});
tvWidget
.chart()
.onSymbolChanged()
.subscribe(null, function (symbolData) {
console.log('Symbol change ' + symbolData);
// thisComponent.getPattern();
});
// tvWidget.chart().createStudy('Strange Indicator', false, true);
// tvWidget.chart().createStudy('ESS Indicator', false, true);
// tvWidget.chart().createStudy('ESL Indicator', false, true);
// tvWidget.chart().createStudy('EPS Indicator', false, true);
// tvWidget.chart().createStudy('EPL Indicator', false, true);
// tvWidget.chart().createStudy('ETS Indicator', false, true);
// tvWidget.chart().createStudy('ETL Indicator', false, true);
});
});
};
const componentWillUnmount = () => {
if (tvWidget !== null) {
tvWidget.remove();
tvWidget = null;
}
};
// useEffect(() => {
// componentDidMount();
// // getPattern();
// // drawPattern();
// // // removeAllShape();
// return () => {
// componentWillUnmount();
// }
// }, [symbol])
useEffect(() => {
setSymbol(props.symbol)
componentDidMount();
// getPattern();
// drawPattern();
// // removeAllShape();
return () => {
componentWillUnmount();
}
}, []);
return <div id="tv_chart_container" className={'TVChartContainer'} />;
main page componenet
const TestPage = ({selected}) => {
const [symbol, setSymbol] = useState('AAPL');
useEffect(() => {
setSymbol(selected)
}, [selected])
return (
<div>
<TVChartContainer symbol={symbol} />
</div>
);
}
const mapStateToProps = (state) => {
return {
selected: state.Watchlist.stock.selected,
}
}
export default connect(mapStateToProps)(TestPage)
watchlist
const Security = ({index, name, stocks, selected}) => {
const dispatch = useDispatch();
const [taskName, setTaskName] =useState(name)
const [prevState, setPrevState] = useState(stocks)
const removeTask = (e) => {
e.stopPropagation()
setPrevState(stocks)
dispatch(removeStock(index))
}
const selectAStock = () => {
dispatch(stockSelected(name))
}
useEffect(() => {
setPrevState(stocks)
}, [])
useEffect(() => {
if(prevState !== stocks) dispatch(updateWatchlist(stocks, selected))
}, [stocks])
return (
<Row className="list-group-item">
<div className="item-titles" onClick={() => selectAStock()}>
{name}
</div>
<button onClick={(e) => removeTask(e)} className="remove-item">
<i className="glyphicon glyphicon-remove"></i>
</button>
</Row>
);
}
const mapStateToProps = (state) => {
return {
stocks: state.Watchlist.stock.watchlist,
}
}
export default connect(mapStateToProps, {removeStock, updateWatchlist, stockSelected})(Security);
this.tvWidget?.setSymbol("BINANCE", "5" as ResolutionString, () => null)
The setSymbol accept 3 parameters.
(symbol: string, interval: ResolutionString, callback: EmptyCallback): void
Symbol: which is a string
Interval: which is of type ResolutionString. ("5" as ResolutionString) use the 'as' to prevent error)
callback: just an empty callback
on componentDidUpdate() you can update the tradingView Widget with the following parameters.

Apollo MockedProvider not returning expected data

I wrote a hook that calls apollo useQuery. It's pretty simple:
useDecider:
import { useState } from 'react';
import { useQuery, gql } from '#apollo/client';
export const GET_DECIDER = gql`
query GetDecider($name: [String]!) {
deciders(names: $name) {
decision
name
value
}
}
`;
export const useDecider = name => {
const [enabled, setEnabled] = useState(false);
useQuery(GET_DECIDER, {
variables: {
name
},
onCompleted: data => {
const decision = data?.deciders[0]?.decision;
setEnabled(decision);
},
onError: error => {
return error;
}
});
return {
enabled
};
};
I'm trying to test it now and the MockedProvider is not returning the expected data:
import React from 'react';
import { render, screen } from '#testing-library/react';
import '#testing-library/jest-dom';
import { MockedProvider } from '#apollo/client/testing';
import { useDecider, GET_DECIDER } from './useDecider';
const getMock = (value = false, decider = '') => [
{
request: {
query: GET_DECIDER,
variables: {
name: decider
}
},
result: () => {
console.log('APOLLO RESULT');
return {
data: {
deciders: [
{
decision: value,
name: decider,
value: 10
}
]
}
};
}
}
];
const FakeComponent = ({ decider }) => {
const { enabled } = useDecider(decider);
return <div>{enabled ? 'isEnabled' : 'isDisabled'}</div>;
};
const WrappedComponent = ({ decider, value }) => (
<MockedProvider mocks={getMock(value, decider)} addTypename={false}>
<FakeComponent decider={decider} />
</MockedProvider>
);
describe('useDecider', () => {
it('when decider returns true', () => {
// should return true
render(<WrappedComponent decider="fake_decider" value={true} />);
screen.debug();
const result = screen.getByText('isEnabled');
expect(result).toBeInTheDocument();
});
});
I simplified your hook implementation and put together a working example:
import { useQuery, gql } from "#apollo/client";
export const GET_DECIDER = gql`
query GetDecider($name: [String]!) {
deciders(names: $name) {
decision
name
value
}
}
`;
export const useDecider = (name) => {
const { data } = useQuery(GET_DECIDER, { variables: { name } });
return { enabled: data?.deciders[0]?.decision || false };
};
Note that in the test I also updated your getBy to an await findBy:
describe("useDecider", () => {
it("when decider returns true", async () => {
// should return true
render(<WrappedComponent decider="fake_decider" value={true} />);
screen.debug();
const result = await screen.findByText("isEnabled");
expect(result).toBeInTheDocument();
});
});
This is because you need to wait for your API call to complete before the data will be on the page, hence you would not expect the data to be there on the first render.
From https://www.apollographql.com/docs/react/development-testing/testing/#testing-the-success-state
To test how your component is rendered after its query completes, you
can await a zero-millisecond timeout before performing your checks.
This delays the checks until the next "tick" of the event loop, which
gives MockedProvider an opportunity to populate the mocked result
try adding before your expect call
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 0));
});

Action doesn't receive the correct prop

I'm using React/Redux in this code and I'm trying to pass the correct prop by action. My intention is to change converter name on click modal button. But when I debbug, console server shows me the same action with no alteration clicking on confirm button.
My action in file actions:
export const saveOrUpdateConverter = converter => {
return {
converter,
type: CONVERTER.SAVE_OR_UPDATE_CONVERTER
};
};
The function I'm using to do that:
export const saveOrUpdateConverter = (converter, type) => {
const url = `${BASE_URL}/SaveOrUpdateConverter`;
let converterWithoutId = {
...converter,
Id: 0
};
return makeRequest(
{
method: "post",
url,
data: type === "edit" ? converter : converterWithoutId
},
(data, dispatch) => {
// if we are adding a new converter, we need to remove it from newConverters
if (type === "add") {
dispatch(actions.removeFromNewConverters(converter));
}
dispatch(actions.saveOrUpdateConverter(data));
},
true
);
};
The file where I'm calling the function
const handleSaveUpdateConverter = async () => {
let type = "edit";
return await props.saveOrUpdateConverter(converter, type);
};
Component receiving function by prop:
<AddOrEditConverterModal
converter={converter}
show={showEditConverterModal}
onCloseModal={() => setShowEditConverterModal(false)}
saveOrUpdateConverter={(converter, propsType) =>
handleSaveUpdateConverter(converter, propsType)
}
type={"edit"}
/>
I finally call the props saveOrUpdateConverter in other file:
const updateConverter = async () => {
if (converter.IntervalToSavePayload < 5) {
props.requestError(
true,
props.intl.formatMessage({
id: "modal.base.converter.interval.save.pyload.error"
})
);
return;
}
await props.saveOrUpdateConverter(converter, props.type);
debugger
props.onCloseModal();
};
Connect function to use saveOrUpdateConverter :
import { connect } from "react-redux";
import { saveOrUpdateConverter } from "Features/Devices/Converters/actions";
import ConverterPage from "./ConverterPage";
const mapStateToProps = state => ({
activeConverters: state.converter.activeConverters,
activeInstruments: state.instrument.activeInstruments
});
export default connect(mapStateToProps, {saveOrUpdateConverter})(ConverterPage);

Categories