axe-core
Version:
Accessibility engine for automated Web UI testing
109 lines (93 loc) • 2.73 kB
JavaScript
import { v4 as createUuid } from './uuid';
import assert from './assert';
import { setDefaultFrameMessenger } from './frame-messenger';
let closeHandler;
let postMessage;
const topicHandlers = {};
/**
* Post a message to a window who may or may not respond to it.
* @param {Window} win The window to post the message to
* @param {String} topic The topic of the message
* @param {Object} message The message content
* @param {Boolean} keepalive Whether to allow multiple responses - default is false
* @param {Function} replyHandler The function to invoke when/if the message is responded to
*/
export default function respondable(
win,
topic,
message,
keepalive,
replyHandler
) {
const data = {
topic,
message,
channelId: `${createUuid()}:${createUuid()}`,
keepalive
};
return postMessage(win, data, replyHandler);
}
/**
* Handle incoming window messages
* @param {Object} data
* @param {Function} responder
*/
function messageListener(data, responder) {
const { topic, message, keepalive } = data;
const topicHandler = topicHandlers[topic];
if (!topicHandler) {
return;
}
try {
topicHandler(message, keepalive, responder);
} catch (error) {
axe.log(error);
responder(error, keepalive);
}
}
/**
* Update how respondable communicates with iframes.
* @param {Function} frameHandler Object with open, post, and close functions
*/
respondable.updateMessenger = function updateMessenger({ open, post }) {
assert(typeof open === 'function', 'open callback must be a function');
assert(typeof post === 'function', 'post callback must be a function');
if (closeHandler) {
closeHandler();
}
var close = open(messageListener);
if (close) {
assert(
typeof close === 'function',
'open callback must return a cleanup function'
);
closeHandler = close;
} else {
closeHandler = null;
}
postMessage = post;
};
/**
* Subscribe to messages sent via the `respondable` module.
*
* Axe._load uses this to listen for messages from other frames
*
* @param {String} topic The topic to listen to
* @param {Function} topicHandler The function to invoke when a message is received
*/
respondable.subscribe = function subscribe(topic, topicHandler) {
assert(
typeof topicHandler === 'function',
'Subscriber callback must be a function'
);
assert(!topicHandlers[topic], `Topic ${topic} is already registered to.`);
topicHandlers[topic] = topicHandler;
};
/**
* checks if the current context is inside a frame
* @return {Boolean}
*/
respondable.isInFrame = function isInFrame(win = window) {
return !!win.frameElement;
};
setDefaultFrameMessenger(respondable);