testbed.createComponents ngsyntax error - angularjs

Hello I am trying to set up karma testing in an angular 2 project however I can't get rid of an error. It was almost always the same, and after using the .catch after createComponents I got the following.
LOG: Error{ngSyntaxError: true, line: 38169, sourceURL:'http://localhost:9876/base/src/test.ts?62c9c9f2f8eb0595c7519c8d7dc8192bb118a027', stack: 'parse#http://localhost:9876/base/src/test.ts?62c9c9f2f8eb0595c7519c8d7dc8192bb118a027:38169:72
_compileTemplate#http://localhost:9876/base/src/test.ts?62c9c9f2f8eb0595c7519c8d7dc8192bb118a027:51920:44
http://localhost:9876/base/src/test.ts?62c9c9f2f8eb0595c7519c8d7dc8192bb118a027:51844:78
forEach#http://localhost:9876/base/src/polyfills.ts?0c8ebfd4331d2badb8d2ce96ff9ff990e8c26a03:1522:12
_compileComponents#http://localhost:9876/base/src/test.ts?62c9c9f2f8eb0595c7519c8d7dc8192bb118a027:51844:26
createResult#http://localhost:9876/base/src/test.ts?62c9c9f2f8eb0595c7519c8d7dc8192bb118a027:51750:37
invoke#http://localhost:9876/base/src/polyfills.ts?0c8ebfd4331d2badb8d2ce96ff9ff990e8c26a03:3039:31
onInvoke#http://localhost:9876/base/src/test.ts?62c9c9f2f8eb0595c7519c8d7dc8192bb118a027:53668:45
onInvoke#http://localhost:9876/base/src/test.ts?62c9c9f2f8eb0595c7519c8d7dc8192bb118a027:54427:47
invoke#http://localhost:9876/base/src/polyfills.ts?0c8ebfd4331d2badb8d2ce96ff9ff990e8c26a03:3038:40
run#http://localhost:9876/base/src/polyfills.ts?0c8ebfd4331d2badb8d2ce96ff9ff990e8c26a03:2789:49
http://localhost:9876/base/src/polyfills.ts?0c8ebfd4331d2badb8d2ce96ff9ff990e8c26a03:3465:60
invokeTask#http://localhost:9876/base/src/polyfills.ts?0c8ebfd4331d2badb8d2ce96ff9ff990e8c26a03:3072:36
onInvokeTask#http://localhost:9876/base/src/test.ts?62c9c9f2f8eb0595c7519c8d7dc8192bb118a027:54454:49
invokeTask#http://localhost:9876/base/src/polyfills.ts?0c8ebfd4331d2badb8d2ce96ff9ff990e8c26a03:3071:48
runTask#http://localhost:9876/base/src/polyfills.ts?0c8ebfd4331d2badb8d2ce96ff9ff990e8c26a03:2839:57
drainMicroTaskQueue#http://localhost:9876/base/src/polyfills.ts?0c8ebfd4331d2badb8d2ce96ff9ff990e8c26a03:3232:42
invoke#http://localhost:9876/base/src/polyfills.ts?0c8ebfd4331d2badb8d2ce96ff9ff990e8c26a03:3138:44
timer#http://localhost:9876/base/src/polyfills.ts?0c8ebfd4331d2badb8d2ce96ff9ff990e8c26a03:4160:34', __zone_symbol__currentTask: ZoneTask{_zone: Zone{_properties: ..., _parent: ..., _name: ..., _zoneDelegate: ...}, runCount: 0, _zoneDelegates: null, _state: 'notScheduled', type: 'microTask', source: 'Promise.then', data: undefined, scheduleFn: undefined, cancelFn: null, callback: function () { ... }, invoke: function () { ... }}}
I am not sure whether someone is capable of making something of this. My source code is below.
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { PlumberAcceptedComponent } from './plumber-accepted.component';
import { DataProvider } from "../../../providers/data.provider";
import { OrderService } from "../order.service";
import { Router } from "#angular/router";
import { AccountProvider } from "../../../providers/account.provider";
import {MockRouter} from '../../../mock';
import * as firebase from 'firebase';
describe('PlumberAcceptedComponent', () => {
let component: PlumberAcceptedComponent;
let fixture: ComponentFixture<PlumberAcceptedComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [PlumberAcceptedComponent],
imports: [ ],
providers: [{provide: Router, useClass:MockRouter},
DataProvider,
OrderService,
AccountProvider
]
})
.compileComponents().catch((err)=>{console.log(err);});
}));
it('should be created', async(() => {
console.log("fdasjklfdjaklfdajfklasdjfklasdjfklasdjklfsdjklfdsjafkldsfasd");
TestBed.compileComponents().then(() => {
console.log(" fjasdklfjasdklfjdasklfjasd");
const fixture = TestBed.createComponent(PlumberAcceptedComponent);
// Access the dependency injected component instance
const app = fixture.componentInstance;
// Access the element
const element = fixture.nativeElement;
// Detect changes as necessary
fixture.detectChanges();
expect(app).toBeTruthy();
}).catch((err)=>{console.log(err);});
}));
});

Related

Apollo MockedProvider: “Failed to match x mocks for this query” where x is off by 1. Never sees the query I ask for

I’m experiencing the most bizarre Apollo behavior… in my tests, any time I issue a query for which I have a mock, Apollo magically can’t find it.
I have 5 mocks covering queries based on a collectionId and some pagination variables. When I have the component under test look up ID 834940, I get this:
Expected variables: {"page":1,"perPage":20,"collectionId":834940}
Failed to match 4 mocks for this query, which had the following variables:
{"collectionId":374276,"page":1,"perPage":20}
{"collectionId":112805,"page":1,"perPage":20}
{"collectionId":238350,"page":1,"perPage":20}
{"collectionId":-1,"page":1,"perPage":20}
Note that it says “4 mocks”. So it’s not seeing the mock I need, and the query fails. Make sense… until I change the component under test to look up ID 112805, which you can clearly see listed in the 4 mocks Apollo ostensibly knows about.
Expected variables: {"page":1,"perPage":20,"collectionId":112805}
Failed to match 4 mocks for this query, which had the following variables:
{"collectionId":374276,"page":1,"perPage":20}
{"collectionId":834940,"page":1,"perPage":20}
{"collectionId":238350,"page":1,"perPage":20}
{"collectionId":-1,"page":1,"perPage":20}
It can still only see 4 mocks, but the mocks it can see have changed. Now, all of a sudden, 112805 is missing in the mocks it sees. And now it CAN see the mock for 834940, the ID I queried for last time! So basically, whatever I ask for is the query it can’t resolve!!
Has anyone encountered this before?
Context
Collection.tsx:
A functional component with 3 different useQuery calls. Only the third one is failing:
import collectionQuery from './Collection.gql';
export type CollectionProps = {
className?: string;
collection: {
id: number;
name: string;
};
};
export function Collection( props: CollectionProps ) {
/* [useQuery #1] */
/* [useQuery #2] */
// useQuery #3
const {
data, error, loading, refetch,
} = useQuery<CollectionQuery, CollectionQueryVariables>( collectionQuery, {
skip: variables.collectionId === -1,
variables,
} );
return null;
}
queryMocks.ts:
import collectionQuery from './Collection.gql';
import { CollectionQuery_collection } from '../../../graphqlTypes/CollectionQuery';
const page = 1;
const perPage = 20;
export const firstCollection: CollectionQuery_collection = {
id: 374276,
name: 'First Collection',
};
export const secondCollection: CollectionQuery_collection = {
id: 834940,
name: 'Second Collection',
};
export const thirdCollection: CollectionQuery_collection = {
id: 112805,
name: 'Third Collection',
}
export const fourthCollection: CollectionQuery_collection = {
id: 238350,
name: 'Fourth Collection',
};
export const fifthCollection: CollectionQuery_collection = {
id: -1,
name 'Fifth Collection (Error)',
};
export const queryMocks: MockedResponse[] = [
{
request: {
query: collectionQuery,
variables: {
collectionId: firstCollection.id,
page,
perPage,
},
},
result: {
data: {
collection: firstCollection,
},
},
},
{
request: {
query: collectionQuery,
variables: {
collectionId: secondCollection.id,
page,
perPage,
},
},
result: {
data: {
collection: secondCollection,
},
},
},
{
request: {
query: collectionQuery,
variables: {
collectionId: thirdCollection.id,
page,
perPage,
},
},
result: {
data: {
collection: thirdCollection,
},
},
}, {
request: {
query: collectionQuery,
variables: {
collectionId: fourthCollection.id,
page,
perPage,
},
},
result: {
data: {
collection: fourthCollection,
},
},
}, {
request: {
query: collectionQuery,
variables: {
collectionId: fifthCollection.id,
page,
perPage,
},
},
result: {
data: {
collection: fifthCollection,
},
},
},
];
Collection.test.tsx:
import React from 'react';
import { MockedProvider } from '#apollo/client/testing';
import { MemoryRouter } from 'react-router';
import { secondCollection, queryMocks } from './queryMocks';
import { Collection } from './Collection';
it( 'Renders', () => {
render(
<MockedProvider mocks={ queryMocks } addTypename={ false }>
<MemoryRouter>
<Collection collection={ { id: secondCollection.id } } />
</MemoryRouter>
</MockedProvider>,
);
} );
The component is fetching collectionQuery twice, likely due to a state change. This could be from a useState set* call, or it could be from having multiple useQuerys. In the latter case, when one of the 3 queries resolves (with data changing from undefined to defined), it triggers a component re-render, which then calls the other query or queries again.
The reason why this breaks MockedProvider is because each mock can only be used to resolve a single query. So on the first matching API call, the mock is “spent”, reducing the queryMocks length from 5 to 4. Then, when the component re-renders and calls the same query again, Apollo can no longer find a matching mock. So to solve this, you have to either A.) refactor the component to only call the query once, or B.) add two of the same mock to the queryMocks array, like so:
queryMocks.ts:
const secondMock = {
request: {
query: collectionQuery,
variables: {
collectionId: secondCollection.id,
page,
perPage,
},
},
result: {
data: {
collection: secondCollection,
},
},
};
export queryMocks: MockedResponse[] = [
/* [mockOne] */
mockTwo,
mockTwo,
/* [mockThree, ..., mockFive] */
];

How can i make a button actvate my searchSpell function?

i am finishing my first react project. But I cant make this button work. I keep giving me the same error below
error console message: Uncaught TypeError_axios_api_js__WEBPACK_IMPORTED_MODULE_1__.default.request is not a function
code:
function Header(){
const { searchSpell } = Hooker();
const [whatSpell, setWhatSpell] = useState()
function pesquisar () {
if(!whatSpell){
alert("escreva alguma coisa antes de pesquisar")
return
} else{
return searchSpell(whatSpell)
}
}
another code:
import { SpellContext } from "../provider.js"
function Hooker (){
const { spellState, searchSpell } = useContext(SpellContext)
return { spellState, searchSpell }
}
export default Hooker
and the function one:
function BaseInfo({ children }){
const [spellState, setSpellState] = useState({
name: undefined,
desc: undefined,
higher_level: undefined,
range: undefined,
components: undefined,
material: undefined,
ritual: false,
duration: undefined,
concentration: false,
casting_time: undefined,
level: 0,
});
const searchSpell = (spellName) => {
api.request(spellName).then(function(response) {
setSpellState(prevState => ({
...prevState,
name: response.data.name,
desc: response.data.desc,
higher_level: response.data.higher_level,
range: response.data.range,
components: response.data.components,
material: response.data.material,
ritual: response.data.ritual,
duration: response.data.duration,
concentration: response.data.concentration,
casting_time: response.data.duration,
level: response.data.duration,
})
)
})
console.log(spellState.duration)
}
const contextValue = {
spellState,
searchSpell: useCallback((spellName) => searchSpell(spellName), []),
}
return(
<SpellContext.Provider value={contextValue}>
{children}
</SpellContext.Provider>
)
}
export default BaseInfo
I don't think it's the button's fault. The error message indicates you're calling searchSpell, but there is something wrong with the way you're writing the API request.
If you're using axios as the error suggests. Check the axios documentation for proper installation, importing and usage.
Example usage for your use case:
import axios from "axios"
// ... ...
axios.request(spellName).then(function() {
// ... ...

TradingView Chart Library not working on production

I've implemented the TradingView charting library on my react project following the documentation provided.
Copy charting_library folder from
https://github.com/tradingview/charting_library/ to /public and to
/src folders. Copy datafeeds folder from
https://github.com/tradingview/charting_library/ to /public.
exactly as mentioned in the documentation , when i run yarn start it's working and showing however , when I build and try to serve it using nginx .
I get an error saying .
TypeError: Cannot read properties of undefined (reading
'UDFCompatibleDatafeed').
import * as React from "react";
import "./index.css";
import {
widget,
ChartingLibraryWidgetOptions,
IChartingLibraryWidget
} from "../../../charting_library";
import { useMarket, USE_MARKETS } from "../../utils/markets";
import * as saveLoadAdapter from "./saveLoadAdapter";
import { flatten } from "../../utils/utils";
import { BONFIDA_DATA_FEED } from "../../utils/bonfidaConnector";
import {} from "../../../charting_library";
export interface ChartContainerProps {
symbol: ChartingLibraryWidgetOptions["symbol"];
interval: ChartingLibraryWidgetOptions["interval"];
auto_save_delay: ChartingLibraryWidgetOptions["auto_save_delay"];
// BEWARE: no trailing slash is expected in feed URL
// datafeed: any;
datafeedUrl: string;
libraryPath: ChartingLibraryWidgetOptions["library_path"];
chartsStorageUrl: ChartingLibraryWidgetOptions["charts_storage_url"];
chartsStorageApiVersion: ChartingLibraryWidgetOptions["charts_storage_api_version"];
clientId: ChartingLibraryWidgetOptions["client_id"];
userId: ChartingLibraryWidgetOptions["user_id"];
fullscreen: ChartingLibraryWidgetOptions["fullscreen"];
autosize: ChartingLibraryWidgetOptions["autosize"];
studiesOverrides: ChartingLibraryWidgetOptions["studies_overrides"];
containerId: ChartingLibraryWidgetOptions["container_id"];
theme: string;
}
export interface ChartContainerState {}
export const TVChartContainer = () => {
// let datafeed = useTvDataFeed();
const defaultProps: ChartContainerProps = {
symbol: "BTC/USDC",
// #ts-ignore
interval: "60",
auto_save_delay: 5,
theme: "Dark",
containerId: "tv_chart_container",
// datafeed: datafeed,
libraryPath: "/charting_library/",
chartsStorageApiVersion: "1.1",
clientId: "tradingview.com",
userId: "public_user_id",
fullscreen: false,
autosize: true,
datafeedUrl: BONFIDA_DATA_FEED,
studiesOverrides: {}
};
const tvWidgetRef = React.useRef<IChartingLibraryWidget | null>(null);
const { market } = useMarket();
const chartProperties = JSON.parse(
localStorage.getItem("chartproperties") || "{}"
);
React.useEffect(() => {
const savedProperties = flatten(chartProperties, {
restrictTo: ["scalesProperties", "paneProperties", "tradingProperties"]
});
let newWindow :any = window ;
console.log("defaultProps.datafeedUrl", newWindow.Datafeeds);
const widgetOptions: ChartingLibraryWidgetOptions = {
symbol:
USE_MARKETS.find(
(m) => m.address.toBase58() === market?.publicKey.toBase58()
)?.name || "SRM/USDC",
// BEWARE: no trailing slash is expected in feed URL
// tslint:disable-next-line:no-any
// #ts-ignore
// datafeed: datafeed,
// #ts-ignore
datafeed:new (window as any).Datafeeds.UDFCompatibleDatafeed(
defaultProps.datafeedUrl,
),
interval:
defaultProps.interval as ChartingLibraryWidgetOptions["interval"],
container_id:
defaultProps.containerId as ChartingLibraryWidgetOptions["container_id"],
library_path: defaultProps.libraryPath as string,
auto_save_delay: 5,
locale: "en",
disabled_features: ["use_localstorage_for_settings"],
enabled_features: ["study_templates"],
load_last_chart: true,
client_id: defaultProps.clientId,
user_id: defaultProps.userId,
fullscreen: defaultProps.fullscreen,
autosize: defaultProps.autosize,
studies_overrides: defaultProps.studiesOverrides,
theme: defaultProps.theme === "Dark" ? "Dark" : "Light",
overrides: {
...savedProperties,
"mainSeriesProperties.candleStyle.upColor": "#41C77A",
"mainSeriesProperties.candleStyle.downColor": "#F23B69",
"mainSeriesProperties.candleStyle.borderUpColor": "#41C77A",
"mainSeriesProperties.candleStyle.borderDownColor": "#F23B69",
"mainSeriesProperties.candleStyle.wickUpColor": "#41C77A",
"mainSeriesProperties.candleStyle.wickDownColor": "#F23B69",
"background": "#141424 ",
"backgroundColor" : "#141424 "
},
// #ts-ignore
save_load_adapter: saveLoadAdapter,
settings_adapter: {
initialSettings: {
"trading.orderPanelSettingsBroker": JSON.stringify({
showRelativePriceControl: false,
showCurrencyRiskInQty: false,
showPercentRiskInQty: false,
showBracketsInCurrency: false,
showBracketsInPercent: false
}),
// "proterty"
"trading.chart.proterty":
localStorage.getItem("trading.chart.proterty") ||
JSON.stringify({
hideFloatingPanel: 1
}),
"chart.favoriteDrawings":
localStorage.getItem("chart.favoriteDrawings") ||
JSON.stringify([]),
"chart.favoriteDrawingsPosition":
localStorage.getItem("chart.favoriteDrawingsPosition") ||
JSON.stringify({})
},
setValue: (key, value) => {
localStorage.setItem(key, value);
},
removeValue: (key) => {
localStorage.removeItem(key);
}
}
};
const tvWidget = new widget(widgetOptions);
tvWidget.onChartReady(() => {
tvWidgetRef.current = tvWidget;
tvWidget
// #ts-ignore
.subscribe("onAutoSaveNeeded", () => tvWidget.saveChartToServer());
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [market, tvWidgetRef.current]);
return <div id={defaultProps.containerId} className={"TVChartContainer"} />;
};
function useTvDataFeed() {
throw new Error("Function not implemented.");
}
ps: i've tried to implement this project : https://github.com/project-serum/serum-dex-ui

Backbone - Unable to fetch data from Specified URL

I'm getting the following error even though I'm pretty sure I have specified the URL in my collection:
Uncaught Error: A `url property or function must be specified
Here's my collection:
import $ from 'jquery';
import Backbone from 'backbone';
import ViewModel from './view-model';
import InstancesCollection from '../instances/instances-collection-model';
export default Backbone.Collection.extend({
model: ViewModel,
url: $.createURL('/api/view'),
initialize: (models = []) => {
models.forEach(element => {
element.instances = new InstancesCollection(element.instances);
});
},
parse: (response) => {
return response.views;
}
});
I tried to follow the response given here but in vain. Does anybody know if I'm missing something here?
Just in case you needed to see the model as well, here it is:
import _ from 'lodash';
import Backbone from 'backbone';
import InstancesCollection from '../instances/instances-collection-model';
export default Backbone.Model.extend({
defaults: {
id: null,
name: null,
count: 0,
instances: []
},
initialize: function (models) {
const attributes = Object.assign({}, this.defaults, models);
if (_.isArray(attributes.instances)) {
this.set({instances: new InstancesCollection(attributes.instances)}, {silent: true});
}
},
toJSON: function () {
let json = Object.assign({}, this.attributes);
for (const attr of Object.keys(json)) {
if ((json[attr] instanceof Backbone.Model) || (json[attr] instanceof Backbone.Collection)) {
json[attr] = json[attr].toJSON();
}
}
return json;
}
});
Any help would be appreciated. Thank you.

How do I mock react-i18next and i18n.js in jest?

package.json
"moduleNameMapper": {
"i18next": "<rootDir>/__mocks__/i18nextMock.js"
}
i18n.js
import i18n from 'i18next'
import XHR from 'i18next-xhr-backend'
// import Cache from 'i18next-localstorage-cache'
import LanguageDetector from 'i18next-browser-languagedetector'
i18n
.use(XHR)
// .use(Cache)
.use(LanguageDetector)
.init({
fallbackLng: 'en',
// wait: true, // globally set to wait for loaded translations in translate hoc
lowerCaseLng: true,
load: 'languageOnly',
// have a common namespace used around the full app
ns: ['common'],
defaultNS: 'common',
debug: true,
// cache: {
// enabled: true
// },
interpolation: {
escapeValue: false, // not needed for react!!
formatSeparator: ',',
format: function (value, format, lng) {
if (format === 'uppercase') return value.toUpperCase()
return value
}
}
})
export default i18n
i18nextMock.js
/* global jest */
const i18next = jest.genMockFromModule('react-i18next')
i18next.t = (i) => i
i18next.translate = (c) => (k) => k
module.exports = i18next
For some reason the jest unit tests are not getting a component.
Here is a unit test:
import React from 'react'
import { Provider } from 'react-redux'
import { MemoryRouter } from 'react-router-dom'
import { mount } from 'enzyme'
import { storeFake } from 'Base/core/storeFake'
import Container from '../container'
describe('MyContainer (Container) ', () => {
let Component;
beforeEach(() => {
const store = storeFake({})
const wrapper = mount(
<MemoryRouter>
<Provider store={store}>
<Container />
</Provider>
</MemoryRouter>
)
Component = wrapper.find(Container)
});
it('should render', () => {
// Component is undefined here
expect(Component.length).toBeTruthy()
})
})
You don't need to mock the t function, only the translate one is required. For the second one, your usage of the parameters are confusing, also, you need to return a Component.
I was able to make it work on my project. Here are my mock file and my Jest configuration
Jest configuration
"moduleNameMapper": {
"react-i18next": "<rootDir>/__mocks__/reacti18nextMock.js"
}
The source code to mock react-i18next
/* global jest */
import React from 'react'
const react_i18next = jest.genMockFromModule('react-i18next')
const translate = () => Component => props => <Component t={() => ''} {...props} />
react_i18next.translate = translate
module.exports = react_i18next
I used Atemu's anwser in my jest tests, but ended up with the following one line in the mock:
module.exports = {t: key => key};
Also modified jest config as well since I import 't' from 'i18next':
"moduleNameMapper": {
"i18next": "<rootDir>/__mocks__/reacti18nextMock.js"
}
In my case, using the useTranslation hook with TypeScript, it was as follows:
const reactI18Next: any = jest.createMockFromModule('react-i18next');
reactI18Next.useTranslation = () => {
return {
t: (str: string) => str,
i18n: {
changeLanguage: () => new Promise(() => {}),
},
};
};
module.exports = reactI18Next;
export default {};
The jest.config.ts:
const config: Config.InitialOptions = {
verbose: true,
moduleNameMapper: {
'react-i18next': '<rootDir>/__mocks__/react-i18next.ts',
},
};
As of 2023 and since this question has no accepted answer and I've had to modify slightly the examples provided by react-i18next I am posting the following hoping it will be of help to somebody. I am using jest and react-testing-library (RTL).
If you need different mocks in different tests the need for the mock is sparse, you can just mock the module at the beginning of each test (for example in one test you might just need to mock it and in another you want to spy on its use...). Nevertheless, if you are going to mock it, one way or another, in several test you'd better create the mocks separately as other answers recommend.
Just mock
If you just need to mock the module so the tests run seamlessly, react-i18next recommends you do the following:
jest.mock('react-i18next', () => ({
// this mock makes sure any components using the translate hook can use it without a warning being shown
useTranslation: () => {
return {
t: (str: string) => str,
i18n: {
changeLanguage: () => new Promise(() => {}),
// You can include here any property your component may use
},
}
},
}))
describe('Tests go here', () => {
it('Whatever', () => {})
})
Mock and spy
If you are using the useTranslation hook and need to spy on its use, that's another story. In the example provided by react-i18next they use enzyme which hasn't kept up with react and it doesn't run with RTL, here is the fix:
import { useTranslation } from 'react-i18next'
jest.mock('react-i18next', () => ({
useTranslation: jest.fn(),
}))
const tSpy = jest.fn((str) => str)
const changeLanguageSpy = jest.fn((lng: string) => new Promise(() => {}))
const useTranslationSpy = useTranslation as jest.Mock
beforeEach(() => {
jest.clearAllMocks()
useTranslationSpy.mockReturnValue({
t: tSpy,
i18n: {
changeLanguage: changeLanguageSpy,
language: 'en',
},
})
})
describe('Tests go here', () => {
it('Whatever', () => {})
})
The only change is that you have to set up the mock return value before each test otherwise what will reach the component is undefined, then you can assert the t function and the changeLanguage function have been called.
Returning the key "as is" isn't the best. We are using English text as the key and it would be nice to "evaluate" values that we pass in (i.e. t('{{timePeriod}} left') evaluated to: '5 days left' ). In this case I created a helper function to do this. Below are the jest and extra files needed:
Jest configuration (i.e. jest.config.js) :
moduleNameMapper: {
'react-i18next': '<rootDir>/src/tests/i18nextReactMocks.tsx',
'i18next': '<rootDir>/src/tests/i18nextMocks.ts',
// ...
},
i18nextMocks.ts:
function replaceBetween(startIndex: number, endIndex: number, original: string, insertion: string) {
const result = original.substring(0, startIndex) + insertion + original.substring(endIndex);
return result;
}
export function mockT(i18nKey: string, args?: any) {
let key = i18nKey;
while (key.includes('{{')) {
const startIndex = key.indexOf('{{');
const endIndex = key.indexOf('}}');
const currentArg = key.substring(startIndex + 2, endIndex);
const value = args[currentArg];
key = replaceBetween(startIndex, endIndex + 2, key, value);
}
return key;
}
const i18next: any = jest.createMockFromModule('i18next');
i18next.t = mockT;
i18next.language = 'en';
i18next.changeLanguage = (locale: string) => new Promise(() => {});
export default i18next;
i18nextReactMocks.tsx:
import React from 'react';
import * as i18nextMocks from './i18nextMocks';
export const useTranslation = () => {
return {
t: i18nextMocks.mockT,
i18n: {
changeLanguage: () => new Promise(() => {}),
},
};
};
export const Trans = ({ children }) => <React.Fragment>{children}</React.Fragment>;
And I'll throw in the mock's unit tests for free :)
import * as i18nextMocks from './i18nextMocks';
describe('i18nextMocks', () => {
describe('mockT', () => {
it('should return correctly with no arguments', async () => {
const testText = `The company's new IT initiative, code named Phoenix Project, is critical to the
future of Parts Unlimited, but the project is massively over budget and very late. The CEO wants
Bill to report directly to him and fix the mess in ninety days or else Bill's entire department
will be outsourced.`;
const translatedText = i18nextMocks.mockT(testText);
expect(translatedText).toBe(testText);
});
test.each`
testText | args | expectedText
${'{{fileName}} is invalid.'} | ${{ fileName: 'example_5.csv' }} | ${'example_5.csv is invalid.'}
${'{{fileName}} {is}.'} | ${{ fileName: ' ' }} | ${' {is}.'}
${'{{number}} of {{total}}'} | ${{ number: 0, total: 999 }} | ${'0 of 999'}
${'There was an error:\n{{error}}'} | ${{ error: 'Failed' }} | ${'There was an error:\nFailed'}
${'Click:{{li}}{{li2}}{{li_3}}'} | ${{ li: '', li2: 'https://', li_3: '!##$%' }} | ${'Click:https://!##$%'}
${'{{happy}}😏y✔{{sad}}{{laugh}}'} | ${{ happy: '😃', sad: '😢', laugh: '🤣' }} | ${'😃😏y✔😢🤣'}
`('should return correctly while handling arguments in different scenarios', ({ testText, args, expectedText }) => {
const translatedText = i18nextMocks.mockT(testText, args);
expect(translatedText).toBe(expectedText);
});
});
describe('language', () => {
it('should return language', async () => {
const language = i18nextMocks.default.language;
expect(language).toBe('en');
});
});
});

Resources