diff --git a/frontend/src/app/components/ExperimentDisableDialog.js b/frontend/src/app/containers/ExperimentPage/ExperimentDisableDialog.js similarity index 95% rename from frontend/src/app/components/ExperimentDisableDialog.js rename to frontend/src/app/containers/ExperimentPage/ExperimentDisableDialog.js index 8ce0c8546d..82261e83e4 100644 --- a/frontend/src/app/components/ExperimentDisableDialog.js +++ b/frontend/src/app/containers/ExperimentPage/ExperimentDisableDialog.js @@ -2,8 +2,8 @@ import { Localized } from 'fluent-react/compat'; import React from 'react'; -import Copter from './Copter'; -import { buildSurveyURL } from '../lib/utils'; +import Copter from '../../components/Copter'; +import { buildSurveyURL } from '../../lib/utils'; type ExperimentDisableDialogProps = { experiment: Object, diff --git a/frontend/src/app/components/ExperimentEolDialog.js b/frontend/src/app/containers/ExperimentPage/ExperimentEolDialog.js similarity index 100% rename from frontend/src/app/components/ExperimentEolDialog.js rename to frontend/src/app/containers/ExperimentPage/ExperimentEolDialog.js diff --git a/frontend/src/app/components/ExperimentPreFeedbackDialog.js b/frontend/src/app/containers/ExperimentPage/ExperimentPreFeedbackDialog.js similarity index 100% rename from frontend/src/app/components/ExperimentPreFeedbackDialog.js rename to frontend/src/app/containers/ExperimentPage/ExperimentPreFeedbackDialog.js diff --git a/frontend/src/app/components/ExperimentTourDialog.js b/frontend/src/app/containers/ExperimentPage/ExperimentTourDialog.js similarity index 98% rename from frontend/src/app/components/ExperimentTourDialog.js rename to frontend/src/app/containers/ExperimentPage/ExperimentTourDialog.js index e462da8586..79a63877f7 100644 --- a/frontend/src/app/components/ExperimentTourDialog.js +++ b/frontend/src/app/containers/ExperimentPage/ExperimentTourDialog.js @@ -4,7 +4,7 @@ import classnames from 'classnames'; import { Localized } from 'fluent-react/compat'; import React from 'react'; -import { experimentL10nId } from '../lib/utils'; +import { experimentL10nId } from '../../lib/utils'; type ExperimentTourDialogProps = { experiment: Object, diff --git a/frontend/src/app/containers/ExperimentPage/TestpilotPromo.js b/frontend/src/app/containers/ExperimentPage/TestpilotPromo.js index e5e7e4d383..80aad02397 100644 --- a/frontend/src/app/containers/ExperimentPage/TestpilotPromo.js +++ b/frontend/src/app/containers/ExperimentPage/TestpilotPromo.js @@ -16,8 +16,8 @@ export default class TestpilotPromo extends React.Component { render() { const { hasAddon, graduated, experiment } = this.props; - const { title } = experiment; - if (hasAddon === null || hasAddon || graduated || experiment.web_url) { + const { title, web_url } = experiment; + if (hasAddon === null || hasAddon || graduated || web_url) { return null; } return ( diff --git a/frontend/src/app/containers/ExperimentPage/index.js b/frontend/src/app/containers/ExperimentPage/index.js index e6932d22c7..70cafb3b27 100644 --- a/frontend/src/app/containers/ExperimentPage/index.js +++ b/frontend/src/app/containers/ExperimentPage/index.js @@ -10,12 +10,8 @@ import { buildSurveyURL, experimentL10nId, formatDate } from '../../lib/utils'; import NotFoundPage from '../NotFoundPage'; import EmailDialog from '../../components/EmailDialog'; -import ExperimentDisableDialog from '../../components/ExperimentDisableDialog'; -import ExperimentEolDialog from '../../components/ExperimentEolDialog'; -import ExperimentTourDialog from '../../components/ExperimentTourDialog'; import LocalizedHtml from '../../components/LocalizedHtml'; import ExperimentCardList from '../../components/ExperimentCardList'; -import ExperimentPreFeedbackDialog from '../../components/ExperimentPreFeedbackDialog'; import View from '../../components/View'; import Warning from '../../components/Warning'; @@ -23,6 +19,10 @@ import ExperimentPlatforms from '../../components/ExperimentPlatforms'; import Banner from '../../components/Banner'; import LayoutWrapper from '../../components/LayoutWrapper'; +import ExperimentPreFeedbackDialog from './ExperimentPreFeedbackDialog'; +import ExperimentDisableDialog from './ExperimentDisableDialog'; +import ExperimentEolDialog from './ExperimentEolDialog'; +import ExperimentTourDialog from './ExperimentTourDialog'; import IncompatibleAddons from './IncompatibleAddons'; import TestpilotPromo from './TestpilotPromo'; diff --git a/frontend/src/app/containers/ExperimentPage/tests.js b/frontend/src/app/containers/ExperimentPage/tests.js index 759628d6ac..75e57bca52 100644 --- a/frontend/src/app/containers/ExperimentPage/tests.js +++ b/frontend/src/app/containers/ExperimentPage/tests.js @@ -12,67 +12,10 @@ import { defaultState } from '../../reducers/newsletter-form'; import ExperimentPage, { ExperimentDetail } from './index'; import IncompatibleAddons from './IncompatibleAddons'; import TestpilotPromo from './TestpilotPromo'; - -describe('app/containers/ExperimentPage/IncompatibleAddons', () => { - let mockExperiment, props, subject; - beforeEach(() => { - mockExperiment = { - slug: 'testing', - title: 'Testing', - incompatible: {} - }; - props = { - experiment: mockExperiment, - installedAddons: [] - }; - subject = shallow(); - }); - - it('should render a warning only if incompatible add-ons are installed', () => { - expect(subject.find('.incompatible-addons')).to.have.property('length', 0); - - const experiment = { ...mockExperiment, incompatible: { foo: 1, bar: 2 } }; - subject.setProps({ experiment }); - - subject.setProps({ installedAddons: ['baz'] }); - expect(subject.find('.incompatible-addons')).to.have.property('length', 0); - - subject.setProps({ installedAddons: ['baz', 'bar'] }); - expect(subject.find('.incompatible-addons')).to.have.property('length', 1); - }); -}); - -describe('app/containers/ExperimentPage/TestpilotPromo', () => { - let mockExperiment, props, subject; - beforeEach(() => { - mockExperiment = { - slug: 'testing', - title: 'Testing', - incompatible: {} - }; - props = { - experiment: mockExperiment, - isFirefox: false, - isMinFirefox: false, - graduated: false, - hasAddon: false, - varianttests: {}, - installExperiment: () => {} - }; - subject = shallow(); - }); - - it('should display a call-to-action to install Test Pilot without add-on', () => { - expect(subject.find('#testpilot-promo')).to.have.property('length', 1); - expect(subject.find('MainInstallButton')).to.have.property('length', 1); - }); - - it('should not display a call-to-action to install Test Pilot with add-on installed', () => { - subject.setProps({ hasAddon: true }); - expect(subject.find('.experiment-promo')).to.have.property('length', 0); - expect(subject.find('MainInstallButton')).to.have.property('length', 0); - }); -}); +import ExperimentPreFeedbackDialog from './ExperimentPreFeedbackDialog'; +import ExperimentDisableDialog from './ExperimentDisableDialog'; +import ExperimentEolDialog from './ExperimentEolDialog'; +import ExperimentTourDialog from './ExperimentTourDialog'; describe('app/containers/ExperimentPage', () => { const mockExperiment = { @@ -96,7 +39,6 @@ describe('app/containers/ExperimentPage', () => { }); }); - describe('app/containers/ExperimentPage:ExperimentDetail', () => { let mockExperiment, mockClickEvent, props, subject; beforeEach(() => { @@ -615,3 +557,343 @@ describe('app/containers/ExperimentPage:ExperimentDetail', () => { }); }); }); + +describe('app/containers/ExperimentPage/IncompatibleAddons', () => { + let mockExperiment, props, subject; + beforeEach(() => { + mockExperiment = { + slug: 'testing', + title: 'Testing', + incompatible: {} + }; + props = { + experiment: mockExperiment, + installedAddons: [] + }; + subject = shallow(); + }); + + it('should render a warning only if incompatible add-ons are installed', () => { + expect(subject.find('.incompatible-addons')).to.have.property('length', 0); + + const experiment = { ...mockExperiment, incompatible: { foo: 1, bar: 2 } }; + subject.setProps({ experiment }); + + subject.setProps({ installedAddons: ['baz'] }); + expect(subject.find('.incompatible-addons')).to.have.property('length', 0); + + subject.setProps({ installedAddons: ['baz', 'bar'] }); + expect(subject.find('.incompatible-addons')).to.have.property('length', 1); + }); +}); + +describe('app/containers/ExperimentPage/TestpilotPromo', () => { + let mockExperiment, props, subject; + beforeEach(() => { + mockExperiment = { + slug: 'testing', + title: 'Testing', + incompatible: {} + }; + props = { + experiment: mockExperiment, + isFirefox: false, + isMinFirefox: false, + graduated: false, + hasAddon: false, + varianttests: {}, + installExperiment: () => {} + }; + subject = shallow(); + }); + + it('should display a call-to-action to install Test Pilot without add-on', () => { + expect(subject.find('#testpilot-promo')).to.have.property('length', 1); + expect(subject.find('MainInstallButton')).to.have.property('length', 1); + }); + + it('should not display a call-to-action to install Test Pilot with add-on installed', () => { + subject.setProps({ hasAddon: true }); + expect(subject.find('.experiment-promo')).to.have.property('length', 0); + expect(subject.find('MainInstallButton')).to.have.property('length', 0); + }); +}); + +describe('app/containers/ExperimentPage/ExperimentDisableDialog', () => { + const experiment = { title: 'foobar', survey_url: 'https://example.com' }; + const installed = { ex1: true, ex2: true }; + const clientUUID = '38c51b84-9586-499f-ac52-94626e2b29cf'; + + let onSubmit, onCancel, sendToGA, preventDefault, mockClickEvent, subject; + beforeEach(function() { + onSubmit = sinon.spy(); + onCancel = sinon.spy(); + sendToGA = sinon.spy(); + preventDefault = sinon.spy(); + mockClickEvent = { preventDefault }; + subject = shallow( + + ); + }); + + it('should render a modal container', () => { + expect(subject.find('.modal-container')).to.have.property('length', 1); + expect(findLocalizedById(subject, 'feedbackUninstallTitle').props()['$title']) + .to.equal(experiment.title); + }); + + it('should call onCancel when cancel button clicked', () => { + subject.find('.modal-cancel').simulate('click', mockClickEvent); + expect(onCancel.called).to.be.true; + expect(preventDefault.called).to.be.true; + }); + + it('should launch a survey when submit button clicked', () => { + const submitLink = subject.find('.modal-actions a.submit'); + const expectedHref = 'https://example.com?ref=disable&experiment=foobar&cid=38c51b84-9586-499f-ac52-94626e2b29cf&installed=ex1&installed=ex2'; + + expect(submitLink.props().href).to.equal(expectedHref); + + submitLink.simulate('click', mockClickEvent); + expect(onSubmit.called).to.be.true; + expect(preventDefault.called).to.be.false; + expect(sendToGA.lastCall.args).to.deep.equal(['event', { + eventCategory: 'ExperimentDetailsPage Interactions', + eventAction: 'button click', + eventLabel: 'exit survey disabled' + }]); + }); +}); + +describe('app/containers/ExperimentPage/ExperimentEolDialog', () => { + let props, mockClickEvent, subject; + beforeEach(function() { + props = { + onSubmit: sinon.spy(), + onCancel: sinon.spy() + }; + mockClickEvent = { + preventDefault: sinon.spy() + }; + subject = shallow(); + }); + + it('should display expected content', () => { + expect(subject.find('#retire-dialog-modal')).to.have.property('length', 1); + }); + + it('calls onCancel when the cancel button is clicked', () => { + subject.find('.modal-cancel').simulate('click', mockClickEvent); + expect(props.onCancel.called).to.be.true; + }); + + it('calls onSubmit when the disable button is clicked', () => { + findLocalizedById(subject, 'disableExperiment').find('button') + .simulate('click', mockClickEvent); + expect(props.onSubmit.called).to.be.true; + }); + +}); + +describe('app/containers/ExperimentPage/ExperimentPreFeedbackDialog', () => { + const experiment = { + title: 'foobar', + survey_url: 'https://example.com/survey', + pre_feedback_image: '/foo.png', + pre_feedback_copy: '

markup works!

' + }; + const surveyURL = experiment.survey_url; + + let sendToGA, onCancel, preventDefault, getAttribute, mockClickEvent, subject; + beforeEach(function() { + sendToGA = sinon.spy(); + onCancel = sinon.spy(); + preventDefault = sinon.spy(); + getAttribute = sinon.spy(() => surveyURL); + mockClickEvent = { preventDefault, target: { getAttribute } }; + subject = shallow( + + ); + }); + + it('should render expected content', () => { + expect(subject.find('.modal-container')) + .to.have.property('length', 1); + expect(findLocalizedById(subject, 'experimentPreFeedbackTitle').prop('$title')) + .to.equal(experiment.title); + expect(findLocalizedById(subject, 'experimentPreFeedbackLinkCopy').prop('$title')) + .to.equal(experiment.title); + expect(subject.find('.tour-image img').props().src) + .to.equal(experiment.pre_feedback_image); + expect(subject.find('.tour-text').first().html()) + .to.contain(experiment.pre_feedback_copy); + }); + + it('should call onCancel on cancel button click', () => { + subject.find('.modal-cancel').simulate('click', mockClickEvent); + expect(onCancel.called).to.be.true; + expect(sendToGA.lastCall.args).to.deep.equal(['event', { + eventCategory: 'ExperimentDetailsPage Interactions', + eventAction: 'button click', + eventLabel: 'cancel feedback' + }]); + }); + + it('should launch feedback on feedback button click', () => { + subject.find('.tour-text a').simulate('click', mockClickEvent); + expect(onCancel.called).to.be.false; + expect(getAttribute.called).to.be.true; + expect(sendToGA.lastCall.args).to.deep.equal(['event', { + eventCategory: 'ExperimentDetailsPage Interactions', + eventAction: 'PreFeedback Confirm', + eventLabel: 'foobar', + outboundURL: surveyURL + }]); + }); +}); + +describe('app/containers/ExperimentPage/ExperimentTourDialog', () => { + let props, mockClickEvent, subject; + beforeEach(() => { + mockClickEvent = { preventDefault: sinon.spy() }; + + props = { + experiment: { + title: 'Test Experiment', + slug: 'test', + tour_steps: [ + { image: '/example1.png', copy: 'Example 1', copy_l10nsuffix: 'foo' }, + { image: '/example2.png', copy: 'Example 2' }, + { image: '/example3.png', copy: 'Example 3' }, + ] + }, + isExperimentEnabled: () => true, + sendToGA: sinon.spy(), + onComplete: sinon.spy(), + onCancel: sinon.spy() + }; + + subject = mount(); + }); + + it('should render expected default content', () => { + expect(findLocalizedById(subject, 'tourOnboardingTitle').prop('$title')) + .to.equal(props.experiment.title); + + const expectedTourStep = props.experiment.tour_steps[0]; + expect(subject.find('.tour-image > img').prop('src')) + .to.equal(expectedTourStep.image); + // There is now a LocalizedHtml element between the + // .tour-text element and the p element, so + // '.tour-text > p' won't work, but '.tour-text p' does + expect(subject.find('.tour-text p').html()) + .to.include(expectedTourStep.copy); + }); + + it('should render only the experiment title if not enabled', () => { + subject.setProps({ isExperimentEnabled: () => false, + experiment: { ...props.experiment } }); + expect(subject.find('.modal-header').text()).to.equal(props.experiment.title); + }); + + it('should have the correct l10n IDs', () => { + expect(findLocalizedById(subject, 'testToursteps0CopyFoo').length).to.equal(1); + }); + + it('should not have l10n IDs if the experiment is dev-only', () => { + subject.setProps({ experiment: { dev: true, ...props.experiment } }); + expect(subject.find('.tour-text > Localized').prop('id')).to.equal(null); + }); + + it('should advance one step and ping GA when the next button is clicked', () => { + subject.find('.tour-next').simulate('click', mockClickEvent); + + const expectedTourStep = props.experiment.tour_steps[1]; + expect(subject.find('.tour-image > img').prop('src')) + .to.equal(expectedTourStep.image); + expect(subject.find('.tour-text').html()) + .to.include(expectedTourStep.copy); + + expect(subject.state('currentStep')).to.equal(1); + + expect(props.sendToGA.lastCall.args).to.deep.equal(['event', { + eventCategory: 'ExperimentDetailsPage Interactions', + eventAction: 'button click', + eventLabel: `forward to step 1` + }]); + }); + + it('should rewind one step and ping GA when the back button is clicked', () => { + expect(subject.find('.tour-back').hasClass('hidden')).to.be.true; + subject.setState({ currentStep: 1 }); + expect(subject.find('.tour-back').hasClass('hidden')).to.be.false; + + subject.find('.tour-back').simulate('click', mockClickEvent); + + const expectedTourStep = props.experiment.tour_steps[0]; + expect(subject.find('.tour-image > img').prop('src')) + .to.equal(expectedTourStep.image); + expect(subject.find('.tour-text').html()) + .to.include(expectedTourStep.copy); + + expect(subject.state('currentStep')).to.equal(0); + + expect(props.sendToGA.lastCall.args).to.deep.equal(['event', { + eventCategory: 'ExperimentDetailsPage Interactions', + eventAction: 'button click', + eventLabel: `back to step 0` + }]); + }); + + it('should render dots to indicate and choose tour steps', () => { + expect(subject.find('.tour-image .dot')) + .to.have.property('length', props.experiment.tour_steps.length); + + subject.setState({ currentStep: 2 }); + expect(subject.find('.tour-image .dot').at(2).hasClass('current')).to.be.true; + + subject.find('.tour-image .dot').at(0).simulate('click', mockClickEvent); + + expect(subject.state('currentStep')).to.equal(0); + + expect(props.sendToGA.lastCall.args).to.deep.equal(['event', { + eventCategory: 'ExperimentDetailsPage Interactions', + eventAction: 'button click', + eventLabel: `dot to step 0` + }]); + }); + + it('should ping GA and call onCancel when cancel button clicked', () => { + subject.find('.modal-cancel').simulate('click', mockClickEvent); + + expect(props.onCancel.called).to.be.true; + + expect(props.sendToGA.lastCall.args).to.deep.equal(['event', { + eventCategory: 'ExperimentDetailsPage Interactions', + eventAction: 'button click', + eventLabel: 'cancel tour' + }]); + }); + + it('should ping GA and call onComplete when done button clicked', () => { + expect(subject.find('.tour-next').hasClass('no-display')).to.be.false; + expect(subject.find('.tour-done').hasClass('no-display')).to.be.true; + subject.setState({ currentStep: 2 }); + expect(subject.find('.tour-next').hasClass('no-display')).to.be.true; + expect(subject.find('.tour-done').hasClass('no-display')).to.be.false; + + subject.find('.tour-done').simulate('click', mockClickEvent); + + expect(props.onComplete.called).to.be.true; + + expect(props.sendToGA.lastCall.args).to.deep.equal(['event', { + eventCategory: 'ExperimentDetailsPage Interactions', + eventAction: 'button click', + eventLabel: 'complete tour' + }]); + }); +}); diff --git a/frontend/test/app/components/ExperimentDisableDialog-test.js b/frontend/test/app/components/ExperimentDisableDialog-test.js deleted file mode 100644 index 109bcc9201..0000000000 --- a/frontend/test/app/components/ExperimentDisableDialog-test.js +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import { expect } from 'chai'; -import sinon from 'sinon'; -import { shallow } from 'enzyme'; -import { findLocalizedById } from '../util'; - -import ExperimentDisableDialog from '../../../src/app/components/ExperimentDisableDialog'; - -describe('app/components/ExperimentDisableDialog', () => { - const experiment = { title: 'foobar', survey_url: 'https://example.com' }; - const installed = { ex1: true, ex2: true }; - const clientUUID = '38c51b84-9586-499f-ac52-94626e2b29cf'; - - let onSubmit, onCancel, sendToGA, preventDefault, mockClickEvent, subject; - beforeEach(function() { - onSubmit = sinon.spy(); - onCancel = sinon.spy(); - sendToGA = sinon.spy(); - preventDefault = sinon.spy(); - mockClickEvent = { preventDefault }; - subject = shallow( - - ); - }); - - it('should render a modal container', () => { - expect(subject.find('.modal-container')).to.have.property('length', 1); - expect(findLocalizedById(subject, 'feedbackUninstallTitle').props()['$title']) - .to.equal(experiment.title); - }); - - it('should call onCancel when cancel button clicked', () => { - subject.find('.modal-cancel').simulate('click', mockClickEvent); - expect(onCancel.called).to.be.true; - expect(preventDefault.called).to.be.true; - }); - - it('should launch a survey when submit button clicked', () => { - const submitLink = subject.find('.modal-actions a.submit'); - const expectedHref = 'https://example.com?ref=disable&experiment=foobar&cid=38c51b84-9586-499f-ac52-94626e2b29cf&installed=ex1&installed=ex2'; - - expect(submitLink.props().href).to.equal(expectedHref); - - submitLink.simulate('click', mockClickEvent); - expect(onSubmit.called).to.be.true; - expect(preventDefault.called).to.be.false; - expect(sendToGA.lastCall.args).to.deep.equal(['event', { - eventCategory: 'ExperimentDetailsPage Interactions', - eventAction: 'button click', - eventLabel: 'exit survey disabled' - }]); - }); -}); diff --git a/frontend/test/app/components/ExperimentEolDialog-test.js b/frontend/test/app/components/ExperimentEolDialog-test.js deleted file mode 100644 index ec603b9a68..0000000000 --- a/frontend/test/app/components/ExperimentEolDialog-test.js +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { expect } from 'chai'; -import sinon from 'sinon'; -import { shallow } from 'enzyme'; -import { findLocalizedById } from '../util'; - -import ExperimentEolDialog from '../../../src/app/components/ExperimentEolDialog'; - -describe('app/components/ExperimentEolDialog', () => { - let props, mockClickEvent, subject; - beforeEach(function() { - props = { - onSubmit: sinon.spy(), - onCancel: sinon.spy() - }; - mockClickEvent = { - preventDefault: sinon.spy() - }; - subject = shallow(); - }); - - it('should display expected content', () => { - expect(subject.find('#retire-dialog-modal')).to.have.property('length', 1); - }); - - it('calls onCancel when the cancel button is clicked', () => { - subject.find('.modal-cancel').simulate('click', mockClickEvent); - expect(props.onCancel.called).to.be.true; - }); - - it('calls onSubmit when the disable button is clicked', () => { - findLocalizedById(subject, 'disableExperiment').find('button') - .simulate('click', mockClickEvent); - expect(props.onSubmit.called).to.be.true; - }); - -}); diff --git a/frontend/test/app/components/ExperimentPreFeedbackDialog-test.js b/frontend/test/app/components/ExperimentPreFeedbackDialog-test.js deleted file mode 100644 index 4f08595f31..0000000000 --- a/frontend/test/app/components/ExperimentPreFeedbackDialog-test.js +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import { expect } from 'chai'; -import sinon from 'sinon'; -import { shallow } from 'enzyme'; -import { findLocalizedById } from '../util'; - -import ExperimentPreFeedbackDialog from '../../../src/app/components/ExperimentPreFeedbackDialog'; - -describe('app/components/ExperimentPreFeedbackDialog', () => { - const experiment = { - title: 'foobar', - survey_url: 'https://example.com/survey', - pre_feedback_image: '/foo.png', - pre_feedback_copy: '

markup works!

' - }; - const surveyURL = experiment.survey_url; - - let sendToGA, onCancel, preventDefault, getAttribute, mockClickEvent, subject; - beforeEach(function() { - sendToGA = sinon.spy(); - onCancel = sinon.spy(); - preventDefault = sinon.spy(); - getAttribute = sinon.spy(() => surveyURL); - mockClickEvent = { preventDefault, target: { getAttribute } }; - subject = shallow( - - ); - }); - - it('should render expected content', () => { - expect(subject.find('.modal-container')) - .to.have.property('length', 1); - expect(findLocalizedById(subject, 'experimentPreFeedbackTitle').prop('$title')) - .to.equal(experiment.title); - expect(findLocalizedById(subject, 'experimentPreFeedbackLinkCopy').prop('$title')) - .to.equal(experiment.title); - expect(subject.find('.tour-image img').props().src) - .to.equal(experiment.pre_feedback_image); - expect(subject.find('.tour-text').first().html()) - .to.contain(experiment.pre_feedback_copy); - }); - - it('should call onCancel on cancel button click', () => { - subject.find('.modal-cancel').simulate('click', mockClickEvent); - expect(onCancel.called).to.be.true; - expect(sendToGA.lastCall.args).to.deep.equal(['event', { - eventCategory: 'ExperimentDetailsPage Interactions', - eventAction: 'button click', - eventLabel: 'cancel feedback' - }]); - }); - - it('should launch feedback on feedback button click', () => { - subject.find('.tour-text a').simulate('click', mockClickEvent); - expect(onCancel.called).to.be.false; - expect(getAttribute.called).to.be.true; - expect(sendToGA.lastCall.args).to.deep.equal(['event', { - eventCategory: 'ExperimentDetailsPage Interactions', - eventAction: 'PreFeedback Confirm', - eventLabel: 'foobar', - outboundURL: surveyURL - }]); - }); -}); diff --git a/frontend/test/app/components/ExperimentTourDialog-test.js b/frontend/test/app/components/ExperimentTourDialog-test.js deleted file mode 100644 index 377b3145d3..0000000000 --- a/frontend/test/app/components/ExperimentTourDialog-test.js +++ /dev/null @@ -1,149 +0,0 @@ -import React from 'react'; -import { expect } from 'chai'; -import sinon from 'sinon'; -import { mount } from 'enzyme'; -import { findLocalizedById } from '../util'; - -import ExperimentTourDialog from '../../../src/app/components/ExperimentTourDialog'; - -describe('app/components/ExperimentTourDialog', () => { - let props, mockClickEvent, subject; - beforeEach(() => { - mockClickEvent = { preventDefault: sinon.spy() }; - - props = { - experiment: { - title: 'Test Experiment', - slug: 'test', - tour_steps: [ - { image: '/example1.png', copy: 'Example 1', copy_l10nsuffix: 'foo' }, - { image: '/example2.png', copy: 'Example 2' }, - { image: '/example3.png', copy: 'Example 3' }, - ] - }, - isExperimentEnabled: () => true, - sendToGA: sinon.spy(), - onComplete: sinon.spy(), - onCancel: sinon.spy() - }; - - subject = mount(); - }); - - it('should render expected default content', () => { - expect(findLocalizedById(subject, 'tourOnboardingTitle').prop('$title')) - .to.equal(props.experiment.title); - - const expectedTourStep = props.experiment.tour_steps[0]; - expect(subject.find('.tour-image > img').prop('src')) - .to.equal(expectedTourStep.image); - // There is now a LocalizedHtml element between the - // .tour-text element and the p element, so - // '.tour-text > p' won't work, but '.tour-text p' does - expect(subject.find('.tour-text p').html()) - .to.include(expectedTourStep.copy); - }); - - it('should render only the experiment title if not enabled', () => { - subject.setProps({ isExperimentEnabled: () => false, - experiment: { ...props.experiment } }); - expect(subject.find('.modal-header').text()).to.equal(props.experiment.title); - }); - - it('should have the correct l10n IDs', () => { - expect(findLocalizedById(subject, 'testToursteps0CopyFoo').length).to.equal(1); - }); - - it('should not have l10n IDs if the experiment is dev-only', () => { - subject.setProps({ experiment: { dev: true, ...props.experiment } }); - expect(subject.find('.tour-text > Localized').prop('id')).to.equal(null); - }); - - it('should advance one step and ping GA when the next button is clicked', () => { - subject.find('.tour-next').simulate('click', mockClickEvent); - - const expectedTourStep = props.experiment.tour_steps[1]; - expect(subject.find('.tour-image > img').prop('src')) - .to.equal(expectedTourStep.image); - expect(subject.find('.tour-text').html()) - .to.include(expectedTourStep.copy); - - expect(subject.state('currentStep')).to.equal(1); - - expect(props.sendToGA.lastCall.args).to.deep.equal(['event', { - eventCategory: 'ExperimentDetailsPage Interactions', - eventAction: 'button click', - eventLabel: `forward to step 1` - }]); - }); - - it('should rewind one step and ping GA when the back button is clicked', () => { - expect(subject.find('.tour-back').hasClass('hidden')).to.be.true; - subject.setState({ currentStep: 1 }); - expect(subject.find('.tour-back').hasClass('hidden')).to.be.false; - - subject.find('.tour-back').simulate('click', mockClickEvent); - - const expectedTourStep = props.experiment.tour_steps[0]; - expect(subject.find('.tour-image > img').prop('src')) - .to.equal(expectedTourStep.image); - expect(subject.find('.tour-text').html()) - .to.include(expectedTourStep.copy); - - expect(subject.state('currentStep')).to.equal(0); - - expect(props.sendToGA.lastCall.args).to.deep.equal(['event', { - eventCategory: 'ExperimentDetailsPage Interactions', - eventAction: 'button click', - eventLabel: `back to step 0` - }]); - }); - - it('should render dots to indicate and choose tour steps', () => { - expect(subject.find('.tour-image .dot')) - .to.have.property('length', props.experiment.tour_steps.length); - - subject.setState({ currentStep: 2 }); - expect(subject.find('.tour-image .dot').at(2).hasClass('current')).to.be.true; - - subject.find('.tour-image .dot').at(0).simulate('click', mockClickEvent); - - expect(subject.state('currentStep')).to.equal(0); - - expect(props.sendToGA.lastCall.args).to.deep.equal(['event', { - eventCategory: 'ExperimentDetailsPage Interactions', - eventAction: 'button click', - eventLabel: `dot to step 0` - }]); - }); - - it('should ping GA and call onCancel when cancel button clicked', () => { - subject.find('.modal-cancel').simulate('click', mockClickEvent); - - expect(props.onCancel.called).to.be.true; - - expect(props.sendToGA.lastCall.args).to.deep.equal(['event', { - eventCategory: 'ExperimentDetailsPage Interactions', - eventAction: 'button click', - eventLabel: 'cancel tour' - }]); - }); - - it('should ping GA and call onComplete when done button clicked', () => { - expect(subject.find('.tour-next').hasClass('no-display')).to.be.false; - expect(subject.find('.tour-done').hasClass('no-display')).to.be.true; - subject.setState({ currentStep: 2 }); - expect(subject.find('.tour-next').hasClass('no-display')).to.be.true; - expect(subject.find('.tour-done').hasClass('no-display')).to.be.false; - - subject.find('.tour-done').simulate('click', mockClickEvent); - - expect(props.onComplete.called).to.be.true; - - expect(props.sendToGA.lastCall.args).to.deep.equal(['event', { - eventCategory: 'ExperimentDetailsPage Interactions', - eventAction: 'button click', - eventLabel: 'complete tour' - }]); - }); -});