In Mocha test beforeEach hook, I am trying to destroy all table records.
import { db } from '../src/db/models';
export const truncateTable = () => {
const promises = Object.keys(db).map(key => {
if (key !== 'Sequelize' && key !== 'sequelize') {
console.log(key);
return db[key].destroy({ where: {} });
}
});
return Promise.all(promises);
};
Then in the test, I am doing this:
describe.only('application mutations', () => {
beforeEach(() => truncateTable());
...
The error I am getting:
SequelizeDatabaseError: could not serialize access due to concurrent
update
TL/DR: in your tests, if you want a quick way to delete models and reset your DB, use sync.
describe.only('application mutations', () => {
beforeEach(async () => {
await db.sync({force: true})
});
}
If you want to individually destroy your models, you must properly await for your promise to finish before initiating a new one. Currently, your promises are being initiated all at once, hence the Sequelize error.
export const truncateTable = async () => {
const promises = Object.keys(db).map(key => {
if (key !== 'Sequelize' && key !== 'sequelize') {
await db[key].destroy({ where: {} });
}
});
};
// in your test file
describe.only('application mutations', () => {
beforeEach(async () => {
await truncateTable();
});
})
Related
Configured my store this way with redux toolkit for sure
const rootReducer = combineReducers({
someReducer,
systemsConfigs
});
const store = return configureStore({
devTools: true,
reducer: rootReducer ,
// middleware: [middleware, logger],
middleware: (getDefaultMiddleware) => getDefaultMiddleware({ thunk: false }).concat(middleware),
});
middleware.run(sagaRoot)
And thats my channel i am connecting to it
export function createSocketChannel(
productId: ProductId,
pair: string,
createSocket = () => new WebSocket('wss://somewebsocket')
) {
return eventChannel<SocketEvent>((emitter) => {
const socket_OrderBook = createSocket();
socket_OrderBook.addEventListener('open', () => {
emitter({
type: 'connection-established',
payload: true,
});
socket_OrderBook.send(
`subscribe-asdqwe`
);
});
socket_OrderBook.addEventListener('message', (event) => {
if (event.data?.includes('bids')) {
emitter({
type: 'message',
payload: JSON.parse(event.data),
});
//
}
});
socket_OrderBook.addEventListener('close', (event: any) => {
emitter(new SocketClosedByServer());
});
return () => {
if (socket_OrderBook.readyState === WebSocket.OPEN) {
socket_OrderBook.send(
`unsubscribe-order-book-${pair}`
);
}
if (socket_OrderBook.readyState === WebSocket.OPEN || socket_OrderBook.readyState === WebSocket.CONNECTING) {
socket_OrderBook.close();
}
};
}, buffers.expanding<SocketEvent>());
}
And here's how my saga connecting handlers looks like
export function* handleConnectingSocket(ctx: SagaContext) {
try {
const productId = yield select((state: State) => state.productId);
const requested_pair = yield select((state: State) => state.requested_pair);
if (ctx.socketChannel === null) {
ctx.socketChannel = yield call(createSocketChannel, productId, requested_pair);
}
//
const message: SocketEvent = yield take(ctx.socketChannel!);
if (message.type !== 'connection-established') {
throw new SocketUnexpectedResponseError();
}
yield put(connectedSocket());
} catch (error: any) {
reportError(error);
yield put(
disconnectedSocket({
reason: SocketStateReasons.BAD_CONNECTION,
})
);
}
}
export function* handleConnectedSocket(ctx: SagaContext) {
try {
while (true) {
if (ctx.socketChannel === null) {
break;
}
const events = yield flush(ctx.socketChannel);
const startedExecutingAt = performance.now();
if (Array.isArray(events)) {
const deltas = events.reduce(
(patch, event) => {
if (event.type === 'message') {
patch.bids.push(...event.payload.data?.bids);
patch.asks.push(...event.payload.data?.asks);
//
}
//
return patch;
},
{ bids: [], asks: [] } as SocketMessage
);
if (deltas.bids.length || deltas.asks.length) {
yield putResolve(receivedDeltas(deltas));
}
}
yield call(delayNextDispatch, startedExecutingAt);
}
} catch (error: any) {
reportError(error);
yield put(
disconnectedSocket({
reason: SocketStateReasons.UNKNOWN,
})
);
}
}
After Debugging I got the following:
The Thing is that when I Provide one Reducer to my store the channel works well and data is fetched where as when providing combinedReducers I am getting
an established connection from my handleConnectingSocket generator function
and an empty event array [] from
const events = yield flush(ctx.socketChannel) written in handleConnectedSocket
Tried to clarify as much as possible
ok so I start refactoring my typescript by changing the types, then saw all the places that break, there was a problem in my sagas.tsx.
Ping me if someone faced such an issue in the future
I create a site in nuxt and got data from worpdress api.
I have a few store: home.js, solutions.js, tipo.js, portfolio.js and options.js.
In fetch i check, if the store array is empty, than call dispatch and fill arrays.
export default {
async fetch({ store }) {
try {
if (store.getters['home/home'].length === 0) {
await store.dispatch('home/fetchHome');
}
if (store.getters["solutions/getSolutions"].length === 0) {
await store.dispatch('solutions/fetchSolutions');
}
if (store.getters["tipo/getTipo"].length === 0) {
await store.dispatch('tipo/fetchTipo');
}
if (store.getters["portfolio/getPortfolio"].length === 0) {
await store.dispatch('portfolio/fetchPortfolio');
}
if(store.getters["options/getOptions"].length === 0){
await store.dispatch('options/fetchOptions');
}
} catch (e) {
console.log(e, 'e no data')
}
},
components: { HomeContacts, PortofolioSlider, Clients, ChiSiamo, Solutions, HomeIntro }
}
But the problem is, that the page is loading to long time. Because i call dispatches throw await, and i think, this is the problem.
How can i call all dispatches in fethc, without async, but parallel?
I see the advantage of working with fetch over asyncData in that only the first time when I load the page, I need to wait a little, the arrays will fill up and when I get to the current page from another page, there will be no requests through the api, and the data will be output from the store.
It's just that there is very little information on nuxt in terms of ideology, how to work and what is better to use and when. In next, this is better.
This method doesn't work.
fetch({ store }) {
const promise1 = new Promise((resolve, reject) => {
if (store.getters['home/home'].length === 0) {
resolve(store.dispatch('home/fetchHome'));
}
});
const promise2 = new Promise((resolve, reject) => {
if (store.getters["solutions/getSolutions"].length === 0) {
resolve(store.dispatch('solutions/fetchSolutions'));
}
});
const promise3 = new Promise((resolve, reject) => {
if (store.getters["tipo/getTipo"].length === 0) {
resolve(store.dispatch('tipo/fetchTipo'));
}
});
const promise4 = new Promise((resolve, reject) => {
if (store.getters["portfolio/getPortfolio"].length === 0) {
resolve(store.dispatch('portfolio/fetchPortfolio'));
}
});
const promise5 = new Promise((resolve, reject) => {
if (store.getters["options/getOptions"].length === 0) {
resolve(store.dispatch('options/fetchOptions'));
}
});
Promise.all([promise1, promise2, promise3, promise4, promise5])
.then((data) => console.log(data))
.catch((error) => console.log(error));
Assuming that:
store.dispatch() returns Promise,
the first attempt in the question is generally correct,
the objective is to perform relevant dispatches in parallel,
then:
elimitate await from the store.dispatch() sequence,
accumulate the promises returned by store.dispatch() in an array,
don't use a new Promise() wrapper,
await the Promise returned by Promise.all(promises).
export default {
async fetch({ store }) {
try {
let promises = [];
if (store.getters['home/home'].length === 0) {
promises.push(store.dispatch('home/fetchHome'));
}
if (store.getters['solutions/getSolutions'].length === 0) {
promises.push(store.dispatch('solutions/fetchSolutions'));
}
if (store.getters['tipo/getTipo'].length === 0) {
promises.push(store.dispatch('tipo/fetchTipo'));
}
if (store.getters['portfolio/getPortfolio'].length === 0) {
promises.push(store.dispatch('portfolio/fetchPortfolio'));
}
if(store.getters['options/getOptions'].length === 0) {
promises.push(store.dispatch('options/fetchOptions'));
}
let data = await Promise.all(promises);
console.log(data);
} catch (e) {
console.log(e);
}
},
components: { HomeContacts, PortofolioSlider, Clients, ChiSiamo, Solutions, HomeIntro }
}
For convenience, this can be proceduralised as follows:
export default {
async fetch({ store }) {
try {
let paths = [
{ get: 'home/home', fetch: 'home/fetchHome' },
{ get: 'solutions/getSolutions', fetch: 'solutions/fetchSolutions' },
{ get: 'tipo/getTipo', fetch: 'tipo/fetchTipo' },
{ get: 'portfolio/getPortfolio', fetch: 'portfolio/fetchPortfolio' },
{ get: 'options/getOptions', fetch: 'options/fetchOptions' }
];
let promises = paths.filter(p => store.getters[p.get].length === 0).map(p => store.dispatch(p.fetch));
let data = await Promise.all(promises);
console.log(data);
} catch (e) {
console.log(e);
}
},
components: { HomeContacts, PortofolioSlider, Clients, ChiSiamo, Solutions, HomeIntro }
}
It may make more sense to define the paths array elsewhere in the code and pass it to a simplified fetch(), giving it the profile :
fetch({ store, paths })
If it still doesn't work, then there's something your're not telling us.
Promise.all can be useful here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Or even Promise.allSettled(), depending on what you're trying to do: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
Then, this is a matter of displaying something while your page does the fetching. You could use a v-if="$fetchState.pending" at the top level of your page to display a loader while the whole thing is being fetched.
There is nothing related to ideology here, there are 2 hooks that do data fetching by either blocking the render of the page (asyncData()) or allowing you to render it while the data is fetched (fetch()).
Nothing related to the framework by itself, you're free to do as you'd like.
app.js
const mapStateToProps = (state) => {
return {home:state}
}
const mapDispatchToProps = (dispatch) => {
return {
guestLogin: (data)=>{dispatch(guestLogin(data)).then(()=>{
dispatch(initiateTrans(stateProps.home))
})},
};
}
const mergeProps = (stateProps, dispatchProps, ownProps) => {
return Object.assign({}, ownProps, stateProps, dispatchProps,{
initiateTrans: () => dispatchProps.initiateTrans(stateProps.home),
})
}
Action.js
export const guestLogin= (state)=>{
var data={
'email':state.email,
'name':state.name,
'phone_number':state.ph_number,
'phone_code':state.country_code
}
return function(dispatch) {
return dataservice.guestSignup(data).then(res => {
dispatch(afterLoggedGuest(res))
}).catch(error => {
throw(error);
});
}
}
function afterLoggedGuest(result) {
return {type: guestLoginChange, result};
}
export const initiateTrans= (updatedState)=>{
return function(dispatch) {
return dataservice.initiateTransaction(updatedState).then(res => {
console.log("initiateTransaction",res)
}).catch(error => {
throw(error);
});
}
}
Reducer.js
if(action.type === guestLoginChange){
return {
...state,guestData: {
...state.guestData,
Authorization: action.result.authentication ,
auth_token: action.result.auth_token ,
platform: action.result.platform
} ,
}
}
I am having two api requests.. After first api request success i want to update state value then pass that updated state to another api request..
I tried to get the updted props
how to dispatch the initiateTrans with update props
I need to update value at api request success in call back i need to call one more request with updated state value
currently i am not able to get the update props value
I think this is a good use case for thunk (redux-thunk), which is a middleware that allows you to execute multiple dispatches in an action.
You will need to apply the middleware when you configure the initial store (see docs on link above). But then in your actions, you can wrap the code with a dispatch return statement, which gives you access to multiple calls. For example:
export const guestLogin= (state)=>{
return dispatch => {
var data={...} // some data in here
return dataservice.guestSignup(data).then(res => {
dispatch(afterLoggedGuest(res))
}).catch(error => {
throw(error);
// could dispatch here as well...
});
}
}
I'm trying to write a test that will run a GET over all items. To do this, I get that list in the before block, then I want to have an it block for each item. I am trying to do this by putting the it block inside itemList.forEach. However, I suspect that the problem here is that the blocks never get registered for the test. How can I run this test as desired?
let token;
let itemList;
describe('GET items/:itemId with Admin', async () => {
before(async () => {
// NOTE: item.find({}) returns a promise of a list of all items
itemList = await item.find({});
console.log(item[0]._id) // this logs correctly!
const res = await userLogin(admin);
token = res.body.accessToken.toString();
});
it('registers initial it test', () => {
// This test passes and logs the statement
console.log('first test registered')
console.log(itemList.length) // successfully logs non-zero value
})
await itemList.forEach(async (item) => {
it('respond with json with a item', () => {
const itemId = item._id;
return getItem(itemId, token)
.then((response) => {
assert.property(response.body, '_id');
});
});
});
});
Afaik the before setup runs before every it test. It doesn't run immediately, and definitely does not wait for anything until you try to iterate your itemList. I think you will need to do either
describe('GET items/:itemId with Admin', async () => {
let token;
before(async() => {
const res = await userLogin(admin);
token = res.body.accessToken.toString();
});
// a list of all items for which tests should be created
const itemList = await item.find({});
console.log(itemList.length) // successfully logs non-zero value
for (const item of itemList) {
it('responds with json for item '+item, () => {
const itemId = item._id;
return getItem(itemId, token).then((response) => {
assert.property(response.body, '_id');
});
}
});
or
describe('GET items/:itemId with Admin', () => {
let itemList;
let token;
before(async() => {
[itemList, token] = await Promise.all([
item.find({}),
userLogin(admin).then(res => res.body.accessToken.toString())
]);
});
it('responds with json for every item', () => {
return Promise.all(itemList.map(item => {
const itemId = item._id;
return getItem(itemId, token)
.then((response) => {
assert.property(response.body, '_id');
});
});
}));
});
});
This is the solution I ended up with. I ended up putting a new describe block in the before block. The before block results the promise that gives the list of items. There is an it block in the top level so that mocha registers the test in the first place.
describe('GET items/:itemId with Admin', async () => {
before((done) => {
Item.find({}).then(async (itemList) => {
// create the admin user to get the items with
await createUsers([admin]);
const res = await userLogin(admin);
const token = res.body.accessToken.toString();
itemList.forEach((item, index) => {
const itemId = item._id;
describe(`get item number ${index}: _id: ${itemId}`, () => {
it('responds with item id', () =>
getItem(item, token)
.expect(200)
.then((response) => {
assert.notProperty(response.body, 'error');
assert.property(response.body, '_id');
assert.equal(response.body._id, itemId);
}));
});
});
done();
});
});
// If there is no it block here, it will not run the before block!
it(`register the initial it`, () => {
assert.equal('regression test!', 'regression test!');
});
});
I have been playing with rxjs and redux-observable for the last few days and have been struggle to find a way to a test for Observable.ajax. I have the following epic which create a request to https://jsonplaceholder.typicode.com/,
export function testApiEpic (action$) {
return action$.ofType(REQUEST)
.switchMap(action =>
Observable.ajax({ url, method })
.map(data => successTestApi(data.response))
.catch(error => failureTestApi(error))
.takeUntil(action$.ofType(CLEAR))
)
}
where,
export const REQUEST = 'my-app/testApi/REQUEST'
export const SUCCESS = 'my-app/testApi/SUCCESS'
export const FAILURE = 'my-app/testApi/FAILURE'
export const CLEAR = 'my-app/testApi/CLEAR'
export function requestTestApi () {
return { type: REQUEST }
}
export function successTestApi (response) {
return { type: SUCCESS, response }
}
export function failureTestApi (error) {
return { type: FAILURE, error }
}
export function clearTestApi () {
return { type: CLEAR }
}
The code works fine when runs in browser but not when testing with Jest.
I have try,
1) Create a test based on https://redux-observable.js.org/docs/recipes/WritingTests.html. The store.getActions() returns only { type: REQUEST }.
const epicMiddleware = createEpicMiddleware(testApiEpic)
const mockStore = configureMockStore([epicMiddleware])
describe.only('fetchUserEpic', () => {
let store
beforeEach(() => {
store = mockStore()
})
afterEach(() => {
epicMiddleware.replaceEpic(testApiEpic)
})
it('returns a response, () => {
store.dispatch({ type: REQUEST })
expect(store.getActions()).toEqual([
{ type: REQUEST },
{ type: SUCCESS, response }
])
})
})
2) Create a test based on Redux-observable: failed jest test for epic. It returns with
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
it('returns a response', (done) => {
const action$ = ActionsObservable.of({ type: REQUEST })
const store = { getState: () => {} }
testApiEpic(action$, store)
.toArray()
.subscribe(actions => {
expect(actions).to.deep.equal([
{ type: SUCCESS, response }
])
done()
})
})
Can someone point me out what is the correct way to test Observable.ajax ?
I would follow the second example, from StackOverflow. To make it work you'll need to make some minor adjustments. Instead of importing Observable.ajax in your epic file and using that reference directly, you need to use some form of dependency injection. One way is to provide it to the middleware when you create it.
import { ajax } from 'rxjs/observable/dom/ajax';
const epicMiddleware = createEpicMiddleware(rootEpic, {
dependencies: { ajax }
});
The object we passed as dependencies will be give to all epics as the third argument
export function testApiEpic (action$, store, { ajax }) {
return action$.ofType(REQUEST)
.switchMap(action =>
ajax({ url, method })
.map(data => successTestApi(data.response))
.catch(error => failureTestApi(error))
.takeUntil(action$.ofType(CLEAR))
);
}
Alternatively, you could not use the dependencies option of the middleware and instead just use default parameters:
export function testApiEpic (action$, store, ajax = Observable.ajax) {
return action$.ofType(REQUEST)
.switchMap(action =>
ajax({ url, method })
.map(data => successTestApi(data.response))
.catch(error => failureTestApi(error))
.takeUntil(action$.ofType(CLEAR))
);
}
Either one you choose, when we test the epic we can now call it directly and provide our own mock for it. Here are examples for success/error/cancel paths These are untested and might have issues, but should give you the general idea
it('handles success path', (done) => {
const action$ = ActionsObservable.of(requestTestApi())
const store = null; // not used by epic
const dependencies = {
ajax: (url, method) => Observable.of({ url, method })
};
testApiEpic(action$, store, dependencies)
.toArray()
.subscribe(actions => {
expect(actions).to.deep.equal([
successTestApi({ url: '/whatever-it-is', method: 'WHATEVERITIS' })
])
done();
});
});
it('handles error path', (done) => {
const action$ = ActionsObservable.of(requestTestApi())
const store = null; // not used by epic
const dependencies = {
ajax: (url, method) => Observable.throw({ url, method })
};
testApiEpic(action$, store, dependencies)
.toArray()
.subscribe(actions => {
expect(actions).to.deep.equal([
failureTestApi({ url: '/whatever-it-is', method: 'WHATEVERITIS' })
])
done();
});
});
it('supports cancellation', (done) => {
const action$ = ActionsObservable.of(requestTestApi(), clearTestApi())
const store = null; // not used by epic
const dependencies = {
ajax: (url, method) => Observable.of({ url, method }).delay(100)
};
const onNext = chai.spy();
testApiEpic(action$, store, dependencies)
.toArray()
.subscribe({
next: onNext,
complete: () => {
onNext.should.not.have.been.called();
done();
}
});
});
For the first way:
First, use isomorphic-fetch instead of Observable.ajax for nock support, like this
const fetchSomeData = (api: string, params: FetchDataParams) => {
const request = fetch(`${api}?${stringify(params)}`)
.then(res => res.json());
return Observable.from(request);
};
So my epic is:
const fetchDataEpic: Epic<GateAction, ImGateState> = action$ =>
action$
.ofType(FETCH_MODEL)
.mergeMap((action: FetchModel) =>
fetchDynamicData(action.url, action.params)
.map((payload: FetchedData) => fetchModelSucc(payload.data))
.catch(error => Observable.of(
fetchModelFail(error)
)));
Then, you may need an interval to decide when to finish the test.
describe("epics", () => {
let store: MockStore<{}>;
beforeEach(() => {
store = mockStore();
});
afterEach(() => {
nock.cleanAll();
epicMiddleware.replaceEpic(epic);
});
it("fetch data model succ", () => {
const payload = {
code: 0,
data: someData,
header: {},
msg: "ok"
};
const params = {
data1: 100,
data2: "4"
};
const mock = nock("https://test.com")
.get("/test")
.query(params)
.reply(200, payload);
const go = new Promise((resolve) => {
store.dispatch({
type: FETCH_MODEL,
url: "https://test.com/test",
params
});
let interval: number;
interval = window.setInterval(() => {
if (mock.isDone()) {
clearInterval(interval);
resolve(store.getActions());
}
}, 20);
});
return expect(go).resolves.toEqual([
{
type: FETCH_MODEL,
url: "https://test.com/assignment",
params
},
{
type: FETCH_MODEL_SUCC,
data: somData
}
]);
});
});
enjoy it :)