Error testing reducer initial state - unit-testing

I'm receiving an error when trying to do a basic reducer test such as those found on the redux docs: https://redux.js.org/recipes/writing-tests#reducers
TypeError: Cannot read property 'GROUP_LIST_REQUEST' of undefined
22 |
23 | switch (action.type) {
> 24 | case GROUP_LIST_REQUEST:
25 | return {
26 | ...state,
27 | fetching: true,
at group (app/reducers/group.js:24:10)
at node_modules/redux/lib/combineReducers.js:53:24
at Array.forEach (native)
at assertReducerShape (node_modules/redux/lib/combineReducers.js:51:25)
at combineReducers (node_modules/redux/lib/combineReducers.js:107:5)
at Object.<anonymous> (app/reducers/all.js:23:16)
at Object.<anonymous> (app/store.js:31:1)
at Object.<anonymous> (app/models/api.js:31:1)
at Object.<anonymous> (app/actions/group.js:1:1)
at Object.<anonymous> (app/reducers/group.js:1:1)
at Object.<anonymous> (__tests__/groups/reducer.js:6:1)
The stack trace seems to imply that the whole app is being run, when I just want to run my reducer method in isolation.
But even weirder is that it can't find my action type in the reducer switch that I'm not even using in the test.
import reducer from '../../app/reducers/group';
import * as actions from '../../app/actions/group';
describe('group reducer', () => {
test('should return the initial state', () => {
expect(reducer(undefined, {})).toEqual({
selected: null,
fetching: false,
fetched: false,
error: null,
items: [],
});
});
});
Reducer:
import {
GROUP_LIST_REQUEST,
GROUP_LIST_SUCCESS,
GROUP_LIST_FAILURE,
} from '../actions/group';
export const initialState = {
selected: null,
fetching: false,
fetched: false,
error: null,
items: [],
};
export default function group(state = initialState, action) {
console.log(action);
switch (action.type) {
case GROUP_LIST_REQUEST:
return {
...state,
fetching: true,
fetched: false,
};
case GROUP_LIST_SUCCESS:
return {
...state,
fetching: false,
fetched: true,
items: action.groupList === undefined ? [] : action.groupList,
};
case GROUP_LIST_FAILURE:
return {
...state,
fetching: false,
fetched: false,
};
default:
return state;
}
}
When I log the action I get { type: '##redux/INIT' } which is weird because for the test I am passing an empty object.
EDIT: I am using Jest to test, maybe it has to do with the configs?

I figured it out. The stack trace was really the major clue. When I no longer imported my actions and put them direct in the reducer file it fixed the problem. The actions import was the culprit:
import {
GROUP_LIST_REQUEST,
GROUP_LIST_SUCCESS,
GROUP_LIST_FAILURE,
} from '../actions/group';
At the top of that file imported the api, which imported the store, then imported the reducers, then combined all the reducers, which of course included my group reducer I already included.
So, I'm not sure of the exact cause, but my test was definitely including way more code then it should have, which probably points to an architectural failure somewhere in the app.
My solution for now is to follow more of a ducks approach where actions are included with the reducer and action creators all in one file.
Another approach could be separating the action constants out into their own file.

Related

How to update an item after being newly created in AWS DynamoDB and Amplify

I am trying to update a query in AWS Dynamo using AWS Amplify on top of Next.js.
My scenario is simple.
On page load, if there exists a user and the user has not visited a page before, a new object will be created with set values using SWR.
const fetchUserSite = async (owner, code) => {
try {
// Create site object if no site exists
if (userData == null) {
const siteInfo = {
id: uuidv4(),
code: parkCode,
owner: user?.username,
bookmarked: false,
visited: false,
}
await API.graphql({
query: createSite,
variables: {input: siteInfo},
authMode: 'AMAZON_COGNITO_USER_POOLS',
})
console.log(`${code} added for the first time`)
}
return userData || null
} catch (err) {
console.log('Site not added by user', data, err)
}
}
// Only call the fetchUserSite method if `user` exists
const {data} = useSWR(user ? [user?.username, parkCode] : null, fetchUserSite)
Currently, this works. The object is added to the database with the above attributes. HOWEVER, when I click a button to update this newly created object, I get an error of path: null, locations: (1) […], message: "Variable 'input' has coerced Null value for NonNull type 'ID!'"
This is my call to update the object when I click a button with the onClick handler "handleDBQuery".
const handleDBQuery = async () => {
await API.graphql({
query: updateSite,
variables: {
input: {
id: data?.id,
bookmarked: true,
owner: user?.username,
},
},
authMode: 'AMAZON_COGNITO_USER_POOLS',
})
console.log(`${name} Bookmarked`)
}
My hunch is that the updateSite query does not know about the createSite query on page load.
In short, how can I update an item after I just created it?
I looked into the code at master branch and follow along as you describe. I found that the data?.id here comes from a state variable and it is set only before the call to createSite. I suggest you try setId again using the data returned from the createSite
Try this
const fetchUserSite = async (owner, code) => {
try {
// Create site object if no site exists
if (userData == null) {
const siteInfo = {
id: uuidv4(),
code: parkCode,
owner: user?.username,
bookmarked: false,
visited: false,
}
const { data: newData } = await API.graphql({
query: createSite,
variables: {input: siteInfo},
authMode: 'AMAZON_COGNITO_USER_POOLS',
});
setId(newData.id); // <====== here (or setId(siteInfo.id))
console.log(`${code} added for the first time`)
return newData; // <======= and this, maybe? (you may have to modify the qraphql query to make it return the same item as in the listSite
}
return userData || null
} catch (err) {
console.log('Site not added by user', data, err)
}
}

Apollo GraphQL client doesn't return cached nested types in a query

I'm performing a query to get PowerMeter details in which contains another type inside called Project. I write the query this way:
query getPowerMeter($powerMeterId: ID!) {
powerMeter: powerMeter(powerMeterId: $powerMeterId) {
id
name
registry
project {
id
name
}
}
}
When I perform the query for the first time, project is successfully returned. The problem is that when I perform subsequent queries with the same parameters and default fetchPolicy (cache-first), project isn't returned anymore.
How may I solve this problem?
Also, I call readFragment to check how powerMeter is saved in the cache and the response shows that powerMeter has project saved.
const frag = client.readFragment({
fragment: gql`
fragment P on PowerMeter {
id
name
registry
project {
id
name
}
}
`,
id: 'PowerMeter:' + powerMeterId,
});
Power Meter returned first time
{
"powerMeter":{
"id":"7168adb4-4198-443e-ab76-db0725be2b18",
"name":"asd123123",
"registry":"as23",
"project":{
"id":"41d8e71b-d1e9-41af-af96-5b4ae9e492c1",
"name":"ProjectName",
"__typename":"Project"
},
"__typename":"PowerMeter"
}
}
Fragment after calling power meter first time
{
"id":"7168adb4-4198-443e-ab76-db0725be2b18",
"name":"asd123123",
"registry":"as23",
"project":{
"id":"41d8e71b-d1e9-41af-af96-5b4ae9e492c1",
"name":"ProjectName",
"__typename":"Project"
},
"__typename":"PowerMeter"
}
Power Meter returned second time
{
"powerMeter":{
"id":"7168adb4-4198-443e-ab76-db0725be2b18",
"name":"asd123123",
"registry":"as23",
"__typename":"PowerMeter"
}
}
Fragment after calling power meter second time
{
"id":"7168adb4-4198-443e-ab76-db0725be2b18",
"name":"asd123123",
"registry":"as23",
"project":{
"id":"41d8e71b-d1e9-41af-af96-5b4ae9e492c1",
"name":"ProjectName",
"__typename":"Project"
},
"__typename":"PowerMeter"
}
Edit 1: Fetching Query
The code below is how I'm fetching data. I'm using useApolloClient and not a query hook because I'm using AWS AppSync and it doesn't support query hook yet.
import { useApolloClient } from '#apollo/react-hooks';
import gql from 'graphql-tag';
import { useEffect, useState } from 'react';
export const getPowerMeterQuery = gql`
query getPowerMeter($powerMeterId: ID!) {
powerMeter: powerMeter(powerMeterId: $powerMeterId) {
id
name
registry
project {
id
name
}
}
}
`;
export const useGetPowerMeter = (powerMeterId?: string) => {
const client = useApolloClient();
const [state, setState] = useState<{
loading: boolean;
powerMeter?: PowerMeter;
error?: string;
}>({
loading: true,
});
useEffect(() => {
if (!powerMeterId) {
return setState({ loading: false });
}
client
.query<GetPowerMeterQueryResponse, GetPowerMeterQueryVariables>({
query: getPowerMeterQuery,
variables: {
powerMeterId,
},
})
.then(({ data, errors }) => {
if (errors) {
setState({ loading: false, error: errors[0].message });
}
console.log(JSON.stringify(data));
const frag = client.readFragment({
fragment: gql`
fragment P on PowerMeter {
id
name
registry
project {
id
name
}
}
`,
id: 'PowerMeter:' + powerMeterId,
});
console.log(JSON.stringify(frag));
setState({
loading: false,
powerMeter: data.powerMeter,
});
})
.catch(err => setState({ loading: false, error: err.message }));
}, [powerMeterId]);
return state;
};
Edit 2: Fetching Policy Details
When I use fetchPolice equals cache-first or network-only, the error persists. When I use no-cache, I don't get the error.
I think this might have been the solution:
https://github.com/apollographql/apollo-client/issues/7050
Probably way too late, but it could help people coming to this issue in the future.
When using apollo client's InMemoryCache it seems you need to provide a list of possible types so the fragment matching can be done correctly when using the InMemoryCache.
You can do that manually when having few union types and a pretty stable API which doesn't change very often.
Or you automatically generate these types into a json file, which you can use directly in the InMemoryCache's possibleTypes config directly.
Visit this link to the official docs to find out how to do it.
Cheers.

get response from expo sqlite query

According to Expo documentation with SQLite I would make a query like so:
tx.executeSql(sqlStatement, arguments, success, error)
I execute it like this:
db.transaction(tx => {
tx.executeSql('SELECT * FROM dr_report_properties WHERE orderId = (?)', [this.state.orderId]);
},
error => {
alert(error);
},
(tx, results) => {
console.log(results);
}
);
My question is how do I get the response? The above returns as undefined.
I then try (not expecting it to work but just for kicks)
console.log(tx);
This does give a console.log
(tx, results) => {
console.log('I got data');
}
)
According to the documentation:
ResultSet objects are returned through second parameter of the success callback for the tx.executeSql() method on a Transaction (see above). They have the following form:
{
insertId,
rowsAffected,
rows: {
length,
item(),
_array,
},
}
I would expect result would be this object. Any ideas at what I'm doing wrong?
The problem with the above was that I placed the call AFTER the execution it's actually in the same method as that.
The result should have gone in the callback like so:
db.transaction(
tx => {
tx.executeSql('select * from my_table', [], (trans, result) => {
console.log(trans, result)
});
}
);
Thanks to #charliecruzan from expo team!

Why is my jest async action creator test not working?

I am very new to unit testing, and am trying to go through my react-redux project to write some tests.
Why is this test not working, and how could I make it pass?
Here is the test. I want to test my fetch posts action creator. This is for a small blog application.:
import configureStore from 'redux-mock-store'; // ES6 modules
import { findSinglePost, sendEdit, changeRedirect, checkBoxChange } from '../client/redux/actions/postActions';
import thunk from 'redux-thunk';
import axios from 'axios';
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
describe('asynchronous action creators', () => {
it('should fetch posts', () => {
let store = mockStore({})
//my async action creator. It uses mock data that's in the same folder.
const fetchPosts = () => function(dispatch) {
dispatch({type: 'FETCH_POSTS'});
return axios.get('./MOCK.json').then((response) => {
dispatch({type: 'FETCH_POSTS_FUFILLED', payload: response.data});
}).catch((err) => {
dispatch({type: 'FETCH_POSTS_REJECTED', payload: err});
});
};
//this doesn't equal FETCH_POSTS_FUFILLED, it ends up equaling just "FETCH_POSTS"
return store.dispatch(fetchPosts()).then(() => {
const actions = store.getActions();
expect(actions[0]).toEqual({type: 'FETCH_POSTS_FUFILLED'});
})
})
});
Here is jest's feedback. I want it to equal 'FETCH_POSTS_'FUFILLED', but it's returning 'FETCH_POSTS'. :
FAIL _test_\actions.test.js
● asynchronous action creators › should fetch posts
expect(received).toEqual(expected)
Expected value to equal:
{"type": "FETCH_POSTS_FUFILLED"}
Received:
{"type": "FETCH_POSTS"}
Difference:
- Expected
+ Received
Object {
- "type": "FETCH_POSTS_FUFILLED",
+ "type": "FETCH_POSTS",
}
88 | return store.dispatch(fetchPosts()).then(() => {
89 | const actions = store.getActions();
> 90 | expect(actions[0]).toEqual({type: 'FETCH_POSTS_FUFILLED'});
91 | })
92 | })
93 | });
at _test_/actions.test.js:90:26
PASS client\views\LoginPage\LoginPage.test.jsx
Test Suites: 1 failed, 1 passed, 2 total
Tests: 1 failed, 5 passed, 6 total
Snapshots: 0 total
Time: 1.49s
Ran all test suites related to changed files.
Also, here is the project's github repo if you want to try to run it.
Also, if there's a standard way in the industry that's more well known on how to do this, I'd love the advice.
Edit:
When I change actions[0] to actions[ 1] I get this error:
Expected value to equal:
{"type": "FETCH_POSTS_FUFILLED"}
Received:
{"payload": {Symbol(impl): {"message": "The string did not match the expected pattern.", "name": "SyntaxError", Symbol(wrapper): [Circular]}}, "type": "FETCH_POSTS_REJECTED"}
Difference:
- Expected
+ Received
Object {
- "type": "FETCH_POSTS_FUFILLED",
+ "payload": DOMException {
+ Symbol(impl): DOMExceptionImpl {
+ "message": "The string did not match the expected pattern.",
+ "name": "SyntaxError",
+ Symbol(wrapper): [Circular],
+ },
+ },
+ "type": "FETCH_POSTS_REJECTED",
Here is the picture form of jest's feedback:
The mocked store you are using will store all dispatched calls that have been made to it. In your case, two dispatch calls should be made, the first being FETCH_POSTS and the second being either FETCH_POST_FULFILLED or FETCH_POST_REJECTED.
Hence when you retrieve the dispatched actions from the mocked store, the first entry (which you are using in your expect) will be the FETCH_POSTS. You should check the second value in the array, which would be either FETCH_POSTS_FULFILLED or FETCH_POSTS_REJECTED based on how the promise is resolved in the function you are testing.

How to mock a Node.js module loaded with dojo/node

I have an application with the server code running on Node.js and using Dojo. I have a config module defined like:
define([
'dojo/node!nconf',
'dojo/_base/config'
], function (nconf, dojoConfig) {
nconf.argv().file({
file: dojoConfig.baseDir + '/config.json'
});
console.log('-- file name:', dojoConfig.baseDir + '/config.json');
console.log('-- context:', nconf.get('context'));
// ... logic here ...
return nconf.get(nconf.get('context'));
});
To be able to unit test this module, I've written two mocks: one for the nconf native module and one for dojoConfig. Here is the test:
define([
'require',
'intern!object',
'intern/chai!assert'
], function (require, registerSuite, assert) {
registerSuite({
name: 'config utility',
'load default settings': function () {
require.undef('dojo/node!nconf');
require.undef('dojo/_base/config');
require({ map: {
'*': {
'dojo/node!nconf': 'server/utils/tests/nconfMock',
'dojo/_base/config': 'server/utils/tests/dojoConfigMock'
}
}});
require(['../config', './nconfMock'], this.async(1000).callback(
function (config, nconfMock) {
assert.isNotNull(config);
assert.isNotNull(nconf);
// assert.deepEqual(config, nconfMock.contextSettings.test);
}
));
}
});
});
I can see that my mock of dojoConfig is correctly loaded, but not the mock of the nconf module. During a webcast on Intern, Dylan mentioned that the mapping does not consider the plugin, that there's the way to force dojo/node module to load this nconfMock. Would you mind to give me more details?
Obviously, this is verbose, so if this continues to be a common request, we’ll probably do something to make it simpler in the future.
Important note: Without mapping dojo/node to intern/node_modules/dojo/node, the loading of my initial config module as defined above fails in the Intern environment. The mapping is done in the intern.js file. The reported error is:
Error: node plugin failed to load because environment is not Node.js
at d:/git/fco2/src/libs/dojo/node.js:3:9
at execModule (d:\git\fco2\node_modules\intern\node_modules\dojo\dojo.js:512:54)
at d:\git\fco2\node_modules\intern\node_modules\dojo\dojo.js:579:7
at guardCheckComplete (d:\git\fco2\node_modules\intern\node_modules\dojo\dojo.js:563:4)
at checkComplete (d:\git\fco2\node_modules\intern\node_modules\dojo\dojo.js:571:27)
at onLoadCallback (d:\git\fco2\node_modules\intern\node_modules\dojo\dojo.js:653:7)
at d:\git\fco2\node_modules\intern\node_modules\dojo\dojo.js:758:5
at fs.js:266:14
at Object.oncomplete (fs.js:107:15)
Solution: As suggested by Colin Snover below, I now use Mockery. I also do NOT use the contextual require, only the default one. Here is a (simplified) solution working with the version 1.9.3 of the Dojo toolkit.
define([
'intern!object',
'intern/chai!assert',
'intern/node_modules/dojo/node!mockery',
'./nconfMock'
], function (registerSuite, assert, mockery, nconfMock) {
registerSuite({
name: 'config utility',
teardown: function () {
mockery.disable();
mockery.deregisterAll();
require({ map: { '*': { 'dojo/_base/config': 'dojo/_base/config' } } });
require.undef('dojo/_base/config');
require.undef('server/utils/config');
},
'load default settings': function () {
mockery.enable();
mockery.registerMock('nconf', nconfMock);
require({ map: { '*': { 'dojo/_base/config': 'server/utils/tests/dojoConfigMock' } } });
require.undef('dojo/_base/config');
require.undef('server/utils/config');
require(
['server/utils/config'],
this.async(1000).callback(function (config) {
assert.isNotNull(config);
assert.deepEqual(config, nconfMock.contextSettings.test);
})
);
}
});
});
Thanks, Dom
In order to mock a Node.js dependency, you will probably want to simply use one of the various available projects for mocking Node.js modules. Mockery is a good choice since it’s stand-alone.
Since it looks like you’re using dojo/node and not the one from Intern, in your case, you’d do it like this:
define([
'intern!object', 'dojo/node!mockery', 'dojo/Deferred', 'require'
], function (registerSuite, mockery, Deferred, require) {
var moduleUsingMock;
registerSuite({
setup: function () {
var dfd = new Deferred();
mockery.enable();
mockery.registerMock('module-to-mock', mockObject);
require([ 'module-using-mock' ], function (value) {
moduleUsingMock = value;
dfd.resolve();
});
return dfd.promise;
},
teardown: function () {
mockery.disable();
},
'some test': function () {
moduleUsingMock.whatever();
// ...
}
});
});