Last week I was challenged with a particularly interesting problem: How do I provide a fixtures to the React+Redux application when running e2e tests in cucumber using selenium webdriver ? Well we have a couple of options how to tackle this problem.

Solution 1.)

The first obvious solution is to expose your store as a global variable on browser Window object. That way you can access the redux store instance directly and dispatch any Flux Standard Action (FSA) and modify the state of your application to expected state. From obvious reasons I did not like this solution. It exposes implementation details and there is no way to distinguish actions that the application dispatched itself and actions that were dispatched on global store instance. What is even worse is that there is no way (actually there is by ugly if) to pragmatically control (switch off/on) exposing global store instance only in test environment or even during runtime.

Solution 2.)

After I stipulated that the Solution 1.) is a no go, I started to investigate how to implement this in more "fashionable" way. I was already familiar with Window.postMessage interface and redux store enhancer concept so I started to investigate how to use both of them to achieve what I wanted.

After some prototyping I came up with the following redux store enhancer.

import { isFSA, isError } from 'flux-standard-action' ; import { isNil } from 'ramda' ; export const postMessageEnhancer = window => createStore => (reducer, preloadedState, enhancer) => { const store = createStore(reducer, preloadedState, enhancer); if (isNil( window )) { return store; } const dispatchReduxAction = ({ data: action }) => { if (!isFSA(action) || isError(action)) { return null ; } return store.dispatch({ type: 'POST_MESSAGE_DISPATCH' , payload: action, }); }; const unsubscribePostMessage = () => window .removeEventListener( 'message' , dispatchReduxAction); window .addEventListener( 'message' , dispatchReduxAction); return { ...store, unsubscribePostMessage }; };

This enhancer is consuming a Window object (if available) and returns a new enhanced store with a new unsubscribePostMessage method. The enhancer subscribes to Window message event which is being emitted every time the Window.postMessage is called. If it detects that the message event payload is FSA then it translates it into new FSA of type POST_MESSAGE_DISPATCH and assigns the original FSA as a payload of POST_MESSAGE_DISPATCH action.

Now let's try to use this enhancer to enhance our store.

import { pipe } from 'ramda' ; import { createStore, applyMiddleware } from 'redux' ; const store = createStore( reducer, initialState, pipe(applyMiddleware(someMiddleware), postMessageEnhancer( window )), );

If we want to disable the enhancer we just call the unsubscribePostMessage any time on our store instance.

store.unsubscribePostMessage();

To dispatch any FSA via postMessage interface, just open your browser console and write the following code.

window .postMessage({ type : 'TEST' , payload: 'data' }, '*' );

You should be able to see that running this code dispatches POST_MESSAGE_DISPATCH action and the FSA that you dispatched via the postMessage interface became the actual payload of this action. Now you may be wondering when does your original action gets dispatched. And you are right, we are still missing one important piece of the puzzle - the middleware that dispatches your original action.

export const postMessageMiddleware = store => next => (action) => { if (action.type === 'POST_MESSAGE_DISPATCH' ) { store.dispatch(action.payload); } return next(action); };

Let's plug this middleware into the store creation.

import { pipe } from 'ramda' ; import { createStore, applyMiddleware } from 'redux' ; import { postMessageMiddleware } from './middleware' ; const store = createStore( reducer, initialState, pipe(applyMiddleware(postMessageMiddleware), postMessageEnhancer( window )), );

Having a separation between the enhancer and middleware gives us additional control over our actions, we can identify actions that are dispatched via postMessage interface and actions that are created internally by our application.

We now have our redux store connected to postMessage interface. So let's use this fact to solve our original problem - providing fixtures via selenium webdriver to our React+Redux application. I will assume that you already have an instance of selenium webdriver created.

driver.executeScript( 'window .postMessage(arguments[ 0 ], "*" );', { type : 'SET_DAT A', payload: 'dat a', });

Yep, that's it! That's all you need to do now. You can completely control your application via selenium webdriver by dispatching FSAs through postMessage interface. The added benefit here is that if you embed your application via IFrame you can still control your application via embedding Window and send command to embeded application inside the IFrame. Basically that's what the postMessage interface is primarily used for - cross-origin communication between Window objects.

I hope this solution has already saved you some time. If you have any questions regarding the solution or you have come up with a better or simpler one, please post a comment below this article.

Like always, I end my article with the following axiom: Define your code-base as pure functions and lift them only if needed. And compose, compose, compose…