New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@formsort/web-embed-api

Package Overview
Dependencies
Maintainers
7
Versions
45
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@formsort/web-embed-api - npm Package Compare versions

Comparing version 2.2.0 to 2.2.1

CHANGELOG.md

4

lib/iframe-utils.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMessageSender = void 0;
var getMessageSender = function (iframe) { return function (message) {
const getMessageSender = (iframe) => (message) => {
var _a;
(_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(message, "*");
}; };
};
exports.getMessageSender = getMessageSender;

@@ -18,3 +18,10 @@ import { AnalyticsEventType, IFlowAnswers } from '@formsort/constants';

}
export declare const supportedAnalyticsEvents: readonly [AnalyticsEventType.FlowLoaded, AnalyticsEventType.FlowClosed, AnalyticsEventType.FlowFinalized, AnalyticsEventType.StepLoaded, AnalyticsEventType.StepCompleted];
export declare enum SupportedAnalyticsEvent {
FlowLoaded = "FlowLoaded",
FlowClosed = "FlowClosed",
FlowFinalized = "FlowFinalized",
StepLoaded = "StepLoaded",
StepCompleted = "StepCompleted"
}
export declare const isSupportedEventType: (eventType: AnalyticsEventType | SupportedAnalyticsEvent) => eventType is SupportedAnalyticsEvent;
interface IBaseEventData {

@@ -26,9 +33,4 @@ answers: IFlowAnswers | undefined;

}
export interface IAnalyticsEventMap {
FlowLoaded: (props: IBaseEventData) => void;
FlowClosed: (props: IBaseEventData) => void;
FlowFinalized: (props: IBaseEventData) => void;
StepLoaded: (props: IBaseEventData) => void;
StepCompleted: (props: IBaseEventData) => void;
}
export declare type IEventListener = (props: IBaseEventData) => void;
export declare type IAnalyticsEventMap = Record<SupportedAnalyticsEvent, IEventListener>;
export interface IEventMap extends IAnalyticsEventMap {

@@ -35,0 +37,0 @@ redirect: (props: IRedirectEventData) => {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.supportedAnalyticsEvents = void 0;
var constants_1 = require("@formsort/constants");
var iframe_utils_1 = require("./iframe-utils");
var typeGuards_1 = require("./typeGuards");
var utils_1 = require("./utils");
var DEFAULT_FLOW_ORIGIN = "https://flow.formsort.com";
var DEFAULT_CONFIG = {
exports.isSupportedEventType = exports.SupportedAnalyticsEvent = void 0;
const constants_1 = require("@formsort/constants");
const iframe_utils_1 = require("./iframe-utils");
const typeGuards_1 = require("./typeGuards");
const utils_1 = require("./utils");
const DEFAULT_FLOW_DOMAIN = 'formsort.app';
const DEFAULT_CONFIG = {
useHistoryAPI: false,
origin: DEFAULT_FLOW_ORIGIN,
origin: undefined,
};
exports.supportedAnalyticsEvents = [
constants_1.AnalyticsEventType.FlowLoaded,
constants_1.AnalyticsEventType.FlowClosed,
constants_1.AnalyticsEventType.FlowFinalized,
constants_1.AnalyticsEventType.StepLoaded,
constants_1.AnalyticsEventType.StepCompleted,
];
var isSupportedEventType = function (eventType) {
return exports.supportedAnalyticsEvents.includes(eventType);
};
var FormsortWebEmbed = function (rootEl, config) {
if (config === void 0) { config = DEFAULT_CONFIG; }
var iframeEl = document.createElement('iframe');
var style = config.style, autoHeight = config.autoHeight;
var formsortOrigin = config.origin || DEFAULT_FLOW_ORIGIN;
var SupportedAnalyticsEvent;
(function (SupportedAnalyticsEvent) {
SupportedAnalyticsEvent["FlowLoaded"] = "FlowLoaded";
SupportedAnalyticsEvent["FlowClosed"] = "FlowClosed";
SupportedAnalyticsEvent["FlowFinalized"] = "FlowFinalized";
SupportedAnalyticsEvent["StepLoaded"] = "StepLoaded";
SupportedAnalyticsEvent["StepCompleted"] = "StepCompleted";
})(SupportedAnalyticsEvent = exports.SupportedAnalyticsEvent || (exports.SupportedAnalyticsEvent = {}));
const isSupportedEventType = (eventType) => eventType in SupportedAnalyticsEvent;
exports.isSupportedEventType = isSupportedEventType;
const FormsortWebEmbed = (rootEl, config = DEFAULT_CONFIG) => {
const iframeEl = document.createElement('iframe');
const { style, autoHeight } = config;
let formsortOrigin = config.origin;
iframeEl.style.border = 'none';
if (style) {
var _a = style.width, width = _a === void 0 ? '' : _a, _b = style.height, height = _b === void 0 ? '' : _b;
const { width = '', height = '' } = style;
iframeEl.style.width = width;

@@ -35,15 +34,15 @@ iframeEl.style.height = height;

rootEl.appendChild(iframeEl);
var sendMessage = iframe_utils_1.getMessageSender(iframeEl);
var eventListenersArrayMap = {
FlowLoaded: [],
FlowClosed: [],
FlowFinalized: [],
StepLoaded: [],
StepCompleted: [],
const sendMessage = (0, iframe_utils_1.getMessageSender)(iframeEl);
const eventListenersArrayMap = {
[SupportedAnalyticsEvent.FlowLoaded]: [],
[SupportedAnalyticsEvent.FlowClosed]: [],
[SupportedAnalyticsEvent.FlowFinalized]: [],
[SupportedAnalyticsEvent.StepLoaded]: [],
[SupportedAnalyticsEvent.StepCompleted]: [],
redirect: [],
unauthorized: [],
};
var onTokenRequest = function (data) {
const onTokenRequest = (data) => {
var _a;
var payload = data.payload;
const { payload } = data;
if (payload === constants_1.TokenRequestPayload.ID) {

@@ -53,18 +52,17 @@ if ((_a = config.authentication) === null || _a === void 0 ? void 0 : _a.idToken) {

type: constants_1.WebEmbedMessage.EMBED_TOKEN_RESPONSE_MSG,
payload: { token: config.authentication.idToken }
payload: { token: config.authentication.idToken },
});
}
else {
throw new Error("The loaded Flow requires authentication using an ID token, please provide it in config.authentication.idToken.");
throw new Error(`The loaded Flow requires authentication using an ID token, please provide it in config.authentication.idToken.`);
}
}
};
var onRedirectMessage = function (redirectData) {
var url = redirectData.payload, answers = redirectData.answers;
if (!utils_1.isEmpty(eventListenersArrayMap.redirect)) {
var cancelRedirect = false;
const onRedirectMessage = (redirectData) => {
const { payload: url, answers } = redirectData;
if (!(0, utils_1.isEmpty)(eventListenersArrayMap.redirect)) {
let cancelRedirect = false;
// Cancel redirect if any of the redirect listeners return `{ cancel: true }`
for (var _i = 0, _a = eventListenersArrayMap.redirect; _i < _a.length; _i++) {
var redirectListener = _a[_i];
var cancel = (redirectListener({ url: url, answers: answers }) || {}).cancel;
for (const redirectListener of eventListenersArrayMap.redirect) {
const { cancel } = redirectListener({ url, answers }) || {};
if (!cancelRedirect && cancel) {

@@ -87,6 +85,5 @@ cancelRedirect = true;

};
var onUnauthorizedMessage = function () {
if (!utils_1.isEmpty(eventListenersArrayMap.unauthorized)) {
for (var _i = 0, _a = eventListenersArrayMap.unauthorized; _i < _a.length; _i++) {
var unathorizedListener = _a[_i];
const onUnauthorizedMessage = () => {
if (!(0, utils_1.isEmpty)(eventListenersArrayMap.unauthorized)) {
for (const unathorizedListener of eventListenersArrayMap.unauthorized) {
unathorizedListener();

@@ -96,4 +93,4 @@ }

};
var onResizeMessage = function (data) {
var _a = data.payload, width = _a.width, height = _a.height;
const onResizeMessage = (data) => {
const { width, height } = data.payload;
setSize(width, height);

@@ -103,4 +100,4 @@ };

// and can be typed as MessageEvent<unknown> to increase type safety.
var onWindowMessage = function (message) {
var msgOrigin = message.origin, source = message.source, data = message.data;
const onWindowMessage = (message) => {
const { origin: msgOrigin, source, data } = message;
if (source !== iframeEl.contentWindow) {

@@ -114,18 +111,18 @@ // If we have multiple Formsort instances within a page, only listen to events coming

}
if (!typeGuards_1.isIWebEmbedEventData(data)) {
if (!(0, typeGuards_1.isIWebEmbedEventData)(data)) {
return;
}
if (typeGuards_1.isIFrameAnalyticsEventData(data)) {
if ((0, typeGuards_1.isIFrameAnalyticsEventData)(data)) {
onEventMessage(data);
}
else if (typeGuards_1.isIFrameTokenRequestEventData(data)) {
else if ((0, typeGuards_1.isIFrameTokenRequestEventData)(data)) {
onTokenRequest(data);
}
else if (typeGuards_1.isIFrameRedirectEventData(data)) {
else if ((0, typeGuards_1.isIFrameRedirectEventData)(data)) {
onRedirectMessage(data);
}
else if (typeGuards_1.isIFrameResizeEventData(data) && autoHeight) {
else if ((0, typeGuards_1.isIFrameResizeEventData)(data) && autoHeight) {
onResizeMessage(data);
}
else if (typeGuards_1.isIFrameUnauthorizedEventData(data)) {
else if ((0, typeGuards_1.isIFrameUnauthorizedEventData)(data)) {
onUnauthorizedMessage();

@@ -137,3 +134,3 @@ }

}
var setSize = function (width, height) {
const setSize = (width, height) => {
if (width !== undefined) {

@@ -146,4 +143,4 @@ iframeEl.style.width = width.toString();

};
var getEventListenerArray = function (eventType) {
if (isSupportedEventType(eventType)) {
const getEventListenerArray = (eventType) => {
if ((0, exports.isSupportedEventType)(eventType)) {
return eventListenersArrayMap[eventType];

@@ -153,4 +150,4 @@ }

};
var onEventMessage = function (eventData) {
var eventType = eventData.eventType, answers = eventData.answers;
const onEventMessage = (eventData) => {
const { eventType, answers } = eventData;
if (eventType === constants_1.AnalyticsEventType.FlowClosed) {

@@ -160,26 +157,24 @@ removeListeners();

}
var eventListenersArr = getEventListenerArray(eventType);
const eventListenersArr = getEventListenerArray(eventType);
if (!eventListenersArr) {
return;
}
for (var _i = 0, eventListenersArr_1 = eventListenersArr; _i < eventListenersArr_1.length; _i++) {
var eventListener = eventListenersArr_1[_i];
eventListener({ answers: answers });
for (const eventListener of eventListenersArr) {
eventListener({ answers });
}
};
var removeListeners = function () {
const removeListeners = () => {
window.removeEventListener('message', onWindowMessage);
};
var loadFlow = function (clientLabel, flowLabel, variantLabel, queryParams) {
var url = formsortOrigin + "/client/" + clientLabel + "/flow/" + flowLabel;
const loadFlow = (clientLabel, flowLabel, variantLabel, queryParams) => {
// We overwrite `formsortOrigin` because `onWindowMessage` will read it
formsortOrigin = formsortOrigin || `https://${clientLabel}.${DEFAULT_FLOW_DOMAIN}`;
let url = `${formsortOrigin}/client/${clientLabel}/flow/${flowLabel}`;
if (variantLabel) {
url += "/variant/" + variantLabel;
url += `/variant/${variantLabel}`;
}
if (queryParams) {
url += "?" + queryParams
.map(function (_a) {
var key = _a[0], value = _a[1];
return encodeURIComponent(key) + "=" + encodeURIComponent(value);
})
.join('&');
url += `?${queryParams
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&')}`;
}

@@ -189,9 +184,9 @@ iframeEl.src = url;

return {
loadFlow: loadFlow,
setSize: setSize,
addEventListener: function (eventName, fn) {
utils_1.addToArrayMap(eventListenersArrayMap, eventName, fn);
loadFlow,
setSize,
addEventListener(eventName, fn) {
(0, utils_1.addToArrayMap)(eventListenersArrayMap, eventName, fn);
},
removeEventListener: function (eventName, eventListener) {
utils_1.removeFromArrayMap(eventListenersArrayMap, eventName, eventListener);
removeEventListener(eventName, eventListener) {
(0, utils_1.removeFromArrayMap)(eventListenersArrayMap, eventName, eventListener);
},

@@ -198,0 +193,0 @@ };

"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var constants_1 = require("@formsort/constants");
var _1 = require(".");
var DEFAULT_FLOW_ORIGIN = 'https://flow.formsort.com';
var EMBEDDING_WINDOW_ORIGIN = 'https://test-origin.formsort.com';
var clientLabel = 'test-client';
var flowLabel = 'test-flow';
var variantLabel = 'test-variant';
describe('FormsortWebEmbed', function () {
var pushStateSpy = jest
const tslib_1 = require("tslib");
const constants_1 = require("@formsort/constants");
const _1 = tslib_1.__importStar(require("."));
const EMBEDDING_WINDOW_ORIGIN = 'https://test-origin.formsort.com';
const clientLabel = 'test-client';
const flowLabel = 'test-flow';
const variantLabel = 'test-variant';
describe('FormsortWebEmbed', () => {
const pushStateSpy = jest
.spyOn(window.history, 'pushState')
.mockImplementation(jest.fn);
var originalAddEventListener = window.addEventListener;
var messageHandlers = [];
const originalAddEventListener = window.addEventListener;
let messageHandlers = [];
jest
.spyOn(window, 'addEventListener')
.mockImplementation(function (type, listener) {
.mockImplementation((type, listener) => {
if (type === 'message') {

@@ -76,14 +29,14 @@ messageHandlers.push(listener);

*/
var mockPostMessage = function (msg) {
messageHandlers.forEach(function (m) {
const mockPostMessage = (msg) => {
messageHandlers.forEach((m) => {
m(msg);
});
};
var location = window.location;
beforeAll(function () {
const { location } = window;
beforeAll(() => {
// @ts-ignore
delete window.location;
window.location = __assign(__assign({}, location), { assign: jest.fn(), origin: EMBEDDING_WINDOW_ORIGIN });
window.location = Object.assign(Object.assign({}, location), { assign: jest.fn(), origin: EMBEDDING_WINDOW_ORIGIN });
});
beforeEach(function () {
beforeEach(() => {
pushStateSpy.mockClear();

@@ -94,65 +47,65 @@ window.location.assign.mockClear();

});
afterAll(function () {
afterAll(() => {
window.location = location;
});
test('does not load anything if instantiated without calling load', function () {
_1.default(document.body);
var iframes = document.body.querySelectorAll('iframe');
test('does not load anything if instantiated without calling load', () => {
(0, _1.default)(document.body);
const iframes = document.body.querySelectorAll('iframe');
expect(iframes.length).toBe(1);
var iframe = iframes[0];
const iframe = iframes[0];
expect(iframe.src).toBe('');
});
test('mounts at the specified root element', function () {
var rootEl = document.createElement('div');
test('mounts at the specified root element', () => {
const rootEl = document.createElement('div');
document.body.appendChild(rootEl);
_1.default(rootEl);
var iframes = document.body.querySelectorAll('iframe');
(0, _1.default)(rootEl);
const iframes = document.body.querySelectorAll('iframe');
expect(iframes.length).toBe(1);
var iframe = iframes[0];
const iframe = iframes[0];
expect(iframe.src).toBe('');
expect(iframe.parentElement).toBe(rootEl);
});
test('loads with a specific size if specified', function () {
var width = '400px';
var height = '300px';
_1.default(document.body, {
style: { width: width, height: height },
test('loads with a specific size if specified', () => {
const width = '400px';
const height = '300px';
(0, _1.default)(document.body, {
style: { width, height },
});
var iframes = document.body.querySelectorAll('iframe');
const iframes = document.body.querySelectorAll('iframe');
expect(iframes.length).toBe(1);
var iframe = iframes[0];
const iframe = iframes[0];
expect(iframe.style.width).toBe(width);
expect(iframe.style.height).toBe(height);
});
test('Handles present-but-empty style', function () {
_1.default(document.body, {
test('Handles present-but-empty style', () => {
(0, _1.default)(document.body, {
style: {},
});
var iframes = document.body.querySelectorAll('iframe');
const iframes = document.body.querySelectorAll('iframe');
expect(iframes.length).toBe(1);
var iframe = iframes[0];
const iframe = iframes[0];
expect(iframe.style.width).toBe('');
expect(iframe.style.height).toBe('');
});
test('loads a flow when load is called', function () {
var loadFlow = _1.default(document.body).loadFlow;
var iframes = document.body.querySelectorAll('iframe');
test('loads a flow when load is called', () => {
const { loadFlow } = (0, _1.default)(document.body);
const iframes = document.body.querySelectorAll('iframe');
expect(iframes.length).toBe(1);
loadFlow(clientLabel, flowLabel);
var iframe = iframes[0];
expect(iframe.src).toBe("https://flow.formsort.com/client/" + clientLabel + "/flow/" + flowLabel);
const iframe = iframes[0];
expect(iframe.src).toBe(`https://${clientLabel}.formsort.app/client/${clientLabel}/flow/${flowLabel}`);
});
test('loads and handles messages from custom origin when specified', function () {
var customOrigin = 'http://localhost:4040';
var embed = _1.default(document.body, {
test('loads and handles messages from custom origin when specified', () => {
const customOrigin = 'http://localhost:4040';
const embed = (0, _1.default)(document.body, {
origin: customOrigin,
});
var iframes = document.body.querySelectorAll('iframe');
const iframes = document.body.querySelectorAll('iframe');
expect(iframes.length).toBe(1);
embed.loadFlow(clientLabel, flowLabel);
var iframe = iframes[0];
expect(iframe.src).toBe(customOrigin + "/client/" + clientLabel + "/flow/" + flowLabel);
var flowLoadedSpy = jest.fn();
embed.addEventListener('FlowLoaded', flowLoadedSpy);
var msg = new MessageEvent('message', {
const iframe = iframes[0];
expect(iframe.src).toBe(`${customOrigin}/client/${clientLabel}/flow/${flowLabel}`);
const flowLoadedSpy = jest.fn();
embed.addEventListener(_1.SupportedAnalyticsEvent.FlowLoaded, flowLoadedSpy);
const msg = new MessageEvent('message', {
source: iframe.contentWindow,

@@ -169,19 +122,19 @@ origin: customOrigin,

});
test('loads a variant when load is called', function () {
var loadFlow = _1.default(document.body).loadFlow;
var iframes = document.body.querySelectorAll('iframe');
test('loads a variant when load is called', () => {
const { loadFlow } = (0, _1.default)(document.body);
const iframes = document.body.querySelectorAll('iframe');
expect(iframes.length).toBe(1);
loadFlow(clientLabel, flowLabel, variantLabel);
var iframe = iframes[0];
expect(iframe.src).toBe("https://flow.formsort.com" +
("/client/" + clientLabel + "/flow/" + flowLabel + "/variant/" + variantLabel));
const iframe = iframes[0];
expect(iframe.src).toBe(`https://${clientLabel}.formsort.app` +
`/client/${clientLabel}/flow/${flowLabel}/variant/${variantLabel}`);
});
test('loads with query parameters if specified', function () {
var loadFlow = _1.default(document.body).loadFlow;
var iframes = document.body.querySelectorAll('iframe');
test('loads with query parameters if specified', () => {
const { loadFlow } = (0, _1.default)(document.body);
const iframes = document.body.querySelectorAll('iframe');
expect(iframes.length).toBe(1);
var queryParamA = 'queryParamA';
var queryValueA = 'queryValueA';
var queryParamB = 'queryParamB';
var queryValueB = 'queryValueB';
const queryParamA = 'queryParamA';
const queryValueA = 'queryValueA';
const queryParamB = 'queryParamB';
const queryValueB = 'queryValueB';
loadFlow(clientLabel, flowLabel, undefined, [

@@ -191,449 +144,393 @@ [queryParamA, queryValueA],

]);
var iframe = iframes[0];
expect(iframe.src).toBe("https://flow.formsort.com" +
("/client/" + clientLabel + "/flow/" + flowLabel) +
("?" + queryParamA + "=" + queryValueA + "&" + queryParamB + "=" + queryValueB));
const iframe = iframes[0];
expect(iframe.src).toBe(`https://${clientLabel}.formsort.app` +
`/client/${clientLabel}/flow/${flowLabel}` +
`?${queryParamA}=${queryValueA}&${queryParamB}=${queryValueB}`);
});
test('ignores events from unknown origins', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, flowLoadedSpy, msg;
return __generator(this, function (_a) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
flowLoadedSpy = jest.fn();
embed.addEventListener('FlowClosed', flowLoadedSpy);
embed.loadFlow(clientLabel, flowLabel);
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: 'https://example.com',
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowLoaded,
},
});
mockPostMessage(msg);
expect(flowLoadedSpy).toBeCalledTimes(0);
return [2 /*return*/];
test('ignores events from unknown origins', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
const flowLoadedSpy = jest.fn();
embed.addEventListener(_1.SupportedAnalyticsEvent.FlowClosed, flowLoadedSpy);
embed.loadFlow(clientLabel, flowLabel);
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: 'https://example.com',
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowLoaded,
},
});
}); });
test('ignores events without data', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, flowLoadedSpy, msg;
return __generator(this, function (_a) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
flowLoadedSpy = jest.fn();
embed.addEventListener('FlowLoaded', flowLoadedSpy);
embed.loadFlow(clientLabel, flowLabel);
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: undefined,
});
mockPostMessage(msg);
expect(flowLoadedSpy).toBeCalledTimes(0);
return [2 /*return*/];
mockPostMessage(msg);
expect(flowLoadedSpy).toBeCalledTimes(0);
}));
test('ignores events without data', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
const flowLoadedSpy = jest.fn();
embed.addEventListener(_1.SupportedAnalyticsEvent.FlowLoaded, flowLoadedSpy);
embed.loadFlow(clientLabel, flowLabel);
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: undefined,
});
}); });
test('handles messages from multiple flows within the same window', function () { return __awaiter(void 0, void 0, void 0, function () {
var firstEmbed, secondEmbed, iframes, secondFlowLabel, firstFlowIframe, secondFlowIframe, firstFlowFinalized, secondFlowFinalized, msg;
return __generator(this, function (_a) {
firstEmbed = _1.default(document.body);
secondEmbed = _1.default(document.body);
iframes = document.body.querySelectorAll('iframe');
expect(iframes.length).toBe(2);
secondFlowLabel = 'second-test-flow';
firstEmbed.loadFlow(clientLabel, flowLabel);
secondEmbed.loadFlow(clientLabel, secondFlowLabel);
firstFlowIframe = iframes[0];
expect(firstFlowIframe.src).toBe("https://flow.formsort.com/client/" + clientLabel + "/flow/" + flowLabel);
secondFlowIframe = iframes[1];
expect(secondFlowIframe.src).toBe("https://flow.formsort.com/client/" + clientLabel + "/flow/" + secondFlowLabel);
firstFlowFinalized = jest.fn();
firstEmbed.addEventListener('FlowFinalized', firstFlowFinalized);
secondFlowFinalized = jest.fn();
secondEmbed.addEventListener('FlowFinalized', secondFlowFinalized);
msg = new MessageEvent('message', {
source: firstFlowIframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowFinalized,
},
});
mockPostMessage(msg);
// We received a message from the first iframe, so only that frame should
// have its callback called.
expect(firstFlowFinalized).toBeCalledTimes(1);
expect(secondFlowFinalized).toBeCalledTimes(0);
return [2 /*return*/];
mockPostMessage(msg);
expect(flowLoadedSpy).toBeCalledTimes(0);
}));
test('handles messages from multiple flows within the same window', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const firstEmbed = (0, _1.default)(document.body);
const secondEmbed = (0, _1.default)(document.body);
const iframes = document.body.querySelectorAll('iframe');
expect(iframes.length).toBe(2);
const secondFlowLabel = 'second-test-flow';
firstEmbed.loadFlow(clientLabel, flowLabel);
secondEmbed.loadFlow(clientLabel, secondFlowLabel);
const firstFlowIframe = iframes[0];
expect(firstFlowIframe.src).toBe(`https://${clientLabel}.formsort.app/client/${clientLabel}/flow/${flowLabel}`);
const secondFlowIframe = iframes[1];
expect(secondFlowIframe.src).toBe(`https://${clientLabel}.formsort.app/client/${clientLabel}/flow/${secondFlowLabel}`);
const firstFlowFinalized = jest.fn();
firstEmbed.addEventListener(_1.SupportedAnalyticsEvent.FlowFinalized, firstFlowFinalized);
const secondFlowFinalized = jest.fn();
secondEmbed.addEventListener(_1.SupportedAnalyticsEvent.FlowFinalized, secondFlowFinalized);
const msg = new MessageEvent('message', {
source: firstFlowIframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowFinalized,
},
});
}); });
test('handles flow loaded event', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, flowLoadedSpy, msg;
return __generator(this, function (_a) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
flowLoadedSpy = jest.fn();
embed.addEventListener('FlowLoaded', flowLoadedSpy);
embed.loadFlow(clientLabel, flowLabel);
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowLoaded,
},
});
mockPostMessage(msg);
expect(flowLoadedSpy).toBeCalledTimes(1);
return [2 /*return*/];
mockPostMessage(msg);
// We received a message from the first iframe, so only that frame should
// have its callback called.
expect(firstFlowFinalized).toBeCalledTimes(1);
expect(secondFlowFinalized).toBeCalledTimes(0);
}));
test('handles flow loaded event', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
const flowLoadedSpy = jest.fn();
embed.addEventListener(_1.SupportedAnalyticsEvent.FlowLoaded, flowLoadedSpy);
embed.loadFlow(clientLabel, flowLabel);
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowLoaded,
},
});
}); });
test('handles adding and removing event handlers', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, flowLoadedSpy1, flowLoadedSpy2, flowLoadedSpy3, msg1, msg2, msg3;
return __generator(this, function (_a) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
flowLoadedSpy1 = jest.fn();
flowLoadedSpy2 = jest.fn();
flowLoadedSpy3 = jest.fn();
embed.addEventListener('FlowLoaded', flowLoadedSpy1);
embed.addEventListener('FlowLoaded', flowLoadedSpy2);
embed.addEventListener('FlowLoaded', flowLoadedSpy3);
embed.loadFlow(clientLabel, flowLabel);
msg1 = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowLoaded,
},
});
mockPostMessage(msg1);
expect(flowLoadedSpy1).toBeCalledTimes(1);
expect(flowLoadedSpy2).toBeCalledTimes(1);
expect(flowLoadedSpy3).toBeCalledTimes(1);
// Remove second event listener
embed.removeEventListener('FlowLoaded', flowLoadedSpy2);
msg2 = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowLoaded,
},
});
mockPostMessage(msg2);
expect(flowLoadedSpy1).toBeCalledTimes(2);
// removed listener should not be called again
expect(flowLoadedSpy2).toBeCalledTimes(1);
expect(flowLoadedSpy3).toBeCalledTimes(2);
// Remove rest of event listeners
embed.removeEventListener('FlowLoaded', flowLoadedSpy1);
embed.removeEventListener('FlowLoaded', flowLoadedSpy3);
embed.removeEventListener('FlowLoaded', flowLoadedSpy2);
msg3 = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowLoaded,
},
});
mockPostMessage(msg3);
// removed listeners should not be called again
expect(flowLoadedSpy1).toBeCalledTimes(2);
expect(flowLoadedSpy2).toBeCalledTimes(1);
expect(flowLoadedSpy3).toBeCalledTimes(2);
return [2 /*return*/];
mockPostMessage(msg);
expect(flowLoadedSpy).toBeCalledTimes(1);
}));
test('handles adding and removing event handlers', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
const flowLoadedSpy1 = jest.fn();
const flowLoadedSpy2 = jest.fn();
const flowLoadedSpy3 = jest.fn();
embed.addEventListener(_1.SupportedAnalyticsEvent.FlowLoaded, flowLoadedSpy1);
embed.addEventListener(_1.SupportedAnalyticsEvent.FlowLoaded, flowLoadedSpy2);
embed.addEventListener(_1.SupportedAnalyticsEvent.FlowLoaded, flowLoadedSpy3);
embed.loadFlow(clientLabel, flowLabel);
const msg1 = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowLoaded,
},
});
}); });
test('handles flow finalized event', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, flowFinalizedSpy, msg;
return __generator(this, function (_a) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
flowFinalizedSpy = jest.fn();
embed.addEventListener('FlowFinalized', flowFinalizedSpy);
embed.loadFlow(clientLabel, flowLabel);
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowFinalized,
},
});
mockPostMessage(msg);
expect(flowFinalizedSpy).toBeCalledTimes(1);
return [2 /*return*/];
mockPostMessage(msg1);
expect(flowLoadedSpy1).toBeCalledTimes(1);
expect(flowLoadedSpy2).toBeCalledTimes(1);
expect(flowLoadedSpy3).toBeCalledTimes(1);
// Remove second event listener
embed.removeEventListener(_1.SupportedAnalyticsEvent.FlowLoaded, flowLoadedSpy2);
const msg2 = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowLoaded,
},
});
}); });
test('handles flow closed event', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, removeEventListenerSpy, flowClosedSpy, msg;
return __generator(this, function (_a) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
flowClosedSpy = jest.fn();
embed.addEventListener('FlowClosed', flowClosedSpy);
embed.loadFlow(clientLabel, flowLabel);
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowClosed,
},
});
mockPostMessage(msg);
expect(flowClosedSpy).toBeCalledTimes(1);
expect(removeEventListenerSpy).toBeCalledTimes(1);
expect(removeEventListenerSpy.mock.calls[0][0]).toBe('message');
return [2 /*return*/];
mockPostMessage(msg2);
expect(flowLoadedSpy1).toBeCalledTimes(2);
// removed listener should not be called again
expect(flowLoadedSpy2).toBeCalledTimes(1);
expect(flowLoadedSpy3).toBeCalledTimes(2);
// Remove rest of event listeners
embed.removeEventListener(_1.SupportedAnalyticsEvent.FlowLoaded, flowLoadedSpy1);
embed.removeEventListener(_1.SupportedAnalyticsEvent.FlowLoaded, flowLoadedSpy3);
embed.removeEventListener(_1.SupportedAnalyticsEvent.FlowLoaded, flowLoadedSpy2);
const msg3 = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowLoaded,
},
});
}); });
test('handles resize event when autoHeight is enabled', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, width, height, msg, newHeight, newWidth, heightMsg, widthMsg;
return __generator(this, function (_a) {
embed = _1.default(document.body, {
style: { width: '100px', height: '100px' },
autoHeight: true,
});
iframe = document.body.querySelector('iframe');
embed.loadFlow(clientLabel, flowLabel);
width = '357px';
height = '733px';
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_RESIZE_MSG,
payload: {
width: width,
height: height,
},
},
});
mockPostMessage(msg);
expect(iframe.style.width).toBe(width);
expect(iframe.style.height).toBe(height);
newHeight = '999px';
newWidth = '888px';
heightMsg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_RESIZE_MSG,
payload: {
height: newHeight,
},
},
});
mockPostMessage(heightMsg);
expect(iframe.style.width).toBe(width);
expect(iframe.style.height).toBe(newHeight);
widthMsg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_RESIZE_MSG,
payload: {
width: newWidth,
},
},
});
mockPostMessage(widthMsg);
expect(iframe.style.width).toBe(newWidth);
expect(iframe.style.height).toBe(newHeight);
return [2 /*return*/];
mockPostMessage(msg3);
// removed listeners should not be called again
expect(flowLoadedSpy1).toBeCalledTimes(2);
expect(flowLoadedSpy2).toBeCalledTimes(1);
expect(flowLoadedSpy3).toBeCalledTimes(2);
}));
test('handles flow finalized event', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
const flowFinalizedSpy = jest.fn();
embed.addEventListener(_1.SupportedAnalyticsEvent.FlowFinalized, flowFinalizedSpy);
embed.loadFlow(clientLabel, flowLabel);
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowFinalized,
},
});
}); });
test('ignores resize event when autoHeight is enabled', function () { return __awaiter(void 0, void 0, void 0, function () {
var originalWidth, originalHeight, embed, iframe, newWidth, newHeight, msg;
return __generator(this, function (_a) {
originalWidth = '200px';
originalHeight = '300px';
embed = _1.default(document.body, {
style: { width: originalWidth, height: originalHeight },
});
iframe = document.body.querySelector('iframe');
embed.loadFlow(clientLabel, flowLabel);
newWidth = '357px';
newHeight = '733px';
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_RESIZE_MSG,
payload: {
width: newWidth,
height: newHeight,
},
mockPostMessage(msg);
expect(flowFinalizedSpy).toBeCalledTimes(1);
}));
test('handles flow closed event', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
const flowClosedSpy = jest.fn();
embed.addEventListener(_1.SupportedAnalyticsEvent.FlowClosed, flowClosedSpy);
embed.loadFlow(clientLabel, flowLabel);
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowClosed,
},
});
mockPostMessage(msg);
expect(flowClosedSpy).toBeCalledTimes(1);
expect(removeEventListenerSpy).toBeCalledTimes(1);
expect(removeEventListenerSpy.mock.calls[0][0]).toBe('message');
}));
test('handles resize event when autoHeight is enabled', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body, {
style: { width: '100px', height: '100px' },
autoHeight: true,
});
const iframe = document.body.querySelector('iframe');
embed.loadFlow(clientLabel, flowLabel);
const width = '357px';
const height = '733px';
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_RESIZE_MSG,
payload: {
width,
height,
},
});
mockPostMessage(msg);
expect(iframe.style.width).toBe(originalWidth);
expect(iframe.style.height).toBe(originalHeight);
return [2 /*return*/];
},
});
}); });
test('handles redirecting to a URL', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, redirectSpy, redirectUrl, msg;
return __generator(this, function (_a) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
redirectSpy = jest.fn();
embed.addEventListener('redirect', redirectSpy);
embed.loadFlow(clientLabel, flowLabel);
redirectUrl = 'https://example.com';
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
mockPostMessage(msg);
expect(iframe.style.width).toBe(width);
expect(iframe.style.height).toBe(height);
// The resize message can be partial (for example, just height changes)
// Make sure we can handle those.
const newHeight = '999px';
const newWidth = '888px';
const heightMsg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_RESIZE_MSG,
payload: {
height: newHeight,
},
});
mockPostMessage(msg);
expect(redirectSpy).toBeCalledTimes(1);
expect(redirectSpy).toBeCalledWith({ url: redirectUrl });
expect(window.location.assign).toBeCalledTimes(1);
expect(window.location.assign).toBeCalledWith(redirectUrl);
return [2 /*return*/];
},
});
}); });
test('handles redirecting to a URL if no callback returns `{cancel: true}`', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, redirectCallback1, redirectCallback2, redirectCallback3, redirectUrl, msg;
return __generator(this, function (_a) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
redirectCallback1 = jest.fn(function () { return ({ cancel: false }); });
redirectCallback2 = jest.fn(function () { return ({}); });
redirectCallback3 = jest.fn(function () { return ({}); });
embed.addEventListener('redirect', redirectCallback1);
embed.addEventListener('redirect', redirectCallback2);
embed.addEventListener('redirect', redirectCallback3);
embed.loadFlow(clientLabel, flowLabel);
redirectUrl = 'https://example.com';
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
mockPostMessage(heightMsg);
expect(iframe.style.width).toBe(width);
expect(iframe.style.height).toBe(newHeight);
const widthMsg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_RESIZE_MSG,
payload: {
width: newWidth,
},
});
mockPostMessage(msg);
expect(redirectCallback1).toBeCalledTimes(1);
expect(redirectCallback1).toBeCalledWith({ url: redirectUrl });
expect(redirectCallback2).toBeCalledTimes(1);
expect(redirectCallback2).toBeCalledWith({ url: redirectUrl });
expect(redirectCallback3).toBeCalledTimes(1);
expect(redirectCallback3).toBeCalledWith({ url: redirectUrl });
expect(window.location.assign).toBeCalledTimes(1);
expect(window.location.assign).toBeCalledWith(redirectUrl);
return [2 /*return*/];
},
});
}); });
test('Cancels redirect if callback returns `cancel: true`', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, redirectUrl, redirectCallback, msg;
return __generator(this, function (_a) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
redirectUrl = 'https://example.com';
redirectCallback = jest.fn(function () { return ({ cancel: true }); });
embed.addEventListener('redirect', redirectCallback);
embed.loadFlow(clientLabel, flowLabel);
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
mockPostMessage(widthMsg);
expect(iframe.style.width).toBe(newWidth);
expect(iframe.style.height).toBe(newHeight);
}));
test('ignores resize event when autoHeight is enabled', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const originalWidth = '200px';
const originalHeight = '300px';
const embed = (0, _1.default)(document.body, {
style: { width: originalWidth, height: originalHeight },
});
const iframe = document.body.querySelector('iframe');
embed.loadFlow(clientLabel, flowLabel);
const newWidth = '357px';
const newHeight = '733px';
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_RESIZE_MSG,
payload: {
width: newWidth,
height: newHeight,
},
});
mockPostMessage(msg);
expect(redirectCallback).toBeCalledTimes(1);
expect(redirectCallback).toBeCalledWith({ url: redirectUrl });
expect(window.location.assign).not.toHaveBeenCalled();
return [2 /*return*/];
},
});
}); });
test('Cancels redirect if any callback returns `cancel: true`', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, redirectUrl, redirectCallback1, redirectCallback2, redirectCallback3, msg;
return __generator(this, function (_a) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
redirectUrl = 'https://example.com';
redirectCallback1 = jest.fn(function () { return ({ cancel: false }); });
redirectCallback2 = jest.fn(function () { return ({ cancel: true }); });
redirectCallback3 = jest.fn(function () { return ({}); });
embed.addEventListener('redirect', redirectCallback1);
embed.addEventListener('redirect', redirectCallback2);
embed.addEventListener('redirect', redirectCallback3);
mockPostMessage(msg);
expect(iframe.style.width).toBe(originalWidth);
expect(iframe.style.height).toBe(originalHeight);
}));
test('handles redirecting to a URL', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
const redirectSpy = jest.fn();
embed.addEventListener('redirect', redirectSpy);
embed.loadFlow(clientLabel, flowLabel);
const redirectUrl = 'https://example.com';
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
},
});
mockPostMessage(msg);
expect(redirectSpy).toBeCalledTimes(1);
expect(redirectSpy).toBeCalledWith({ url: redirectUrl });
expect(window.location.assign).toBeCalledTimes(1);
expect(window.location.assign).toBeCalledWith(redirectUrl);
}));
test('handles redirecting to a URL if no callback returns `{cancel: true}`', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
const redirectCallback1 = jest.fn(() => ({ cancel: false }));
const redirectCallback2 = jest.fn(() => ({}));
const redirectCallback3 = jest.fn(() => ({}));
embed.addEventListener('redirect', redirectCallback1);
embed.addEventListener('redirect', redirectCallback2);
embed.addEventListener('redirect', redirectCallback3);
embed.loadFlow(clientLabel, flowLabel);
const redirectUrl = 'https://example.com';
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
},
});
mockPostMessage(msg);
expect(redirectCallback1).toBeCalledTimes(1);
expect(redirectCallback1).toBeCalledWith({ url: redirectUrl });
expect(redirectCallback2).toBeCalledTimes(1);
expect(redirectCallback2).toBeCalledWith({ url: redirectUrl });
expect(redirectCallback3).toBeCalledTimes(1);
expect(redirectCallback3).toBeCalledWith({ url: redirectUrl });
expect(window.location.assign).toBeCalledTimes(1);
expect(window.location.assign).toBeCalledWith(redirectUrl);
}));
test('Cancels redirect if callback returns `cancel: true`', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
const redirectUrl = 'https://example.com';
const redirectCallback = jest.fn(() => ({ cancel: true }));
embed.addEventListener('redirect', redirectCallback);
embed.loadFlow(clientLabel, flowLabel);
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
},
});
mockPostMessage(msg);
expect(redirectCallback).toBeCalledTimes(1);
expect(redirectCallback).toBeCalledWith({ url: redirectUrl });
expect(window.location.assign).not.toHaveBeenCalled();
}));
test('Cancels redirect if any callback returns `cancel: true`', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
const redirectUrl = 'https://example.com';
const redirectCallback1 = jest.fn(() => ({ cancel: false }));
const redirectCallback2 = jest.fn(() => ({ cancel: true }));
const redirectCallback3 = jest.fn(() => ({}));
embed.addEventListener('redirect', redirectCallback1);
embed.addEventListener('redirect', redirectCallback2);
embed.addEventListener('redirect', redirectCallback3);
embed.loadFlow(clientLabel, flowLabel);
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
},
});
mockPostMessage(msg);
expect(redirectCallback1).toBeCalledTimes(1);
expect(redirectCallback1).toBeCalledWith({ url: redirectUrl });
expect(redirectCallback2).toBeCalledTimes(1);
expect(redirectCallback2).toBeCalledWith({ url: redirectUrl });
expect(redirectCallback3).toBeCalledTimes(1);
expect(redirectCallback3).toBeCalledWith({ url: redirectUrl });
expect(window.location.assign).not.toHaveBeenCalled();
}));
describe.each(Object.values(_1.SupportedAnalyticsEvent))('%s', (event) => {
const sendMessage = (eventType, answers) => {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
const eventListenerSpy = jest.fn();
embed.addEventListener(eventType, eventListenerSpy);
embed.loadFlow(clientLabel, flowLabel);
msg = new MessageEvent('message', {
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType,
answers,
},
});
mockPostMessage(msg);
expect(redirectCallback1).toBeCalledTimes(1);
expect(redirectCallback1).toBeCalledWith({ url: redirectUrl });
expect(redirectCallback2).toBeCalledTimes(1);
expect(redirectCallback2).toBeCalledWith({ url: redirectUrl });
expect(redirectCallback3).toBeCalledTimes(1);
expect(redirectCallback3).toBeCalledWith({ url: redirectUrl });
expect(window.location.assign).not.toHaveBeenCalled();
return [2 /*return*/];
expect(eventListenerSpy).toBeCalledTimes(1);
return eventListenerSpy;
};
it('passes answers when defined', () => {
const answers = {
'a-question': 'an-answer',
};
const eventListenerSpy = sendMessage(event, answers);
expect(eventListenerSpy).toBeCalledWith({ answers });
});
}); });
test.each(_1.supportedAnalyticsEvents.flatMap(function (eventType) {
return [
[
eventType,
{
answers: {
'a-question': 'an-answer',
},
},
],
[eventType, { answers: undefined }],
];
}))('Passes answers if available for event %s', function (eventType, _a) {
var answers = _a.answers;
return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, eventListenerSpy, msg;
return __generator(this, function (_b) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
eventListenerSpy = jest.fn();
embed.addEventListener(eventType, eventListenerSpy);
embed.loadFlow(clientLabel, flowLabel);
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: eventType,
answers: answers,
},
});
mockPostMessage(msg);
expect(eventListenerSpy).toBeCalledTimes(1);
expect(eventListenerSpy).toBeCalledWith(answers ? { answers: answers } : {});
return [2 /*return*/];
});
it('does not crash with empty answers', () => {
const eventListenerSpy = sendMessage(event, undefined);
expect(eventListenerSpy).toBeCalledWith({});
});

@@ -648,134 +545,115 @@ });

{ answers: undefined },
])('Passes answers if available for redirect event', function (_a) {
var answers = _a.answers;
return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, redirectSpy, redirectUrl, msg, expectedCallArgs;
return __generator(this, function (_b) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
redirectSpy = jest.fn();
embed.addEventListener('redirect', redirectSpy);
embed.loadFlow(clientLabel, flowLabel);
redirectUrl = 'https://example.com';
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
answers: answers,
},
});
mockPostMessage(msg);
expect(redirectSpy).toBeCalledTimes(1);
expectedCallArgs = {
url: redirectUrl,
};
if (answers) {
expectedCallArgs.answers = answers;
}
expect(redirectSpy).toBeCalledWith(expectedCallArgs);
return [2 /*return*/];
});
])('Passes answers if available for redirect event', ({ answers }) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
const redirectSpy = jest.fn();
embed.addEventListener('redirect', redirectSpy);
embed.loadFlow(clientLabel, flowLabel);
const redirectUrl = 'https://example.com';
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
answers,
},
});
});
test('handles events even when corresponding handlers are not set', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, redirectMsg, resizeMsg, eventMsg, unknownMsg;
return __generator(this, function (_a) {
embed = _1.default(document.body);
iframe = document.body.querySelector('iframe');
embed.loadFlow(clientLabel, flowLabel);
redirectMsg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: 'https://example.com',
},
});
mockPostMessage(redirectMsg);
resizeMsg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_RESIZE_MSG,
payload: {
width: '100px',
height: '200px',
},
},
});
mockPostMessage(resizeMsg);
eventMsg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: 'https://example.com',
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowLoaded,
},
});
mockPostMessage(eventMsg);
unknownMsg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: 'https://example.com',
data: {
type: 'some unknown type',
},
});
mockPostMessage(unknownMsg);
return [2 /*return*/];
mockPostMessage(msg);
expect(redirectSpy).toBeCalledTimes(1);
const expectedCallArgs = {
url: redirectUrl,
};
if (answers) {
expectedCallArgs.answers = answers;
}
expect(redirectSpy).toBeCalledWith(expectedCallArgs);
}));
test('handles events even when corresponding handlers are not set', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body);
const iframe = document.body.querySelector('iframe');
embed.loadFlow(clientLabel, flowLabel);
const redirectMsg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: 'https://example.com',
},
});
}); });
test('handles redirecting to a using history API when redirecting in the same origin', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, redirectSpy, redirectUrl, msg;
return __generator(this, function (_a) {
embed = _1.default(document.body, { useHistoryAPI: true });
iframe = document.body.querySelector('iframe');
redirectSpy = jest.fn();
embed.addEventListener('redirect', redirectSpy);
embed.loadFlow(clientLabel, flowLabel);
redirectUrl = EMBEDDING_WINDOW_ORIGIN + "/some-other-page-in-the-parent-origin";
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
mockPostMessage(redirectMsg);
const resizeMsg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_RESIZE_MSG,
payload: {
width: '100px',
height: '200px',
},
});
mockPostMessage(msg);
expect(redirectSpy).toBeCalledTimes(1);
expect(redirectSpy).toBeCalledWith({ url: redirectUrl });
expect(pushStateSpy).toBeCalledTimes(1);
expect(pushStateSpy).toBeCalledWith({}, '', redirectUrl);
return [2 /*return*/];
},
});
}); });
test('ignores useHistoryAPI when redirecting to a different origin', function () { return __awaiter(void 0, void 0, void 0, function () {
var embed, iframe, redirectSpy, redirectUrl, msg;
return __generator(this, function (_a) {
embed = _1.default(document.body, { useHistoryAPI: true });
iframe = document.body.querySelector('iframe');
redirectSpy = jest.fn();
embed.addEventListener('redirect', redirectSpy);
embed.loadFlow(clientLabel, flowLabel);
redirectUrl = "https://www.some-other-origin.com/some-other-page";
msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: DEFAULT_FLOW_ORIGIN,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
},
});
mockPostMessage(msg);
expect(redirectSpy).toBeCalledTimes(1);
expect(redirectSpy).toBeCalledWith({ url: redirectUrl });
expect(window.location.assign).toBeCalledTimes(1);
expect(window.location.assign).toBeCalledWith(redirectUrl);
expect(pushStateSpy).toBeCalledTimes(0);
return [2 /*return*/];
mockPostMessage(resizeMsg);
const eventMsg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: 'https://example.com',
data: {
type: constants_1.WebEmbedMessage.EMBED_EVENT_MSG,
createdAt: new Date(),
eventType: constants_1.AnalyticsEventType.FlowLoaded,
},
});
}); });
mockPostMessage(eventMsg);
const unknownMsg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: 'https://example.com',
data: {
type: 'some unknown type',
},
});
mockPostMessage(unknownMsg);
}));
test('handles redirecting to a using history API when redirecting in the same origin', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body, { useHistoryAPI: true });
const iframe = document.body.querySelector('iframe');
const redirectSpy = jest.fn();
embed.addEventListener('redirect', redirectSpy);
embed.loadFlow(clientLabel, flowLabel);
const redirectUrl = `${EMBEDDING_WINDOW_ORIGIN}/some-other-page-in-the-parent-origin`;
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
},
});
mockPostMessage(msg);
expect(redirectSpy).toBeCalledTimes(1);
expect(redirectSpy).toBeCalledWith({ url: redirectUrl });
expect(pushStateSpy).toBeCalledTimes(1);
expect(pushStateSpy).toBeCalledWith({}, '', redirectUrl);
}));
test('ignores useHistoryAPI when redirecting to a different origin', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const embed = (0, _1.default)(document.body, { useHistoryAPI: true });
const iframe = document.body.querySelector('iframe');
const redirectSpy = jest.fn();
embed.addEventListener('redirect', redirectSpy);
embed.loadFlow(clientLabel, flowLabel);
const redirectUrl = `https://www.some-other-origin.com/some-other-page`;
const msg = new MessageEvent('message', {
source: iframe.contentWindow,
origin: `https://${clientLabel}.formsort.app`,
data: {
type: constants_1.WebEmbedMessage.EMBED_REDIRECT_MSG,
payload: redirectUrl,
},
});
mockPostMessage(msg);
expect(redirectSpy).toBeCalledTimes(1);
expect(redirectSpy).toBeCalledWith({ url: redirectUrl });
expect(window.location.assign).toBeCalledTimes(1);
expect(window.location.assign).toBeCalledWith(redirectUrl);
expect(pushStateSpy).toBeCalledTimes(0);
}));
});
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isIFrameUnauthorizedEventData = exports.isIFrameResizeEventData = exports.isIFrameRedirectEventData = exports.isIFrameTokenRequestEventData = exports.isIFrameAnalyticsEventData = exports.isIWebEmbedEventData = void 0;
var constants_1 = require("@formsort/constants");
const constants_1 = require("@formsort/constants");
function isRecord(val) {

@@ -16,5 +16,3 @@ return val !== null && typeof val === 'object';

*/
var isEnumMember = function (val, anEnum) {
return Object.values(anEnum).includes(val);
};
const isEnumMember = (val, anEnum) => Object.values(anEnum).includes(val);
function isIWebEmbedEventData(val) {

@@ -21,0 +19,0 @@ return (isRecord(val) &&

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var constants_1 = require("@formsort/constants");
var typeGuards_1 = require("./typeGuards");
describe('isIWebEmbedEventData', function () {
test.each([1, 'hello', null, undefined, {}, { type: 'hello' }])('%p is not IWebEmbedEventData', function (val) {
expect(typeGuards_1.isIWebEmbedEventData(val)).toEqual(false);
const constants_1 = require("@formsort/constants");
const typeGuards_1 = require("./typeGuards");
describe('isIWebEmbedEventData', () => {
test.each([1, 'hello', null, undefined, {}, { type: 'hello' }])('%p is not IWebEmbedEventData', (val) => {
expect((0, typeGuards_1.isIWebEmbedEventData)(val)).toEqual(false);
});
test.each(Object.values(constants_1.WebEmbedMessage).map(function (type) { return ({ type: type }); }))('%p is IWebEmbedEventData', function (val) {
expect(typeGuards_1.isIWebEmbedEventData(val)).toEqual(true);
test.each(Object.values(constants_1.WebEmbedMessage).map((type) => ({ type })))('%p is IWebEmbedEventData', (val) => {
expect((0, typeGuards_1.isIWebEmbedEventData)(val)).toEqual(true);
});
});

@@ -9,3 +9,3 @@ "use strict";

function removeFromArrayMap(arrayMap, key, val) {
var indexOfElem = arrayMap[key].findIndex(function (elem) { return elem === val; });
const indexOfElem = arrayMap[key].findIndex((elem) => elem === val);
if (indexOfElem !== -1) {

@@ -12,0 +12,0 @@ arrayMap[key].splice(indexOfElem, 1);

{
"name": "@formsort/web-embed-api",
"version": "2.2.0",
"version": "2.2.1",
"description": "Embed Formsort flows within other webpages",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"main": "lib/index.js",

@@ -10,9 +14,7 @@ "types": "lib/index.d.ts",

"coverage": "jest --coverage",
"build": "tsc",
"prepare": "npm run build",
"prepublishOnly": "npm run lint",
"preversion": "npm run lint",
"format": "eslint -c .eslintrc.js --ext .ts,.tsx src --fix",
"lint": "eslint -c .eslintrc.js --ext .ts,.tsx src",
"pretty-check": "prettier --check src"
"build": "tsc --project tsconfig.build.json",
"format": "eslint --ext .ts,.tsx src --fix",
"lint": "eslint --ext .ts,.tsx src",
"pack": "yarn pack",
"release": "craft prepare --publish"
},

@@ -31,3 +33,3 @@ "repository": {

],
"author": "Fil Zembowicz <fil@formsort.com>",
"author": "Formsort Engineering <engineering@formsort.com>",
"license": "MIT",

@@ -37,4 +39,4 @@ "devDependencies": {

"@types/jest": "^27.0.1",
"@typescript-eslint/eslint-plugin": "^4.11.0",
"eslint": "^7.16.0",
"@typescript-eslint/eslint-plugin": "^5.17.0",
"eslint": "^8.12.0",
"eslint-plugin-import": "^2.22.1",

@@ -47,7 +49,26 @@ "eslint-plugin-jsdoc": "^30.7.9",

"ts-jest": "^27.0.5",
"typescript": "^3.8.3"
"typescript": "^4.6.3"
},
"dependencies": {
"@formsort/constants": "^1.6.0"
},
"jest": {
"cacheDirectory": "./.jest-cache",
"roots": [
"<rootDir>/src"
],
"testMatch": [
"**/__tests__/**/*.+(ts|tsx|js)",
"**/?(*.)+(spec|test).+(ts|tsx|js)"
],
"testPathIgnorePatterns": [
"/node_modules/",
"/lib/",
"/dist/"
],
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
},
"testEnvironment": "jsdom"
}
}

@@ -12,2 +12,6 @@ # @formsort/web-embed-api

```shell
yarn add @formsort/web-embed-api
```
```shell
npm install @formsort/web-embed-api --save

@@ -14,0 +18,0 @@ ```

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc