Hope anyone already overcome this issue. codesandbox
I want to check, that after changing appliedIds, loadCampaign will be executed. As we say in disposer:
addDisposer( self, reaction(() => self.appliedIds, this.loadCampaign), );
So how can I do it? How to wait in jest tests for reaction execution? I see here that I can use const reaction = jest.spyOn(mobx, 'reaction');
but I want to check the result of execution function in disposer.
src/store/filters/campaignFilterStore.ts
import { reaction } from 'mobx';
import { Instance, addDisposer, flow, types } from 'mobx-state-tree';
export const CampaignFull = types.model('CampaignFull', {
id: types.identifierNumber,
name: types.string,
userId: types.number,
});
export type ICampaignFull = Instance<typeof CampaignFull>;
interface ICampaignData {
id: number;
name: string;
userId: number;
}
export const CampaignFilterStore = types
.model('CampaignFilterStore', {
appliedIds: types.array(types.number),
campaignIds: types.array(types.number),
campaign: types.maybe(CampaignFull),
})
.actions((self) => ({
afterCreate(): void {
console.log('afterCreate');
addDisposer(
self,
reaction(() => self.appliedIds, this.loadCampaign),
);
},
loadCampaign: flow(function* loadCampaign(): Generator<
Promise<ICampaignData>,
void,
ICampaignData
> {
const campaignId = self.appliedIds[0];
console.log('loadCampaign', campaignId);
if (!campaignId) {
return;
}
try {
const data = yield new Promise((res) => {
setTimeout(() => {
res({
id: 1,
name: 'name',
userId: 12,
});
}, 500);
});
self.campaign = CampaignFull.create({
id: data.id,
name: data.name,
userId: data.userId,
});
} catch (e) {
console.error(e);
}
}),
apply(): void {
self.appliedIds.replace([...self.campaignIds]);
},
toggleCampaign(campaign: { id: number; name: string }): void {
self.campaignIds.push(campaign.id);
},
}));
src/store/filters/campaignFilterStore.test.ts
import { isObservable } from 'mobx';
import { CampaignFilterStore } from 'store/filters/campaignFilterStore';
const CAMPAIGN_1 = {
id: 1,
name: 'name',
userId: 12,
};
describe('CampaignFilterStore', () => {
test('should load campaign by disposer - DOESNT WORK', async () => {
const campaigns = CampaignFilterStore.create();
campaigns.toggleCampaign(CAMPAIGN_1);
expect(campaigns.campaign).toBeUndefined();
try {
expect(isObservable(campaigns.appliedIds)).toBe(true);
campaigns.apply();
expect(campaigns.appliedIds).toEqual([CAMPAIGN_1.id]);
} finally {
// campaign SHOULD be loaded
expect(campaigns.campaign).not.toBeUndefined();
}
});
test('should load campaign', async () => {
const campaigns = CampaignFilterStore.create({
appliedIds: [CAMPAIGN_1.id],
});
expect(campaigns.campaign).toBeUndefined();
await campaigns.loadCampaign();
expect(campaigns.campaign).not.toBeUndefined();
expect(campaigns.campaign?.name).toEqual(CAMPAIGN_1.name);
});
});
self.loadCampaign
action needs to be called explicitly inside reaction effect callback
addDisposer(
self,
reaction(
() => getSnapshot(self.appliedIds),
() => self.loadCampaign()
)
);
Then in your test to can create a spy on loadCampaign
action and make sure you change appliedIds
const campaigns = CampaignFilterStore.create();
const loadCampaignSpy = jest.spyOn(campaigns, 'loadCampaign');
campaigns.setAppliedIds([1,2,3]); // appliedIds should be changed in order to run the effect defined in reaction()
expect(loadCampaignSpy).toHaveBeenCalledTimes(1);
Unlike autorun, the side effect won't run once when initialized, but only after the data expression returns a new value for the first time.