JavaScript unit testing with Jasmine
Magento uses a custom Grunt task named spec
to run Jasmine tests. The task collects the tests from <magento_root_dir>dev/tests/js/jasmine/tests
and can be run for a theme.
Prepare environment
Step 1. Install Node.js.
Step 2. Install grunt-cli.
Step 3. In <magento_root_dir>
, create Gruntfile.js
and copy Gruntfile.js.sample
into it.
Step 4. In <magento_root_dir>
, create package.json
and copy package.json.sample
into it.
Step 5. In <magento_root_dir>
, install all dependencies:
1
npm install
Step 6. In <magento_root_dir>
, generate static view files in Magento that are going to be tested
1
bin/magento setup:static-content:deploy -f
Note that normally you don’t have permissions to <magento_root_dir>/app/code/
, in fact the generated static view file is being tested.
For CentOS and Ubuntu users
If the command fails with the error message:
1
/var/www/html/magento2ce/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs: error while loading shared libraries: libfontconfig.so.1: cannot open shared object file: No such file or directory
install fontconfig library:
- CentOS:
1
yum install fontconfig
- Ubuntu:
1
apt-get install fontconfig
Learn more in Deploy static view files.
Run tests
Gruntfile.js
contains the test run task, so you can run tests for a theme using the following command in the Magento root directory:
1
grunt spec:<THEME>
Example:
1
grunt spec:backend
Write a test
All tests are distributed through modules stored in <magento_root_dir>/dev/tests/js/jasmine/tests
. Let’s see how to write a test using an example of an existing test:
app/code/Magento/Ui/base/js/grid/columns/actions.test.js
which tests a JS module:
<magento_root_dir>/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js
in its static representations generated in Step 6 previously:
<magento_root_dir>/pub/static/<area>/<theme>/<localisation>/Magento_Ui/js/columns/actions.js
.
Step 1. Create a new file with name <fileName>.test.js
in an appropriate module directory.
For convenience, we can reflect the directory structure of a file to test.
A path to JS module that we want to cover with tests: app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js
A path to a test of the module: app/code/Magento/Ui/base/js/grid/columns/actions.test.js
In <magento_root_dir>/dev/tests/js/jasmine/tests
create the test with appropriate path.
Step 2. Require a file that you want to test.
For our example we need to cover all static view files ending with Magento_Ui/js/grid/columns/actions
.
1
2
3
4
5
6
7
8
define([
'Magento_Ui/js/grid/columns/actions'
], function (Actions) {
'use strict';
//Test code
//...
});
Step 3. Write your Jasmine test code.
A Jasmine test consists of main two parts:
describe
blocksit
blocks
Both the describe
and it
functions contains two parameters:
- a text string with description of what is going to be done
- a function with block of code implementing described action
In describe
you can use beforeEach
and afterEach
functions performing a preparation of what must be done before and after every it
test followed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
define([
'underscore',
'Magento_Ui/js/grid/columns/actions'
], function (_, Actions) {
'use strict';
describe('ui/js/grid/columns/actions', function () {
var model,
action;
beforeEach(function () {
model = new Actions({
index: 'actions',
name: 'listing_action',
indexField: 'id',
dataScope: '',
rows: [{
identifier: 'row'
}]
});
action = {
index: 'delete',
hidden: true,
rowIndex: 0,
callback: function() {
return true;
}
};
});
it('Check addAction function', function () {
expect(model.addAction('delete', action)).toBe(model);
});
it('Check getAction function', function () {
var someAction = _.clone(action);
someAction.index = 'edit';
model.addAction('edit', someAction);
expect(model.getAction(0, 'edit')).toEqual(someAction);
});
it('Check getVisibleActions function', function () {
var someAction = _.clone(action);
someAction.hidden = false;
someAction.index= 'view';
model.addAction('delete', action);
model.addAction('view', someAction);
expect(model.getVisibleActions('0')).toEqual([someAction]);
});
it('Check updateActions function', function () {
expect(model.updateActions()).toEqual(model);
});
it('Check applyAction function', function () {
model.addAction('delete', action);
expect(model.applyAction('delete', 0)).toEqual(model);
});
it('Check isSingle and isMultiple function', function () {
var someAction = _.clone(action);
action.hidden = false;
model.addAction('delete', action);
expect(model.isSingle(0)).toBeTruthy();
someAction.hidden = false;
someAction.index = 'edit';
model.addAction('edit', someAction);
expect(model.isSingle(0)).toBeFalsy();
expect(model.isMultiple(0)).toBeTruthy();
});
it('Check isActionVisible function', function () {
expect(model.isActionVisible(action)).toBeFalsy();
action.hidden = false;
expect(model.isActionVisible(action)).toBeTruthy();
});
});
});
This topic doesn’t provide Jasmine test writing methodology.
Learn more about testing with Jasmine.
Known issues and solutions
Error: Cannot find module ‘<module>’
Issue:
An error message appears:
1
2
3
4
5
Loading "Gruntfile.js" tasks...ERROR
>> Error: Cannot find module '<module>'
Warning: Task "spec" not found. Use --force to continue.
Solution:
- Make sure your Node.js version is up-to-date.
- Remove
package.json
,Gruntfile.js
. - Copy
package.json
,Gruntfile.js
frompackage.json.sample
,Gruntfile.js.sample
. - Delete the
node_modules
directory. - Run
npm install
in your terminal.
Warning: Cannot read property ‘pid’ of undefined
Issue:
An error message appears:
1
2
3
Warning: Cannot read property 'pid' of undefined
Use --force to continue. Aborted due to warnings.
Solution:
Run in your terminal:
1
cd <magento_root>/node_modules/grunt-contrib-jasmine
1
npm install