React Test Utilities do not Simulate 'message' events

React’s Test Utilities have a method for every event that React understands. At this point, React doesn’t understand the message event emitted by the window object. Should it? I’m not sure. I’ll try to make a long story short…

I have a project scaffold that makes it easy to set up a web application interface that does the whole OAuth2 thing through a popup window. When the server is provided with valid login credentials, it sends an authentication token back to the web application through a React component that provides the callback functionalilty.

The relevant parts look like this:

1
2
3
4
5
6
7
8
9
10
11
var Oauth2Callback = React.createClass({
componentDidMount: function() {
var queryString = window.location.hash.split('#')[1];
var params = this.parseKeyValue(queryString);
window.opener.postMessage(params, '*');
window.close();
},
// ...
});

The server sends the authentication token embedded in a query string. The authentication token is isolated and then passed back to the application window that opened the OAuth2 popup through a call to that object’s postMessage function. The postMessage function is what emits the message event not understood by react. The listener for the postMessage-produced message event looks a little like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var AuthenticateMenu = React.createClass({
// ...
componentDidMount: function() {
// ...
window.addEventListener('message', function(event) {
if (event.data.access_token) {
verifyToken(event.data.access_token, function(err, data) {
// ...
localStorage.setItem('archiver-hai-key', event.data.access_token);
// ...
});
}
});
// ...
},
// ...
});

So how do you test all this if React’s Test Utilities don’t understand the message event?

It’s no big deal. Just find and call the function assigned to the event in the window object itself. Here’s a sample test for Jest:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
describe('window message event', function() {
// ...
it('should store the token received from the popup authentication dialog', function() {
var authenticate = TestUtils.renderIntoDocument(<AuthenticateMenu />);
expect(localStorage.getItem('my-authentication-key')).toBe(undefined);
// Simulate a message being sent
var event = { data : {
access_token: 'SomePseudoRandomAuthenticationKey', type: 'bearer' }
};
window._listeners.message.false[0](event);
expect(localStorage.getItem('my-authentication-key')).
toEqual('SomePseudoRandomAuthenticationKey');
});
// ...
});

This part

1
window._listeners.message.false[0](event);

calls the function listening for the message event. You can see all of window‘s listeners in window._listeners.

Incidentally, localStorage was mocked in the Jest tests thusly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var mock = (function() {
var store = {};
return {
getItem: function(key) {
return store[key];
},
setItem: function(key, value) {
store[key] = value.toString();
},
clear: function() {
store = {};
}
};
})();
Object.defineProperty(window, 'localStorage', { value: mock });

This code is courtesy of Martin Danielson.