diff --git a/gulpfile.js b/gulpfile.js
index b47ad562..29f5c1a1 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -135,7 +135,7 @@ gulp.task(
gulp.series('build:rollup', function () {
return gulp
.src('dist/pathfora.js')
- .pipe(replace('`{{apiurl}}`', 'https://c.lytics.io'))
+ .pipe(replace('`{{apiurl}}`', APIURL))
.pipe(replace('`{{cssurl}}`', CSSURL))
.pipe(replace('`{{templates}}`', prepareTemplates()))
.pipe(gulp.dest('dist'))
@@ -176,7 +176,7 @@ gulp.task(
return gulp
.src('dist/pathfora.js')
.pipe(replace('`{{apiurl}}`', TESTAPIURL))
- .pipe(replace('`{{cssurl}`}', TESTCSSURL))
+ .pipe(replace('`{{cssurl}}`', TESTCSSURL))
.pipe(replace('`{{templates}}`', prepareTemplates()))
.pipe(gulp.dest('dist'))
.pipe(connect.reload());
diff --git a/test/acceptance/ab-testing.spec.js b/test/acceptance/ab-testing.spec.js
index a540a7c4..d49e7d12 100644
--- a/test/acceptance/ab-testing.spec.js
+++ b/test/acceptance/ab-testing.spec.js
@@ -1,30 +1,28 @@
import globalReset from '../utils/global-reset';
+import { createMessageWidget } from '../utils/test-helpers';
-// -------------------------
-// A/B TESTING
-// -------------------------
-describe('when performing AB testing', function () {
+describe('a/b testing', function () {
beforeEach(function () {
globalReset();
});
it('should select only one A/B Test group to show', function () {
- var widgetA = new pathfora.Message({
+ var widgetA = createMessageWidget({
id: 'ab-widget1-a',
msg: 'A',
- layout: 'slideout'
+ layout: 'slideout',
});
- var widgetB = new pathfora.Message({
+ var widgetB = createMessageWidget({
id: 'ab-widget1-b',
msg: 'B',
- layout: 'slideout'
+ layout: 'slideout',
});
var ab = new pathfora.ABTest({
id: 'ab-1',
type: '50/50',
- groups: [[widgetA], [widgetB]]
+ groups: [[widgetA], [widgetB]],
});
pathfora.initializeABTesting([ab]);
@@ -35,34 +33,37 @@ describe('when performing AB testing', function () {
});
it('should show all widgets in an A/B test group', function () {
- var widget1A = new pathfora.Message({
+ var widget1A = createMessageWidget({
id: 'ab-widget2-1a',
msg: 'A',
- layout: 'slideout'
+ layout: 'slideout',
});
- var widget2A = new pathfora.Message({
+ var widget2A = createMessageWidget({
id: 'ab-widget2-2a',
msg: 'A',
- layout: 'slideout'
+ layout: 'slideout',
});
- var widget1B = new pathfora.Message({
+ var widget1B = createMessageWidget({
id: 'ab-widget2-1b',
msg: 'B',
- layout: 'slideout'
+ layout: 'slideout',
});
- var widget2B = new pathfora.Message({
+ var widget2B = createMessageWidget({
id: 'ab-widget2-2b',
msg: 'B',
- layout: 'slideout'
+ layout: 'slideout',
});
var ab = new pathfora.ABTest({
id: 'ab-2',
type: '50/50',
- groups: [[widget1A, widget2A], [widget1B, widget2B]]
+ groups: [
+ [widget1A, widget2A],
+ [widget1B, widget2B],
+ ],
});
pathfora.initializeABTesting([ab]);
@@ -73,10 +74,7 @@ describe('when performing AB testing', function () {
var first = w.first();
expect(first.find('.pf-widget-message').text()).toEqual(
- first
- .next()
- .find('.pf-widget-message')
- .text()
+ first.next().find('.pf-widget-message').text()
);
});
@@ -85,29 +83,29 @@ describe('when performing AB testing', function () {
pathfora.utils.write('PathforaTest_' + id, 0.2164252290967852);
- var widgetA = new pathfora.Message({
+ var widgetA = createMessageWidget({
id: 'ab-widget3-a',
msg: 'A',
- layout: 'modal'
+ layout: 'modal',
});
- var widgetB = new pathfora.Message({
+ var widgetB = createMessageWidget({
id: 'ab-widget3-b',
msg: 'B',
- layout: 'modal'
+ layout: 'modal',
});
var ab = new pathfora.ABTest({
id: id,
type: '50/50',
- groups: [[widgetA], [widgetB]]
+ groups: [[widgetA], [widgetB]],
});
pathfora.initializeABTesting([ab]);
pathfora.initializeWidgets([widgetA, widgetB]);
var wB = $('#' + widgetB.id),
- wA = $('#' + widgetA.id);
+ wA = $('#' + widgetA.id);
expect(wB.length).toBe(1);
expect(wA.length).toBe(0);
});
@@ -116,77 +114,77 @@ describe('when performing AB testing', function () {
var id = 'ab-4';
pathfora.utils.saveCookie('PathforaTest_' + id, 0.7077720651868731);
- var widgetA = new pathfora.Message({
+ var widgetA = createMessageWidget({
id: 'ab-widget4-a',
msg: 'A',
- layout: 'modal'
+ layout: 'modal',
});
- var widgetB = new pathfora.Message({
+ var widgetB = createMessageWidget({
id: 'ab-widget4-b',
msg: 'B',
- layout: 'modal'
+ layout: 'modal',
});
var ab = new pathfora.ABTest({
id: id,
type: '50/50',
- groups: [[widgetA], [widgetB]]
+ groups: [[widgetA], [widgetB]],
});
pathfora.initializeABTesting([ab]);
pathfora.initializeWidgets([widgetA, widgetB]);
var wB = $('#' + widgetB.id),
- wA = $('#' + widgetA.id);
+ wA = $('#' + widgetA.id);
expect(wA.length).toBe(1);
expect(wB.length).toBe(0);
});
it('should allow multiple A/B tests per page', function () {
- var widgetA = new pathfora.Message({
+ var widgetA = createMessageWidget({
id: 'ab-widget5-a',
msg: 'A',
- layout: 'modal'
+ layout: 'modal',
});
- var widgetB = new pathfora.Message({
+ var widgetB = createMessageWidget({
id: 'ab-widget5-b',
msg: 'B',
- layout: 'modal'
+ layout: 'modal',
});
var ab = new pathfora.ABTest({
id: 'ab-5',
type: '50/50',
- groups: [[widgetA], [widgetB]]
+ groups: [[widgetA], [widgetB]],
});
- var widgetC = new pathfora.Message({
+ var widgetC = createMessageWidget({
id: 'ab-widget6-c',
msg: 'C',
- layout: 'modal'
+ layout: 'modal',
});
- var widgetD = new pathfora.Message({
+ var widgetD = createMessageWidget({
id: 'ab-widget6-d',
msg: 'D',
- layout: 'modal'
+ layout: 'modal',
});
var ab2 = new pathfora.ABTest({
id: 'ab-6',
type: '50/50',
- groups: [[widgetC], [widgetD]]
+ groups: [[widgetC], [widgetD]],
});
pathfora.initializeABTesting([ab, ab2]);
pathfora.initializeWidgets([widgetA, widgetB, widgetC, widgetD]);
var w = $('[id*="ab-widget"]'),
- w5 = $('[id*="ab-widget5"]'),
- w6 = $('[id*="ab-widget6"]');
+ w5 = $('[id*="ab-widget5"]'),
+ w6 = $('[id*="ab-widget6"]');
expect(w.length).toBe(2);
expect(w5.length).toBe(1);
@@ -196,40 +194,40 @@ describe('when performing AB testing', function () {
it('should handle A/B Tests in conjunction with audience targeting', function () {
window.lio = {
data: {
- segments: ['all', 'smt_new']
+ segments: ['all', 'smt_new'],
},
account: {
- id: '0'
- }
+ id: '0',
+ },
};
window.lio.loaded = true;
- var widgetA = new pathfora.Message({
+ var widgetA = createMessageWidget({
id: 'ab-widget10-a',
layout: 'slideout',
- msg: 'A'
+ msg: 'A',
});
- var widgetB = new pathfora.Message({
+ var widgetB = createMessageWidget({
id: 'ab-widget10-b',
layout: 'slideout',
- msg: 'B'
+ msg: 'B',
});
var ab = new pathfora.ABTest({
id: 'ab-10',
type: '50/50',
- groups: [[widgetA], [widgetB]]
+ groups: [[widgetA], [widgetB]],
});
var widgets = {
target: [
{
segment: 'smt_new',
- widgets: [widgetA, widgetB]
- }
- ]
+ widgets: [widgetA, widgetB],
+ },
+ ],
};
pathfora.initializeABTesting([ab]);
@@ -243,16 +241,16 @@ describe('when performing AB testing', function () {
var id = 'ab-11';
pathfora.utils.saveCookie('PathforaTest_' + id, 0.7077720651868731);
- var widget = new pathfora.Message({
+ var widget = createMessageWidget({
id: 'ab-widget11-a',
msg: 'A',
- layout: 'slideout'
+ layout: 'slideout',
});
var ab = new pathfora.ABTest({
id: 'ab-11',
type: '80/20',
- groups: [[], [widget]]
+ groups: [[], [widget]],
});
pathfora.initializeABTesting([ab]);
@@ -263,22 +261,22 @@ describe('when performing AB testing', function () {
});
it('should not allow a widget to be used in more than one A/B test', function () {
- var widgetA = new pathfora.Message({
+ var widgetA = createMessageWidget({
id: 'ab-widget8-a',
msg: 'A',
- layout: 'slideout'
+ layout: 'slideout',
});
var ab = new pathfora.ABTest({
id: 'ab-7',
type: '50/50',
- groups: [[widgetA], []]
+ groups: [[widgetA], []],
});
var ab2 = new pathfora.ABTest({
id: 'ab-8',
type: '50/50',
- groups: [[widgetA], []]
+ groups: [[widgetA], []],
});
expect(function () {
@@ -291,28 +289,28 @@ describe('when performing AB testing', function () {
});
it('should not allow a widget to be used in more than one A/B test', function () {
- var widgetA = new pathfora.Message({
+ var widgetA = createMessageWidget({
id: 'ab-widget9-a',
msg: 'A',
- layout: 'slideout'
+ layout: 'slideout',
});
- var widgetB = new pathfora.Message({
+ var widgetB = createMessageWidget({
id: 'ab-widget9-b',
msg: 'B',
- layout: 'slideout'
+ layout: 'slideout',
});
var ab = new pathfora.ABTest({
id: 'ab-9',
type: '50/50',
- groups: [[widgetA], []]
+ groups: [[widgetA], []],
});
var ab2 = new pathfora.ABTest({
id: 'ab-9',
type: '50/50',
- groups: [[widgetB], []]
+ groups: [[widgetB], []],
});
expect(function () {
diff --git a/test/acceptance/callbacks.spec.js b/test/acceptance/callbacks.spec.js
new file mode 100644
index 00000000..b0d55bd1
--- /dev/null
+++ b/test/acceptance/callbacks.spec.js
@@ -0,0 +1,215 @@
+import globalReset from '../utils/global-reset';
+import { createMessageWidget, createFormWidget } from '../utils/test-helpers';
+
+describe('callbacks', function () {
+ beforeEach(function () {
+ globalReset();
+ });
+
+ it('should trigger after pressing action button', function () {
+ var modal = createMessageWidget({
+ id: 'confirm-action-test',
+ layout: 'modal',
+ msg: 'Confirm action test modal',
+ confirmAction: {
+ name: 'Test confirm action',
+ callback: function () {
+ alert('test confirmation');
+ },
+ },
+ });
+
+ pathfora.initializeWidgets([modal]);
+
+ var widget = $('#confirm-action-test');
+ spyOn(modal.confirmAction, 'callback');
+ expect(modal.confirmAction.callback).not.toHaveBeenCalled();
+ widget.find('.pf-widget-ok').click();
+ expect(modal.confirmAction.callback).toHaveBeenCalled();
+ });
+
+ it('should trigger after pressing action with form data.', function () {
+ var modal = createFormWidget({
+ id: 'confirm-action-form-test',
+ layout: 'modal',
+ msg: 'Confirm action test modal',
+ confirmAction: {
+ callback: function () {
+ alert('test confirmation');
+ },
+ },
+ });
+
+ pathfora.initializeWidgets([modal]);
+
+ var widget = $('#' + modal.id);
+ widget.find('input[name="username"]').val('test name');
+ widget.find('input[name="email"]').val('test@example.com');
+ spyOn(modal.confirmAction, 'callback');
+ expect(modal.confirmAction.callback).not.toHaveBeenCalled();
+ widget.find('.pf-widget-ok').click();
+ expect(modal.confirmAction.callback).toHaveBeenCalledWith(
+ 'modalConfirm',
+ jasmine.objectContaining({
+ data: [
+ { name: 'username', value: 'test name' },
+ { name: 'email', value: 'test@example.com' },
+ { name: 'title', value: '' },
+ { name: 'message', value: '' },
+ ],
+ })
+ );
+ });
+
+ it('should trigger after pressing action with custom form data.', function () {
+ var modal = createFormWidget({
+ id: 'custom-confirm-action-test',
+ layout: 'modal',
+ msg: 'Confirm action test modal',
+ formElements: [
+ {
+ type: 'text',
+ required: true,
+ label: 'Email Address',
+ name: 'email',
+ },
+ {
+ type: 'checkbox-group',
+ required: true,
+ label: 'Which feeds would you like to subscribe to?',
+ name: 'subscription_feeds',
+ values: [
+ {
+ label: 'Beauty & Perfumes',
+ value: 'beauty',
+ },
+ {
+ label: 'Electronics',
+ value: 'electronics',
+ },
+ {
+ label: 'Fashion',
+ value: 'fashion',
+ },
+ ],
+ },
+ ],
+ confirmAction: {
+ callback: function () {
+ alert('test confirmation');
+ },
+ },
+ });
+
+ pathfora.initializeWidgets([modal]);
+
+ var widget = $('#' + modal.id);
+ widget.find('input[name="email"]').val('test@example.com');
+ widget.find('input[name="subscription_feeds"]')[2].checked = true;
+ spyOn(modal.confirmAction, 'callback');
+ expect(modal.confirmAction.callback).not.toHaveBeenCalled();
+ widget.find('.pf-widget-ok').click();
+ expect(modal.confirmAction.callback).toHaveBeenCalledWith(
+ 'modalConfirm',
+ jasmine.objectContaining({
+ data: [
+ { name: 'email', value: 'test@example.com' },
+ { name: 'subscription_feeds', value: 'fashion' },
+ ],
+ })
+ );
+ });
+
+ it('should not close the modal on a button action if specified', function (done) {
+ var modal = createMessageWidget({
+ id: 'confirm-close-action-test',
+ layout: 'modal',
+ msg: 'Confirm action test modal',
+ confirmAction: {
+ name: 'Test confirm action',
+ close: false,
+ callback: function () {
+ // do something
+ },
+ },
+ cancelAction: {
+ close: false,
+ },
+ });
+
+ pathfora.initializeWidgets([modal]);
+
+ setTimeout(function () {
+ var widget = $('#' + modal.id);
+ expect(widget).toBeDefined();
+ expect(widget.hasClass('opened')).toBeTruthy();
+
+ setTimeout(function () {
+ widget.find('.pf-widget-ok').click();
+ widget.find('.pf-widget-cancel').click();
+ expect(widget).toBeDefined();
+ expect(widget.hasClass('opened')).toBeTruthy();
+ done();
+ }, 300);
+ }, 300);
+ });
+
+ it('should be able to trigger action on cancel', function () {
+ var modal = createMessageWidget({
+ id: 'cancel-action-test',
+ layout: 'modal',
+ msg: 'Welcome to our website',
+ cancelAction: {
+ name: 'Test cancel action',
+ callback: function () {
+ alert('test cancel');
+ },
+ },
+ });
+
+ pathfora.initializeWidgets([modal]);
+
+ var widget = $('#cancel-action-test');
+ spyOn(modal.cancelAction, 'callback');
+ widget.find('.pf-widget-cancel').click();
+ expect(modal.cancelAction.callback).toHaveBeenCalled();
+ });
+
+ it("shouldn't fire submit function on cancel, and cancel functions on submit", function () {
+ var w1 = createMessageWidget({
+ id: 'widget-with-action-callback',
+ msg: 'Cancel action negative test',
+ confirmAction: {
+ name: 'Test confirm action',
+ callback: function () {
+ alert('test confirmation');
+ },
+ },
+ });
+
+ var w2 = createMessageWidget({
+ id: 'widget-with-cancel-callback',
+ msg: 'Cancel action negative test',
+ cancelAction: {
+ name: 'Test cancel action',
+ callback: function () {
+ alert('test cancel');
+ },
+ },
+ });
+
+ pathfora.initializeWidgets([w1, w2]);
+
+ var widgetA = $('#widget-with-action-callback'),
+ widgetB = $('#widget-with-cancel-callback');
+
+ spyOn(w1.confirmAction, 'callback');
+ spyOn(w2.cancelAction, 'callback');
+
+ widgetA.find('.pf-widget-cancel').click();
+ expect(w1.confirmAction.callback).not.toHaveBeenCalled();
+
+ widgetB.find('.pf-widget-ok').click();
+ expect(w2.cancelAction.callback).not.toHaveBeenCalled();
+ });
+});
diff --git a/test/acceptance/content-recommendation.spec.js b/test/acceptance/content-recommendation.spec.js
index c2b41bb7..1409044d 100644
--- a/test/acceptance/content-recommendation.spec.js
+++ b/test/acceptance/content-recommendation.spec.js
@@ -1,8 +1,6 @@
import globalReset from '../utils/global-reset';
+import { createMessageWidget } from '../utils/test-helpers';
-// -------------------------
-// CONTENT RECOMMENDATIONS
-// -------------------------
describe('the content recommendation component', function () {
beforeEach(function () {
globalReset();
@@ -25,7 +23,7 @@ describe('the content recommendation component', function () {
pathfora.acctid = 321;
- var sampleModal = new pathfora.Message({
+ var sampleModal = createMessageWidget({
id: 'recommendation-modal-sample1',
msg: 'A',
layout: 'modal',
@@ -69,7 +67,7 @@ describe('the content recommendation component', function () {
window.liosetup.value = 'customValue';
pathfora.acctid = 321;
- var sampleModal = new pathfora.Message({
+ var sampleModal = createMessageWidget({
id: 'recommendation-modal-sample2',
msg: 'A',
layout: 'modal',
@@ -111,7 +109,7 @@ describe('the content recommendation component', function () {
pathfora.acctid = 321;
- var sampleModal = new pathfora.Message({
+ var sampleModal = createMessageWidget({
id: 'recommendation-modal-sample3',
msg: 'A',
layout: 'modal',
@@ -148,7 +146,7 @@ describe('the content recommendation component', function () {
loaded: true,
};
- var modal = new pathfora.Message({
+ var modal = createMessageWidget({
id: 'recommendation-modal',
msg: 'A',
layout: 'modal',
@@ -159,7 +157,7 @@ describe('the content recommendation component', function () {
},
});
- var defaultModal = new pathfora.Message({
+ var defaultModal = createMessageWidget({
id: 'recommendation-modal2',
msg: 'A',
layout: 'modal',
@@ -255,7 +253,7 @@ describe('the content recommendation component', function () {
loaded: true,
};
- var errorModal = new pathfora.Message({
+ var errorModal = createMessageWidget({
id: 'recommendation-modal4',
msg: 'A',
layout: 'modal',
@@ -266,7 +264,7 @@ describe('the content recommendation component', function () {
},
});
- var errorModal2 = new pathfora.Message({
+ var errorModal2 = createMessageWidget({
id: 'recommendation-modal5',
msg: 'A',
layout: 'button',
@@ -277,7 +275,7 @@ describe('the content recommendation component', function () {
},
});
- var errorModal3 = new pathfora.Message({
+ var errorModal3 = createMessageWidget({
id: 'recommendation-modal6',
msg: 'A',
layout: 'slideout',
@@ -347,7 +345,7 @@ describe('the content recommendation component', function () {
loaded: true,
};
- var astModal = new pathfora.Message({
+ var astModal = createMessageWidget({
id: 'ast-modal',
msg: 'A',
layout: 'modal',
@@ -391,7 +389,7 @@ describe('the content recommendation component', function () {
loaded: true,
};
- var relativeModal = new pathfora.Message({
+ var relativeModal = createMessageWidget({
id: 'relative-modal',
msg: 'A',
layout: 'modal',
@@ -434,7 +432,7 @@ describe('the content recommendation component', function () {
loaded: true,
};
- var displayModal = new pathfora.Message({
+ var displayModal = createMessageWidget({
id: 'recDisplayModal',
msg: 'A',
layout: 'modal',
@@ -476,7 +474,7 @@ describe('the content recommendation component', function () {
expect(info.html()).toBe('by Test Example | January 1, 2017');
expect(desc.html().length < 103).toBeTruthy();
- var displayModal2 = new pathfora.Message({
+ var displayModal2 = createMessageWidget({
id: 'recDisplayModal2',
msg: 'A',
layout: 'modal',
@@ -524,7 +522,7 @@ describe('the content recommendation component', function () {
loaded: true,
};
- var modal = new pathfora.Message({
+ var modal = createMessageWidget({
id: 'modal1',
layout: 'modal',
msg: 'test',
@@ -575,7 +573,7 @@ describe('the content recommendation component', function () {
loaded: true,
};
- var modal = new pathfora.Message({
+ var modal = createMessageWidget({
id: 'modal1',
layout: 'modal',
msg: 'test',
diff --git a/test/acceptance/cull-expired-localstorage-on-init.spec.js b/test/acceptance/cull-expired-localstorage-on-init.spec.js
deleted file mode 100644
index 1a7b5bcd..00000000
--- a/test/acceptance/cull-expired-localstorage-on-init.spec.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import globalReset from '../utils/global-reset';
-
-describe('Culling expired localStorage on init', function () {
- beforeEach(globalReset);
-
- it('should cull expired records from localStorage eagerly on init', function () {
- var Pathfora = pathfora.constructor;
-
- pathfora.utils.store.ttl('expired', 'bonk', -10000);
- pathfora.utils.store.ttl('current', 'bonk', 10000);
-
- expect(localStorage.getItem('expired')).not.toBe(null);
- expect(localStorage.getItem('current')).not.toBe(null);
-
- new Pathfora();
-
- expect(localStorage.getItem('expired')).toBe(null);
- expect(localStorage.getItem('current')).not.toBe(null);
- });
-});
diff --git a/test/acceptance/display-conditions-legacy.spec.js b/test/acceptance/display-conditions-legacy.spec.js
index ff4895bf..1ec29802 100644
--- a/test/acceptance/display-conditions-legacy.spec.js
+++ b/test/acceptance/display-conditions-legacy.spec.js
@@ -1,9 +1,7 @@
import globalReset from '../utils/global-reset';
+import { createFormWidget } from '../utils/test-helpers';
-// -------------------------
-// DISPLAY CONDITIONS LEGACY
-// -------------------------
-describe('when setting display conditions', function () {
+describe('when setting legacy display conditions', function () {
beforeEach(function () {
globalReset();
});
@@ -12,7 +10,7 @@ describe('when setting display conditions', function () {
var widgetId = 'legacyImpressionWidget1';
sessionStorage.setItem('PathforaImpressions_' + widgetId, 0);
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -21,9 +19,9 @@ describe('when setting display conditions', function () {
displayConditions: {
impressions: {
session: 1,
- total: 5
- }
- }
+ total: 5,
+ },
+ },
});
pathfora.initializeWidgets([form]);
@@ -36,7 +34,7 @@ describe('when setting display conditions', function () {
var widgetId = 'legacyImpressionWidget2';
sessionStorage.setItem('PathforaImpressions_' + widgetId, 2);
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -45,9 +43,9 @@ describe('when setting display conditions', function () {
displayConditions: {
impressions: {
session: 1,
- total: 5
- }
- }
+ total: 5,
+ },
+ },
});
pathfora.initializeWidgets([form]);
@@ -58,12 +56,9 @@ describe('when setting display conditions', function () {
it('should show if impression buffer met', function () {
var widgetId = 'legacyImpressionWidget3';
- pathfora.utils.write(
- 'PathforaImpressions_' + widgetId,
- '2|' + Date.now()
- );
+ pathfora.utils.write('PathforaImpressions_' + widgetId, '2|' + Date.now());
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -72,9 +67,9 @@ describe('when setting display conditions', function () {
displayConditions: {
impressions: {
session: 3,
- buffer: 2
- }
- }
+ buffer: 2,
+ },
+ },
});
setTimeout(function () {
@@ -87,12 +82,9 @@ describe('when setting display conditions', function () {
it('should not show if impression buffer not met', function () {
var widgetId = 'legacyImpressionWidget3';
- pathfora.utils.write(
- 'PathforaImpressions_' + widgetId,
- '2|' + Date.now()
- );
+ pathfora.utils.write('PathforaImpressions_' + widgetId, '2|' + Date.now());
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -101,9 +93,9 @@ describe('when setting display conditions', function () {
displayConditions: {
impressions: {
session: 3,
- buffer: 60
- }
- }
+ buffer: 60,
+ },
+ },
});
pathfora.initializeWidgets([form]);
@@ -115,12 +107,9 @@ describe('when setting display conditions', function () {
// NOTE Retain support for cookies with comma - can remove on 5/2/2016
it('should accept and parse impression cookies with comma values', function () {
var widgetId = 'impressionComma';
- pathfora.utils.write(
- 'PathforaImpressions_' + widgetId,
- '2,' + Date.now()
- );
+ pathfora.utils.write('PathforaImpressions_' + widgetId, '2,' + Date.now());
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -128,9 +117,9 @@ describe('when setting display conditions', function () {
position: 'bottom-right',
displayConditions: {
impressions: {
- total: 2
- }
- }
+ total: 2,
+ },
+ },
});
pathfora.initializeWidgets([form]);
@@ -141,46 +130,46 @@ describe('when setting display conditions', function () {
it('should consider multiple display conditions and watchers', function (done) {
var id = 'multiple-conditions',
- id2 = 'multiple-conditions-2',
- id3 = 'multiple-conditions-3';
+ id2 = 'multiple-conditions-2',
+ id3 = 'multiple-conditions-3';
- var form = new pathfora.Form({
+ var form = createFormWidget({
msg: 'subscription',
headline: 'Header',
layout: 'slideout',
id: id,
displayConditions: {
impressions: {
- session: 3
+ session: 3,
},
- manualTrigger: true
- }
+ manualTrigger: true,
+ },
});
- var form2 = new pathfora.Form({
+ var form2 = createFormWidget({
msg: 'subscription',
headline: 'Header',
layout: 'slideout',
id: id2,
displayConditions: {
impressions: {
- session: 1
+ session: 1,
},
- manualTrigger: true
- }
+ manualTrigger: true,
+ },
});
- var form3 = new pathfora.Form({
+ var form3 = createFormWidget({
msg: 'subscription',
headline: 'Header',
layout: 'slideout',
id: id3,
displayConditions: {
impressions: {
- session: 3
+ session: 3,
},
- manualTrigger: true
- }
+ manualTrigger: true,
+ },
});
sessionStorage.setItem('PathforaImpressions_' + id, 2);
sessionStorage.setItem('PathforaImpressions_' + id2, 2);
@@ -211,7 +200,7 @@ describe('when setting display conditions', function () {
pathfora.utils.write('PathforaCancel_' + widgetId, '1,' + Date.now());
pathfora.utils.write('PathforaClosed_' + widgetId, '1,' + Date.now());
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -221,16 +210,16 @@ describe('when setting display conditions', function () {
hideAfterAction: {
confirm: {
hideCount: 3,
- duration: 1440
+ duration: 1440,
},
cancel: {
- hideCount: 1
+ hideCount: 1,
},
closed: {
- duration: 30
- }
- }
- }
+ duration: 30,
+ },
+ },
+ },
});
pathfora.initializeWidgets([form]);
@@ -242,12 +231,9 @@ describe('when setting display conditions', function () {
// NOTE Retain support for cookies with comma - can remove on 5/2/2016
it('should accept and parse impression cookies with comma values', function () {
var widgetId = 'impressionComma';
- pathfora.utils.write(
- 'PathforaImpressions_' + widgetId,
- '2,' + Date.now()
- );
+ pathfora.utils.write('PathforaImpressions_' + widgetId, '2,' + Date.now());
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -256,10 +242,10 @@ describe('when setting display conditions', function () {
displayConditions: {
impressions: {
widget: {
- total: 2
- }
- }
- }
+ total: 2,
+ },
+ },
+ },
});
pathfora.initializeWidgets([form]);
diff --git a/test/acceptance/display-conditions.spec.js b/test/acceptance/display-conditions.spec.js
index a3855d08..29809fac 100644
--- a/test/acceptance/display-conditions.spec.js
+++ b/test/acceptance/display-conditions.spec.js
@@ -1,8 +1,6 @@
import globalReset from '../utils/global-reset';
+import { createMessageWidget, createFormWidget } from '../utils/test-helpers';
-// -------------------------
-// DISPLAY CONDITIONS
-// -------------------------
function makeMouseEvent(type, params) {
var evt;
try {
@@ -45,7 +43,7 @@ describe('when setting display conditions', function () {
var height = $(document.body).height();
window.scroll(0, height);
- var subscription = new pathfora.Message({
+ var subscription = createMessageWidget({
layout: 'modal',
id: 'scrollModal',
headline: 'Heyyyy!',
@@ -76,7 +74,7 @@ describe('when setting display conditions', function () {
"
Test
"
);
- var subscription = new pathfora.Message({
+ var subscription = createMessageWidget({
layout: 'modal',
id: 'scrollModal',
headline: 'Heyyyy!',
@@ -105,7 +103,7 @@ describe('when setting display conditions', function () {
});
it('should show when all manualTrigger widgets are triggered', function () {
- var customWidget = new pathfora.Message({
+ var customWidget = createMessageWidget({
msg: 'custom trigger test',
id: 'custom-widget',
layout: 'modal',
@@ -114,7 +112,7 @@ describe('when setting display conditions', function () {
},
});
- var customWidget2 = new pathfora.Message({
+ var customWidget2 = createMessageWidget({
msg: 'custom trigger test2',
id: 'custom-widget2',
layout: 'modal',
@@ -141,7 +139,7 @@ describe('when setting display conditions', function () {
});
it('should show all manualTrigger widgets on initialization if they have already been triggered', function () {
- var customWidget3 = new pathfora.Message({
+ var customWidget3 = createMessageWidget({
msg: 'custom trigger test3',
id: 'custom-widget3',
layout: 'modal',
@@ -155,7 +153,7 @@ describe('when setting display conditions', function () {
var widget = $('#' + customWidget3.id);
expect(widget.length).toBe(1);
- var customWidget4 = new pathfora.Message({
+ var customWidget4 = createMessageWidget({
msg: 'custom trigger test4',
id: 'custom-widget4',
layout: 'modal',
@@ -173,7 +171,7 @@ describe('when setting display conditions', function () {
it('should be able to show after specified time', function () {
jasmine.clock().install();
- var delayedWidget = new pathfora.Message({
+ var delayedWidget = createMessageWidget({
msg: 'Delayed widget test',
id: 'delayed-widget',
layout: 'modal',
@@ -196,7 +194,7 @@ describe('when setting display conditions', function () {
it('should not show when page views requirement has not been reached', function () {
pathfora.utils.saveCookie('PathforaPageView', 0);
- var form = new pathfora.Form({
+ var form = createFormWidget({
msg: 'subscription',
id: 'page-view-widget-1',
headline: 'Header',
@@ -214,7 +212,7 @@ describe('when setting display conditions', function () {
});
it('should show when page views requirement has been reached', function () {
- var form = new pathfora.Form({
+ var form = createFormWidget({
msg: 'subscription',
headline: 'Header',
id: 'page-view-widget-2',
@@ -237,7 +235,7 @@ describe('when setting display conditions', function () {
limitDate.setMonth(1);
limitDate.setFullYear(2016);
- var form = new pathfora.Form({
+ var form = createFormWidget({
msg: 'subscription',
headline: 'Header',
id: 'date-widget-1',
@@ -262,7 +260,7 @@ describe('when setting display conditions', function () {
limitDate.setMonth(1);
limitDate.setFullYear(2016);
- var form = new pathfora.Form({
+ var form = createFormWidget({
msg: 'subscription',
headline: 'Header',
layout: 'slideout',
@@ -285,7 +283,7 @@ describe('when setting display conditions', function () {
var widgetId = 'hideAfterActionWidget1';
pathfora.utils.saveCookie('PathforaClosed_' + widgetId, '1|' + Date.now());
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -310,7 +308,7 @@ describe('when setting display conditions', function () {
var widgetId = 'hideAfterActionWidget2';
pathfora.utils.saveCookie('PathforaConfirm_' + widgetId, '1|' + Date.now());
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -338,7 +336,7 @@ describe('when setting display conditions', function () {
var widgetId = 'hideAfterActionWidget3';
pathfora.utils.saveCookie('PathforaCancel_' + widgetId, '2|' + Date.now());
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -362,7 +360,7 @@ describe('when setting display conditions', function () {
var widgetId = 'hideAfterActionWidget4';
pathfora.utils.saveCookie('PathforaConfirm_' + widgetId, '2|' + Date.now());
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -390,7 +388,7 @@ describe('when setting display conditions', function () {
var widgetId = 'impressionWidget1';
sessionStorage.setItem('PathforaImpressions_' + widgetId, 0);
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -419,7 +417,7 @@ describe('when setting display conditions', function () {
'2|' + Date.now()
);
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -450,7 +448,7 @@ describe('when setting display conditions', function () {
'2|' + Date.now()
);
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -478,7 +476,7 @@ describe('when setting display conditions', function () {
var widgetId = 'impressionWidget2';
sessionStorage.setItem('PathforaImpressions_' + widgetId, 2);
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -509,7 +507,7 @@ describe('when setting display conditions', function () {
sessionStorage.setItem('PathforaImpressions_AnotherWidget', 0);
sessionStorage.setItem('PathforaImpressions_' + widgetId, 0);
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -536,7 +534,7 @@ describe('when setting display conditions', function () {
sessionStorage.setItem('PathforaImpressions_b' + widgetId, 2);
sessionStorage.setItem('PathforaImpressions_c' + widgetId, 2);
- var form = new pathfora.Form({
+ var form = createFormWidget({
id: widgetId,
msg: 'subscription',
headline: 'Header',
@@ -565,7 +563,7 @@ describe('when setting display conditions', function () {
m2;
beforeEach(function () {
- m1 = new pathfora.Form({
+ m1 = createFormWidget({
id: m1id,
msg: 'modal 1',
layout: 'slideout',
@@ -579,7 +577,7 @@ describe('when setting display conditions', function () {
},
});
- m2 = new pathfora.Form({
+ m2 = createFormWidget({
id: m2id,
msg: 'modal 2',
layout: 'slideout',
@@ -630,7 +628,7 @@ describe('when setting display conditions', function () {
m2;
beforeEach(function () {
- m1 = new pathfora.Form({
+ m1 = createFormWidget({
id: m1id,
msg: 'modal 1',
layout: 'slideout',
@@ -645,7 +643,7 @@ describe('when setting display conditions', function () {
},
});
- m2 = new pathfora.Form({
+ m2 = createFormWidget({
id: m2id,
msg: 'modal 2',
layout: 'slideout',
@@ -733,7 +731,7 @@ describe('when setting display conditions', function () {
});
it('should show when the url matches the display conditions', function () {
- var form = new pathfora.Form({
+ var form = createFormWidget({
msg: 'subscription',
headline: 'Header',
layout: 'slideout',
@@ -744,7 +742,7 @@ describe('when setting display conditions', function () {
},
});
- var form2 = new pathfora.Form({
+ var form2 = createFormWidget({
msg: 'subscription',
headline: 'Header',
layout: 'slideout',
@@ -764,7 +762,7 @@ describe('when setting display conditions', function () {
});
it("should not show when the url doesn't match the display conditions", function () {
- var form = new pathfora.Form({
+ var form = createFormWidget({
msg: 'subscription',
headline: 'Header',
layout: 'slideout',
@@ -782,7 +780,7 @@ describe('when setting display conditions', function () {
});
it('should show respect excluded matching rule', function () {
- var form = new pathfora.Form({
+ var form = createFormWidget({
msg: 'subscription',
headline: 'Header',
layout: 'slideout',
@@ -819,7 +817,7 @@ describe('when setting display conditions', function () {
});
it('should show using simple match', function () {
- var form1 = new pathfora.Form({
+ var form1 = createFormWidget({
id: '88ee86cf72b44e67bf758cc743ac1a5d',
msg: 'subscription',
headline: 'Header',
@@ -835,7 +833,7 @@ describe('when setting display conditions', function () {
},
});
- var form2 = new pathfora.Form({
+ var form2 = createFormWidget({
id: 'a793b7352c3346e493573a6827be7815',
msg: 'subscription',
headline: 'Header',
@@ -861,7 +859,7 @@ describe('when setting display conditions', function () {
});
it('should show using exact match', function () {
- var form1 = new pathfora.Form({
+ var form1 = createFormWidget({
id: 'e71c5416ac7345bcba8c5330d14c4a2e',
msg: 'subscription',
headline: 'Header',
@@ -877,7 +875,7 @@ describe('when setting display conditions', function () {
},
});
- var form2 = new pathfora.Form({
+ var form2 = createFormWidget({
id: '3ef7653e7f5f4889a0f2f860a679639a',
msg: 'subscription',
headline: 'Header',
@@ -902,7 +900,7 @@ describe('when setting display conditions', function () {
});
it('should show using string match', function () {
- var form1 = new pathfora.Form({
+ var form1 = createFormWidget({
id: '3044aae3e5ad463fbd868a626a7998ca',
msg: 'subscription',
headline: 'Header',
@@ -918,7 +916,7 @@ describe('when setting display conditions', function () {
},
});
- var form2 = new pathfora.Form({
+ var form2 = createFormWidget({
id: 'd66ec2855d284cb2b6ce3edd3c756a1b',
msg: 'subscription',
headline: 'Header',
@@ -934,7 +932,7 @@ describe('when setting display conditions', function () {
},
});
- var form3 = new pathfora.Form({
+ var form3 = createFormWidget({
id: 'f3ededaa19fd4301b066b4da5758e16a',
msg: 'subscription',
headline: 'Header',
@@ -963,7 +961,7 @@ describe('when setting display conditions', function () {
});
it('should show using regex match', function () {
- var form1 = new pathfora.Form({
+ var form1 = createFormWidget({
id: '87a84e6f0d5d480595eebaf5de76693f',
msg: 'subscription',
headline: 'Header',
@@ -978,7 +976,7 @@ describe('when setting display conditions', function () {
],
},
});
- var form2 = new pathfora.Form({
+ var form2 = createFormWidget({
id: '3ecbf9717fef4f7c80b2bbc70193ab64',
msg: 'subscription',
headline: 'Header',
@@ -993,7 +991,7 @@ describe('when setting display conditions', function () {
],
},
});
- var form3 = new pathfora.Form({
+ var form3 = createFormWidget({
id: 'e9890969538c49d4ba9c7f516215fa61',
msg: 'subscription',
headline: 'Header',
@@ -1008,7 +1006,7 @@ describe('when setting display conditions', function () {
],
},
});
- var form4 = new pathfora.Form({
+ var form4 = createFormWidget({
id: 'ad547747786249ae8ba9e1cc3f5b86cf',
msg: 'subscription',
headline: 'Header',
@@ -1041,7 +1039,7 @@ describe('when setting display conditions', function () {
it('should ignore trailing slashes for the exact match rule', function () {
window.history.pushState({}, '', '/test/');
- var form1 = new pathfora.Form({
+ var form1 = createFormWidget({
id: 'e71c5416ac7345bcba8c5330d14c4a2e',
msg: 'subscription',
headline: 'Header',
@@ -1056,7 +1054,7 @@ describe('when setting display conditions', function () {
],
},
});
- var form2 = new pathfora.Form({
+ var form2 = createFormWidget({
id: '3ef7653e7f5f4889a0f2f860a679639a',
msg: 'subscription',
headline: 'Header',
@@ -1085,7 +1083,7 @@ describe('when setting display conditions', function () {
it('should ignore trailing slashes in the simple match rule', function () {
window.history.pushState({}, '', '/test/');
- var form1 = new pathfora.Form({
+ var form1 = createFormWidget({
id: 'simple-match1',
msg: 'subscription',
headline: 'Header',
@@ -1101,7 +1099,7 @@ describe('when setting display conditions', function () {
},
});
- var form2 = new pathfora.Form({
+ var form2 = createFormWidget({
id: 'simple-match2',
msg: 'subscription',
headline: 'Header',
@@ -1131,7 +1129,7 @@ describe('when setting display conditions', function () {
it('should ignore order of query params for exact rule', function () {
window.history.pushState({}, '', '/context.html?bar=2&foo=1');
- var form1 = new pathfora.Form({
+ var form1 = createFormWidget({
id: 'f41a595548c54321a4e12b613c466159',
msg: 'subscription',
headline: 'Header',
@@ -1147,7 +1145,7 @@ describe('when setting display conditions', function () {
},
});
- var form2 = new pathfora.Form({
+ var form2 = createFormWidget({
id: 'ef2848a4949d4474b3a5d12ba1017eb7',
msg: 'subscription',
headline: 'Header',
@@ -1177,7 +1175,7 @@ describe('when setting display conditions', function () {
it('should not ignore "?" if there are no queries', function () {
window.history.pushState({}, '', '/context.html');
- var queryTest1 = new pathfora.Form({
+ var queryTest1 = createFormWidget({
id: 'query-test1',
headline: 'Header',
layout: 'slideout',
@@ -1195,7 +1193,7 @@ describe('when setting display conditions', function () {
},
});
- var queryTest2 = new pathfora.Form({
+ var queryTest2 = createFormWidget({
id: 'query-test2',
headline: 'Header',
layout: 'slideout',
@@ -1229,7 +1227,7 @@ describe('when setting display conditions', function () {
'/context.html?bar=2&foo=1&lytics_variation_preview_id=7b26ca56afb84669bba0bf0810ec459f'
);
- var form1 = new pathfora.Form({
+ var form1 = createFormWidget({
id: '7b26ca56afb84669bba0bf0810ec459f',
msg: 'subscription',
headline: 'Header',
@@ -1256,7 +1254,7 @@ describe('when setting display conditions', function () {
it('should ignore order of query params and extra params for string rule', function () {
window.history.pushState({}, '', '/context.html?bar=2&foo=1&baz=3');
- var form1 = new pathfora.Form({
+ var form1 = createFormWidget({
id: '339f97d11af84630add78cfd39da1105',
msg: 'subscription',
headline: 'Header',
@@ -1272,7 +1270,7 @@ describe('when setting display conditions', function () {
},
});
- var form2 = new pathfora.Form({
+ var form2 = createFormWidget({
id: 'f8cc3cdf8a1c4532a1ebbc1e7af453b1',
msg: 'subscription',
headline: 'Header',
@@ -1283,7 +1281,7 @@ describe('when setting display conditions', function () {
},
});
- var form3 = new pathfora.Form({
+ var form3 = createFormWidget({
id: '6372bf4e1acc45d695b45a8656dd19ec',
msg: 'subscription',
headline: 'Header',
@@ -1294,7 +1292,7 @@ describe('when setting display conditions', function () {
},
});
- var form4 = new pathfora.Form({
+ var form4 = createFormWidget({
id: '9c353546a52843f9868ca1b3a1012f6e',
msg: 'subscription',
headline: 'Header',
@@ -1328,7 +1326,7 @@ describe('when setting display conditions', function () {
});
it('should consider multiple display conditions', function () {
- var form = new pathfora.Form({
+ var form = createFormWidget({
msg: 'subscription',
headline: 'Header',
id: 'display-widget-1',
@@ -1339,7 +1337,7 @@ describe('when setting display conditions', function () {
},
});
- var form2 = new pathfora.Form({
+ var form2 = createFormWidget({
msg: 'subscription',
headline: 'Header',
id: 'display-widget-2',
@@ -1350,7 +1348,7 @@ describe('when setting display conditions', function () {
},
});
- var form3 = new pathfora.Form({
+ var form3 = createFormWidget({
msg: 'subscription',
headline: 'Header',
id: 'display-widget-3',
@@ -1373,7 +1371,7 @@ describe('when setting display conditions', function () {
id2 = 'multiple-conditions-2',
id3 = 'multiple-conditions-3';
- var form = new pathfora.Form({
+ var form = createFormWidget({
msg: 'subscription',
headline: 'Header',
layout: 'slideout',
@@ -1388,7 +1386,7 @@ describe('when setting display conditions', function () {
},
});
- var form2 = new pathfora.Form({
+ var form2 = createFormWidget({
msg: 'subscription',
headline: 'Header',
layout: 'slideout',
@@ -1403,7 +1401,7 @@ describe('when setting display conditions', function () {
},
});
- var form3 = new pathfora.Form({
+ var form3 = createFormWidget({
msg: 'subscription',
headline: 'Header',
layout: 'slideout',
@@ -1460,7 +1458,7 @@ describe('when setting display conditions', function () {
describe('by itself', function () {
beforeEach(function () {
- subscription = new pathfora.Message({
+ subscription = createMessageWidget({
layout: 'modal',
id: id,
headline: "Don't leave yet!",
@@ -1535,7 +1533,7 @@ describe('when setting display conditions', function () {
window.scroll(0, 0);
- subscription = new pathfora.Message({
+ subscription = createMessageWidget({
layout: 'modal',
id: id,
headline: "Don't leave yet!",
@@ -1591,7 +1589,7 @@ describe('when setting display conditions', function () {
var modal;
beforeEach(function () {
- modal = new pathfora.Message({
+ modal = createMessageWidget({
layout: 'modal',
id: id,
headline: 'This will show...',
diff --git a/test/acceptance/entity-template.spec.js b/test/acceptance/entity-template.spec.js
index 55b5cf13..5a44ed91 100644
--- a/test/acceptance/entity-template.spec.js
+++ b/test/acceptance/entity-template.spec.js
@@ -1,8 +1,6 @@
import globalReset from '../utils/global-reset';
+import { createMessageWidget, createFormWidget } from '../utils/test-helpers';
-// -------------------------
-// ENTITY FIELD TEMPLATES
-// -------------------------
describe('the entity templates', function () {
beforeEach(function () {
globalReset();
@@ -12,82 +10,82 @@ describe('the entity templates', function () {
window.lio = {
data: {
promoCode: '123FREE',
- email: 'fake@gmail.com'
+ email: 'fake@gmail.com',
},
account: {
- id: '0'
- }
+ id: '0',
+ },
};
window.lio.loaded = true;
pathfora.customData = {
- customField: 'test'
+ customField: 'test',
};
- var fieldWidget1 = new pathfora.Message({
+ var fieldWidget1 = createMessageWidget({
id: 'field-widget-1',
layout: 'slideout',
headline: 'Free shipping on your next purchase',
- msg: 'Enter this promo code: {{promoCode}}'
+ msg: 'Enter this promo code: {{promoCode}}',
});
- var fieldWidget2 = new pathfora.Form({
+ var fieldWidget2 = createFormWidget({
id: 'field-widget-2',
layout: 'slideout',
headline: '{{email | no email provided}}',
- msg: 'Sign up with your email.'
+ msg: 'Sign up with your email.',
});
- var fieldWidget3 = new pathfora.Form({
+ var fieldWidget3 = createFormWidget({
id: 'field-widget-3',
layout: 'slideout',
headline: 'Hi {{name}}',
- msg: 'Sign up with your email.'
+ msg: 'Sign up with your email.',
});
- var fieldWidget4 = new pathfora.Form({
+ var fieldWidget4 = createFormWidget({
id: 'field-widget-4',
layout: 'slideout',
headline: 'Hi {{name}}',
msg: 'Sign up with your email.',
displayConditions: {
- showOnMissingFields: true
- }
+ showOnMissingFields: true,
+ },
});
- var fieldWidget5 = new pathfora.Form({
+ var fieldWidget5 = createFormWidget({
id: 'field-widget-5',
layout: 'slideout',
headline: 'Welcome',
- msg: 'Welcome {{name | No Name}}!'
+ msg: 'Welcome {{name | No Name}}!',
});
- var fieldWidget6 = new pathfora.Form({
+ var fieldWidget6 = createFormWidget({
id: 'field-widget-6',
layout: 'slideout',
headline: 'Welcome',
- msg: 'Welcome {{myUrl | https://www.google.com/}}!'
+ msg: 'Welcome {{myUrl | https://www.google.com/}}!',
});
- var fieldWidget7 = new pathfora.Form({
+ var fieldWidget7 = createFormWidget({
id: 'field-widget-7',
layout: 'slideout',
headline: 'Welcome',
- msg: 'Welcome {{customField | fail}}!'
+ msg: 'Welcome {{customField | fail}}!',
});
- var fieldWidget8 = new pathfora.Form({
+ var fieldWidget8 = createFormWidget({
id: 'fieldWidget8',
layout: 'slideout',
headline: 'Welcome',
msg: 'my {{data | string}} will get {{data2}} defaulted wrong',
displayConditions: {
- showOnMissingFields: true
- }
+ showOnMissingFields: true,
+ },
});
- var fieldWidget9 = new pathfora.Message({
+ var fieldWidget9 = createMessageWidget({
id: 'fieldWidget9',
layout: 'slideout',
headline: 'Welcome',
@@ -97,8 +95,8 @@ describe('the entity templates', function () {
callback: function () {
window.customvar =
'https://www.google.com/?promo={{promoCode}}&email={{email}}';
- }
- }
+ },
+ },
});
pathfora.initializeWidgets([
@@ -110,7 +108,7 @@ describe('the entity templates', function () {
fieldWidget6,
fieldWidget7,
fieldWidget8,
- fieldWidget9
+ fieldWidget9,
]);
var w1 = $('#' + fieldWidget1.id);
@@ -170,30 +168,30 @@ describe('the entity templates', function () {
data: {
userName: 'John',
userEmail: 'john@example.com',
- promoCode: 'SAVE20'
+ promoCode: 'SAVE20',
},
account: {
- id: '0'
- }
+ id: '0',
+ },
};
window.lio.loaded = true;
- var templateWidget1 = new pathfora.Message({
+ var templateWidget1 = createMessageWidget({
id: 'template-dependency-01',
layout: 'modal',
headline: 'Hello {{userName}}!',
- msg: 'Welcome back!'
+ msg: 'Welcome back!',
});
- var templateWidget2 = new pathfora.Form({
+ var templateWidget2 = createFormWidget({
id: 'template-dependency-02',
layout: 'slideout',
headline: 'Special offer for {{userEmail}}',
- msg: 'Use code {{promoCode}} for savings!'
+ msg: 'Use code {{promoCode}} for savings!',
});
- var templateWidget3 = new pathfora.Message({
+ var templateWidget3 = createMessageWidget({
id: 'template-dependency-03',
layout: 'modal',
headline: 'Hi there!',
@@ -201,15 +199,16 @@ describe('the entity templates', function () {
confirmAction: {
name: 'confirm',
callback: function () {
- window.trackingUrl = 'https://analytics.com/track?user={{userName}}&promo={{promoCode}}';
- }
- }
+ window.trackingUrl =
+ 'https://analytics.com/track?user={{userName}}&promo={{promoCode}}';
+ },
+ },
});
pathfora.initializeWidgets([
templateWidget1,
templateWidget2,
- templateWidget3
+ templateWidget3,
]);
var dependencies = pathfora.getWidgetDependencies();
@@ -223,7 +222,9 @@ describe('the entity templates', function () {
expect(dependencies[templateWidget2.id].entityField).toContain('promoCode');
expect(dependencies[templateWidget3.id]).toBeDefined();
- expect(dependencies[templateWidget3.id].entityField).toContain('contactEmail');
+ expect(dependencies[templateWidget3.id].entityField).toContain(
+ 'contactEmail'
+ );
expect(dependencies[templateWidget3.id].entityField).toContain('userName');
expect(dependencies[templateWidget3.id].entityField).toContain('promoCode');
});
diff --git a/test/acceptance/forms.spec.js b/test/acceptance/forms.spec.js
new file mode 100644
index 00000000..7d6d2136
--- /dev/null
+++ b/test/acceptance/forms.spec.js
@@ -0,0 +1,962 @@
+import globalReset from '../utils/global-reset';
+import {
+ createMessageWidget,
+ createFormWidget,
+ createSubscriptionWidget,
+} from '../utils/test-helpers';
+
+describe('forms', function () {
+ beforeEach(function () {
+ globalReset();
+ });
+
+ it('should show success or error state if waitForAsyncResponse is set', function (done) {
+ var formStatesWidget = createFormWidget({
+ id: 'form-states',
+ msg: 'subscription',
+ headline: 'Header',
+ layout: 'slideout',
+ confirmAction: {
+ waitForAsyncResponse: true,
+ callback: function (name, payload, cb) {
+ if (payload.data[0].value === 'test') {
+ cb(true);
+ return;
+ }
+ cb(false);
+ },
+ },
+ formStates: {
+ success: {
+ headline: 'test',
+ msg: 'a custom success message',
+ delay: 0,
+ okShow: true,
+ okMessage: 'confirm success',
+ confirmAction: {
+ name: 'confirm success',
+ callback: function () {
+ alert('confirm success');
+ },
+ },
+ cancelShow: true,
+ cancelMessage: 'cancel success',
+ cancelAction: {
+ name: 'cancel success',
+ callback: function () {
+ alert('cancel success');
+ },
+ },
+ },
+ error: {
+ headline: 'test',
+ msg: 'a custom error message',
+ delay: 0,
+ okShow: true,
+ okMessage: 'confirm error',
+ confirmAction: {
+ name: 'confirm error',
+ callback: function () {
+ alert('confirm error');
+ },
+ },
+ cancelShow: true,
+ cancelMessage: 'cancel error',
+ cancelAction: {
+ name: 'cancel error',
+ callback: function () {
+ alert('cancel error');
+ },
+ },
+ },
+ },
+ });
+ window.pathfora.initializeWidgets([formStatesWidget]);
+
+ var widget = $('#' + formStatesWidget.id),
+ form = widget.find('form');
+ expect(form.length).toBe(1);
+
+ var name = form.find('input[name="username"]');
+ expect(name.length).toBe(1);
+ name.val('test');
+
+ var email = form.find('input[name="email"]');
+ expect(email.length).toBe(1);
+ email.val('test@example.com');
+
+ spyOn(formStatesWidget.confirmAction, 'callback').and.callThrough();
+ expect(formStatesWidget.confirmAction.callback).not.toHaveBeenCalled();
+
+ form.find('.pf-widget-ok').click();
+
+ expect(formStatesWidget.confirmAction.callback).toHaveBeenCalledWith(
+ 'modalConfirm',
+ jasmine.any(Object),
+ jasmine.any(Function)
+ );
+
+ var success = widget.find('.success-state'),
+ error = widget.find('.error-state');
+
+ expect(form.css('display')).toBe('none');
+ expect(success.css('display')).toBe('block');
+ expect(widget.hasClass('success')).toBeTruthy();
+ expect(success.find('.pf-widget-headline').html()).toBe(
+ formStatesWidget.formStates.success.headline
+ );
+ expect(success.find('.pf-widget-message').html()).toBe(
+ formStatesWidget.formStates.success.msg
+ );
+
+ expect(success.find('.pf-widget-ok').html()).toBe(
+ formStatesWidget.formStates.success.okMessage
+ );
+ expect(success.find('.pf-widget-cancel').html()).toBe(
+ formStatesWidget.formStates.success.cancelMessage
+ );
+
+ spyOn(jstag, 'send');
+ spyOn(formStatesWidget.formStates.success.confirmAction, 'callback');
+ expect(
+ formStatesWidget.formStates.success.confirmAction.callback
+ ).not.toHaveBeenCalled();
+ success.find('.pf-widget-ok').click();
+
+ expect(
+ formStatesWidget.formStates.success.confirmAction.callback
+ ).toHaveBeenCalled();
+ expect(jstag.send).toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ 'pf-widget-id': formStatesWidget.id,
+ 'pf-widget-type': 'form',
+ 'pf-widget-layout': 'slideout',
+ 'pf-widget-event': 'success.confirm',
+ 'pf-widget-action':
+ formStatesWidget.formStates.success.confirmAction.name,
+ })
+ );
+ pathfora.clearAll();
+ pathfora.closeWidget(formStatesWidget.id, true);
+
+ setTimeout(function () {
+ window.pathfora.initializeWidgets([formStatesWidget]);
+
+ widget = $('#' + formStatesWidget.id);
+ form = widget.find('form');
+ expect(form.length).toBe(1);
+
+ name = form.find('input[name="username"]');
+ expect(name.length).toBe(1);
+ name.val('bad');
+
+ email = form.find('input[name="email"]');
+ expect(email.length).toBe(1);
+ email.val('bad@example.com');
+ form.find('.pf-widget-ok').click();
+
+ success = widget.find('.success-state');
+ expect(success.length).toBe(1);
+ error = widget.find('.error-state');
+ expect(error.length).toBe(1);
+ expect(form.css('display')).toBe('none');
+ expect(success.css('display')).toBe('none');
+ expect(error.css('display')).toBe('block');
+ expect(widget.hasClass('error')).toBeTruthy();
+ expect(error.find('.pf-widget-headline').html()).toBe(
+ formStatesWidget.formStates.error.headline
+ );
+ expect(error.find('.pf-widget-message').html()).toBe(
+ formStatesWidget.formStates.error.msg
+ );
+ expect(error.find('.pf-widget-ok').html()).toBe(
+ formStatesWidget.formStates.error.okMessage
+ );
+ expect(error.find('.pf-widget-cancel').html()).toBe(
+ formStatesWidget.formStates.error.cancelMessage
+ );
+
+ spyOn(formStatesWidget.formStates.error.cancelAction, 'callback');
+ expect(
+ formStatesWidget.formStates.error.cancelAction.callback
+ ).not.toHaveBeenCalled();
+ error.find('.pf-widget-cancel').click();
+
+ expect(
+ formStatesWidget.formStates.error.cancelAction.callback
+ ).toHaveBeenCalled();
+ expect(jstag.send).toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ 'pf-widget-id': formStatesWidget.id,
+ 'pf-widget-type': 'form',
+ 'pf-widget-layout': 'slideout',
+ 'pf-widget-event': 'error.cancel',
+ 'pf-widget-action':
+ formStatesWidget.formStates.error.cancelAction.name,
+ })
+ );
+ done();
+ }, 600);
+ });
+
+ // -------------------------
+ // LEGACY SUCCESS STATES
+ // -------------------------
+
+ it('should show success state if one is set by the user', function (done) {
+ var successForm = createSubscriptionWidget({
+ id: 'success-form',
+ msg: 'subscription',
+ headline: 'Header',
+ layout: 'slideout',
+ success: {
+ msg: 'a custom success message',
+ delay: 2,
+ },
+ });
+
+ pathfora.initializeWidgets([successForm]);
+
+ var widget = $('#' + successForm.id);
+ var form = widget.find('form');
+ expect(form.length).toBe(1);
+
+ var email = form.find('input[name="email"]');
+ expect(email.length).toBe(1);
+ email.val('test@example.com');
+ form.find('.pf-widget-ok').click();
+
+ var success = widget.find('.success-state');
+
+ expect(form.css('display')).toBe('none');
+ expect(success.css('display')).toBe('block');
+ expect(widget.hasClass('success')).toBeTruthy();
+ expect(success.find('.pf-widget-message').html()).toBe(
+ successForm.success.msg
+ );
+ expect(success.find('.pf-widget-ok')).toBeUndefined;
+ expect(success.find('.pf-widget-cancel')).toBeUndefined;
+
+ setTimeout(function () {
+ expect(widget.hasClass('opened')).toBeFalsy();
+ done();
+ }, 2000);
+ });
+
+ it('should not hide the module if the success state delay is 0', function (done) {
+ var successForm2 = createSubscriptionWidget({
+ id: 'success-form-no-delay',
+ msg: 'subscription',
+ headline: 'Header',
+ layout: 'slideout',
+ success: {
+ msg: 'a custom success message',
+ delay: 0,
+ },
+ });
+
+ pathfora.initializeWidgets([successForm2]);
+
+ var widget = $('#' + successForm2.id);
+ var form = widget.find('form');
+ expect(form.length).toBe(1);
+
+ var email = form.find('input[name="email"]');
+ expect(email.length).toBe(1);
+ email.val('test@example.com');
+ form.find('.pf-widget-ok').click();
+
+ var success = widget.find('.success-state');
+
+ expect(form.css('display')).toBe('none');
+ expect(success.css('display')).toBe('block');
+ expect(widget.hasClass('success')).toBeTruthy();
+ expect(success.find('.pf-widget-message').html()).toBe(
+ successForm2.success.msg
+ );
+ expect(success.find('.pf-widget-ok')).toBeUndefined;
+ expect(success.find('.pf-widget-cancel')).toBeUndefined;
+
+ setTimeout(function () {
+ expect(widget.hasClass('opened')).toBeTruthy();
+ expect(widget.hasClass('success')).toBeTruthy();
+
+ done();
+ }, 3000);
+ });
+
+ it('should recognize success state buttons and callbacks', function (done) {
+ var successForm3 = createSubscriptionWidget({
+ id: 'success-form-cbs',
+ msg: 'subscription',
+ headline: 'Header',
+ layout: 'slideout',
+ success: {
+ headline: 'test',
+ msg: 'a custom success message',
+ okShow: true,
+ cancelShow: true,
+ cancelMessage: 'Custom Cancel',
+ confirmAction: {
+ name: 'test success confirmation',
+ callback: function () {
+ window.alert('confirmed');
+ },
+ },
+ cancelAction: {
+ name: 'test success cancelation',
+ callback: function () {
+ window.alert('canceled');
+ },
+ },
+ delay: 0,
+ },
+ });
+
+ pathfora.initializeWidgets([successForm3]);
+ var widget = $('#' + successForm3.id);
+ var form = widget.find('form');
+ form.find('input[name="email"]').val('test@example.com');
+ form.find('.pf-widget-ok').click();
+
+ var success = widget.find('.success-state');
+ expect(form.css('display')).toBe('none');
+ expect(success.css('display')).toBe('block');
+ expect(widget.hasClass('success')).toBeTruthy();
+ expect(success.find('.pf-widget-headline').html()).toBe(
+ successForm3.success.headline
+ );
+ expect(success.find('.pf-widget-message').html()).toBe(
+ successForm3.success.msg
+ );
+
+ expect(success.find('.pf-widget-ok').html()).toBe('Confirm');
+ expect(success.find('.pf-widget-cancel').html()).toBe('Custom Cancel');
+
+ spyOn(jstag, 'send');
+ spyOn(window, 'alert');
+ success.find('.pf-widget-ok').click();
+
+ expect(jstag.send).toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ 'pf-widget-id': successForm3.id,
+ 'pf-widget-type': 'subscription',
+ 'pf-widget-layout': 'slideout',
+ 'pf-widget-event': 'success.confirm',
+ 'pf-widget-action': successForm3.success.confirmAction.name,
+ })
+ );
+ expect(window.alert).toHaveBeenCalledWith('confirmed');
+
+ setTimeout(function () {
+ pathfora.clearAll();
+ pathfora.initializeWidgets([successForm3]);
+
+ widget = $('#' + successForm3.id);
+ form = widget.find('form');
+ form.find('input[name="email"]').val('test@example.com');
+ form.find('.pf-widget-ok').click();
+
+ success = widget.find('.success-state');
+ success.find('.pf-widget-cancel').click();
+
+ expect(jstag.send).toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ 'pf-widget-id': successForm3.id,
+ 'pf-widget-type': 'subscription',
+ 'pf-widget-layout': 'slideout',
+ 'pf-widget-event': 'success.cancel',
+ 'pf-widget-action': successForm3.success.cancelAction.name,
+ })
+ );
+ expect(window.alert).toHaveBeenCalledWith('canceled');
+
+ setTimeout(function () {
+ done();
+ }, 1000);
+ }, 1000);
+ });
+
+ // -------------------------
+ // CUSTOM BUTTONS
+ // -------------------------
+
+ it('should be able to configure custom text', function () {
+ var modal = createMessageWidget({
+ id: 'custom-button-text-test',
+ layout: 'modal',
+ msg: 'Custom button text test',
+ headline: 'Hello',
+ okMessage: 'Confirm Here',
+ cancelMessage: 'Cancel Here',
+ });
+
+ pathfora.initializeWidgets([modal]);
+
+ var widget = $('#' + modal.id),
+ actionBtn = widget.find('.pf-widget-ok'),
+ cancelBtn = widget.find('.pf-widget-cancel');
+
+ expect(actionBtn.html()).toBe('Confirm Here');
+ expect(cancelBtn.html()).toBe('Cancel Here');
+ });
+
+ // -------------------------
+ // OLD CUSTOM FIELDS
+ // -------------------------
+
+ it('should be able to hide and show fields based on config', function () {
+ var formfields = createFormWidget({
+ id: 'sample-form',
+ msg: 'subscription',
+ headline: 'Header',
+ layout: 'slideout',
+ fields: {
+ title: false,
+ username: false,
+ },
+ required: {
+ message: true,
+ email: false,
+ },
+ });
+
+ pathfora.initializeWidgets([formfields]);
+
+ var theform = $('#' + formfields.id).find('form');
+ expect(theform.length).toBe(1);
+
+ for (var elem in theform[0].children) {
+ if (typeof theform[0].children[elem].getAttribute !== 'undefined') {
+ var inputname = theform[0].children[elem].getAttribute('name'),
+ inputrequired =
+ theform[0].children[elem].getAttribute('data-required');
+
+ if (inputname === 'message') {
+ expect(inputrequired).toBe('true');
+ } else if (inputname !== null) {
+ expect(inputrequired).toBe(null);
+ }
+
+ expect(inputname).not.toBe('username');
+ expect(inputname).not.toBe('title');
+ }
+ }
+ });
+
+ // -------------------------
+ // FORM BUILDER
+ // -------------------------
+
+ it('should track custom fields to lytics', function (done) {
+ var customForm = createFormWidget({
+ id: 'custom-form-1',
+ msg: 'custom form',
+ layout: 'slideout',
+ formElements: [
+ {
+ type: 'input',
+ name: 'name',
+ placeholder: 'Your Name',
+ required: true,
+ },
+ {
+ type: 'checkbox-group',
+ name: 'terms_agreement',
+ required: true,
+ values: [
+ {
+ label: 'I agree',
+ value: 'agree',
+ },
+ ],
+ },
+ ],
+ });
+
+ pathfora.initializeWidgets([customForm]);
+
+ var widget = $('#' + customForm.id);
+ widget.find('[name=terms_agreement]').click();
+ widget.find('[name=name]').val('my name here');
+ spyOn(jstag, 'send');
+
+ widget.find('form').find('.pf-widget-ok').click();
+
+ expect(jstag.send).toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ 'pf-widget-id': customForm.id,
+ 'pf-widget-type': 'form',
+ 'pf-widget-layout': 'slideout',
+ 'pf-widget-event': 'submit',
+ 'pf-custom-form': {
+ terms_agreement: ['agree'],
+ name: 'my name here',
+ },
+ })
+ );
+ done();
+ });
+
+ it('should add labels and placeholders for custom fields if defined', function () {
+ var customForm = createFormWidget({
+ id: 'custom-form-2',
+ msg: 'custom form',
+ layout: 'slideout',
+ formElements: [
+ {
+ type: 'select',
+ label: "What's your favorite animal?",
+ placeholder: 'Select an animal...',
+ name: 'favorite_animal',
+ required: true,
+ values: [
+ {
+ label: 'Cat',
+ value: 'cat',
+ },
+ {
+ label: 'Dog',
+ value: 'dog',
+ },
+ {
+ label: 'Horse',
+ value: 'horse',
+ },
+ ],
+ },
+ {
+ type: 'checkbox-group',
+ label: 'Which ice cream flavors do you like the most?',
+ name: 'ice_cream_flavors',
+ required: true,
+ values: [
+ {
+ label: 'Vanilla',
+ value: 'vanilla',
+ },
+ {
+ label: 'Chocolate',
+ value: 'chocolate',
+ },
+ {
+ label: 'Strawberry',
+ value: 'strawberry',
+ },
+ ],
+ },
+ {
+ type: 'textarea',
+ label: 'Comments',
+ name: 'comments',
+ placeholder: 'Any more comments?',
+ required: true,
+ },
+ ],
+ });
+
+ pathfora.initializeWidgets([customForm]);
+
+ var widget = $('#' + customForm.id);
+ var labels = widget.find('.pf-form-label');
+ var divs = widget.find('.pf-has-label');
+
+ expect(labels.length).toBe(customForm.formElements.length);
+ expect(divs.length).toBe(customForm.formElements.length);
+
+ var i;
+
+ for (i = 0; i < labels.length; i++) {
+ expect(
+ labels[i].innerHTML.indexOf(customForm.formElements[i].label) !== -1
+ ).toBeTruthy();
+ }
+
+ for (i = 0; i < divs.length; i++) {
+ var field = divs[i];
+ var configElem = customForm.formElements[i];
+ if (field.placeholder && configElem.placeholder) {
+ expect(field.placeholder).toBe(configElem.placeholder);
+ }
+
+ if (configElem.type === 'select') {
+ expect(field.children[0].innerHTML).toBe(configElem.placeholder);
+ }
+ }
+ });
+
+ it('should not submit the form if required fields are not filled out', function (done) {
+ var customForm = new pathfora.Form({
+ id: 'custom-form-3',
+ msg: 'custom form',
+ layout: 'slideout',
+ formElements: [
+ {
+ type: 'input',
+ placeholder: "What's your favorite animal?",
+ name: 'favorite_animal',
+ required: true,
+ },
+ {
+ type: 'radio-group',
+ label: 'Which ice cream flavors do you like the most?',
+ name: 'ice_cream_flavors',
+ required: true,
+ values: [
+ {
+ label: 'Vanilla',
+ value: 'vanilla',
+ },
+ {
+ label: 'Chocolate',
+ value: 'chocolate',
+ },
+ {
+ label: 'Strawberry',
+ value: 'strawberry',
+ },
+ ],
+ },
+ ],
+ });
+
+ pathfora.initializeWidgets([customForm]);
+
+ var widget = $('#' + customForm.id);
+ spyOn(jstag, 'send');
+
+ setTimeout(function () {
+ widget.find('form').find('.pf-widget-ok').click();
+ expect(jstag.send).not.toHaveBeenCalled();
+ expect(widget.hasClass('opened')).toBeTruthy();
+
+ var required = widget.find('[data-required=true]');
+ expect(required.length).toBe(customForm.formElements.length);
+
+ for (var i = 0; i < required.length; i++) {
+ var req = required[i].parentNode;
+ expect(req.className.indexOf('pf-form-required') !== -1).toBeTruthy();
+ expect(req.className.indexOf('invalid') !== -1).toBeTruthy();
+ }
+ done();
+ }, 200);
+ });
+
+ it('should not submit the form if fields are invalid', function (done) {
+ var customForm = createFormWidget({
+ id: 'custom-form-3',
+ msg: 'custom form',
+ layout: 'slideout',
+ formElements: [
+ {
+ type: 'email',
+ placeholder: 'Email',
+ name: 'email',
+ required: true,
+ },
+ {
+ type: 'radio-group',
+ label: 'Which ice cream flavors do you like the most?',
+ name: 'ice_cream_flavors',
+ values: [
+ {
+ label: 'Vanilla',
+ value: 'vanilla',
+ },
+ {
+ label: 'Chocolate',
+ value: 'chocolate',
+ },
+ {
+ label: 'Strawberry',
+ value: 'strawberry',
+ },
+ ],
+ },
+ ],
+ });
+
+ pathfora.initializeWidgets([customForm]);
+
+ var widget = $('#' + customForm.id);
+ spyOn(jstag, 'send');
+
+ setTimeout(function () {
+ widget.find('input[name=email]').val('zkjhfkdjh');
+ widget.find('form').find('.pf-widget-ok').click();
+ expect(jstag.send).not.toHaveBeenCalled();
+ expect(widget.hasClass('opened')).toBeTruthy();
+
+ var invalid = widget.find('[data-validate=true]');
+ expect(invalid.length).toBe(1);
+
+ for (var i = 0; i < invalid.length; i++) {
+ var req = invalid[i].parentNode;
+ expect(req.className.indexOf('pf-form-required') !== -1).toBeTruthy();
+ expect(req.className.indexOf('bad-validation') !== -1).toBeTruthy();
+ }
+
+ // also check required validation
+ widget.find('input[name=email]').val('');
+ widget.find('form').find('.pf-widget-ok').click();
+ expect(jstag.send).not.toHaveBeenCalled();
+ expect(widget.hasClass('opened')).toBeTruthy();
+
+ invalid = widget.find('[data-required=true]');
+ expect(invalid.length).toBe(1);
+
+ for (var j = 0; j < invalid.length; j++) {
+ var reqField = invalid[j].parentNode;
+ expect(
+ reqField.className.indexOf('pf-form-required') !== -1
+ ).toBeTruthy();
+ expect(reqField.className.indexOf('invalid') !== -1).toBeTruthy();
+ }
+ done();
+ }, 200);
+ });
+
+ it('should not submit the form if a date field is invalid', function (done) {
+ var customForm = createFormWidget({
+ id: 'custom-form-3',
+ msg: 'custom form',
+ layout: 'slideout',
+ formElements: [
+ {
+ type: 'date',
+ name: 'birthday',
+ maxDate: 'today',
+ minDate: '01-01-2020',
+ },
+ {
+ type: 'radio-group',
+ label: 'Which ice cream flavors do you like the most?',
+ name: 'ice_cream_flavors',
+ values: [
+ {
+ label: 'Vanilla',
+ value: 'vanilla',
+ },
+ {
+ label: 'Chocolate',
+ value: 'chocolate',
+ },
+ {
+ label: 'Strawberry',
+ value: 'strawberry',
+ },
+ ],
+ },
+ ],
+ });
+
+ pathfora.initializeWidgets([customForm]);
+
+ var widget = $('#' + customForm.id);
+ spyOn(jstag, 'send');
+
+ setTimeout(function () {
+ widget.find('input[name=birthday]').val('2010-10-10');
+ widget.find('form').find('.pf-widget-ok').click();
+ expect(jstag.send).not.toHaveBeenCalled();
+ expect(widget.hasClass('opened')).toBeTruthy();
+
+ var invalid = widget.find('[data-validate=true]');
+ expect(invalid.length).toBe(1);
+
+ for (var i = 0; i < invalid.length; i++) {
+ var req = invalid[i].parentNode;
+ expect(req.className.indexOf('pf-form-required') !== -1).toBeTruthy();
+ expect(req.className.indexOf('bad-validation') !== -1).toBeTruthy();
+ }
+ done();
+ }, 200);
+ });
+
+ // -------------------------
+ // CUSTOM FORM VALIDATION
+ // -------------------------
+
+ it('should not submit the form if custom validation fails', function (done) {
+ var customForm = createFormWidget({
+ id: 'custom-form-4',
+ msg: 'custom form',
+ layout: 'slideout',
+ formElements: [
+ {
+ type: 'text',
+ placeholder: 'Only 5 Digits Allowed',
+ name: 'postal_code',
+ pattern: '^[0-9]{5}$',
+ required: true,
+ },
+ ],
+ });
+
+ pathfora.initializeWidgets([customForm]);
+
+ var widget = $('#' + customForm.id);
+ spyOn(jstag, 'send');
+
+ setTimeout(function () {
+ var form = widget.find('form');
+ var field = form.find('input[name="postal_code"]');
+
+ field.val('notvalid');
+ form.find('.pf-widget-ok').trigger('click');
+ expect(jstag.send).not.toHaveBeenCalled();
+ expect(widget.hasClass('opened')).toBeTruthy();
+
+ var required = widget.find('[data-required=true]');
+ expect(required.length).toBe(customForm.formElements.length);
+
+ for (var i = 0; i < required.length; i++) {
+ var req = required[i].parentNode;
+ expect(req.className.indexOf('invalid') !== -1).toBeFalsy();
+ }
+
+ done();
+ }, 500);
+ });
+
+ it('should not submit the form if only 1 of 2 fields pass validation', function (done) {
+ var customForm = createFormWidget({
+ id: 'custom-form-4',
+ msg: 'custom form',
+ layout: 'slideout',
+ formElements: [
+ {
+ type: 'text',
+ placeholder: '6 Digits zbzbzb',
+ name: 'custom_field_1',
+ pattern: '^[zb]{6}$',
+ },
+ {
+ type: 'text',
+ placeholder: 'Only 5 Digits Allowed',
+ name: 'custom_field_2',
+ pattern: '^[0-9]{5}$',
+ },
+ ],
+ });
+
+ pathfora.initializeWidgets([customForm]);
+
+ var widget = $('#' + customForm.id);
+ spyOn(jstag, 'send');
+
+ setTimeout(function () {
+ var form = widget.find('form');
+
+ var field1 = form.find('input[name="custom_field_1"]');
+ field1.val('notvalid');
+
+ var field2 = form.find('input[name="custom_field_2"]');
+ field2.val('12345');
+
+ form.find('.pf-widget-ok').trigger('click');
+
+ expect(jstag.send).not.toHaveBeenCalled();
+ expect(widget.hasClass('opened')).toBeTruthy();
+
+ var validationRequirement = widget.find('[data-validate=true]');
+ expect(validationRequirement.length).toBe(customForm.formElements.length);
+
+ // expect field1 to be invalid
+ var req = validationRequirement[0].parentNode;
+ expect(req.className.indexOf('bad-validation') !== -1).toBeTruthy();
+
+ // expect field2 to be valid
+ req = validationRequirement[1].parentNode;
+ expect(req.className.indexOf('bad-validation') !== -1).toBeFalsy();
+
+ done();
+ }, 500);
+ });
+
+ it('should submit the form if custom validation passes', function (done) {
+ var customForm = createFormWidget({
+ id: 'custom-form-5',
+ msg: 'custom form',
+ layout: 'slideout',
+ formElements: [
+ {
+ type: 'text',
+ placeholder: 'Only 5 Digits Allowed',
+ name: 'postal_code',
+ pattern: '^[0-9]{5}$',
+ required: true,
+ },
+ ],
+ });
+
+ pathfora.initializeWidgets([customForm]);
+
+ var widget = $('#' + customForm.id);
+ spyOn(jstag, 'send');
+
+ setTimeout(function () {
+ var form = widget.find('form');
+ var field = form.find('input[name="postal_code"]');
+
+ field.val('12345');
+ form.find('.pf-widget-ok').trigger('click');
+ expect(jstag.send).toHaveBeenCalled();
+ expect(widget.hasClass('opened')).toBeFalsy();
+
+ var required = widget.find('[data-required=true]');
+ expect(required.length).toBe(customForm.formElements.length);
+
+ for (var i = 0; i < required.length; i++) {
+ var req = required[i].parentNode;
+ expect(req.className.indexOf('invalid') !== -1).toBeFalsy();
+ }
+
+ done();
+ }, 200);
+ });
+
+ it('should add validation parameters if special case of us-postal-code', function (done) {
+ var customForm = createFormWidget({
+ id: 'custom-form-6',
+ msg: 'custom form',
+ layout: 'slideout',
+ formElements: [
+ {
+ type: 'us-postal-code',
+ placeholder: 'Only 5 Digits Allowed',
+ name: 'postal_code',
+ required: true,
+ },
+ ],
+ });
+
+ pathfora.initializeWidgets([customForm]);
+
+ var widget = $('#' + customForm.id);
+ spyOn(jstag, 'send');
+
+ setTimeout(function () {
+ var form = widget.find('form');
+ var field = form.find('input[name="postal_code"]');
+
+ var pattern = field.attr('enforcePattern');
+ expect(pattern).toBe('^[0-9]{5}$');
+
+ field.val('1234a');
+ form.find('.pf-widget-ok').trigger('click');
+ expect(jstag.send).not.toHaveBeenCalled();
+ expect(widget.hasClass('opened')).toBeTruthy();
+
+ var required = widget.find('[data-required=true]');
+ expect(required.length).toBe(customForm.formElements.length);
+
+ for (var i = 0; i < required.length; i++) {
+ var req = required[i].parentNode;
+ expect(req.className.indexOf('invalid') !== -1).toBeFalsy();
+ }
+
+ done();
+ }, 500);
+ });
+});
diff --git a/test/acceptance/gate.spec.js b/test/acceptance/gate.spec.js
index 4d225c1c..d85f5100 100644
--- a/test/acceptance/gate.spec.js
+++ b/test/acceptance/gate.spec.js
@@ -1,66 +1,62 @@
import globalReset from '../utils/global-reset';
+import {
+ createSiteGateWidget,
+ expectWidgetVisible,
+ expectWidgetClosed,
+} from '../utils/test-helpers';
-// -------------------------
-// GATE
-// -------------------------
describe('the gate component', function () {
beforeEach(function () {
globalReset();
});
it('should open gate when the record is not set', function (done) {
- var gate = new pathfora.SiteGate({
+ var gate = createSiteGateWidget({
headline: 'Blocking Widget',
id: 'sitegate-widget-1',
- msg: 'Submit this widget to access the website.'
+ msg: 'Submit this widget to access the website.',
});
pathfora.initializeWidgets([gate]);
- var widget = $('#' + gate.id);
-
setTimeout(function () {
- expect(widget.hasClass('opened')).toBeTruthy();
+ expectWidgetVisible(gate.id);
done();
}, 200);
});
it('should should work with showForm: false', function (done) {
- var gate = new pathfora.SiteGate({
+ var gate = createSiteGateWidget({
id: 'gate-hide-form',
headline: 'Gated Site Feature',
msg: 'Please agree to the terms to proceed.',
showForm: false,
- okMessage: 'I Agree'
+ okMessage: 'I Agree',
});
expect(() => {
pathfora.initializeWidgets([gate]);
}).not.toThrow();
- var widget = $('#' + gate.id);
-
setTimeout(function () {
- expect(widget.hasClass('opened')).toBeTruthy();
+ expectWidgetVisible(gate.id);
done();
}, 200);
});
it('should not gate when the record is already set', function (done) {
- var gate = new pathfora.SiteGate({
+ var gate = createSiteGateWidget({
headline: 'Blocking Widget',
id: 'sitegate-widget-2',
- msg: 'Submit this widget to access the website.'
+ msg: 'Submit this widget to access the website.',
});
pathfora.utils.write('PathforaUnlocked_' + gate.id, true);
pathfora.initializeWidgets([gate]);
- var widget = $('#' + gate.id);
-
setTimeout(function () {
- expect(widget.hasClass('opened')).toBeFalsy();
+ expectWidgetClosed(gate.id);
done();
}, 200);
});
diff --git a/test/acceptance/personalization.spec.js b/test/acceptance/inline-personalization.spec.js
similarity index 97%
rename from test/acceptance/personalization.spec.js
rename to test/acceptance/inline-personalization.spec.js
index abe293c3..a277cf02 100644
--- a/test/acceptance/personalization.spec.js
+++ b/test/acceptance/inline-personalization.spec.js
@@ -1,16 +1,11 @@
import globalReset from '../utils/global-reset';
+import { createMessageWidget } from '../utils/test-helpers';
-// -------------------------
-// INLINE PERSONALIZATION TEST
-// -------------------------
-describe('Inline Personalization', function () {
+describe('inline personalization', function () {
beforeEach(function () {
globalReset();
});
- // -------------------------
- // TRIGGER ELEMENTS
- // -------------------------
describe('pftrigger elements', function () {
it('should select to show the first matching element per group', function (done) {
window.lio = {
@@ -108,13 +103,13 @@ describe('Inline Personalization', function () {
'Has Email
'
);
- var testModule = new pathfora.Message({
+ var testModule = createMessageWidget({
id: '9ec53f71a1514339bb1552280ae76682',
layout: 'slideout',
msg: 'show this to people with an email',
});
- var testModule2 = new pathfora.Message({
+ var testModule2 = createMessageWidget({
id: 'ba6a6df43f774d769058950969b07a16',
layout: 'slideout',
msg: 'show this to people without an email',
@@ -153,9 +148,6 @@ describe('Inline Personalization', function () {
});
});
- // -------------------------
- // RECOMMENDATION ELEMENTS
- // -------------------------
describe('pfrecommend elements', function () {
beforeEach(function () {
pathfora.acctid = 123;
diff --git a/test/acceptance/pathfora.spec.js b/test/acceptance/pathfora.spec.js
index cdb61598..bb1f7539 100644
--- a/test/acceptance/pathfora.spec.js
+++ b/test/acceptance/pathfora.spec.js
@@ -1,19 +1,17 @@
import createAndDispatchKeydown from '../utils/create-and-dispatch-keydown.js';
import globalReset from '../utils/global-reset';
+import {
+ createMessageWidget,
+ createSubscriptionWidget,
+} from '../utils/test-helpers';
-('use strict');
-
-// -------------------------
-// PATHFORA TESTS
-// -------------------------
-
-describe('Pathfora', function () {
+describe('pathfora', function () {
beforeEach(function () {
globalReset();
});
it('should keep focus during a tab cycle in a modal or site gate', function (done) {
- var modal = new pathfora.Message({
+ var modal = createMessageWidget({
msg: 'msg',
id: 'tab-cycle-test',
layout: 'modal',
@@ -45,7 +43,7 @@ describe('Pathfora', function () {
describe('clearAll', function () {
it('should clear delayed widgets', function () {
jasmine.clock().install();
- var delayedWidget = new pathfora.Message({
+ var delayedWidget = createMessageWidget({
msg: 'Delayed clear test',
id: 'delayed-widget-clear',
layout: 'modal',
@@ -54,7 +52,7 @@ describe('Pathfora', function () {
},
});
- var delayedWidget2 = new pathfora.Message({
+ var delayedWidget2 = createMessageWidget({
msg: 'Delayed clear test',
id: 'delayed-widget-clear2',
layout: 'modal',
@@ -63,7 +61,7 @@ describe('Pathfora', function () {
},
});
- var delayedWidget3 = new pathfora.Message({
+ var delayedWidget3 = createMessageWidget({
msg: 'Delayed clear test',
id: 'delayed-widget-clear3',
layout: 'modal',
@@ -96,7 +94,7 @@ describe('Pathfora', function () {
var addEventListenerSpy = spyOn(window, 'addEventListener');
var removeEventListenerSpy = spyOn(window, 'removeEventListener');
- var scrollWidget = new pathfora.Subscription({
+ var scrollWidget = createSubscriptionWidget({
msg: 'Fake scroll widget',
id: 'fake-scroll-widget',
displayConditions: {
@@ -123,7 +121,7 @@ describe('Pathfora', function () {
var addEventListenerSpy = spyOn(document, 'addEventListener');
var removeEventListenerSpy = spyOn(document, 'removeEventListener');
- var exitIntentWidget = new pathfora.Subscription({
+ var exitIntentWidget = createSubscriptionWidget({
msg: 'Fake exit-intent widget',
id: 'fake-exit-intent-widget',
displayConditions: {
diff --git a/test/acceptance/prioritized-widgets.spec.js b/test/acceptance/prioritized-widgets.spec.js
index ea7240c6..0cf1de60 100644
--- a/test/acceptance/prioritized-widgets.spec.js
+++ b/test/acceptance/prioritized-widgets.spec.js
@@ -1,19 +1,19 @@
import globalReset from '../utils/global-reset';
+import { createMessageWidget } from '../utils/test-helpers';
-// Prioritized Widget Tests
-describe('Prioritized widgets', function () {
+describe('prioritized widgets', function () {
beforeEach(function () {
globalReset();
});
describe('of type "ordered"', function () {
it('should only show the first valid widget', function (done) {
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
id: 'messageBar1',
layout: 'bar',
msg: 'Welcome to our website',
});
- var modal = new pathfora.Message({
+ var modal = createMessageWidget({
id: 'modal2',
layout: 'modal',
});
@@ -36,7 +36,7 @@ describe('Prioritized widgets', function () {
it('should account for display conditions when determining priority', function (done) {
pathfora.utils.saveCookie('PathforaPageView', 2);
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
id: 'messageBar1',
layout: 'bar',
msg: 'Welcome to our website',
@@ -45,12 +45,12 @@ describe('Prioritized widgets', function () {
},
});
- var modal = new pathfora.Message({
+ var modal = createMessageWidget({
id: 'modal2',
layout: 'modal',
});
- var anotherMessageBar = new pathfora.Message({
+ var anotherMessageBar = createMessageWidget({
id: 'anotherMessageBar',
layout: 'bar',
msg: 'Welcome to our website',
@@ -59,7 +59,7 @@ describe('Prioritized widgets', function () {
},
});
- var anotherModal = new pathfora.Message({
+ var anotherModal = createMessageWidget({
id: 'anotherModal',
layout: 'modal',
});
@@ -90,12 +90,12 @@ describe('Prioritized widgets', function () {
it('should work with entity field templates regardless of load time', function (done) {
window.lio = {};
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
id: 'messageBar1',
layout: 'bar',
msg: 'Welcome to our website {{name}}',
});
- var modal = new pathfora.Message({
+ var modal = createMessageWidget({
id: 'modal2',
layout: 'modal',
msg: 'Welcome to our website',
@@ -145,12 +145,12 @@ describe('Prioritized widgets', function () {
loaded: true,
};
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
id: 'messageBar1',
layout: 'bar',
msg: 'Welcome to our website',
});
- var modal = new pathfora.Message({
+ var modal = createMessageWidget({
id: 'recommendation-modal',
msg: 'A',
layout: 'modal',
@@ -196,12 +196,12 @@ describe('Prioritized widgets', function () {
describe('with no priority defined', function () {
it('should attempt to initialize widgets asyncronously', function (done) {
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
id: 'messageBar1noPriority',
layout: 'bar',
msg: 'Welcome to our website {{name}}',
});
- var modal = new pathfora.Message({
+ var modal = createMessageWidget({
id: 'modal2noPriority',
layout: 'modal',
msg: 'Welcome to our website',
@@ -223,12 +223,12 @@ describe('Prioritized widgets', function () {
describe('with unordered priority defined', function () {
it('should attempt to initialize widgets asyncronously', function (done) {
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
id: 'messageBar1noPriority',
layout: 'bar',
msg: 'Welcome to our website {{name}}',
});
- var modal = new pathfora.Message({
+ var modal = createMessageWidget({
id: 'modal2noPriority',
layout: 'modal',
msg: 'Welcome to our website',
diff --git a/test/acceptance/scaffolding.spec.js b/test/acceptance/scaffolding.spec.js
index bdf41d35..c2ff8aad 100644
--- a/test/acceptance/scaffolding.spec.js
+++ b/test/acceptance/scaffolding.spec.js
@@ -1,8 +1,6 @@
import globalReset from '../utils/global-reset';
+import { createMessageWidget, createFormWidget } from '../utils/test-helpers';
-// -------------------------
-// SCAFFOLDING
-// -------------------------
describe('when building a scaffolding component', function () {
beforeEach(function () {
globalReset();
@@ -18,7 +16,7 @@ describe('when building a scaffolding component', function () {
it('should insert widget into config after building and inserting into scaffold', function () {
var scaffold = pathfora.utils.initWidgetScaffold();
- var tester = new pathfora.Message({
+ var tester = createMessageWidget({
id: 'tester123',
headline: 'Sample Insert',
msg: 'Sample insert message.',
@@ -45,7 +43,7 @@ describe('when building a scaffolding component', function () {
it('should insert multiple widgets into config binding to the same segment', function () {
var scaffold = pathfora.utils.initWidgetScaffold();
- var tester1 = new pathfora.Message({
+ var tester1 = createMessageWidget({
id: 'tester123',
headline: 'Sample Insert',
msg: 'Sample insert message.',
@@ -56,7 +54,7 @@ describe('when building a scaffolding component', function () {
});
pathfora.utils.insertWidget('target', 'smt_new', tester1, scaffold);
- var tester2 = new pathfora.Form({
+ var tester2 = createFormWidget({
id: 'tester456',
headline: 'Sample Insert Two',
msg: 'Sample insert message two.',
@@ -85,7 +83,7 @@ describe('when building a scaffolding component', function () {
it('should insert multiple widgets into config binding to the same segment but excluding', function () {
var scaffold = pathfora.utils.initWidgetScaffold();
- var tester1 = new pathfora.Message({
+ var tester1 = createMessageWidget({
id: 'tester123',
headline: 'Sample Insert',
msg: 'Sample insert message.',
@@ -97,7 +95,7 @@ describe('when building a scaffolding component', function () {
pathfora.utils.insertWidget('exclude', 'smt_new', tester1, scaffold);
- var tester2 = new pathfora.Form({
+ var tester2 = createFormWidget({
id: 'tester456',
headline: 'Sample Insert Two',
msg: 'Sample insert message two.',
diff --git a/test/acceptance/targeting.spec.js b/test/acceptance/targeting.spec.js
index a2609e37..387ce131 100644
--- a/test/acceptance/targeting.spec.js
+++ b/test/acceptance/targeting.spec.js
@@ -1,44 +1,38 @@
import globalReset from '../utils/global-reset';
+import {
+ createMessageWidget,
+ setupLioMock,
+ expectWidgetVisible,
+ expectWidgetHidden,
+} from '../utils/test-helpers';
-// -------------------------
-// SEGMENTS
-// -------------------------
describe('when targeting users by segment', function () {
beforeEach(function () {
globalReset();
});
it('should distinguish newcomers, subscribers and common users', function (done) {
- window.lio = {
- data: {
- segments: ['all', 'b'],
- },
- account: {
- id: '0',
- },
- };
+ setupLioMock(['all', 'b']);
- window.lio.loaded = true;
-
- var messageA = new pathfora.Message({
+ var messageA = createMessageWidget({
id: 'test-bar-01',
msg: 'A',
layout: 'modal',
});
- var messageB = new pathfora.Message({
+ var messageB = createMessageWidget({
id: 'test-bar-02',
msg: 'B',
layout: 'modal',
});
- var messageC = new pathfora.Message({
+ var messageC = createMessageWidget({
id: 'test-bar-03',
msg: 'C',
layout: 'modal',
});
- var messageD = new pathfora.Message({
+ var messageD = createMessageWidget({
id: 'test-bar-04',
msg: 'D',
layout: 'modal',
@@ -67,43 +61,25 @@ describe('when targeting users by segment', function () {
pathfora.initializeWidgets(widgets);
- var widget = $('#' + messageB.id);
- expect(widget).toBeDefined();
-
- var notOpenedA = $('#' + messageA.id);
- var notOpenedC = $('#' + messageC.id);
- var widgetB = $('#' + messageB.id);
- var universalWidget = $('#' + messageD.id);
-
setTimeout(function () {
- expect(widget.hasClass('opened')).toBeTruthy();
- expect(notOpenedA.length).toBe(0);
- expect(notOpenedC.length).toBe(0);
- expect(universalWidget.hasClass('opened')).toBeTruthy();
- expect(widgetB.hasClass('opened')).toBeTruthy();
+ expectWidgetVisible(messageB.id);
+ expectWidgetVisible(messageD.id);
+ expectWidgetHidden(messageA.id);
+ expectWidgetHidden(messageC.id);
done();
}, 200);
});
it('should properly exclude users when their segment membership matches that of the exclude settings', function (done) {
- window.lio = {
- data: {
- segments: ['a', 'b'],
- },
- account: {
- id: '0',
- },
- };
-
- window.lio.loaded = true;
+ setupLioMock(['a', 'b']);
- var messageA = new pathfora.Message({
+ var messageA = createMessageWidget({
id: 'test-bar-01',
msg: 'A',
layout: 'modal',
});
- var messageB = new pathfora.Message({
+ var messageB = createMessageWidget({
id: 'test-bar-02',
msg: 'B',
layout: 'modal',
@@ -126,14 +102,9 @@ describe('when targeting users by segment', function () {
pathfora.initializeWidgets(widgets);
- var widgetA = $('#' + messageA.id),
- widgetB = $('#' + messageB.id);
-
- expect(widgetB).toBeDefined();
-
setTimeout(function () {
- expect(widgetB.hasClass('opened')).toBeTruthy();
- expect(widgetA.length).toBe(0);
+ expectWidgetVisible(messageB.id);
+ expectWidgetHidden(messageA.id);
done();
}, 200);
});
@@ -150,13 +121,13 @@ describe('when targeting users by segment', function () {
window.lio.loaded = true;
- var messageA = new pathfora.Message({
+ var messageA = createMessageWidget({
id: 'test-slideout-01',
msg: 'A',
layout: 'slideout',
});
- var messageB = new pathfora.Message({
+ var messageB = createMessageWidget({
id: 'test-bar-02',
msg: 'B',
layout: 'modal',
@@ -202,19 +173,19 @@ describe('when targeting users by segment', function () {
window.lio.loaded = true;
- var messageA = new pathfora.Message({
+ var messageA = createMessageWidget({
id: 'test-dependency-01',
msg: 'A',
layout: 'modal',
});
- var messageB = new pathfora.Message({
+ var messageB = createMessageWidget({
id: 'test-dependency-02',
msg: 'B',
layout: 'modal',
});
- var messageC = new pathfora.Message({
+ var messageC = createMessageWidget({
id: 'test-dependency-03',
msg: 'C',
layout: 'modal',
@@ -255,9 +226,6 @@ describe('when targeting users by segment', function () {
});
});
-// -------------------------
-// ATTRIBUTES
-// -------------------------
describe('when targeting users by attributes', function () {
beforeEach(function () {
globalReset();
@@ -276,25 +244,25 @@ describe('when targeting users by attributes', function () {
};
window.jstag.config.cid = '123';
- var messageA = new pathfora.Message({
+ var messageA = createMessageWidget({
id: 'test-bar-01',
msg: 'A',
layout: 'modal',
});
- var messageB = new pathfora.Message({
+ var messageB = createMessageWidget({
id: 'test-bar-02',
msg: 'B',
layout: 'modal',
});
- var messageC = new pathfora.Message({
+ var messageC = createMessageWidget({
id: 'test-bar-03',
msg: 'C',
layout: 'modal',
});
- var messageD = new pathfora.Message({
+ var messageD = createMessageWidget({
id: 'test-bar-04',
msg: 'D',
layout: 'modal',
@@ -358,13 +326,13 @@ describe('when targeting users by attributes', function () {
};
window.jstag.config.cid = '123';
- var messageB = new pathfora.Message({
+ var messageB = createMessageWidget({
id: 'test-attr-dependency-02',
msg: 'B',
layout: 'modal',
});
- var messageC = new pathfora.Message({
+ var messageC = createMessageWidget({
id: 'test-attr-dependency-03',
msg: 'C',
layout: 'modal',
@@ -435,7 +403,7 @@ describe('pathfora helper rule functions', function () {
it('should return true if the user is in the flow', function () {
var rule = pathfora.rules.inFlow('test_slug');
var data = {
- flows_step_slugs: { '12345': 'test_slug', '67890': 'test_slug_2' },
+ flows_step_slugs: { 12345: 'test_slug', 67890: 'test_slug_2' },
};
expect(rule(data)).toBeTruthy();
});
@@ -443,7 +411,7 @@ describe('pathfora helper rule functions', function () {
it('should return false otherwise', function () {
var rule = pathfora.rules.inFlow('test_slug');
var data = {
- flows_step_slugs: { '67890': 'test_slug_2' },
+ flows_step_slugs: { 67890: 'test_slug_2' },
};
expect(rule(data)).toBeFalsy();
});
diff --git a/test/acceptance/tracking.spec.js b/test/acceptance/tracking.spec.js
index 150b2ff5..be388984 100644
--- a/test/acceptance/tracking.spec.js
+++ b/test/acceptance/tracking.spec.js
@@ -1,11 +1,16 @@
import globalReset from '../utils/global-reset';
+import {
+ createMessageWidget,
+ setupTrackingSpy,
+ confirmWidget,
+ closeWidget,
+ expectTrackingEvent,
+ createFormWidget,
+} from '../utils/test-helpers';
window.ga = function () {};
window.ga.getAll = function () {};
-// -------------------------
-// TRACKING
-// -------------------------
describe('the tracking component', function () {
beforeEach(function () {
globalReset();
@@ -37,7 +42,7 @@ describe('the tracking component', function () {
});
it('should know if users have interacted in the past', function () {
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
layout: 'bar',
id: 'interest-widget1',
msg: 'Message bar - interest test',
@@ -49,7 +54,7 @@ describe('the tracking component', function () {
},
});
- var messageModal = new pathfora.Message({
+ var messageModal = createMessageWidget({
layout: 'modal',
id: 'interest-widget2',
msg: 'Message modal - interest test',
@@ -61,12 +66,8 @@ describe('the tracking component', function () {
expect(completedActions).toBe(0);
expect(closedWidgets).toBe(0);
- $('#' + messageBar.id)
- .find('.pf-widget-ok')
- .click();
- $('#' + messageModal.id)
- .find('.pf-widget-close')
- .click();
+ confirmWidget(messageBar.id);
+ closeWidget(messageModal.id);
completedActions = pathfora.getDataObject().completedActions.length;
closedWidgets = pathfora.getDataObject().closedWidgets.length;
@@ -91,24 +92,20 @@ describe('the tracking component', function () {
it('should report displaying widgets', function () {
jasmine.Ajax.install();
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
layout: 'modal',
msg: 'Message bar - reporting test',
id: 'modal-display-report',
});
- spyOn(jstag, 'send');
+ setupTrackingSpy();
pathfora.initializeWidgets([messageBar]);
- expect(jstag.send).toHaveBeenCalledWith(
- jasmine.objectContaining({
- 'pf-widget-id': messageBar.id,
- 'pf-widget-type': 'message',
- 'pf-widget-layout': 'modal',
- 'pf-widget-event': 'show',
- })
- );
+ expectTrackingEvent(messageBar.id, 'show', null, {
+ 'pf-widget-type': 'message',
+ 'pf-widget-layout': 'modal',
+ });
expect(window.ga).toHaveBeenCalledWith(
'gtm1.send',
@@ -135,7 +132,7 @@ describe('the tracking component', function () {
jasmine.Ajax.install();
jasmine.clock().install();
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
layout: 'modal',
msg: 'Message bar - close reporting',
id: 'bar-close-report',
@@ -143,19 +140,15 @@ describe('the tracking component', function () {
pathfora.initializeWidgets([messageBar]);
- spyOn(jstag, 'send');
- $('.pf-widget-close').click();
+ setupTrackingSpy();
+ closeWidget(messageBar.id);
jasmine.clock().tick(1000);
- expect(jstag.send).toHaveBeenCalledWith(
- jasmine.objectContaining({
- 'pf-widget-id': messageBar.id,
- 'pf-widget-type': 'message',
- 'pf-widget-layout': 'modal',
- 'pf-widget-event': 'close',
- })
- );
+ expectTrackingEvent(messageBar.id, 'close', null, {
+ 'pf-widget-type': 'message',
+ 'pf-widget-layout': 'modal',
+ });
expect(window.ga).toHaveBeenCalledWith(
'gtm1.send',
@@ -182,7 +175,7 @@ describe('the tracking component', function () {
it('should report completed actions to Lytics API', function (done) {
jasmine.Ajax.install();
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
layout: 'modal',
id: 'tracking-widget1',
msg: 'Message modal - action report test',
@@ -220,7 +213,7 @@ describe('the tracking component', function () {
it('should report cancelled actions to Lytics API', function (done) {
jasmine.Ajax.install();
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
layout: 'modal',
id: 'tracking-widget2',
msg: 'Message modal - cancel report test',
@@ -259,7 +252,7 @@ describe('the tracking component', function () {
});
it('should report submitting forms, with form data', function () {
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
layout: 'modal',
msg: 'Message modal - form submit reports',
id: 'ABCa',
@@ -281,7 +274,7 @@ describe('the tracking component', function () {
});
it('should report to Google Analytics API, when available', function (done) {
- var messageBar = new pathfora.Message({
+ var messageBar = createMessageWidget({
layout: 'modal',
id: 'ga-widget',
msg: 'Message modal - ga test',
@@ -324,7 +317,7 @@ describe('the tracking component', function () {
it('should report hover actions to Lytics API', function (done) {
jasmine.Ajax.install();
- var messageModal = new pathfora.Message({
+ var messageModal = createMessageWidget({
layout: 'modal',
id: 'tracking-widget3',
msg: 'Message modal - report test',
@@ -379,7 +372,7 @@ describe('the tracking component', function () {
it('should report form focus actions to Lytics API', function (done) {
jasmine.Ajax.install();
- var formModal = new pathfora.Form({
+ var formModal = createFormWidget({
layout: 'modal',
id: 'tracking-widget4',
msg: 'Form modal - report test',
@@ -435,7 +428,7 @@ describe('the tracking component', function () {
it('should report form started actions to Lytics API', function (done) {
jasmine.Ajax.install();
- var formModal = new pathfora.Form({
+ var formModal = createFormWidget({
layout: 'modal',
id: 'tracking-widget5',
msg: 'Form modal - report test',
@@ -492,7 +485,7 @@ describe('the tracking component', function () {
});
it('should call censorTrackingKeys with widget.censorTrackingKeys when defined', function (done) {
- var formModal = new pathfora.Form({
+ var formModal = createFormWidget({
layout: 'modal',
id: 'tracking-widget-censored',
msg: 'Form modal - report test',
diff --git a/test/acceptance/utils.spec.js b/test/acceptance/utils.spec.js
index 3be91c8d..cc6e9287 100644
--- a/test/acceptance/utils.spec.js
+++ b/test/acceptance/utils.spec.js
@@ -1,9 +1,6 @@
import globalReset from '../utils/global-reset';
-// -------------------------
-// UTIL TESTS
-// -------------------------
-describe('Utils', function () {
+describe('utils', function () {
beforeEach(function () {
globalReset();
});
@@ -37,8 +34,8 @@ describe('Utils', function () {
it('should not double-encode URIs', function () {
var unescaped = 'http://www.getlytics.com/?foo=a b c&bar=d e f',
- escapedOnce = escapeURI(unescaped, { keepEscaped: true }),
- escapedTwice = escapeURI(escapedOnce, { keepEscaped: true });
+ escapedOnce = escapeURI(unescaped, { keepEscaped: true }),
+ escapedTwice = escapeURI(escapedOnce, { keepEscaped: true });
expect(escapedTwice).toBe(escapedOnce);
});
@@ -51,7 +48,7 @@ describe('Utils', function () {
var params = {
key: 'value',
anotherkey: true,
- athirdkey: 1
+ athirdkey: 1,
};
var expected = '?key=value&anotherkey=true&athirdkey=1';
@@ -63,7 +60,7 @@ describe('Utils', function () {
var params = {
foo: 'value',
bar: [1, 2, 3],
- baz: ['test']
+ baz: ['test'],
};
var expected = '?foo=value&bar[]=1&bar[]=2&bar[]=3&baz[]=test';
@@ -102,7 +99,6 @@ describe('Utils', function () {
};
it('should encode legacy Pathfora cookies', function () {
-
setCookie('PathforaImpressions_2', '1|293847239874932871');
sessionStorage.setItem('PathforaRecommend_2', '{"somejson": "here"}');
setCookie('PathforaImpressions_3', '%badval%');
@@ -115,9 +111,7 @@ describe('Utils', function () {
expect(sessionStorage.getItem('PathforaRecommend_2')).toEqual(
'%7B%22somejson%22%3A%20%22here%22%7D'
);
- expect(pathfora.utils.read('PathforaImpressions_3')).toEqual(
- '%badval%'
- );
+ expect(pathfora.utils.read('PathforaImpressions_3')).toEqual('%badval%');
});
it('should migrate existing cookies to localStorage', function () {
@@ -128,4 +122,23 @@ describe('Utils', function () {
expect(pathfora.utils.read('PathforaImpressions_2')).toBe('test');
});
});
+
+ describe('culling expired localStorage on init', function () {
+ beforeEach(globalReset);
+
+ it('should cull expired records from localStorage eagerly on init', function () {
+ var Pathfora = pathfora.constructor;
+
+ pathfora.utils.store.ttl('expired', 'bonk', -10000);
+ pathfora.utils.store.ttl('current', 'bonk', 10000);
+
+ expect(localStorage.getItem('expired')).not.toBe(null);
+ expect(localStorage.getItem('current')).not.toBe(null);
+
+ new Pathfora();
+
+ expect(localStorage.getItem('expired')).toBe(null);
+ expect(localStorage.getItem('current')).not.toBe(null);
+ });
+ });
});
diff --git a/test/acceptance/widget-position.spec.js b/test/acceptance/widget-position.spec.js
new file mode 100644
index 00000000..de018f77
--- /dev/null
+++ b/test/acceptance/widget-position.spec.js
@@ -0,0 +1,154 @@
+import globalReset from '../utils/global-reset';
+import { createMessageWidget } from '../utils/test-helpers';
+
+describe('widget position', function () {
+ beforeEach(function () {
+ globalReset();
+ });
+
+ it('should use default position if no position is specified', function () {
+ var w1 = createMessageWidget({
+ msg: 'button - default pos test',
+ id: 'position-widget-1',
+ layout: 'button',
+ });
+
+ var w2 = createMessageWidget({
+ msg: 'bar - default pos test',
+ id: 'position-widget-2',
+ layout: 'bar',
+ });
+
+ var w3 = createMessageWidget({
+ msg: 'slideout - default pos test',
+ id: 'position-widget-3',
+ layout: 'slideout',
+ });
+
+ pathfora.initializeWidgets([w1, w2, w3]);
+
+ var widget1 = $('#' + w1.id),
+ widget2 = $('#' + w2.id),
+ widget3 = $('#' + w3.id);
+
+ expect(widget1.hasClass('pf-position-top-left')).toBeTruthy();
+ expect(widget2.hasClass('pf-position-top-absolute')).toBeTruthy();
+ expect(widget3.hasClass('pf-position-bottom-left')).toBeTruthy();
+ });
+
+ it('should warn when invalid position', function () {
+ var w1 = createMessageWidget({
+ msg: 'Widget positioning test',
+ layout: 'modal',
+ id: 'region-widget',
+ position: 'customPos',
+ });
+
+ spyOn(console, 'warn');
+ pathfora.initializeWidgets([w1]);
+
+ expect(console.warn).toHaveBeenCalledWith(
+ 'customPos is not a valid position for modal'
+ );
+ });
+
+ it('should error when custom positionSelector does not exist in dom', function () {
+ var w1 = createMessageWidget({
+ msg: 'Widget positioning test',
+ layout: 'modal',
+ id: 'custom-position-widget',
+ positionSelector: '.does-not-exist',
+ });
+
+ expect(function () {
+ pathfora.initializeWidgets([w1]);
+ }).toThrowError(/Widget could not be initialized in .does-not-exist/);
+ });
+
+ it('should append the widget to the positionSelector element if it does exist', function (done) {
+ var div = document.createElement('div');
+ div.id = 'overlay';
+ document.body.appendChild(div);
+
+ var inline = createMessageWidget({
+ headline: 'Position Custom',
+ layout: 'modal',
+ positionSelector: '#overlay',
+ id: 'custom-position-modal',
+ msg: 'yay',
+ });
+
+ pathfora.initializeWidgets([inline]);
+
+ var parent = $(inline.positionSelector);
+
+ setTimeout(function () {
+ var widget = parent.find('#' + inline.id);
+ expect(widget.length).toBe(1);
+ done();
+ }, 200);
+ });
+
+ // -------------------------
+ // INLINE MODULES
+ // -------------------------
+ it('should throw error if inline positionSelector not found', function () {
+ // legacy position support
+ var inline = createMessageWidget({
+ headline: 'Inline Widget',
+ layout: 'inline',
+ position: '.a-non-existent-div',
+ id: 'inline-1',
+ msg: 'inline',
+ });
+
+ var inlineCustom = createMessageWidget({
+ headline: 'Inline Widget',
+ layout: 'inline',
+ positionSelector: '.a-non-existent-div',
+ id: 'inline-2',
+ msg: 'inline',
+ });
+
+ expect(function () {
+ pathfora.initializeWidgets([inline, inlineCustom]);
+ }).toThrow(
+ new Error('Widget could not be initialized in .a-non-existent-div')
+ );
+ });
+
+ it('should append the inline widget to the positionSelector element', function (done) {
+ var div = document.createElement('div');
+ div.id = 'a-real-div';
+ document.body.appendChild(div);
+
+ // legacy position support
+ var inline = createMessageWidget({
+ headline: 'Inline Widget',
+ layout: 'inline',
+ position: '#a-real-div',
+ id: 'inline-1',
+ msg: 'inline',
+ });
+
+ var inlineCustom = createMessageWidget({
+ headline: 'Inline Widget',
+ layout: 'inline',
+ positionSelector: '#a-real-div',
+ id: 'inline-2',
+ msg: 'inline',
+ });
+
+ pathfora.initializeWidgets([inline, inlineCustom]);
+
+ var parent = $(inline.position);
+
+ setTimeout(function () {
+ var widget = parent.find('#' + inline.id);
+ expect(widget.length).toBe(1);
+ var widget2 = parent.find('#' + inlineCustom.id);
+ expect(widget2.length).toBe(1);
+ done();
+ }, 200);
+ });
+});
diff --git a/test/acceptance/widget-theme.spec.js b/test/acceptance/widget-theme.spec.js
new file mode 100644
index 00000000..35f18d66
--- /dev/null
+++ b/test/acceptance/widget-theme.spec.js
@@ -0,0 +1,200 @@
+import globalReset from '../utils/global-reset';
+import { createMessageWidget, createFormWidget } from '../utils/test-helpers';
+
+describe('widget themes', function () {
+ beforeEach(function () {
+ globalReset();
+ });
+
+ it('should have correct theme configuration', function () {
+ var w1 = createMessageWidget({
+ layout: 'button',
+ position: 'left',
+ msg: 'light button',
+ id: 'light-widget',
+ theme: 'light',
+ });
+
+ var w2 = createMessageWidget({
+ layout: 'button',
+ position: 'right',
+ msg: 'dark button',
+ id: 'dark-widget',
+ theme: 'dark',
+ });
+
+ var w3 = createMessageWidget({
+ layout: 'button',
+ position: 'top-left',
+ msg: 'custom color button',
+ id: 'custom-widget',
+ theme: 'custom',
+ });
+
+ var config = {
+ generic: {
+ colors: {
+ background: '#fff',
+ },
+ },
+ };
+
+ pathfora.initializeWidgets([w1, w2, w3], config);
+
+ var light = $('#' + w1.id),
+ dark = $('#' + w2.id),
+ custom = $('#' + w3.id);
+
+ expect(light.hasClass('pf-theme-light')).toBeTruthy();
+ expect(dark.hasClass('pf-theme-dark')).toBeTruthy();
+ expect(custom.hasClass('pf-theme-custom')).toBeTruthy();
+ expect(custom.css('background-color')).toBe('rgb(255, 255, 255)');
+ });
+
+ it('should fallback to CSS if theme value is "none"', function () {
+ var css = document.createElement('style');
+ css.type = 'text/css';
+ css.innerHTML = '.widget-no-theme-class { background-color: #59f442 }';
+ document.body.appendChild(css);
+
+ var w1 = createMessageWidget({
+ layout: 'button',
+ position: 'left',
+ msg: 'light button',
+ id: 'widget-no-theme',
+ className: 'widget-no-theme-class',
+ theme: 'none',
+ });
+
+ pathfora.initializeWidgets([w1]);
+
+ var w = $('#' + w1.id);
+
+ expect(w.hasClass('pf-theme-none')).toBeTruthy();
+ expect(w.css('background-color')).toBe('rgb(89, 244, 66)');
+ });
+
+ it('can be hidden on initialization', function () {
+ var openedWidget = createMessageWidget({
+ layout: 'modal',
+ msg: 'Displayed on init',
+ id: 'displayed-on-init',
+ });
+
+ var closedWidget = createMessageWidget({
+ layout: 'modal',
+ msg: 'Hidden on init',
+ id: 'hidden-on-init',
+ displayConditions: {
+ showOnInit: false,
+ },
+ });
+
+ pathfora.initializeWidgets([openedWidget, closedWidget]);
+
+ expect($('#' + openedWidget.id)[0]).toBeDefined();
+ expect($('#' + closedWidget.id)[0]).toBeUndefined();
+ });
+
+ it('should be able to adapt colors', function () {
+ var modal = createMessageWidget({
+ id: 'custom-style-test',
+ layout: 'modal',
+ msg: 'Custom style test',
+ headline: 'Hello',
+ theme: 'custom',
+ });
+
+ var config = {
+ generic: {
+ colors: {
+ background: '#eee',
+ headline: '#333',
+ text: '#333',
+ close: '#888',
+ actionText: '#ddd',
+ actionBackground: '#111',
+ cancelText: '#333',
+ cancelBackground: '#eee',
+ },
+ },
+ };
+
+ pathfora.initializeWidgets([modal], config);
+
+ var widget = $('#' + modal.id);
+ var background = widget.find('.pf-widget-content');
+ var headline = widget.find('.pf-widget-headline');
+ var text = widget.find('.pf-widget-message');
+ var closeBtn = widget.find('.pf-widget-close');
+ var actionBtn = widget.find('.pf-widget-ok');
+ var cancelBtn = widget.find('.pf-widget-cancel');
+
+ expect(background.css('background-color')).toBe('rgb(238, 238, 238)');
+ expect(headline.css('color')).toBe('rgb(51, 51, 51)');
+ expect(text.css('color')).toBe('rgb(51, 51, 51)');
+ expect(closeBtn.css('color')).toBe('rgb(136, 136, 136)');
+ expect(actionBtn.css('color')).toBe('rgb(221, 221, 221)');
+ expect(actionBtn.css('background-color')).toBe('rgb(17, 17, 17)');
+ expect(cancelBtn.css('color')).toBe('rgb(51, 51, 51)');
+ expect(cancelBtn.css('background-color')).toBe('rgb(238, 238, 238)');
+ });
+
+ it('should account for required colors on validation', function () {
+ var modal = createFormWidget({
+ id: 'required-color-modal',
+ layout: 'modal',
+ msg: 'Custom style test',
+ headline: 'Hello',
+ theme: 'custom',
+ colors: {
+ required: '#ba00a6',
+ requiredText: '#ebcee8',
+ },
+ formElements: [
+ {
+ type: 'radio-group',
+ label: "What's your favorite color?",
+ name: 'favorite_color',
+ required: true,
+ values: [
+ {
+ label: 'Red',
+ value: 'red',
+ },
+ {
+ label: 'Blue',
+ value: 'blue',
+ },
+ {
+ label: 'Green',
+ value: 'green',
+ },
+ ],
+ },
+ {
+ type: 'input',
+ name: 'name',
+ placeholder: 'Your Name',
+ required: true,
+ },
+ ],
+ });
+
+ pathfora.initializeWidgets([modal]);
+
+ var widget = $('#' + modal.id);
+ var asterisk = widget.find('.pf-form-label span.required');
+ var flag = widget.find('.pf-required-flag');
+
+ expect(asterisk.css('color')).toBe('rgb(186, 0, 166)');
+ expect(flag.css('background-color')).toBe('rgb(186, 0, 166)');
+ expect(flag.find('span').css('border-right-color')).toBe(
+ 'rgb(186, 0, 166)'
+ );
+ expect(flag.css('color')).toBe('rgb(235, 206, 232)');
+
+ var input = widget.find('input[data-required=true]:not(.pf-has-label)');
+ expect(input.css('border-color')).toBe('rgb(186, 0, 166)');
+ });
+});
diff --git a/test/acceptance/widget.spec.js b/test/acceptance/widget.spec.js
deleted file mode 100644
index cfcca9f7..00000000
--- a/test/acceptance/widget.spec.js
+++ /dev/null
@@ -1,2070 +0,0 @@
-import createAndDispatchKeydown from '../utils/create-and-dispatch-keydown.js';
-import globalReset from '../utils/global-reset';
-
-// -------------------------
-// WIDGET TESTS
-// -------------------------
-describe('Widgets', function () {
- beforeEach(function () {
- globalReset();
- });
-
- // -------------------------
- // GENERAL
- // -------------------------
-
- it('should not allow to register 2 widgets with the same id', function () {
- var w1 = new pathfora.Message({
- msg: 'Duplicate id test1',
- layout: 'modal',
- id: 'asd',
- });
-
- var w2 = new pathfora.Form({
- msg: 'Duplcate id test2',
- layout: 'slideout',
- id: 'asd',
- });
-
- expect(function () {
- pathfora.initializeWidgets([w1, w2]);
- }).toThrow(new Error('Cannot add two widgets with the same id'));
- });
-
- it('should use specified global config for all widgets', function () {
- var messageBar = new pathfora.Message({
- layout: 'bar',
- id: 'global-config-1',
- msg: 'test',
- });
-
- var config = {
- generic: {
- theme: 'light',
- },
- };
-
- pathfora.initializeWidgets([messageBar], config);
-
- var bar = $('#' + messageBar.id);
- expect(bar.hasClass('pf-theme-default')).toBe(false);
- expect(bar.hasClass('pf-theme-light')).toBe(true);
- });
-
- it('should be able to clear all widgets and handlers', function (done) {
- var clearDataObject = {
- pageViews: 0,
- timeSpentOnPage: 0,
- closedWidgets: [],
- completedActions: [],
- cancelledActions: [],
- displayedWidgets: [],
- abTestingGroups: [],
- };
-
- var form = new pathfora.Subscription({
- msg: 'test',
- id: 'clear-widget',
- layout: 'modal',
- });
-
- pathfora.initializeWidgets([form]);
-
- var widget = $('#' + form.id);
-
- setTimeout(function () {
- expect(widget.hasClass('opened')).toBeTruthy();
- expect(pathfora.getDataObject()).not.toEqual(clearDataObject);
-
- pathfora.clearAll();
- expect(widget.hasClass('opened')).toBeFalsy();
- expect(pathfora.getDataObject()).toEqual(clearDataObject);
- done();
- }, 200);
- });
-
- it('should be able to clear specific widgets by their IDs', function (done) {
- var widget1 = new pathfora.Message({
- msg: 'Widget 1',
- id: 'clear-by-id-1',
- layout: 'modal',
- });
-
- var widget2 = new pathfora.Message({
- msg: 'Widget 2',
- id: 'clear-by-id-2',
- layout: 'slideout',
- });
-
- var widget3 = new pathfora.Message({
- msg: 'Widget 3',
- id: 'clear-by-id-3',
- layout: 'bar',
- });
-
- pathfora.initializeWidgets([widget1, widget2, widget3]);
-
- var element1 = $('#' + widget1.id);
- var element2 = $('#' + widget2.id);
- var element3 = $('#' + widget3.id);
-
- setTimeout(function () {
- // All widgets should be opened initially
- expect(element1.hasClass('opened')).toBeTruthy();
- expect(element2.hasClass('opened')).toBeTruthy();
- expect(element3.hasClass('opened')).toBeTruthy();
-
- // Clear only widget1 and widget3
- pathfora.clearById(['clear-by-id-1', 'clear-by-id-3']);
-
- setTimeout(function () {
- // Widget1 and widget3 should be closed and removed from DOM
- expect(element1.hasClass('opened')).toBeFalsy();
- expect($('#' + widget1.id).length).toBe(0);
- expect(element3.hasClass('opened')).toBeFalsy();
- expect($('#' + widget3.id).length).toBe(0);
-
- // Widget2 should still be opened
- expect(element2.hasClass('opened')).toBeTruthy();
- expect($('#' + widget2.id).length).toBe(1);
-
- // Clear widget2 as well
- pathfora.clearById(['clear-by-id-2']);
-
- setTimeout(function () {
- // All widgets should now be closed and removed
- expect($('#' + widget1.id).length).toBe(0);
- expect($('#' + widget2.id).length).toBe(0);
- expect($('#' + widget3.id).length).toBe(0);
- done();
- }, 200);
- }, 200);
- }, 200);
- });
-
- it('should handle clearById with invalid input gracefully', function (done) {
- var widget = new pathfora.Message({
- msg: 'Test widget',
- id: 'invalid-input-test',
- layout: 'modal',
- });
-
- pathfora.initializeWidgets([widget]);
-
- // Test with non-array input
- spyOn(console, 'warn');
- pathfora.clearById('not-an-array');
-
- expect(console.warn).toHaveBeenCalledWith(
- 'clearById: widgetIds must be an array'
- );
-
- // Widget should still be opened
- setTimeout(function () {
- expect($('#' + widget.id).hasClass('opened')).toBeTruthy();
- done();
- }, 200);
- });
-
- it('should handle clearById with non-existent widget IDs', function (done) {
- var widget = new pathfora.Message({
- msg: 'Test widget',
- id: 'existing-widget',
- layout: 'modal',
- });
-
- pathfora.initializeWidgets([widget]);
-
- var element = $('#' + widget.id);
-
- setTimeout(function () {
- expect(element.hasClass('opened')).toBeTruthy();
-
- // Try to clear non-existent widget IDs
- pathfora.clearById(['non-existent-1', 'non-existent-2']);
-
- // Existing widget should still be opened
- expect(element.hasClass('opened')).toBeTruthy();
- expect($('#' + widget.id).length).toBe(1);
-
- // Clear the existing widget
- pathfora.clearById(['existing-widget']);
-
- setTimeout(function () {
- expect(element.hasClass('opened')).toBeFalsy();
- expect($('#' + widget.id).length).toBe(0);
- done();
- }, 200);
- }, 200);
- });
-
- it('should be able to be displayed on document', function (done) {
- var promoWidget = new pathfora.Message({
- layout: 'bar',
- msg: 'Opening widget',
- id: 'widget-1',
- });
-
- pathfora.initializeWidgets([promoWidget]);
-
- // should append element to DOM
- var widget = $('#' + promoWidget.id);
- expect(widget).toBeDefined();
-
- // should have class 'opened' after while
- pathfora.showWidget(promoWidget);
-
- setTimeout(function () {
- expect(widget.hasClass('opened')).toBeTruthy();
- done();
- }, 200);
- });
-
- it('should have proper id specified', function (done) {
- var w1 = new pathfora.Message({
- layout: 'slideout',
- position: 'right',
- msg: 'Welcome to our test website',
- id: 'test-id-widget',
- });
-
- expect(function () {
- return new pathfora.Message({
- layout: 'slideout',
- position: 'left',
- msg: 'Welcome to our test website',
- });
- }).toThrow(new Error('All widgets must have an id value'));
-
- pathfora.initializeWidgets([w1]);
-
- setTimeout(function () {
- var right = $('.pf-widget.pf-position-right');
- expect(right).toBeDefined();
- expect(right.attr('id')).toBe('test-id-widget');
- done();
- }, 200);
- });
-
- it("should not append widget second time if it's already opened", function (done) {
- var openedWidget = new pathfora.Message({
- layout: 'modal',
- id: 'append-widget',
- msg: 'test widget',
- });
-
- pathfora.initializeWidgets([openedWidget]);
-
- var widget = $('#' + openedWidget.id);
-
- // timeouts gives some time for appending to DOM
- setTimeout(function () {
- expect(widget.hasClass('opened')).toBeTruthy();
- pathfora.showWidget(openedWidget);
-
- setTimeout(function () {
- expect($('#' + openedWidget.id).length).toEqual(1);
- done();
- }, 200);
- }, 500);
- });
-
- it('should close when the x button is clicked', function (done) {
- var testWidget = new pathfora.Message({
- layout: 'modal',
- msg: 'Close widget test',
- id: 'close-clear-widget',
- });
-
- pathfora.initializeWidgets([testWidget]);
-
- var widget = $('#' + testWidget.id);
- expect(widget).toBeDefined();
-
- setTimeout(function () {
- expect(widget.hasClass('opened')).toBeTruthy();
-
- widget.find('.pf-widget-close').click();
- expect(widget.hasClass('opened')).toBeFalsy();
-
- setTimeout(function () {
- expect($('#' + testWidget.id).length).toBe(0);
- done();
- }, 600);
- }, 200);
- });
-
- it('should close if the escape key is pressed and it is a modal', function (done) {
- var modal = new pathfora.Message({
- id: 'modal-esc-test',
- layout: 'modal',
- headline: 'Message Title',
- msg: 'test',
- });
-
- var gate = new pathfora.SiteGate({
- id: 'modal-esc-test2',
- headline: 'Message Title',
- msg: 'test',
- });
-
- pathfora.initializeWidgets([modal, gate]);
-
- var widget = $('#' + modal.id);
- var widgetGate = $('#' + gate.id);
- expect(widget).toBeDefined();
- expect(widgetGate).toBeDefined();
-
- setTimeout(function () {
- expect(widget.hasClass('opened')).toBeTruthy();
- expect(widgetGate.hasClass('opened')).toBeTruthy();
-
- createAndDispatchKeydown(27, document);
-
- expect(widget.hasClass('opened')).toBeFalsy();
- expect(widgetGate.hasClass('opened')).toBeTruthy();
-
- setTimeout(function () {
- expect($('#' + modal.id).length).toBe(0);
- expect($('#' + gate.id).length).toBe(1);
- done();
- }, 600);
- }, 200);
- });
-
- it('should handle missing values properly and never surface undefined', function () {
- var message = new pathfora.Message({
- id: 'message-test-widget',
- layout: 'slideout',
- headline: 'Message Title',
- theme: 'custom',
- });
-
- var form = new pathfora.Form({
- id: 'form-test-widget',
- layout: 'modal',
- headline: 'Headline Title',
- theme: 'custom',
- });
-
- var subscription = new pathfora.Subscription({
- id: 'subscription-test-widget',
- layout: 'bar',
- theme: 'custom',
- });
-
- pathfora.initializeWidgets([message, form, subscription]);
-
- // test message
- var mwidget = $('#' + message.id),
- mheadline = mwidget.find('.pf-widget-headline'),
- mtext = mwidget.find('.pf-widget-message');
-
- expect(mheadline.html()).not.toEqual('undefined');
- expect(mtext.html()).not.toEqual('undefined');
-
- // test form
- var fwidget = $('#' + form.id),
- fheadline = fwidget.find('.pf-widget-headline'),
- ftext = fwidget.find('.pf-widget-message');
-
- expect(fheadline.html()).not.toEqual('undefined');
- expect(ftext.html()).not.toEqual('undefined');
-
- // test subscription
- var swidget = $('#' + subscription.id);
- var stext = swidget.find('.pf-widget-message');
- expect(stext.html()).not.toEqual('undefined');
- });
-
- it('should not allow to be initialized without default properties', function () {
- var missingParams = function () {
- var promoWidget = new pathfora.Message();
- pathfora.initializeWidgets([promoWidget]);
- };
-
- expect(missingParams).toThrow(new Error('Config object is missing'));
- });
-
- it('should not show branding assets unless set otherwise', function () {
- var w1 = new pathfora.Message({
- msg: 'test',
- id: 'branding1',
- layout: 'slideout',
- branding: true,
- });
-
- var w2 = new pathfora.Message({
- msg: 'test',
- id: 'branding2',
- layout: 'modal',
- });
-
- pathfora.initializeWidgets([w1, w2]);
-
- var widget1 = $('#' + w1.id),
- widget2 = $('#' + w2.id);
-
- expect(widget1.find('.branding svg').length).toBe(1);
- expect(widget2.find('.branding svg').length).toBe(0);
- });
-
- it('should display footer when footerText setting is used', function () {
- var modalFooter = new pathfora.Message({
- id: 'footer1',
- msg: 'test',
- layout: 'modal',
- footerText: 'Footer text',
- });
-
- var modalNoFooter = new pathfora.Message({
- id: 'footer2',
- msg: 'test',
- layout: 'modal',
- });
-
- var slideoutFooter = new pathfora.Message({
- id: 'slidout1',
- msg: 'test',
- layout: 'slideout',
- footerText: 'Footer text',
- });
-
- var slideoutNoFooter = new pathfora.Message({
- id: 'slideout2',
- msg: 'test',
- layout: 'slideout',
- });
-
- pathfora.initializeWidgets([
- modalFooter,
- modalNoFooter,
- slideoutFooter,
- slideoutNoFooter,
- ]);
-
- var modal1 = $('#' + modalFooter.id),
- modal2 = $('#' + modalNoFooter.id),
- slideout1 = $('#' + slideoutFooter.id),
- slideout2 = $('#' + slideoutNoFooter.id);
- expect(modal1.find('.pf-widget-footer').html()).toEqual('Footer text');
- expect(modal2.find('.pf-widget-footer').html()).toEqual('');
- expect(slideout1.find('.pf-widget-footer').html()).toEqual('Footer text');
- expect(slideout2.find('.pf-widget-footer').html()).toEqual('');
- });
-
- it('should contain pf-widget-text div for inline and modal layouts', function () {
- var modal = new pathfora.Message({
- id: 'modal',
- msg: 'testmodal',
- layout: 'modal',
- });
- var div = document.createElement('div');
- div.className = 'some-dom-element';
- document.body.appendChild(div);
- var inline = new pathfora.Message({
- id: 'inline',
- layout: 'inline',
- position: '.some-dom-element',
- msg: 'testing',
- });
- var slideout = new pathfora.Message({
- id: 'slideout',
- msg: 'test',
- layout: 'slideout',
- });
- pathfora.initializeWidgets([modal, inline, slideout]);
- var modalWidget = $('#' + modal.id),
- inlineWidget = $('#' + inline.id),
- slideoutWidget = $('#' + slideout.id);
- expect(modalWidget.find('.pf-widget-text').html()).toBeDefined();
- expect(inlineWidget.find('.pf-widget-text').html()).toBeDefined();
- expect(slideoutWidget.find('.pf-widget-text').html()).toBeUndefined();
- });
-
- it('should append pf-widget-img to pf-widget-content for modal and inline layouts', function () {
- var modal = new pathfora.Message({
- id: 'modal',
- msg: 'testmodal',
- layout: 'modal',
- image: 'https://lytics.github.io/pathforadocs/assets/lion.jpg',
- });
- var div = document.createElement('div');
- div.className = 'some-dom-element';
- document.body.appendChild(div);
- var inline = new pathfora.Message({
- id: 'inline',
- layout: 'inline',
- position: '.some-dom-element',
- msg: 'testing',
- image: 'https://lytics.github.io/pathforadocs/assets/lion.jpg',
- });
- pathfora.initializeWidgets([modal, inline]);
- var modalWidget = $('#' + modal.id),
- inlineWidget = $('#' + inline.id);
- expect(
- modalWidget.find('.pf-widget-content').find('img').html()
- ).toBeDefined();
- expect(
- inlineWidget.find('.pf-widget-content').find('img').html()
- ).toBeDefined();
- expect(
- modalWidget.find('.pf-widget-text').find('img').html()
- ).toBeUndefined();
- });
-
- // -------------------------
- // COLORS/THEME
- // -------------------------
-
- it('should have correct theme configuration', function () {
- var w1 = new pathfora.Message({
- layout: 'button',
- position: 'left',
- msg: 'light button',
- id: 'light-widget',
- theme: 'light',
- });
-
- var w2 = new pathfora.Message({
- layout: 'button',
- position: 'right',
- msg: 'dark button',
- id: 'dark-widget',
- theme: 'dark',
- });
-
- var w3 = new pathfora.Message({
- layout: 'button',
- position: 'top-left',
- msg: 'custom color button',
- id: 'custom-widget',
- theme: 'custom',
- });
-
- var config = {
- generic: {
- colors: {
- background: '#fff',
- },
- },
- };
-
- pathfora.initializeWidgets([w1, w2, w3], config);
-
- var light = $('#' + w1.id),
- dark = $('#' + w2.id),
- custom = $('#' + w3.id);
-
- expect(light.hasClass('pf-theme-light')).toBeTruthy();
- expect(dark.hasClass('pf-theme-dark')).toBeTruthy();
- expect(custom.hasClass('pf-theme-custom')).toBeTruthy();
- expect(custom.css('background-color')).toBe('rgb(255, 255, 255)');
- });
-
- it('should fallback to CSS if theme value is "none"', function () {
- var css = document.createElement('style');
- css.type = 'text/css';
- css.innerHTML = '.widget-no-theme-class { background-color: #59f442 }';
- document.body.appendChild(css);
-
- var w1 = new pathfora.Message({
- layout: 'button',
- position: 'left',
- msg: 'light button',
- id: 'widget-no-theme',
- className: 'widget-no-theme-class',
- theme: 'none',
- });
-
- pathfora.initializeWidgets([w1]);
-
- var w = $('#' + w1.id);
-
- expect(w.hasClass('pf-theme-none')).toBeTruthy();
- expect(w.css('background-color')).toBe('rgb(89, 244, 66)');
- });
-
- it('can be hidden on initialization', function () {
- var openedWidget = new pathfora.Message({
- layout: 'modal',
- msg: 'Displayed on init',
- id: 'displayed-on-init',
- });
-
- var closedWidget = new pathfora.Message({
- layout: 'modal',
- msg: 'Hidden on init',
- id: 'hidden-on-init',
- displayConditions: {
- showOnInit: false,
- },
- });
-
- pathfora.initializeWidgets([openedWidget, closedWidget]);
-
- expect($('#' + openedWidget.id)[0]).toBeDefined();
- expect($('#' + closedWidget.id)[0]).toBeUndefined();
- });
-
- it('should be able to adapt colors', function () {
- var modal = new pathfora.Message({
- id: 'custom-style-test',
- layout: 'modal',
- msg: 'Custom style test',
- headline: 'Hello',
- theme: 'custom',
- });
-
- var config = {
- generic: {
- colors: {
- background: '#eee',
- headline: '#333',
- text: '#333',
- close: '#888',
- actionText: '#ddd',
- actionBackground: '#111',
- cancelText: '#333',
- cancelBackground: '#eee',
- },
- },
- };
-
- pathfora.initializeWidgets([modal], config);
-
- var widget = $('#' + modal.id);
- var background = widget.find('.pf-widget-content');
- var headline = widget.find('.pf-widget-headline');
- var text = widget.find('.pf-widget-message');
- var closeBtn = widget.find('.pf-widget-close');
- var actionBtn = widget.find('.pf-widget-ok');
- var cancelBtn = widget.find('.pf-widget-cancel');
-
- expect(background.css('background-color')).toBe('rgb(238, 238, 238)');
- expect(headline.css('color')).toBe('rgb(51, 51, 51)');
- expect(text.css('color')).toBe('rgb(51, 51, 51)');
- expect(closeBtn.css('color')).toBe('rgb(136, 136, 136)');
- expect(actionBtn.css('color')).toBe('rgb(221, 221, 221)');
- expect(actionBtn.css('background-color')).toBe('rgb(17, 17, 17)');
- expect(cancelBtn.css('color')).toBe('rgb(51, 51, 51)');
- expect(cancelBtn.css('background-color')).toBe('rgb(238, 238, 238)');
- });
-
- it('should account for required colors on validation', function () {
- var modal = new pathfora.Form({
- id: 'required-color-modal',
- layout: 'modal',
- msg: 'Custom style test',
- headline: 'Hello',
- theme: 'custom',
- colors: {
- required: '#ba00a6',
- requiredText: '#ebcee8',
- },
- formElements: [
- {
- type: 'radio-group',
- label: "What's your favorite color?",
- name: 'favorite_color',
- required: true,
- values: [
- {
- label: 'Red',
- value: 'red',
- },
- {
- label: 'Blue',
- value: 'blue',
- },
- {
- label: 'Green',
- value: 'green',
- },
- ],
- },
- {
- type: 'input',
- name: 'name',
- placeholder: 'Your Name',
- required: true,
- },
- ],
- });
-
- pathfora.initializeWidgets([modal]);
-
- var widget = $('#' + modal.id);
- var asterisk = widget.find('.pf-form-label span.required');
- var flag = widget.find('.pf-required-flag');
-
- expect(asterisk.css('color')).toBe('rgb(186, 0, 166)');
- expect(flag.css('background-color')).toBe('rgb(186, 0, 166)');
- expect(flag.find('span').css('border-right-color')).toBe(
- 'rgb(186, 0, 166)'
- );
- expect(flag.css('color')).toBe('rgb(235, 206, 232)');
-
- var input = widget.find('input[data-required=true]:not(.pf-has-label)');
- expect(input.css('border-color')).toBe('rgb(186, 0, 166)');
- });
-
- // -------------------------
- // CALLBACKS
- // -------------------------
-
- it('should trigger callback function after pressing action button', function () {
- var modal = new pathfora.Message({
- id: 'confirm-action-test',
- layout: 'modal',
- msg: 'Confirm action test modal',
- confirmAction: {
- name: 'Test confirm action',
- callback: function () {
- alert('test confirmation');
- },
- },
- });
-
- pathfora.initializeWidgets([modal]);
-
- var widget = $('#confirm-action-test');
- spyOn(modal.confirmAction, 'callback');
- expect(modal.confirmAction.callback).not.toHaveBeenCalled();
- widget.find('.pf-widget-ok').click();
- expect(modal.confirmAction.callback).toHaveBeenCalled();
- });
-
- it('should trigger callback function after pressing action with form data.', function () {
- var modal = new pathfora.Form({
- id: 'confirm-action-form-test',
- layout: 'modal',
- msg: 'Confirm action test modal',
- confirmAction: {
- callback: function () {
- alert('test confirmation');
- },
- },
- });
-
- pathfora.initializeWidgets([modal]);
-
- var widget = $('#' + modal.id);
- widget.find('input[name="username"]').val('test name');
- widget.find('input[name="email"]').val('test@example.com');
- spyOn(modal.confirmAction, 'callback');
- expect(modal.confirmAction.callback).not.toHaveBeenCalled();
- widget.find('.pf-widget-ok').click();
- expect(modal.confirmAction.callback).toHaveBeenCalledWith(
- 'modalConfirm',
- jasmine.objectContaining({
- data: [
- { name: 'username', value: 'test name' },
- { name: 'email', value: 'test@example.com' },
- { name: 'title', value: '' },
- { name: 'message', value: '' },
- ],
- })
- );
- });
-
- it('should trigger callback function after pressing action with custom form data.', function () {
- var modal = new pathfora.Form({
- id: 'custom-confirm-action-test',
- layout: 'modal',
- msg: 'Confirm action test modal',
- formElements: [
- {
- type: 'text',
- required: true,
- label: 'Email Address',
- name: 'email',
- },
- {
- type: 'checkbox-group',
- required: true,
- label: 'Which feeds would you like to subscribe to?',
- name: 'subscription_feeds',
- values: [
- {
- label: 'Beauty & Perfumes',
- value: 'beauty',
- },
- {
- label: 'Electronics',
- value: 'electronics',
- },
- {
- label: 'Fashion',
- value: 'fashion',
- },
- ],
- },
- ],
- confirmAction: {
- callback: function () {
- alert('test confirmation');
- },
- },
- });
-
- pathfora.initializeWidgets([modal]);
-
- var widget = $('#' + modal.id);
- widget.find('input[name="email"]').val('test@example.com');
- widget.find('input[name="subscription_feeds"]')[2].checked = true;
- spyOn(modal.confirmAction, 'callback');
- expect(modal.confirmAction.callback).not.toHaveBeenCalled();
- widget.find('.pf-widget-ok').click();
- expect(modal.confirmAction.callback).toHaveBeenCalledWith(
- 'modalConfirm',
- jasmine.objectContaining({
- data: [
- { name: 'email', value: 'test@example.com' },
- { name: 'subscription_feeds', value: 'fashion' },
- ],
- })
- );
- });
-
- it('should not close the modal on a button action if specified', function (done) {
- var modal = new pathfora.Message({
- id: 'confirm-close-action-test',
- layout: 'modal',
- msg: 'Confirm action test modal',
- confirmAction: {
- name: 'Test confirm action',
- close: false,
- callback: function () {
- // do something
- },
- },
- cancelAction: {
- close: false,
- },
- });
-
- pathfora.initializeWidgets([modal]);
-
- setTimeout(function () {
- var widget = $('#' + modal.id);
- expect(widget).toBeDefined();
- expect(widget.hasClass('opened')).toBeTruthy();
-
- setTimeout(function () {
- widget.find('.pf-widget-ok').click();
- widget.find('.pf-widget-cancel').click();
- expect(widget).toBeDefined();
- expect(widget.hasClass('opened')).toBeTruthy();
- done();
- }, 300);
- }, 300);
- });
-
- it('should be able to trigger action on cancel', function () {
- var modal = new pathfora.Message({
- id: 'cancel-action-test',
- layout: 'modal',
- msg: 'Welcome to our website',
- cancelAction: {
- name: 'Test cancel action',
- callback: function () {
- alert('test cancel');
- },
- },
- });
-
- pathfora.initializeWidgets([modal]);
-
- var widget = $('#cancel-action-test');
- spyOn(modal.cancelAction, 'callback');
- widget.find('.pf-widget-cancel').click();
- expect(modal.cancelAction.callback).toHaveBeenCalled();
- });
-
- it("shouldn't fire submit callbacks on cancel, and cancel callbacks on submit", function () {
- var w1 = new pathfora.Message({
- id: 'widget-with-action-callback',
- msg: 'Cancel action negative test',
- confirmAction: {
- name: 'Test confirm action',
- callback: function () {
- alert('test confirmation');
- },
- },
- });
-
- var w2 = new pathfora.Message({
- id: 'widget-with-cancel-callback',
- msg: 'Cancel action negative test',
- cancelAction: {
- name: 'Test cancel action',
- callback: function () {
- alert('test cancel');
- },
- },
- });
-
- pathfora.initializeWidgets([w1, w2]);
-
- var widgetA = $('#widget-with-action-callback'),
- widgetB = $('#widget-with-cancel-callback');
-
- spyOn(w1.confirmAction, 'callback');
- spyOn(w2.cancelAction, 'callback');
-
- widgetA.find('.pf-widget-cancel').click();
- expect(w1.confirmAction.callback).not.toHaveBeenCalled();
-
- widgetB.find('.pf-widget-ok').click();
- expect(w2.cancelAction.callback).not.toHaveBeenCalled();
- });
-
- // -------------------------
- // POSITION
- // -------------------------
-
- it('should use default position if no position is specified', function () {
- var w1 = new pathfora.Message({
- msg: 'button - default pos test',
- id: 'position-widget-1',
- layout: 'button',
- });
-
- var w2 = new pathfora.Message({
- msg: 'bar - default pos test',
- id: 'position-widget-2',
- layout: 'bar',
- });
-
- var w3 = new pathfora.Message({
- msg: 'slideout - default pos test',
- id: 'position-widget-3',
- layout: 'slideout',
- });
-
- pathfora.initializeWidgets([w1, w2, w3]);
-
- var widget1 = $('#' + w1.id),
- widget2 = $('#' + w2.id),
- widget3 = $('#' + w3.id);
-
- expect(widget1.hasClass('pf-position-top-left')).toBeTruthy();
- expect(widget2.hasClass('pf-position-top-absolute')).toBeTruthy();
- expect(widget3.hasClass('pf-position-bottom-left')).toBeTruthy();
- });
-
- it('should warn when invalid position', function () {
- var w1 = new pathfora.Message({
- msg: 'Widget positioning test',
- layout: 'modal',
- id: 'region-widget',
- position: 'customPos',
- });
-
- spyOn(console, 'warn');
- pathfora.initializeWidgets([w1]);
-
- expect(console.warn).toHaveBeenCalledWith(
- 'customPos is not a valid position for modal'
- );
- });
-
- it('should error when custom positionSelector does not exist in dom', function () {
- var w1 = new pathfora.Message({
- msg: 'Widget positioning test',
- layout: 'modal',
- id: 'custom-position-widget',
- positionSelector: '.does-not-exist',
- });
-
- expect(function () {
- pathfora.initializeWidgets([w1]);
- }).toThrowError(/Widget could not be initialized in .does-not-exist/);
- });
-
- it('should append the widget to the positionSelector element if it does exist', function (done) {
- var div = document.createElement('div');
- div.id = 'overlay';
- document.body.appendChild(div);
-
- var inline = new pathfora.Message({
- headline: 'Position Custom',
- layout: 'modal',
- positionSelector: '#overlay',
- id: 'custom-position-modal',
- msg: 'yay',
- });
-
- pathfora.initializeWidgets([inline]);
-
- var parent = $(inline.positionSelector);
-
- setTimeout(function () {
- var widget = parent.find('#' + inline.id);
- expect(widget.length).toBe(1);
- done();
- }, 200);
- });
-
- // -------------------------
- // INLINE MODULES
- // -------------------------
- it('should throw error if inline positionSelector not found', function () {
- // legacy position support
- var inline = new pathfora.Message({
- headline: 'Inline Widget',
- layout: 'inline',
- position: '.a-non-existent-div',
- id: 'inline-1',
- msg: 'inline',
- });
-
- var inlineCustom = new pathfora.Message({
- headline: 'Inline Widget',
- layout: 'inline',
- positionSelector: '.a-non-existent-div',
- id: 'inline-2',
- msg: 'inline',
- });
-
- expect(function () {
- pathfora.initializeWidgets([inline, inlineCustom]);
- }).toThrow(
- new Error('Widget could not be initialized in .a-non-existent-div')
- );
- });
-
- it('should append the inline widget to the positionSelector element', function (done) {
- var div = document.createElement('div');
- div.id = 'a-real-div';
- document.body.appendChild(div);
-
- // legacy position support
- var inline = new pathfora.Message({
- headline: 'Inline Widget',
- layout: 'inline',
- position: '#a-real-div',
- id: 'inline-1',
- msg: 'inline',
- });
-
- var inlineCustom = new pathfora.Message({
- headline: 'Inline Widget',
- layout: 'inline',
- positionSelector: '#a-real-div',
- id: 'inline-2',
- msg: 'inline',
- });
-
- pathfora.initializeWidgets([inline, inlineCustom]);
-
- var parent = $(inline.position);
-
- setTimeout(function () {
- var widget = parent.find('#' + inline.id);
- expect(widget.length).toBe(1);
- var widget2 = parent.find('#' + inlineCustom.id);
- expect(widget2.length).toBe(1);
- done();
- }, 200);
- });
-
- // -------------------------
- // FORM STATES
- // -------------------------
-
- it('should show success or error state if waitForAsyncResponse is set', function (done) {
- var formStatesWidget = new pathfora.Form({
- id: 'form-states',
- msg: 'subscription',
- headline: 'Header',
- layout: 'slideout',
- confirmAction: {
- waitForAsyncResponse: true,
- callback: function (name, payload, cb) {
- if (payload.data[0].value === 'test') {
- cb(true);
- return;
- }
- cb(false);
- },
- },
- formStates: {
- success: {
- headline: 'test',
- msg: 'a custom success message',
- delay: 0,
- okShow: true,
- okMessage: 'confirm success',
- confirmAction: {
- name: 'confirm success',
- callback: function () {
- alert('confirm success');
- },
- },
- cancelShow: true,
- cancelMessage: 'cancel success',
- cancelAction: {
- name: 'cancel success',
- callback: function () {
- alert('cancel success');
- },
- },
- },
- error: {
- headline: 'test',
- msg: 'a custom error message',
- delay: 0,
- okShow: true,
- okMessage: 'confirm error',
- confirmAction: {
- name: 'confirm error',
- callback: function () {
- alert('confirm error');
- },
- },
- cancelShow: true,
- cancelMessage: 'cancel error',
- cancelAction: {
- name: 'cancel error',
- callback: function () {
- alert('cancel error');
- },
- },
- },
- },
- });
- window.pathfora.initializeWidgets([formStatesWidget]);
-
- var widget = $('#' + formStatesWidget.id),
- form = widget.find('form');
- expect(form.length).toBe(1);
-
- var name = form.find('input[name="username"]');
- expect(name.length).toBe(1);
- name.val('test');
-
- var email = form.find('input[name="email"]');
- expect(email.length).toBe(1);
- email.val('test@example.com');
-
- spyOn(formStatesWidget.confirmAction, 'callback').and.callThrough();
- expect(formStatesWidget.confirmAction.callback).not.toHaveBeenCalled();
-
- form.find('.pf-widget-ok').click();
-
- expect(formStatesWidget.confirmAction.callback).toHaveBeenCalledWith(
- 'modalConfirm',
- jasmine.any(Object),
- jasmine.any(Function)
- );
-
- var success = widget.find('.success-state'),
- error = widget.find('.error-state');
-
- expect(form.css('display')).toBe('none');
- expect(success.css('display')).toBe('block');
- expect(widget.hasClass('success')).toBeTruthy();
- expect(success.find('.pf-widget-headline').html()).toBe(
- formStatesWidget.formStates.success.headline
- );
- expect(success.find('.pf-widget-message').html()).toBe(
- formStatesWidget.formStates.success.msg
- );
-
- expect(success.find('.pf-widget-ok').html()).toBe(
- formStatesWidget.formStates.success.okMessage
- );
- expect(success.find('.pf-widget-cancel').html()).toBe(
- formStatesWidget.formStates.success.cancelMessage
- );
-
- spyOn(jstag, 'send');
- spyOn(formStatesWidget.formStates.success.confirmAction, 'callback');
- expect(
- formStatesWidget.formStates.success.confirmAction.callback
- ).not.toHaveBeenCalled();
- success.find('.pf-widget-ok').click();
-
- expect(
- formStatesWidget.formStates.success.confirmAction.callback
- ).toHaveBeenCalled();
- expect(jstag.send).toHaveBeenCalledWith(
- jasmine.objectContaining({
- 'pf-widget-id': formStatesWidget.id,
- 'pf-widget-type': 'form',
- 'pf-widget-layout': 'slideout',
- 'pf-widget-event': 'success.confirm',
- 'pf-widget-action':
- formStatesWidget.formStates.success.confirmAction.name,
- })
- );
- pathfora.clearAll();
- pathfora.closeWidget(formStatesWidget.id, true);
-
- setTimeout(function () {
- window.pathfora.initializeWidgets([formStatesWidget]);
-
- widget = $('#' + formStatesWidget.id);
- form = widget.find('form');
- expect(form.length).toBe(1);
-
- name = form.find('input[name="username"]');
- expect(name.length).toBe(1);
- name.val('bad');
-
- email = form.find('input[name="email"]');
- expect(email.length).toBe(1);
- email.val('bad@example.com');
- form.find('.pf-widget-ok').click();
-
- success = widget.find('.success-state');
- expect(success.length).toBe(1);
- error = widget.find('.error-state');
- expect(error.length).toBe(1);
- expect(form.css('display')).toBe('none');
- expect(success.css('display')).toBe('none');
- expect(error.css('display')).toBe('block');
- expect(widget.hasClass('error')).toBeTruthy();
- expect(error.find('.pf-widget-headline').html()).toBe(
- formStatesWidget.formStates.error.headline
- );
- expect(error.find('.pf-widget-message').html()).toBe(
- formStatesWidget.formStates.error.msg
- );
- expect(error.find('.pf-widget-ok').html()).toBe(
- formStatesWidget.formStates.error.okMessage
- );
- expect(error.find('.pf-widget-cancel').html()).toBe(
- formStatesWidget.formStates.error.cancelMessage
- );
-
- spyOn(formStatesWidget.formStates.error.cancelAction, 'callback');
- expect(
- formStatesWidget.formStates.error.cancelAction.callback
- ).not.toHaveBeenCalled();
- error.find('.pf-widget-cancel').click();
-
- expect(
- formStatesWidget.formStates.error.cancelAction.callback
- ).toHaveBeenCalled();
- expect(jstag.send).toHaveBeenCalledWith(
- jasmine.objectContaining({
- 'pf-widget-id': formStatesWidget.id,
- 'pf-widget-type': 'form',
- 'pf-widget-layout': 'slideout',
- 'pf-widget-event': 'error.cancel',
- 'pf-widget-action':
- formStatesWidget.formStates.error.cancelAction.name,
- })
- );
- done();
- }, 600);
- });
-
- // -------------------------
- // LEGACY SUCCESS STATES
- // -------------------------
-
- it('should show success state if one is set by the user', function (done) {
- var successForm = new pathfora.Subscription({
- id: 'success-form',
- msg: 'subscription',
- headline: 'Header',
- layout: 'slideout',
- success: {
- msg: 'a custom success message',
- delay: 2,
- },
- });
-
- pathfora.initializeWidgets([successForm]);
-
- var widget = $('#' + successForm.id);
- var form = widget.find('form');
- expect(form.length).toBe(1);
-
- var email = form.find('input[name="email"]');
- expect(email.length).toBe(1);
- email.val('test@example.com');
- form.find('.pf-widget-ok').click();
-
- var success = widget.find('.success-state');
-
- expect(form.css('display')).toBe('none');
- expect(success.css('display')).toBe('block');
- expect(widget.hasClass('success')).toBeTruthy();
- expect(success.find('.pf-widget-message').html()).toBe(
- successForm.success.msg
- );
- expect(success.find('.pf-widget-ok')).toBeUndefined;
- expect(success.find('.pf-widget-cancel')).toBeUndefined;
-
- setTimeout(function () {
- expect(widget.hasClass('opened')).toBeFalsy();
- done();
- }, 2000);
- });
-
- it('should not hide the module if the success state delay is 0', function (done) {
- var successForm2 = new pathfora.Subscription({
- id: 'success-form-no-delay',
- msg: 'subscription',
- headline: 'Header',
- layout: 'slideout',
- success: {
- msg: 'a custom success message',
- delay: 0,
- },
- });
-
- pathfora.initializeWidgets([successForm2]);
-
- var widget = $('#' + successForm2.id);
- var form = widget.find('form');
- expect(form.length).toBe(1);
-
- var email = form.find('input[name="email"]');
- expect(email.length).toBe(1);
- email.val('test@example.com');
- form.find('.pf-widget-ok').click();
-
- var success = widget.find('.success-state');
-
- expect(form.css('display')).toBe('none');
- expect(success.css('display')).toBe('block');
- expect(widget.hasClass('success')).toBeTruthy();
- expect(success.find('.pf-widget-message').html()).toBe(
- successForm2.success.msg
- );
- expect(success.find('.pf-widget-ok')).toBeUndefined;
- expect(success.find('.pf-widget-cancel')).toBeUndefined;
-
- setTimeout(function () {
- expect(widget.hasClass('opened')).toBeTruthy();
- expect(widget.hasClass('success')).toBeTruthy();
-
- done();
- }, 3000);
- });
-
- it('should recognize success state buttons and callbacks', function (done) {
- var successForm3 = new pathfora.Subscription({
- id: 'success-form-cbs',
- msg: 'subscription',
- headline: 'Header',
- layout: 'slideout',
- success: {
- headline: 'test',
- msg: 'a custom success message',
- okShow: true,
- cancelShow: true,
- cancelMessage: 'Custom Cancel',
- confirmAction: {
- name: 'test success confirmation',
- callback: function () {
- window.alert('confirmed');
- },
- },
- cancelAction: {
- name: 'test success cancelation',
- callback: function () {
- window.alert('canceled');
- },
- },
- delay: 0,
- },
- });
-
- pathfora.initializeWidgets([successForm3]);
- var widget = $('#' + successForm3.id);
- var form = widget.find('form');
- form.find('input[name="email"]').val('test@example.com');
- form.find('.pf-widget-ok').click();
-
- var success = widget.find('.success-state');
- expect(form.css('display')).toBe('none');
- expect(success.css('display')).toBe('block');
- expect(widget.hasClass('success')).toBeTruthy();
- expect(success.find('.pf-widget-headline').html()).toBe(
- successForm3.success.headline
- );
- expect(success.find('.pf-widget-message').html()).toBe(
- successForm3.success.msg
- );
-
- expect(success.find('.pf-widget-ok').html()).toBe('Confirm');
- expect(success.find('.pf-widget-cancel').html()).toBe('Custom Cancel');
-
- spyOn(jstag, 'send');
- spyOn(window, 'alert');
- success.find('.pf-widget-ok').click();
-
- expect(jstag.send).toHaveBeenCalledWith(
- jasmine.objectContaining({
- 'pf-widget-id': successForm3.id,
- 'pf-widget-type': 'subscription',
- 'pf-widget-layout': 'slideout',
- 'pf-widget-event': 'success.confirm',
- 'pf-widget-action': successForm3.success.confirmAction.name,
- })
- );
- expect(window.alert).toHaveBeenCalledWith('confirmed');
-
- setTimeout(function () {
- pathfora.clearAll();
- pathfora.initializeWidgets([successForm3]);
-
- widget = $('#' + successForm3.id);
- form = widget.find('form');
- form.find('input[name="email"]').val('test@example.com');
- form.find('.pf-widget-ok').click();
-
- success = widget.find('.success-state');
- success.find('.pf-widget-cancel').click();
-
- expect(jstag.send).toHaveBeenCalledWith(
- jasmine.objectContaining({
- 'pf-widget-id': successForm3.id,
- 'pf-widget-type': 'subscription',
- 'pf-widget-layout': 'slideout',
- 'pf-widget-event': 'success.cancel',
- 'pf-widget-action': successForm3.success.cancelAction.name,
- })
- );
- expect(window.alert).toHaveBeenCalledWith('canceled');
-
- setTimeout(function () {
- done();
- }, 1000);
- }, 1000);
- });
-
- // -------------------------
- // CUSTOM BUTTONS
- // -------------------------
-
- it('should be able to configure custom text', function () {
- var modal = new pathfora.Message({
- id: 'custom-button-text-test',
- layout: 'modal',
- msg: 'Custom button text test',
- headline: 'Hello',
- okMessage: 'Confirm Here',
- cancelMessage: 'Cancel Here',
- });
-
- pathfora.initializeWidgets([modal]);
-
- var widget = $('#' + modal.id),
- actionBtn = widget.find('.pf-widget-ok'),
- cancelBtn = widget.find('.pf-widget-cancel');
-
- expect(actionBtn.html()).toBe('Confirm Here');
- expect(cancelBtn.html()).toBe('Cancel Here');
- });
-
- // -------------------------
- // OLD CUSTOM FIELDS
- // -------------------------
-
- it('should be able to hide and show fields based on config', function () {
- var formfields = new pathfora.Form({
- id: 'sample-form',
- msg: 'subscription',
- headline: 'Header',
- layout: 'slideout',
- fields: {
- title: false,
- username: false,
- },
- required: {
- message: true,
- email: false,
- },
- });
-
- pathfora.initializeWidgets([formfields]);
-
- var theform = $('#' + formfields.id).find('form');
- expect(theform.length).toBe(1);
-
- for (var elem in theform[0].children) {
- if (typeof theform[0].children[elem].getAttribute !== 'undefined') {
- var inputname = theform[0].children[elem].getAttribute('name'),
- inputrequired =
- theform[0].children[elem].getAttribute('data-required');
-
- if (inputname === 'message') {
- expect(inputrequired).toBe('true');
- } else if (inputname !== null) {
- expect(inputrequired).toBe(null);
- }
-
- expect(inputname).not.toBe('username');
- expect(inputname).not.toBe('title');
- }
- }
- });
-
- // -------------------------
- // FORM BUILDER
- // -------------------------
-
- it('should track custom fields to lytics', function (done) {
- var customForm = new pathfora.Form({
- id: 'custom-form-1',
- msg: 'custom form',
- layout: 'slideout',
- formElements: [
- {
- type: 'input',
- name: 'name',
- placeholder: 'Your Name',
- required: true,
- },
- {
- type: 'checkbox-group',
- name: 'terms_agreement',
- required: true,
- values: [
- {
- label: 'I agree',
- value: 'agree',
- },
- ],
- },
- ],
- });
-
- pathfora.initializeWidgets([customForm]);
-
- var widget = $('#' + customForm.id);
- widget.find('[name=terms_agreement]').click();
- widget.find('[name=name]').val('my name here');
- spyOn(jstag, 'send');
-
- widget.find('form').find('.pf-widget-ok').click();
-
- expect(jstag.send).toHaveBeenCalledWith(
- jasmine.objectContaining({
- 'pf-widget-id': customForm.id,
- 'pf-widget-type': 'form',
- 'pf-widget-layout': 'slideout',
- 'pf-widget-event': 'submit',
- 'pf-custom-form': {
- terms_agreement: ['agree'],
- name: 'my name here',
- },
- })
- );
- done();
- });
-
- it('should add labels and placeholders for custom fields if defined', function () {
- var customForm = new pathfora.Form({
- id: 'custom-form-2',
- msg: 'custom form',
- layout: 'slideout',
- formElements: [
- {
- type: 'select',
- label: "What's your favorite animal?",
- placeholder: 'Select an animal...',
- name: 'favorite_animal',
- required: true,
- values: [
- {
- label: 'Cat',
- value: 'cat',
- },
- {
- label: 'Dog',
- value: 'dog',
- },
- {
- label: 'Horse',
- value: 'horse',
- },
- ],
- },
- {
- type: 'checkbox-group',
- label: 'Which ice cream flavors do you like the most?',
- name: 'ice_cream_flavors',
- required: true,
- values: [
- {
- label: 'Vanilla',
- value: 'vanilla',
- },
- {
- label: 'Chocolate',
- value: 'chocolate',
- },
- {
- label: 'Strawberry',
- value: 'strawberry',
- },
- ],
- },
- {
- type: 'textarea',
- label: 'Comments',
- name: 'comments',
- placeholder: 'Any more comments?',
- required: true,
- },
- ],
- });
-
- pathfora.initializeWidgets([customForm]);
-
- var widget = $('#' + customForm.id);
- var labels = widget.find('.pf-form-label');
- var divs = widget.find('.pf-has-label');
-
- expect(labels.length).toBe(customForm.formElements.length);
- expect(divs.length).toBe(customForm.formElements.length);
-
- var i;
-
- for (i = 0; i < labels.length; i++) {
- expect(
- labels[i].innerHTML.indexOf(customForm.formElements[i].label) !== -1
- ).toBeTruthy();
- }
-
- for (i = 0; i < divs.length; i++) {
- var field = divs[i];
- var configElem = customForm.formElements[i];
- if (field.placeholder && configElem.placeholder) {
- expect(field.placeholder).toBe(configElem.placeholder);
- }
-
- if (configElem.type === 'select') {
- expect(field.children[0].innerHTML).toBe(configElem.placeholder);
- }
- }
- });
-
- it('should not submit the form if required fields are not filled out', function (done) {
- var customForm = new pathfora.Form({
- id: 'custom-form-3',
- msg: 'custom form',
- layout: 'slideout',
- formElements: [
- {
- type: 'input',
- placeholder: "What's your favorite animal?",
- name: 'favorite_animal',
- required: true,
- },
- {
- type: 'radio-group',
- label: 'Which ice cream flavors do you like the most?',
- name: 'ice_cream_flavors',
- required: true,
- values: [
- {
- label: 'Vanilla',
- value: 'vanilla',
- },
- {
- label: 'Chocolate',
- value: 'chocolate',
- },
- {
- label: 'Strawberry',
- value: 'strawberry',
- },
- ],
- },
- ],
- });
-
- pathfora.initializeWidgets([customForm]);
-
- var widget = $('#' + customForm.id);
- spyOn(jstag, 'send');
-
- setTimeout(function () {
- widget.find('form').find('.pf-widget-ok').click();
- expect(jstag.send).not.toHaveBeenCalled();
- expect(widget.hasClass('opened')).toBeTruthy();
-
- var required = widget.find('[data-required=true]');
- expect(required.length).toBe(customForm.formElements.length);
-
- for (var i = 0; i < required.length; i++) {
- var req = required[i].parentNode;
- expect(req.className.indexOf('pf-form-required') !== -1).toBeTruthy();
- expect(req.className.indexOf('invalid') !== -1).toBeTruthy();
- }
- done();
- }, 200);
- });
-
- it('should not submit the form if fields are invalid', function (done) {
- var customForm = new pathfora.Form({
- id: 'custom-form-3',
- msg: 'custom form',
- layout: 'slideout',
- formElements: [
- {
- type: 'email',
- placeholder: 'Email',
- name: 'email',
- required: true,
- },
- {
- type: 'radio-group',
- label: 'Which ice cream flavors do you like the most?',
- name: 'ice_cream_flavors',
- values: [
- {
- label: 'Vanilla',
- value: 'vanilla',
- },
- {
- label: 'Chocolate',
- value: 'chocolate',
- },
- {
- label: 'Strawberry',
- value: 'strawberry',
- },
- ],
- },
- ],
- });
-
- pathfora.initializeWidgets([customForm]);
-
- var widget = $('#' + customForm.id);
- spyOn(jstag, 'send');
-
- setTimeout(function () {
- widget.find('input[name=email]').val('zkjhfkdjh');
- widget.find('form').find('.pf-widget-ok').click();
- expect(jstag.send).not.toHaveBeenCalled();
- expect(widget.hasClass('opened')).toBeTruthy();
-
- var invalid = widget.find('[data-validate=true]');
- expect(invalid.length).toBe(1);
-
- for (var i = 0; i < invalid.length; i++) {
- var req = invalid[i].parentNode;
- expect(req.className.indexOf('pf-form-required') !== -1).toBeTruthy();
- expect(req.className.indexOf('bad-validation') !== -1).toBeTruthy();
- }
-
- // also check required validation
- widget.find('input[name=email]').val('');
- widget.find('form').find('.pf-widget-ok').click();
- expect(jstag.send).not.toHaveBeenCalled();
- expect(widget.hasClass('opened')).toBeTruthy();
-
- invalid = widget.find('[data-required=true]');
- expect(invalid.length).toBe(1);
-
- for (var j = 0; j < invalid.length; j++) {
- var reqField = invalid[j].parentNode;
- expect(
- reqField.className.indexOf('pf-form-required') !== -1
- ).toBeTruthy();
- expect(reqField.className.indexOf('invalid') !== -1).toBeTruthy();
- }
- done();
- }, 200);
- });
-
- it('should not submit the form if a date field is invalid', function (done) {
- var customForm = new pathfora.Form({
- id: 'custom-form-3',
- msg: 'custom form',
- layout: 'slideout',
- formElements: [
- {
- type: 'date',
- name: 'birthday',
- maxDate: 'today',
- minDate: '01-01-2020',
- },
- {
- type: 'radio-group',
- label: 'Which ice cream flavors do you like the most?',
- name: 'ice_cream_flavors',
- values: [
- {
- label: 'Vanilla',
- value: 'vanilla',
- },
- {
- label: 'Chocolate',
- value: 'chocolate',
- },
- {
- label: 'Strawberry',
- value: 'strawberry',
- },
- ],
- },
- ],
- });
-
- pathfora.initializeWidgets([customForm]);
-
- var widget = $('#' + customForm.id);
- spyOn(jstag, 'send');
-
- setTimeout(function () {
- widget.find('input[name=birthday]').val('2010-10-10');
- widget.find('form').find('.pf-widget-ok').click();
- expect(jstag.send).not.toHaveBeenCalled();
- expect(widget.hasClass('opened')).toBeTruthy();
-
- var invalid = widget.find('[data-validate=true]');
- expect(invalid.length).toBe(1);
-
- for (var i = 0; i < invalid.length; i++) {
- var req = invalid[i].parentNode;
- expect(req.className.indexOf('pf-form-required') !== -1).toBeTruthy();
- expect(req.className.indexOf('bad-validation') !== -1).toBeTruthy();
- }
- done();
- }, 200);
- });
-
- // -------------------------
- // CUSTOM FORM VALIDATION
- // -------------------------
-
- it('should not submit the form if custom validation fails', function (done) {
- var customForm = new pathfora.Form({
- id: 'custom-form-4',
- msg: 'custom form',
- layout: 'slideout',
- formElements: [
- {
- type: 'text',
- placeholder: 'Only 5 Digits Allowed',
- name: 'postal_code',
- pattern: '^[0-9]{5}$',
- required: true,
- },
- ],
- });
-
- pathfora.initializeWidgets([customForm]);
-
- var widget = $('#' + customForm.id);
- spyOn(jstag, 'send');
-
- setTimeout(function () {
- var form = widget.find('form');
- var field = form.find('input[name="postal_code"]');
-
- field.val('notvalid');
- form.find('.pf-widget-ok').trigger('click');
- expect(jstag.send).not.toHaveBeenCalled();
- expect(widget.hasClass('opened')).toBeTruthy();
-
- var required = widget.find('[data-required=true]');
- expect(required.length).toBe(customForm.formElements.length);
-
- for (var i = 0; i < required.length; i++) {
- var req = required[i].parentNode;
- expect(req.className.indexOf('invalid') !== -1).toBeFalsy();
- }
-
- done();
- }, 500);
- });
-
- it('should not submit the form if only 1 of 2 fields pass validation', function (done) {
- var customForm = new pathfora.Form({
- id: 'custom-form-4',
- msg: 'custom form',
- layout: 'slideout',
- formElements: [
- {
- type: 'text',
- placeholder: '6 Digits zbzbzb',
- name: 'custom_field_1',
- pattern: '^[zb]{6}$',
- },
- {
- type: 'text',
- placeholder: 'Only 5 Digits Allowed',
- name: 'custom_field_2',
- pattern: '^[0-9]{5}$',
- },
- ],
- });
-
- pathfora.initializeWidgets([customForm]);
-
- var widget = $('#' + customForm.id);
- spyOn(jstag, 'send');
-
- setTimeout(function () {
- var form = widget.find('form');
-
- var field1 = form.find('input[name="custom_field_1"]');
- field1.val('notvalid');
-
- var field2 = form.find('input[name="custom_field_2"]');
- field2.val('12345');
-
- form.find('.pf-widget-ok').trigger('click');
-
- expect(jstag.send).not.toHaveBeenCalled();
- expect(widget.hasClass('opened')).toBeTruthy();
-
- var validationRequirement = widget.find('[data-validate=true]');
- expect(validationRequirement.length).toBe(customForm.formElements.length);
-
- // expect field1 to be invalid
- var req = validationRequirement[0].parentNode;
- expect(req.className.indexOf('bad-validation') !== -1).toBeTruthy();
-
- // expect field2 to be valid
- req = validationRequirement[1].parentNode;
- expect(req.className.indexOf('bad-validation') !== -1).toBeFalsy();
-
- done();
- }, 500);
- });
-
- it('should submit the form if custom validation passes', function (done) {
- var customForm = new pathfora.Form({
- id: 'custom-form-5',
- msg: 'custom form',
- layout: 'slideout',
- formElements: [
- {
- type: 'text',
- placeholder: 'Only 5 Digits Allowed',
- name: 'postal_code',
- pattern: '^[0-9]{5}$',
- required: true,
- },
- ],
- });
-
- pathfora.initializeWidgets([customForm]);
-
- var widget = $('#' + customForm.id);
- spyOn(jstag, 'send');
-
- setTimeout(function () {
- var form = widget.find('form');
- var field = form.find('input[name="postal_code"]');
-
- field.val('12345');
- form.find('.pf-widget-ok').trigger('click');
- expect(jstag.send).toHaveBeenCalled();
- expect(widget.hasClass('opened')).toBeFalsy();
-
- var required = widget.find('[data-required=true]');
- expect(required.length).toBe(customForm.formElements.length);
-
- for (var i = 0; i < required.length; i++) {
- var req = required[i].parentNode;
- expect(req.className.indexOf('invalid') !== -1).toBeFalsy();
- }
-
- done();
- }, 200);
- });
-
- it('should add validation parameters if special case of us-postal-code', function (done) {
- var customForm = new pathfora.Form({
- id: 'custom-form-6',
- msg: 'custom form',
- layout: 'slideout',
- formElements: [
- {
- type: 'us-postal-code',
- placeholder: 'Only 5 Digits Allowed',
- name: 'postal_code',
- required: true,
- },
- ],
- });
-
- pathfora.initializeWidgets([customForm]);
-
- var widget = $('#' + customForm.id);
- spyOn(jstag, 'send');
-
- setTimeout(function () {
- var form = widget.find('form');
- var field = form.find('input[name="postal_code"]');
-
- var pattern = field.attr('enforcePattern');
- expect(pattern).toBe('^[0-9]{5}$');
-
- field.val('1234a');
- form.find('.pf-widget-ok').trigger('click');
- expect(jstag.send).not.toHaveBeenCalled();
- expect(widget.hasClass('opened')).toBeTruthy();
-
- var required = widget.find('[data-required=true]');
- expect(required.length).toBe(customForm.formElements.length);
-
- for (var i = 0; i < required.length; i++) {
- var req = required[i].parentNode;
- expect(req.className.indexOf('invalid') !== -1).toBeFalsy();
- }
-
- done();
- }, 500);
- });
-
- // -------------------------
- // IGNORED
- // -------------------------
-
- // Future functionalities
- xit('should allow custom messages on action buttons', function () {
- throw 'pass';
- });
-
- xit('should be able to show after specific number of visits', function () {
- throw 'pass';
- });
-
- xit('should be able to randomly choose one of available variations', function () {
- throw 'pass';
- });
-
- xit('should show warning when user tries to use an invalid position', function () {
- spyOn(console, 'warn');
-
- var w1 = new pathfora.Message({
- msg: 'test warning display',
- id: 'position-widget',
- layout: 'bar',
- });
-
- var w2 = new pathfora.Message({
- msg: 'invalid position test',
- layout: 'bar',
- id: 'wrong-position-2',
- position: 'wrong-position',
- });
-
- pathfora.initializeWidgets([w1]);
- // NOTE Will always fail agaist production env
- // expect(console.warn).not.toHaveBeenCalled();
-
- pathfora.clearAll();
-
- pathfora.initializeWidgets([w2]);
- // NOTE Will always fail agaist production env
- // expect(console.warn).toHaveBeenCalledWith('wrong-position is not valid position for bar');
- });
-});
diff --git a/test/acceptance/widgets.spec.js b/test/acceptance/widgets.spec.js
new file mode 100644
index 00000000..88c894e1
--- /dev/null
+++ b/test/acceptance/widgets.spec.js
@@ -0,0 +1,509 @@
+import createAndDispatchKeydown from '../utils/create-and-dispatch-keydown.js';
+import globalReset from '../utils/global-reset';
+import {
+ createMessageWidget,
+ createFormWidget,
+ createSubscriptionWidget,
+ expectWidgetVisible,
+ expectWidgetHidden,
+ expectWidgetClosed,
+ expectWidgetTheme,
+ createSiteGateWidget,
+} from '../utils/test-helpers';
+
+describe('widgets', function () {
+ beforeEach(function () {
+ globalReset();
+ });
+
+ it('should not allow to register 2 widgets with the same id', function () {
+ var w1 = createMessageWidget({
+ msg: 'Duplicate id test1',
+ layout: 'modal',
+ id: 'asd',
+ });
+
+ var w2 = createFormWidget({
+ msg: 'Duplcate id test2',
+ layout: 'slideout',
+ id: 'asd',
+ });
+
+ expect(function () {
+ pathfora.initializeWidgets([w1, w2]);
+ }).toThrow(new Error('Cannot add two widgets with the same id'));
+ });
+
+ it('should use specified global config for all widgets', function () {
+ var messageBar = createMessageWidget({
+ layout: 'bar',
+ id: 'global-config-1',
+ msg: 'test',
+ });
+
+ var config = {
+ generic: {
+ theme: 'light',
+ },
+ };
+
+ pathfora.initializeWidgets([messageBar], config);
+
+ expectWidgetTheme(messageBar.id, 'light');
+ var bar = $('#' + messageBar.id);
+ expect(bar.hasClass('pf-theme-default')).toBe(false);
+ });
+
+ it('should be able to clear all widgets and handlers', function (done) {
+ var clearDataObject = {
+ pageViews: 0,
+ timeSpentOnPage: 0,
+ closedWidgets: [],
+ completedActions: [],
+ cancelledActions: [],
+ displayedWidgets: [],
+ abTestingGroups: [],
+ };
+
+ var form = createSubscriptionWidget({
+ msg: 'test',
+ id: 'clear-widget',
+ layout: 'modal',
+ });
+
+ pathfora.initializeWidgets([form]);
+
+ setTimeout(function () {
+ expectWidgetVisible(form.id);
+ expect(pathfora.getDataObject()).not.toEqual(clearDataObject);
+
+ pathfora.clearAll();
+ expectWidgetClosed(form.id);
+ expect(pathfora.getDataObject()).toEqual(clearDataObject);
+ done();
+ }, 200);
+ });
+
+ it('should be able to clear specific widgets by their IDs', function (done) {
+ var widget1 = createMessageWidget({
+ msg: 'Widget 1',
+ id: 'clear-by-id-1',
+ layout: 'modal',
+ });
+
+ var widget2 = createMessageWidget({
+ msg: 'Widget 2',
+ id: 'clear-by-id-2',
+ layout: 'slideout',
+ });
+
+ var widget3 = createMessageWidget({
+ msg: 'Widget 3',
+ id: 'clear-by-id-3',
+ layout: 'bar',
+ });
+
+ pathfora.initializeWidgets([widget1, widget2, widget3]);
+
+ setTimeout(function () {
+ // All widgets should be opened initially
+ expectWidgetVisible(widget1.id);
+ expectWidgetVisible(widget2.id);
+ expectWidgetVisible(widget3.id);
+
+ // Clear only widget1 and widget3
+ pathfora.clearById(['clear-by-id-1', 'clear-by-id-3']);
+
+ setTimeout(function () {
+ // Widget1 and widget3 should be closed and removed from DOM
+ expectWidgetHidden(widget1.id);
+ expectWidgetHidden(widget3.id);
+
+ // Widget2 should still be opened
+ expectWidgetVisible(widget2.id);
+
+ // Clear widget2 as well
+ pathfora.clearById(['clear-by-id-2']);
+
+ setTimeout(function () {
+ // All widgets should now be closed and removed
+ expectWidgetHidden(widget1.id);
+ expectWidgetHidden(widget2.id);
+ expectWidgetHidden(widget3.id);
+ done();
+ }, 200);
+ }, 200);
+ }, 200);
+ });
+
+ it('should handle clearById with invalid input gracefully', function (done) {
+ var widget = createMessageWidget({
+ msg: 'Test widget',
+ id: 'invalid-input-test',
+ layout: 'modal',
+ });
+
+ pathfora.initializeWidgets([widget]);
+
+ // Test with non-array input
+ spyOn(console, 'warn');
+ pathfora.clearById('not-an-array');
+
+ expect(console.warn).toHaveBeenCalledWith(
+ 'clearById: widgetIds must be an array'
+ );
+
+ // Widget should still be opened
+ setTimeout(function () {
+ expect($('#' + widget.id).hasClass('opened')).toBeTruthy();
+ done();
+ }, 200);
+ });
+
+ it('should handle clearById with non-existent widget IDs', function (done) {
+ var widget = createMessageWidget({
+ msg: 'Test widget',
+ id: 'existing-widget',
+ layout: 'modal',
+ });
+
+ pathfora.initializeWidgets([widget]);
+
+ var element = $('#' + widget.id);
+
+ setTimeout(function () {
+ expect(element.hasClass('opened')).toBeTruthy();
+
+ // Try to clear non-existent widget IDs
+ pathfora.clearById(['non-existent-1', 'non-existent-2']);
+
+ // Existing widget should still be opened
+ expect(element.hasClass('opened')).toBeTruthy();
+ expect($('#' + widget.id).length).toBe(1);
+
+ // Clear the existing widget
+ pathfora.clearById(['existing-widget']);
+
+ setTimeout(function () {
+ expect(element.hasClass('opened')).toBeFalsy();
+ expect($('#' + widget.id).length).toBe(0);
+ done();
+ }, 200);
+ }, 200);
+ });
+
+ it('should be able to be displayed on document', function (done) {
+ var promoWidget = createMessageWidget({
+ layout: 'bar',
+ msg: 'Opening widget',
+ id: 'widget-1',
+ });
+
+ pathfora.initializeWidgets([promoWidget]);
+
+ // should append element to DOM
+ var widget = $('#' + promoWidget.id);
+ expect(widget).toBeDefined();
+
+ // should have class 'opened' after while
+ pathfora.showWidget(promoWidget);
+
+ setTimeout(function () {
+ expect(widget.hasClass('opened')).toBeTruthy();
+ done();
+ }, 200);
+ });
+
+ it('should have proper id specified', function (done) {
+ var w1 = createMessageWidget({
+ layout: 'slideout',
+ position: 'right',
+ msg: 'Welcome to our test website',
+ id: 'test-id-widget',
+ });
+
+ expect(function () {
+ return new pathfora.Message({
+ layout: 'slideout',
+ position: 'left',
+ msg: 'Welcome to our test website',
+ });
+ }).toThrow(new Error('All widgets must have an id value'));
+
+ pathfora.initializeWidgets([w1]);
+
+ setTimeout(function () {
+ var right = $('.pf-widget.pf-position-right');
+ expect(right).toBeDefined();
+ expect(right.attr('id')).toBe('test-id-widget');
+ done();
+ }, 200);
+ });
+
+ it("should not append widget second time if it's already opened", function (done) {
+ var openedWidget = createMessageWidget({
+ layout: 'modal',
+ id: 'append-widget',
+ msg: 'test widget',
+ });
+
+ pathfora.initializeWidgets([openedWidget]);
+
+ var widget = $('#' + openedWidget.id);
+
+ // timeouts gives some time for appending to DOM
+ setTimeout(function () {
+ expect(widget.hasClass('opened')).toBeTruthy();
+ pathfora.showWidget(openedWidget);
+
+ setTimeout(function () {
+ expect($('#' + openedWidget.id).length).toEqual(1);
+ done();
+ }, 200);
+ }, 500);
+ });
+
+ it('should close when the x button is clicked', function (done) {
+ var testWidget = createMessageWidget({
+ layout: 'modal',
+ msg: 'Close widget test',
+ id: 'close-clear-widget',
+ });
+
+ pathfora.initializeWidgets([testWidget]);
+
+ var widget = $('#' + testWidget.id);
+ expect(widget).toBeDefined();
+
+ setTimeout(function () {
+ expect(widget.hasClass('opened')).toBeTruthy();
+
+ widget.find('.pf-widget-close').click();
+ expect(widget.hasClass('opened')).toBeFalsy();
+
+ setTimeout(function () {
+ expect($('#' + testWidget.id).length).toBe(0);
+ done();
+ }, 600);
+ }, 200);
+ });
+
+ it('should close if the escape key is pressed and it is a modal', function (done) {
+ var modal = createMessageWidget({
+ id: 'modal-esc-test',
+ layout: 'modal',
+ headline: 'Message Title',
+ msg: 'test',
+ });
+
+ var gate = createSiteGateWidget({
+ id: 'modal-esc-test2',
+ headline: 'Message Title',
+ msg: 'test',
+ });
+
+ pathfora.initializeWidgets([modal, gate]);
+
+ var widget = $('#' + modal.id);
+ var widgetGate = $('#' + gate.id);
+ expect(widget).toBeDefined();
+ expect(widgetGate).toBeDefined();
+
+ setTimeout(function () {
+ expect(widget.hasClass('opened')).toBeTruthy();
+ expect(widgetGate.hasClass('opened')).toBeTruthy();
+
+ createAndDispatchKeydown(27, document);
+
+ expect(widget.hasClass('opened')).toBeFalsy();
+ expect(widgetGate.hasClass('opened')).toBeTruthy();
+
+ setTimeout(function () {
+ expect($('#' + modal.id).length).toBe(0);
+ expect($('#' + gate.id).length).toBe(1);
+ done();
+ }, 600);
+ }, 200);
+ });
+
+ it('should handle missing values properly and never surface undefined', function () {
+ var message = createMessageWidget({
+ id: 'message-test-widget',
+ layout: 'slideout',
+ headline: 'Message Title',
+ theme: 'custom',
+ });
+
+ var form = createFormWidget({
+ id: 'form-test-widget',
+ layout: 'modal',
+ headline: 'Headline Title',
+ theme: 'custom',
+ });
+
+ var subscription = createSubscriptionWidget({
+ id: 'subscription-test-widget',
+ layout: 'bar',
+ theme: 'custom',
+ });
+
+ pathfora.initializeWidgets([message, form, subscription]);
+
+ // test message
+ var mwidget = $('#' + message.id),
+ mheadline = mwidget.find('.pf-widget-headline'),
+ mtext = mwidget.find('.pf-widget-message');
+
+ expect(mheadline.html()).not.toEqual('undefined');
+ expect(mtext.html()).not.toEqual('undefined');
+
+ // test form
+ var fwidget = $('#' + form.id),
+ fheadline = fwidget.find('.pf-widget-headline'),
+ ftext = fwidget.find('.pf-widget-message');
+
+ expect(fheadline.html()).not.toEqual('undefined');
+ expect(ftext.html()).not.toEqual('undefined');
+
+ // test subscription
+ var swidget = $('#' + subscription.id);
+ var stext = swidget.find('.pf-widget-message');
+ expect(stext.html()).not.toEqual('undefined');
+ });
+
+ it('should not allow to be initialized without default properties', function () {
+ var missingParams = function () {
+ var promoWidget = new pathfora.Message();
+ pathfora.initializeWidgets([promoWidget]);
+ };
+
+ expect(missingParams).toThrow(new Error('Config object is missing'));
+ });
+
+ it('should not show branding assets unless set otherwise', function () {
+ var w1 = createMessageWidget({
+ msg: 'test',
+ id: 'branding1',
+ layout: 'slideout',
+ branding: true,
+ });
+
+ var w2 = createMessageWidget({
+ msg: 'test',
+ id: 'branding2',
+ layout: 'modal',
+ });
+
+ pathfora.initializeWidgets([w1, w2]);
+
+ var widget1 = $('#' + w1.id),
+ widget2 = $('#' + w2.id);
+
+ expect(widget1.find('.branding svg').length).toBe(1);
+ expect(widget2.find('.branding svg').length).toBe(0);
+ });
+
+ it('should display footer when footerText setting is used', function () {
+ var modalFooter = createMessageWidget({
+ id: 'footer1',
+ msg: 'test',
+ layout: 'modal',
+ footerText: 'Footer text',
+ });
+
+ var modalNoFooter = createMessageWidget({
+ id: 'footer2',
+ msg: 'test',
+ layout: 'modal',
+ });
+
+ var slideoutFooter = createMessageWidget({
+ id: 'slidout1',
+ msg: 'test',
+ layout: 'slideout',
+ footerText: 'Footer text',
+ });
+
+ var slideoutNoFooter = createMessageWidget({
+ id: 'slideout2',
+ msg: 'test',
+ layout: 'slideout',
+ });
+
+ pathfora.initializeWidgets([
+ modalFooter,
+ modalNoFooter,
+ slideoutFooter,
+ slideoutNoFooter,
+ ]);
+
+ var modal1 = $('#' + modalFooter.id),
+ modal2 = $('#' + modalNoFooter.id),
+ slideout1 = $('#' + slideoutFooter.id),
+ slideout2 = $('#' + slideoutNoFooter.id);
+ expect(modal1.find('.pf-widget-footer').html()).toEqual('Footer text');
+ expect(modal2.find('.pf-widget-footer').html()).toEqual('');
+ expect(slideout1.find('.pf-widget-footer').html()).toEqual('Footer text');
+ expect(slideout2.find('.pf-widget-footer').html()).toEqual('');
+ });
+
+ it('should contain pf-widget-text div for inline and modal layouts', function () {
+ var modal = createMessageWidget({
+ id: 'modal',
+ msg: 'testmodal',
+ layout: 'modal',
+ });
+ var div = document.createElement('div');
+ div.className = 'some-dom-element';
+ document.body.appendChild(div);
+ var inline = createMessageWidget({
+ id: 'inline',
+ layout: 'inline',
+ position: '.some-dom-element',
+ msg: 'testing',
+ });
+ var slideout = createMessageWidget({
+ id: 'slideout',
+ msg: 'test',
+ layout: 'slideout',
+ });
+ pathfora.initializeWidgets([modal, inline, slideout]);
+ var modalWidget = $('#' + modal.id),
+ inlineWidget = $('#' + inline.id),
+ slideoutWidget = $('#' + slideout.id);
+ expect(modalWidget.find('.pf-widget-text').html()).toBeDefined();
+ expect(inlineWidget.find('.pf-widget-text').html()).toBeDefined();
+ expect(slideoutWidget.find('.pf-widget-text').html()).toBeUndefined();
+ });
+
+ it('should append pf-widget-img to pf-widget-content for modal and inline layouts', function () {
+ var modal = createMessageWidget({
+ id: 'modal',
+ msg: 'testmodal',
+ layout: 'modal',
+ image: 'https://lytics.github.io/pathforadocs/assets/lion.jpg',
+ });
+ var div = document.createElement('div');
+ div.className = 'some-dom-element';
+ document.body.appendChild(div);
+ var inline = createMessageWidget({
+ id: 'inline',
+ layout: 'inline',
+ position: '.some-dom-element',
+ msg: 'testing',
+ image: 'https://lytics.github.io/pathforadocs/assets/lion.jpg',
+ });
+ pathfora.initializeWidgets([modal, inline]);
+ var modalWidget = $('#' + modal.id),
+ inlineWidget = $('#' + inline.id);
+ expect(
+ modalWidget.find('.pf-widget-content').find('img').html()
+ ).toBeDefined();
+ expect(
+ inlineWidget.find('.pf-widget-content').find('img').html()
+ ).toBeDefined();
+ expect(
+ modalWidget.find('.pf-widget-text').find('img').html()
+ ).toBeUndefined();
+ });
+});
diff --git a/test/utils/test-helpers.js b/test/utils/test-helpers.js
new file mode 100644
index 00000000..95919690
--- /dev/null
+++ b/test/utils/test-helpers.js
@@ -0,0 +1,517 @@
+/**
+ * Create a Message widget with default test configuration
+ * @param {Object} overrides - Properties to override defaults
+ * @returns {Object} Message widget instance
+ */
+export function createMessageWidget(overrides = {}) {
+ const defaults = {
+ id: 'test-message-' + Date.now() + Math.random(),
+ msg: 'Test message',
+ layout: 'modal',
+ headline: 'Test Headline',
+ };
+ return new pathfora.Message({ ...defaults, ...overrides });
+}
+
+/**
+ * Create a Form widget with default test configuration
+ * @param {Object} overrides - Properties to override defaults
+ * @returns {Object} Form widget instance
+ */
+export function createFormWidget(overrides = {}) {
+ const defaults = {
+ id: 'test-form-' + Date.now() + Math.random(),
+ msg: 'Test form message',
+ layout: 'modal',
+ headline: 'Test Form',
+ };
+ return new pathfora.Form({ ...defaults, ...overrides });
+}
+
+/**
+ * Create a Subscription widget with default test configuration
+ * @param {Object} overrides - Properties to override defaults
+ * @returns {Object} Subscription widget instance
+ */
+export function createSubscriptionWidget(overrides = {}) {
+ const defaults = {
+ id: 'test-subscription-' + Date.now() + Math.random(),
+ msg: 'Subscribe to our newsletter',
+ layout: 'slideout',
+ headline: 'Stay Updated',
+ };
+ return new pathfora.Subscription({ ...defaults, ...overrides });
+}
+
+/**
+ * Create a SiteGate widget with default test configuration
+ * @param {Object} overrides - Properties to override defaults
+ * @returns {Object} SiteGate widget instance
+ */
+export function createSiteGateWidget(overrides = {}) {
+ const defaults = {
+ id: 'test-gate-' + Date.now() + Math.random(),
+ headline: 'Welcome!',
+ msg: 'Please provide your email to continue',
+ };
+ return new pathfora.SiteGate({ ...defaults, ...overrides });
+}
+
+/**
+ * Create form elements array with common test fields
+ * @param {string} type - Type of form elements ('basic', 'complex', 'custom')
+ * @param {Array} customFields - Custom fields to add
+ * @returns {Array} Form elements configuration
+ */
+export function createFormElements(type = 'basic', customFields = []) {
+ const formElements = {
+ basic: [
+ {
+ type: 'input',
+ name: 'name',
+ placeholder: 'Your Name',
+ required: true,
+ },
+ {
+ type: 'email',
+ name: 'email',
+ placeholder: 'Email Address',
+ required: true,
+ },
+ ],
+ complex: [
+ {
+ type: 'input',
+ name: 'name',
+ placeholder: 'Your Name',
+ required: true,
+ },
+ {
+ type: 'email',
+ name: 'email',
+ placeholder: 'Email Address',
+ required: true,
+ },
+ {
+ type: 'radio-group',
+ label: "What's your favorite color?",
+ name: 'favorite_color',
+ required: true,
+ values: [
+ { label: 'Red', value: 'red' },
+ { label: 'Blue', value: 'blue' },
+ { label: 'Green', value: 'green' },
+ ],
+ },
+ {
+ type: 'checkbox-group',
+ label: 'Select your interests',
+ name: 'interests',
+ values: [
+ { label: 'Technology', value: 'tech' },
+ { label: 'Design', value: 'design' },
+ { label: 'Marketing', value: 'marketing' },
+ ],
+ },
+ ],
+ custom: customFields,
+ };
+
+ return formElements[type] || formElements.basic;
+}
+
+/**
+ * Create widget with callback actions
+ * @param {Function} WidgetConstructor - Widget constructor (e.g., pathfora.Message)
+ * @param {Function} confirmCallback - Confirm action callback
+ * @param {Function} cancelCallback - Cancel action callback
+ * @param {Object} overrides - Additional widget properties
+ * @returns {Object} Widget instance with callbacks
+ */
+export function createWidgetWithCallbacks(
+ WidgetConstructor,
+ confirmCallback,
+ cancelCallback,
+ overrides = {}
+) {
+ const config = {
+ id: 'test-widget-callbacks-' + Date.now() + Math.random(),
+ msg: 'Test message with callbacks',
+ layout: 'modal',
+ ...overrides,
+ };
+
+ if (confirmCallback) {
+ config.confirmAction = {
+ name: 'Test confirm action',
+ callback: confirmCallback,
+ };
+ }
+
+ if (cancelCallback) {
+ config.cancelAction = {
+ name: 'Test cancel action',
+ callback: cancelCallback,
+ };
+ }
+
+ return new WidgetConstructor(config);
+}
+
+/**
+ * Setup Lytics lio object with test data
+ * @param {Array} segments - Array of segment names
+ * @param {string} accountId - Account ID
+ * @param {Object} additionalData - Additional data to merge into lio.data
+ */
+export function setupLioMock(
+ segments = ['all'],
+ accountId = '0',
+ additionalData = {}
+) {
+ window.lio = {
+ data: {
+ segments: segments,
+ ...additionalData,
+ },
+ account: {
+ id: accountId,
+ },
+ loaded: true,
+ };
+}
+
+/**
+ * Setup jstag mock with user entity data
+ * @param {Object} userData - User data object
+ * @param {string} cid - Client ID
+ */
+export function setupJstagMock(userData = {}, cid = '123') {
+ window.jstag.getEntity = function () {
+ return {
+ data: {
+ user: userData,
+ },
+ };
+ };
+ window.jstag.config.cid = cid;
+}
+
+/**
+ * Setup jstag tracking spy
+ * @returns {Object} Jasmine spy object
+ */
+export function setupTrackingSpy() {
+ return spyOn(window.jstag, 'send');
+}
+
+/**
+ * Mock AJAX response for recommendation widgets
+ * @param {Array} recommendations - Array of recommendation objects
+ * @returns {Object} Mock response object
+ */
+export function mockRecommendationResponse(recommendations = []) {
+ const defaultRecommendation = {
+ url: 'www.example.com/1',
+ title: 'Example Title',
+ description: 'An example description',
+ primary_image:
+ 'http://images.all-free-download.com/images/graphiclarge/blue_envelope_icon_vector_281117.jpg',
+ confidence: 0.499,
+ visited: false,
+ };
+
+ const data =
+ recommendations.length > 0 ? recommendations : [defaultRecommendation];
+
+ return {
+ status: 200,
+ contentType: 'application/json',
+ responseText: JSON.stringify({ data: data }),
+ };
+}
+
+/**
+ * Assert that a widget is visible in the DOM
+ * @param {string} widgetId - Widget ID
+ */
+export function expectWidgetVisible(widgetId) {
+ const widget = $('#' + widgetId);
+ expect(widget.length).toBe(1);
+ expect(widget.hasClass('opened')).toBeTruthy();
+}
+
+/**
+ * Assert that a widget is not visible in the DOM
+ * @param {string} widgetId - Widget ID
+ */
+export function expectWidgetHidden(widgetId) {
+ const widget = $('#' + widgetId);
+ expect(widget.length).toBe(0);
+}
+
+/**
+ * Assert that a widget is not currently shown (closed)
+ * @param {string} widgetId - Widget ID
+ */
+export function expectWidgetClosed(widgetId) {
+ const widget = $('#' + widgetId);
+ expect(widget.hasClass('opened')).toBeFalsy();
+}
+
+/**
+ * Assert that a tracking event was sent
+ * @param {string} widgetId - Widget ID
+ * @param {string} eventType - Event type (show, confirm, cancel, close)
+ * @param {string} eventName - Optional action name
+ * @param {Object} additionalProps - Additional properties to check
+ */
+export function expectTrackingEvent(
+ widgetId,
+ eventType,
+ eventName = null,
+ additionalProps = {}
+) {
+ const expected = {
+ 'pf-widget-id': widgetId,
+ 'pf-widget-event': eventType,
+ ...additionalProps,
+ };
+
+ if (eventName) {
+ expected['pf-widget-action'] = eventName;
+ }
+
+ expect(window.jstag.send).toHaveBeenCalledWith(
+ jasmine.objectContaining(expected)
+ );
+}
+
+/**
+ * Assert widget content matches expected values
+ * @param {string} widgetId - Widget ID
+ * @param {Object} content - Object with headline and/or msg properties
+ */
+export function expectWidgetContent(widgetId, content) {
+ const widget = $('#' + widgetId);
+
+ if (content.headline !== undefined) {
+ const headline = widget.find('.pf-widget-headline');
+ expect(headline.html()).toBe(content.headline);
+ }
+
+ if (content.msg !== undefined) {
+ const message = widget.find('.pf-widget-message');
+ expect(message.html()).toBe(content.msg);
+ }
+}
+
+/**
+ * Assert widget has expected theme class
+ * @param {string} widgetId - Widget ID
+ * @param {string} theme - Theme name (dark, light, custom)
+ */
+export function expectWidgetTheme(widgetId, theme) {
+ const widget = $('#' + widgetId);
+ expect(widget.hasClass('pf-theme-' + theme)).toBeTruthy();
+}
+
+/**
+ * Assert widget has expected layout class
+ * @param {string} widgetId - Widget ID
+ * @param {string} layout - Layout name (modal, slideout, bar, button, inline)
+ */
+export function expectWidgetLayout(widgetId, layout) {
+ const widget = $('#' + widgetId);
+ expect(widget.hasClass('pf-widget-' + layout)).toBeTruthy();
+}
+
+/**
+ * Assert form validation errors are displayed
+ * @param {string} widgetId - Widget ID
+ * @param {number} expectedErrorCount - Expected number of required fields with errors
+ */
+export function expectFormValidationErrors(widgetId, expectedErrorCount) {
+ const widget = $('#' + widgetId);
+ const required = widget.find('[data-required=true]');
+ expect(required.length).toBe(expectedErrorCount);
+
+ for (let i = 0; i < required.length; i++) {
+ const parent = required[i].parentNode;
+ expect(parent.className.indexOf('pf-form-required') !== -1).toBeTruthy();
+ expect(parent.className.indexOf('invalid') !== -1).toBeTruthy();
+ }
+}
+
+/**
+ * Assert form has expected number of fields
+ * @param {string} widgetId - Widget ID
+ * @param {Object} fieldCounts - Object with counts for different field types
+ */
+export function expectFormFields(widgetId, fieldCounts) {
+ const widget = $('#' + widgetId);
+ const form = widget.find('form');
+
+ if (fieldCounts.inputs !== undefined) {
+ expect(form.find('input[type="text"]').length).toBe(fieldCounts.inputs);
+ }
+
+ if (fieldCounts.emails !== undefined) {
+ expect(form.find('input[type="email"]').length).toBe(fieldCounts.emails);
+ }
+
+ if (fieldCounts.radios !== undefined) {
+ expect(form.find('input[type="radio"]').length).toBeGreaterThanOrEqual(
+ fieldCounts.radios
+ );
+ }
+
+ if (fieldCounts.checkboxes !== undefined) {
+ expect(form.find('input[type="checkbox"]').length).toBeGreaterThanOrEqual(
+ fieldCounts.checkboxes
+ );
+ }
+}
+
+/**
+ * Wait for widget to be rendered in the DOM
+ * @param {string} widgetId - Widget ID
+ * @param {number} delay - Delay in milliseconds (default: 200)
+ * @returns {Promise} Promise that resolves with jQuery element
+ */
+export function waitForWidget(widgetId, delay = 200) {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve($('#' + widgetId));
+ }, delay);
+ });
+}
+
+/**
+ * Wait for a condition to be true
+ * @param {Function} condition - Function that returns true when condition is met
+ * @param {number} timeout - Maximum time to wait in milliseconds (default: 1000)
+ * @param {number} interval - Check interval in milliseconds (default: 50)
+ * @returns {Promise} Promise that resolves when condition is met
+ */
+export function waitFor(condition, timeout = 1000, interval = 50) {
+ return new Promise((resolve, reject) => {
+ const startTime = Date.now();
+
+ const check = () => {
+ if (condition()) {
+ resolve();
+ } else if (Date.now() - startTime > timeout) {
+ reject(new Error('Timeout waiting for condition'));
+ } else {
+ setTimeout(check, interval);
+ }
+ };
+
+ check();
+ });
+}
+
+/**
+ * Initialize widgets and wait for them to render (for async/await tests)
+ * @param {Array|Object} widgets - Widget(s) to initialize
+ * @param {Object} config - Optional config object
+ * @param {number} delay - Delay to wait after initialization (default: 200)
+ * @returns {Promise} Promise that resolves after delay
+ */
+export function initializeAndWait(widgets, config = null, delay = 200) {
+ return new Promise((resolve) => {
+ if (config) {
+ pathfora.initializeWidgets(widgets, config);
+ } else {
+ pathfora.initializeWidgets(widgets);
+ }
+
+ setTimeout(resolve, delay);
+ });
+}
+
+// ============================================================================
+// FORM INTERACTION HELPERS
+// ============================================================================
+
+/**
+ * Fill form fields with provided data
+ * @param {string} widgetId - Widget ID
+ * @param {Object} formData - Object with field names as keys and values
+ */
+export function fillForm(widgetId, formData) {
+ const widget = $('#' + widgetId);
+ const form = widget.find('form');
+
+ Object.keys(formData).forEach((name) => {
+ const field = form.find('[name="' + name + '"]');
+ if (field.attr('type') === 'checkbox' || field.attr('type') === 'radio') {
+ // For radio/checkbox, check the one with matching value
+ form
+ .find('[name="' + name + '"][value="' + formData[name] + '"]')
+ .prop('checked', true);
+ } else {
+ field.val(formData[name]);
+ }
+ });
+}
+
+/**
+ * Fill and submit a form
+ * @param {string} widgetId - Widget ID
+ * @param {Object} formData - Object with field names as keys and values
+ */
+export function fillAndSubmitForm(widgetId, formData) {
+ fillForm(widgetId, formData);
+
+ const widget = $('#' + widgetId);
+ widget.find('.pf-widget-ok').click();
+}
+
+/**
+ * Get widget jQuery element
+ * @param {string} widgetId - Widget ID
+ * @returns {Object} jQuery element
+ */
+export function getWidget(widgetId) {
+ return $('#' + widgetId);
+}
+
+/**
+ * Click widget button by class
+ * @param {string} widgetId - Widget ID
+ * @param {string} buttonClass - Button class (e.g., 'pf-widget-ok', 'pf-widget-cancel', 'pf-widget-close')
+ */
+export function clickWidgetButton(widgetId, buttonClass) {
+ const widget = $('#' + widgetId);
+ widget.find('.' + buttonClass).click();
+}
+
+// ============================================================================
+// WIDGET INTERACTION SHORTCUTS
+// ============================================================================
+
+/**
+ * Click the confirm/OK button on a widget
+ * @param {string} widgetId - Widget ID
+ */
+export function confirmWidget(widgetId) {
+ clickWidgetButton(widgetId, 'pf-widget-ok');
+}
+
+/**
+ * Click the cancel button on a widget
+ * @param {string} widgetId - Widget ID
+ */
+export function cancelWidget(widgetId) {
+ clickWidgetButton(widgetId, 'pf-widget-cancel');
+}
+
+/**
+ * Click the close button on a widget
+ * @param {string} widgetId - Widget ID
+ */
+export function closeWidget(widgetId) {
+ clickWidgetButton(widgetId, 'pf-widget-close');
+}