Jest unit tests in Shopware Plugin's administration

Jest unit tests in Shopware Plugin's administration

What is Shopware?

Shopware 6 is an open commerce platform based on Symfony Framework and Vue and is supported by a worldwide community and more than 1.500 community extensions.

Folder structure

The test folder structure should match the source folder structure. You add a test for a file in the same path as the source path.

└── <pluginRoot>/src/Resources/app/administration
    ├── src
    │   └── module
    │       └── custom-cms
    │          └── component
    │              └── custom-cms-sidebar
    │                  ├── index.js
    │                  └── custom-cms-sidebar.html.twig
    ├── test
    │   ├── _mocks_
    │   │   └── entity-schema.json
    │   │ 
    │   └── module
    │       └── custom-cms
    │           └── component
    │               └── custom-cms-sidebar.spec.js
    │
    ├── babel.config.json
    ├── jest.config.js
    └── package.json

Installation

Let’s grab all the dependencies we will need for the setup in package.json

{
    // ...
    "devDependencies": {
        "@babel/plugin-proposal-class-properties": "^7.16.7",
        "@babel/plugin-transform-runtime": "7.13.15",
        "@babel/preset-env": "7.13.15",
        "@shopware-ag/jest-preset-sw6-admin": "^2.0.1",
        "@vue/test-utils": "1.1.0",
        "jest": "26.4.2"
    }
}

@shopware-ag/jest-preset-sw6-admin

Default Jest preset for Shopware 6 administration development.

@vue/test-utils

We are using the Vue Test Utils for easier testing of Vue components. It provides some methods to mount and interact with Vue components in an isolated manner.

Setup

After installing the required dependency packages, please create a file called babel.config.json right next to your own package.json with the following content:

{
    "presets": ["@babel/preset-env"],
    "plugins": [
        "@babel/plugin-transform-runtime",
        "@babel/plugin-proposal-class-properties"
    ]
}

Next up, create a file jest.config.js which should contain the following content:

const { resolve, join } = require('path');

const ADMIN_PATH = resolve('../../../../vendor/shopware/administration/Resources/app/administration');
process.env.ADMIN_PATH = process.env.ADMIN_PATH || ADMIN_PATH;

module.exports = {
    preset: '@shopware-ag/jest-preset-sw6-admin',

    globals: {
        adminPath: process.env.ADMIN_PATH,
    },

    collectCoverageFrom: [
        'src/**/*.js',
    ],

    moduleNameMapper: {
        '^plugin-admin(.*)$': '<rootDir>$1',
        '\@vue/test-utils': '<rootDir>/node_modules/@vue/test-utils',
        '../../_mocks_/entity-schema.json': '<rootDir>/test/_mocks_/entity-schema.json',
    },

    setupFilesAfterEnv: [
        resolve(join(process.env.ADMIN_PATH, 'test/_setup/prepare_environment.js')),
    ],
};

The file entity-schema.json could be generated by running this command:

bin/console framework:schema -s 'entity-schema' custom/plugins/<your_plugin>/src/Resources/app/administration/test/_mocks_/entity-schema.json

Writing Test

We are using a global object as an interface for the whole administration. Every component gets registered to this object, e.g. Shopware.Component.register(). Therefore, we have access to Component with the Shopware.Component.build() method. This creates a native Vue component with a working template. Every override and extension from another component are resolved in the built component.

Your component might look like this:

// src/module/custom-cms/component/custom-cms-sidebar/index.js
Component.extend('custom-cms-sidebar', 'sw-cms-sidebar', {
    // The vue component
});

Create test file custom-cms-sidebar.spec.js for custom-cms-sidebar component.

When you want to mount your component it needs to be imported first:

import 'plugin-admin/src/module/custom-cms/component/custom-cms-sidebar';

Then import it's related components:

import 'src/module/sw-cms/mixin/sw-cms-state.mixin';
import 'src/module/sw-cms/component/sw-cms-sidebar';
import 'src/app/component/base/sw-button';

In the next step we can mount our Vue component which we get from the global Shopware object:

shallowMount(Shopware.Component.build('custom-cms-sidebar'));

Now you can test the component like any other component. Let's try to write our first test:

import { shallowMount } from '@vue/test-utils';
import 'src/module/sw-cms/mixin/sw-cms-state.mixin';
import 'src/module/sw-cms/component/sw-cms-sidebar';
import 'src/app/component/base/sw-button';

import 'plugin-admin/src/module/custom-cms/component/custom-cms-sidebar';

function createWrapper() {
    return shallowMount(Shopware.Component.build('custom-cms-sidebar'), {
        propsData: {
            page: {
                sections: []
            }
        },
        stubs: {
            'sw-button': Shopware.Component.build('sw-button'),
            'sw-sidebar': true,
            'sw-sidebar-item': true,
            'sw-sidebar-collapse': true,
            'sw-text-field': true,
            'sw-select-field': true,
            'sw-cms-block-config': true,
            'sw-cms-block-layout-config': true,
            'sw-cms-section-config': true,
            'sw-context-button': true,
            'sw-context-menu-item': true,
            'sw-cms-sidebar-nav-element': true,
            'sw-entity-single-select': true,
            'sw-modal': true,
            'sw-checkbox-field': true
        },
        provide: {
            repositoryFactory: {
                create: () => ({
                    create: () => ({
                        id: null,
                        slots: []
                    }),
                    save: () => {}
                })
            },
            cmsService: {
                getCmsBlockRegistry: () => ({
                    'foo-bar': {}
                })
            }
        }
    });
}

describe('module/custom-cms/component/custom-cms-sidebar', () => {
    beforeAll(() => {
        Shopware.State.registerModule('cmsPageState', {
            namespaced: true,
            state: {
                isSystemDefaultLanguage: true
            }
        });
    });

    it('should be a Vue.js component', async () => {
        const wrapper = createWrapper();

        expect(wrapper.vm).toBeTruthy();
    });
});

Running Test

Create a script in package.json

"scripts": {
    "jest": "jest --config=jest.config.js",
}

Open a terminal and run this command:

npm run jest

I hope this post helps you with the config testing environment for your plugin. I am really happy to receive your feedback on this article. Thanks for your precious time reading this.