Merge pull request #2998 from overleaf/as-beta-page-updates

Update beta program page to include feedback survey link

GitOrigin-RevId: 549cd2be01b8f64d952d0347c8c102d0d2efae24
This commit is contained in:
Alasdair Smith 2020-07-14 14:24:25 +01:00 committed by Copybot
parent d4d6e2c45d
commit 2556fded1e
4 changed files with 66 additions and 97 deletions

View file

@ -1,60 +1,47 @@
/* eslint-disable
camelcase,
no-unused-vars,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
let BetaProgramController
const BetaProgramHandler = require('./BetaProgramHandler') const BetaProgramHandler = require('./BetaProgramHandler')
const UserGetter = require('../User/UserGetter') const UserGetter = require('../User/UserGetter')
const Settings = require('settings-sharelatex') const Settings = require('settings-sharelatex')
const logger = require('logger-sharelatex') const logger = require('logger-sharelatex')
const AuthenticationController = require('../Authentication/AuthenticationController') const AuthenticationController = require('../Authentication/AuthenticationController')
module.exports = BetaProgramController = { const BetaProgramController = {
optIn(req, res, next) { optIn(req, res, next) {
const user_id = AuthenticationController.getLoggedInUserId(req) const userId = AuthenticationController.getLoggedInUserId(req)
logger.log({ user_id }, 'user opting in to beta program') logger.log({ userId }, 'user opting in to beta program')
if (user_id == null) { if (userId == null) {
return next(new Error('no user id in session')) return next(new Error('no user id in session'))
} }
return BetaProgramHandler.optIn(user_id, function(err) { BetaProgramHandler.optIn(userId, function(err) {
if (err) { if (err) {
return next(err) return next(err)
} }
return res.redirect('/beta/participate') res.redirect('/beta/participate')
}) })
}, },
optOut(req, res, next) { optOut(req, res, next) {
const user_id = AuthenticationController.getLoggedInUserId(req) const userId = AuthenticationController.getLoggedInUserId(req)
logger.log({ user_id }, 'user opting out of beta program') logger.log({ userId }, 'user opting out of beta program')
if (user_id == null) { if (userId == null) {
return next(new Error('no user id in session')) return next(new Error('no user id in session'))
} }
return BetaProgramHandler.optOut(user_id, function(err) { BetaProgramHandler.optOut(userId, function(err) {
if (err) { if (err) {
return next(err) return next(err)
} }
return res.redirect('/beta/participate') res.redirect('/beta/participate')
}) })
}, },
optInPage(req, res, next) { optInPage(req, res, next) {
const user_id = AuthenticationController.getLoggedInUserId(req) const userId = AuthenticationController.getLoggedInUserId(req)
logger.log({ user_id }, 'showing beta participation page for user') logger.log({ user_id: userId }, 'showing beta participation page for user')
return UserGetter.getUser(user_id, function(err, user) { UserGetter.getUser(userId, function(err, user) {
if (err) { if (err) {
logger.warn({ err, user_id }, 'error fetching user') logger.warn({ err, userId }, 'error fetching user')
return next(err) return next(err)
} }
return res.render('beta_program/opt_in', { res.render('beta_program/opt_in', {
title: 'sharelatex_beta_program', title: 'sharelatex_beta_program',
user, user,
languages: Settings.languages languages: Settings.languages
@ -62,3 +49,5 @@ module.exports = BetaProgramController = {
}) })
} }
} }
module.exports = BetaProgramController

View file

@ -1,7 +1,7 @@
extends ../layout extends ../layout
block content block content
.content.content-alt main.content.content-alt
.container.beta-opt-in-wrapper .container.beta-opt-in-wrapper
.row .row
.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 .col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
@ -13,29 +13,44 @@ block content
.container-fluid .container-fluid
.row .row
.col-md-12 .col-md-12
p.text-centered #{translate("beta_program_benefits")} if user.betaProgram
p.text-centered p #{translate("beta_program_already_participating")}.
| #{translate("beta_program_badge_description")} p #{translate("thank_you_for_being_part_of_our_beta_program")}.
span.beta-feature-badge else
p #{translate("beta_program_benefits")}
p #[strong How it works:]
ul
li #{translate("beta_program_badge_description")}#[span(aria-label=translate("beta_feature_badge") role="img").beta-feature-badge]
li #{translate("you_will_be_able_to_contact_us_any_time_to_share_your_feedback")}.
li #{translate("we_may_also_contact_you_from_time_to_time_by_email_with_a_survey")}.
li #{translate("you_can_opt_in_and_out_of_the_program_at_any_time_on_this_page")}.
.row.text-centered .row.text-centered
.col-md-12 .col-md-12
if user.betaProgram if user.betaProgram
p #{translate("beta_program_already_participating")}
form(id="beta-program-opt-out", method="post", action="/beta/opt-out", novalidate) form(id="beta-program-opt-out", method="post", action="/beta/opt-out", novalidate)
input(type="hidden", name="_csrf", value=csrfToken)
.form-group .form-group
input(type="hidden", name="_csrf", value=csrfToken) a(
button.btn.btn-primary( href="https://forms.gle/CFEsmvZQTAwHCd3X9"
target="_blank"
rel="noopener noreferrer"
).btn.btn-primary.btn-lg #{translate("give_feedback")}
.form-group
button.btn.btn-info.btn-sm(
type="submit" type="submit"
) )
span #{translate("beta_program_opt_out_action")} span #{translate("beta_program_opt_out_action")}
.form-group .form-group
a(href="/project").btn.btn-info #{translate("back_to_your_projects")} a(href="/project").btn.btn-link.btn-sm #{translate("back_to_your_projects")}
else else
form(id="beta-program-opt-in", method="post", action="/beta/opt-in", novalidate) form(id="beta-program-opt-in", method="post", action="/beta/opt-in", novalidate)
input(type="hidden", name="_csrf", value=csrfToken)
.form-group .form-group
input(type="hidden", name="_csrf", value=csrfToken)
button.btn.btn-primary( button.btn.btn-primary(
type="submit" type="submit"
) )
span #{translate("beta_program_opt_in_action")} span #{translate("beta_program_opt_in_action")}
.form-group
a(href="/project").btn.btn-link.btn-sm #{translate("back_to_your_projects")}

View file

@ -1,7 +1,3 @@
.beta-opt-in-wrapper {
min-height: 400px;
}
.beta-opt-in { .beta-opt-in {
.form-group { .form-group {
margin-top: 15px; margin-top: 15px;

View file

@ -1,26 +1,12 @@
/* eslint-disable require('chai').should()
max-len,
mocha/no-identical-title,
no-return-assign,
no-unused-vars,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const should = require('chai').should()
const SandboxedModule = require('sandboxed-module') const SandboxedModule = require('sandboxed-module')
const assert = require('assert')
const path = require('path') const path = require('path')
const sinon = require('sinon') const sinon = require('sinon')
const modulePath = path.join( const modulePath = path.join(
__dirname, __dirname,
'../../../../app/src/Features/BetaProgram/BetaProgramController' '../../../../app/src/Features/BetaProgram/BetaProgramController'
) )
const { expect } = require('chai')
describe('BetaProgramController', function() { describe('BetaProgramController', function() {
beforeEach(function() { beforeEach(function() {
@ -67,130 +53,113 @@ describe('BetaProgramController', function() {
redirect: sinon.stub(), redirect: sinon.stub(),
render: sinon.stub() render: sinon.stub()
} }
return (this.next = sinon.stub()) this.next = sinon.stub()
}) })
describe('optIn', function() { describe('optIn', function() {
beforeEach(function() { beforeEach(function() {
return this.BetaProgramHandler.optIn.callsArgWith(1, null) this.BetaProgramHandler.optIn.callsArgWith(1, null)
}) })
it("should redirect to '/beta/participate'", function() { it("should redirect to '/beta/participate'", function() {
this.BetaProgramController.optIn(this.req, this.res, this.next) this.BetaProgramController.optIn(this.req, this.res, this.next)
this.res.redirect.callCount.should.equal(1) this.res.redirect.callCount.should.equal(1)
return this.res.redirect.firstCall.args[0].should.equal( this.res.redirect.firstCall.args[0].should.equal('/beta/participate')
'/beta/participate'
)
}) })
it('should not call next with an error', function() { it('should not call next with an error', function() {
this.BetaProgramController.optIn(this.req, this.res, this.next) this.BetaProgramController.optIn(this.req, this.res, this.next)
return this.next.callCount.should.equal(0) this.next.callCount.should.equal(0)
})
it('should not call next with an error', function() {
this.BetaProgramController.optIn(this.req, this.res, this.next)
return this.next.callCount.should.equal(0)
}) })
it('should call BetaProgramHandler.optIn', function() { it('should call BetaProgramHandler.optIn', function() {
this.BetaProgramController.optIn(this.req, this.res, this.next) this.BetaProgramController.optIn(this.req, this.res, this.next)
return this.BetaProgramHandler.optIn.callCount.should.equal(1) this.BetaProgramHandler.optIn.callCount.should.equal(1)
}) })
describe('when BetaProgramHandler.opIn produces an error', function() { describe('when BetaProgramHandler.opIn produces an error', function() {
beforeEach(function() { beforeEach(function() {
return this.BetaProgramHandler.optIn.callsArgWith(1, new Error('woops')) this.BetaProgramHandler.optIn.callsArgWith(1, new Error('woops'))
}) })
it("should not redirect to '/beta/participate'", function() { it("should not redirect to '/beta/participate'", function() {
this.BetaProgramController.optIn(this.req, this.res, this.next) this.BetaProgramController.optIn(this.req, this.res, this.next)
return this.res.redirect.callCount.should.equal(0) this.res.redirect.callCount.should.equal(0)
}) })
it('should produce an error', function() { it('should produce an error', function() {
this.BetaProgramController.optIn(this.req, this.res, this.next) this.BetaProgramController.optIn(this.req, this.res, this.next)
this.next.callCount.should.equal(1) this.next.callCount.should.equal(1)
return this.next.firstCall.args[0].should.be.instanceof(Error) this.next.firstCall.args[0].should.be.instanceof(Error)
}) })
}) })
}) })
describe('optOut', function() { describe('optOut', function() {
beforeEach(function() { beforeEach(function() {
return this.BetaProgramHandler.optOut.callsArgWith(1, null) this.BetaProgramHandler.optOut.callsArgWith(1, null)
}) })
it("should redirect to '/beta/participate'", function() { it("should redirect to '/beta/participate'", function() {
this.BetaProgramController.optOut(this.req, this.res, this.next) this.BetaProgramController.optOut(this.req, this.res, this.next)
this.res.redirect.callCount.should.equal(1) this.res.redirect.callCount.should.equal(1)
return this.res.redirect.firstCall.args[0].should.equal( this.res.redirect.firstCall.args[0].should.equal('/beta/participate')
'/beta/participate'
)
}) })
it('should not call next with an error', function() { it('should not call next with an error', function() {
this.BetaProgramController.optOut(this.req, this.res, this.next) this.BetaProgramController.optOut(this.req, this.res, this.next)
return this.next.callCount.should.equal(0) this.next.callCount.should.equal(0)
})
it('should not call next with an error', function() {
this.BetaProgramController.optOut(this.req, this.res, this.next)
return this.next.callCount.should.equal(0)
}) })
it('should call BetaProgramHandler.optOut', function() { it('should call BetaProgramHandler.optOut', function() {
this.BetaProgramController.optOut(this.req, this.res, this.next) this.BetaProgramController.optOut(this.req, this.res, this.next)
return this.BetaProgramHandler.optOut.callCount.should.equal(1) this.BetaProgramHandler.optOut.callCount.should.equal(1)
}) })
describe('when BetaProgramHandler.optOut produces an error', function() { describe('when BetaProgramHandler.optOut produces an error', function() {
beforeEach(function() { beforeEach(function() {
return this.BetaProgramHandler.optOut.callsArgWith( this.BetaProgramHandler.optOut.callsArgWith(1, new Error('woops'))
1,
new Error('woops')
)
}) })
it("should not redirect to '/beta/participate'", function() { it("should not redirect to '/beta/participate'", function() {
this.BetaProgramController.optOut(this.req, this.res, this.next) this.BetaProgramController.optOut(this.req, this.res, this.next)
return this.res.redirect.callCount.should.equal(0) this.res.redirect.callCount.should.equal(0)
}) })
it('should produce an error', function() { it('should produce an error', function() {
this.BetaProgramController.optOut(this.req, this.res, this.next) this.BetaProgramController.optOut(this.req, this.res, this.next)
this.next.callCount.should.equal(1) this.next.callCount.should.equal(1)
return this.next.firstCall.args[0].should.be.instanceof(Error) this.next.firstCall.args[0].should.be.instanceof(Error)
}) })
}) })
}) })
describe('optInPage', function() { describe('optInPage', function() {
beforeEach(function() { beforeEach(function() {
return this.UserGetter.getUser.callsArgWith(1, null, this.user) this.UserGetter.getUser.callsArgWith(1, null, this.user)
}) })
it('should render the opt-in page', function() { it('should render the opt-in page', function() {
this.BetaProgramController.optInPage(this.req, this.res, this.next) this.BetaProgramController.optInPage(this.req, this.res, this.next)
this.res.render.callCount.should.equal(1) this.res.render.callCount.should.equal(1)
const { args } = this.res.render.firstCall const { args } = this.res.render.firstCall
return args[0].should.equal('beta_program/opt_in') args[0].should.equal('beta_program/opt_in')
}) })
describe('when UserGetter.getUser produces an error', function() { describe('when UserGetter.getUser produces an error', function() {
beforeEach(function() { beforeEach(function() {
return this.UserGetter.getUser.callsArgWith(1, new Error('woops')) this.UserGetter.getUser.callsArgWith(1, new Error('woops'))
}) })
it('should not render the opt-in page', function() { it('should not render the opt-in page', function() {
this.BetaProgramController.optInPage(this.req, this.res, this.next) this.BetaProgramController.optInPage(this.req, this.res, this.next)
return this.res.render.callCount.should.equal(0) this.res.render.callCount.should.equal(0)
}) })
it('should produce an error', function() { it('should produce an error', function() {
this.BetaProgramController.optInPage(this.req, this.res, this.next) this.BetaProgramController.optInPage(this.req, this.res, this.next)
this.next.callCount.should.equal(1) this.next.callCount.should.equal(1)
return this.next.firstCall.args[0].should.be.instanceof(Error) this.next.firstCall.args[0].should.be.instanceof(Error)
}) })
}) })
}) })