ferp-js
Search…
Testing Your Effects

The Problem

The problem you will initially run into is this: I just want to test an effect, but I don't know how to run the effect on its own. Well, maybe you don't need to run the effect in isolation, and run it through a test app. This will keep your test reflecting the reality of how it is used, while also helping you isolate how the effect affects the state.

The Solution: End to End

Let's take the Custom Effects section example of notificationPermission and notification effects.
notificationEffects.js
1
import { effects } from 'ferp'
2
3
export const notificationPermissionEffect = (
4
grantEffect,
5
denyEffect = effects.none,
6
dismissEffect = effects.none,
7
errorEffect = effects.none
8
) => effects.defer(
9
Notification.requestPermission()
10
.then((result) => {
11
switch (result) {
12
case 'granted':
13
return grantEffect();
14
case 'denied':
15
return denyEffect();
16
default:
17
return dismissEffect();
18
}
19
})
20
.catch(errorEffect)
21
);
22
23
export const notificationEffect = (title, options = {}, resultEffect = effects.none) => effects.thunk(() =>
24
resultEffect(new Notification(title, options))
25
);
Copied!
In this case, we have some browser specific code (ie the Notification class), so we'll just make a global/controllable mock for it. If you are building a browser-based ferp app, you may want to look into a modern browser based test framework. Let's set up a test using describe/it blocks, similar to what you'd seen in mocha or jest:
notificationEffects.test.js
1
import { app, effects } from 'ferp';
2
import { notificationPermissionEffect, notificationEffect } from './notificationEffects.js';
3
4
describe('Notification effects', () => {
5
describe('notificationPermissionEffect', () => {
6
const injectNotificationClass = (permissionResponse) => {
7
global.Notification = class {
8
static requestPermission() {
9
return permissionResponse;
10
}
11
}
12
};
13
14
afterEach(() => {
15
delete global.Notification;
16
});
17
18
const createTestApp = (onComplete) => {
19
const setterEffect = (value) => () => value;
20
const detach = app({
21
init: [
22
null,
23
notificationPermissionEffect(
24
setterEffect('grantEffect'),
25
setterEffect('denyEffect'),
26
setterEffect('dismissEffect'),
27
setterEffect('errorEffect'),
28
),
29
],
30
update: (message, state) => {
31
onComplete(message);
32
detach();
33
return [state, effects.none()];
34
},
35
});
36
37
};
38
39
it('calls the grantEffect', (done) => {
40
injectNotificationClass(Promise.resolve('grant'));
41
createTestApp((whichEffect) => {
42
expect(whichEffect).toBe('grantEffect');
43
done();
44
});
45
});
46
47
it('calls the denyEffect', (done) => {
48
injectNotificationClass(Promise.resolve('deny'));
49
createTestApp((whichEffect) => {
50
expect(whichEffect).toBe('denyEffect');
51
done();
52
});
53
});
54
55
it('calls the dismissEffect', (done) => {
56
injectNotificationClass(Promise.resolve('dismiss'));
57
createTestApp((whichEffect) => {
58
expect(whichEffect).toBe('dismissEffect');
59
done();
60
});
61
});
62
63
it('calls the errorEffect', (done) => {
64
injectNotificationClass(Promise.reject());
65
createTestApp((whichEffect) => {
66
expect(whichEffect).toBe('errorEffect');
67
done();
68
});
69
});
70
});
71
72
describe('notificationEffect', () => {
73
const injectNotificationClass = () => {
74
global.Notification = class {
75
constructor(title, options) {
76
this.constructorArgs = [title, options];
77
}
78
}
79
};
80
81
afterEach(() => {
82
delete global.Notification;
83
});
84
85
const createTestApp = (title, options, onComplete) => {
86
app({
87
init: [
88
null,
89
notificationEffect(title, options, onComplete),
90
],
91
});
92
93
};
94
95
it('creates a notification', (done) => {
96
injectNotificationClass();
97
createTestApp('Hello world', { body: 'From Ferp.JS' }, (notification) => {
98
expect(notification).toBeInstanceOf(Notification);
99
expect(notification.constructorArgs).toEqual(['Hellow world', { body: 'From Ferp.JS' }]);
100
done();
101
return effects.none();
102
});
103
});
104
});
105
});
Copied!
Last modified 10mo ago