Add UTM parameters to Dashboard login buttons (#7639)

* Add UTM parameters to Dashboard login buttons

* Revert "Add UTM parameters to Dashboard login buttons"

This reverts commit 568f12e3cb.

* Add UTM parameters to Dashboard login buttons

* utmCode camel case

Co-authored-by: Zach Bloomquist <github@chary.us>

* Add desktop-gui integration tests for utm code

* Add server unit tests for utm code

Co-authored-by: Zach Bloomquist <github@chary.us>
This commit is contained in:
Zach Panzarino
2020-06-19 16:53:01 -04:00
committed by GitHub
parent 869bcec55c
commit dc2b50d351
9 changed files with 48 additions and 10 deletions

View File

@@ -69,6 +69,12 @@ describe('Login', function () {
})
})
it('passes utm code when it triggers ipc \'begin:auth\'', function () {
cy.then(function () {
expect(this.ipc.beginAuth).to.be.calledWith('Nav Login Button')
})
})
it('disables login button', () => {
cy.get('@loginBtn').should('be.disabled')
})

View File

@@ -303,6 +303,18 @@ describe('Runs List', function () {
it('does not fetch runs', function () {
expect(this.ipc.getRuns).not.to.be.called
})
it('clicking Log In to Dashboard opens login', () => {
cy.contains('button', 'Log In to Dashboard').click().then(function () {
expect(this.ipc.beginAuth).to.be.calledOnce
})
})
it('clicking Log In to Dashboard passes utm code', () => {
cy.contains('button', 'Log In to Dashboard').click().then(function () {
expect(this.ipc.beginAuth).to.be.calledWith('Runs Tab Login Button')
})
})
})
context('without a project id', function () {
@@ -345,7 +357,7 @@ describe('Runs List', function () {
it('clicking Log In to Dashboard opens login', () => {
cy.contains('button', 'Log In to Dashboard').click().then(function () {
expect(this.ipc.beginAuth).to.be.called
expect(this.ipc.beginAuth).to.be.calledOnce
})
})
})

View File

@@ -21,12 +21,12 @@ class AuthApi {
})
}
login () {
login (utm) {
ipc.onAuthMessage((__, message) => {
authStore.setMessage(message)
})
return ipc.beginAuth()
return ipc.beginAuth(utm)
.then((user) => {
authStore.setUser(user)
authStore.setMessage(null)

View File

@@ -115,7 +115,7 @@ class LoginForm extends Component {
this.setState({ isLoggingIn: true })
authApi.login()
authApi.login(this.props.utm)
.then(() => {
this.props.onSuccess()
})

View File

@@ -70,7 +70,7 @@ class LoginContent extends Component {
<BootstrapModal.Dismiss className='btn btn-link close'>x</BootstrapModal.Dismiss>
<h1><i className='fas fa-lock'></i> Log In</h1>
<p>Logging in gives you access to the <a onClick={this._openDashboard}>Cypress Dashboard Service</a>. You can set up projects to be recorded and see test data from your project.</p>
<LoginForm onSuccess={() => this.setState({ succeeded: true })} />
<LoginForm utm='Nav Login Button' onSuccess={() => this.setState({ succeeded: true })} />
</div>
)
}

View File

@@ -284,7 +284,7 @@ class RunsList extends Component {
<img width='150' height='150' src='https://on.cypress.io/images/desktop-onboarding-thumb-3' />
</div>
<LoginForm />
<LoginForm utm='Runs Tab Login Button' />
</div>
)
}

View File

@@ -19,6 +19,7 @@ let authState
let openExternalAttempted = false
let authRedirectReached = false
let server
let utm
const _buildLoginRedirectUrl = (server) => {
const { port } = server.address()
@@ -26,7 +27,7 @@ const _buildLoginRedirectUrl = (server) => {
return `http://127.0.0.1:${port}/redirect-to-auth`
}
const _buildFullLoginUrl = (baseLoginUrl, server) => {
const _buildFullLoginUrl = (baseLoginUrl, server, utmCode) => {
const { port } = server.address()
if (!authState) {
@@ -45,6 +46,16 @@ const _buildFullLoginUrl = (baseLoginUrl, server) => {
platform: os.platform(),
}
if (utmCode) {
authUrl.query = {
utm_source: 'Test Runner',
utm_medium: 'Login Button',
utm_campaign: 'TR-Dashboard',
utm_content: utmCode,
...authUrl.query,
}
}
return authUrl.format()
})
}
@@ -58,7 +69,7 @@ const _getOriginFromUrl = (originalUrl) => {
/**
* @returns a promise that is resolved with a user when auth is complete or rejected when it fails
*/
const start = (onMessage) => {
const start = (onMessage, utmCode) => {
function sendMessage (type, name, arg1) {
onMessage({
type,
@@ -68,6 +79,7 @@ const start = (onMessage) => {
})
}
utm = utmCode
authRedirectReached = false
return user.getBaseLoginUrl()
@@ -110,7 +122,7 @@ const _launchServer = (baseLoginUrl, sendMessage) => {
app.get('/redirect-to-auth', (req, res) => {
authRedirectReached = true
_buildFullLoginUrl(baseLoginUrl, server)
_buildFullLoginUrl(baseLoginUrl, server, utm)
.then((fullLoginUrl) => {
debug('Received GET to /redirect-to-auth, redirecting: %o', { fullLoginUrl })

View File

@@ -155,7 +155,7 @@ const handleEvent = function (options, bus, event, id, type, arg) {
return bus.emit('auth:message', msg)
}
return auth.start(onMessage)
return auth.start(onMessage, arg)
.then(send)
.catch(sendErr)

View File

@@ -13,6 +13,7 @@ const RANDOM_STRING = 'a'.repeat(32)
const PORT = 9001
const REDIRECT_URL = `http://127.0.0.1:${PORT}/redirect-to-auth`
const FULL_LOGIN_URL = `https://foo.invalid/login.html?port=${PORT}&state=${RANDOM_STRING}&machineId=abc123&cypressVersion=${pkg.version}&platform=linux`
const FULL_LOGIN_URL_UTM = `https://foo.invalid/login.html?utm_source=Test%20Runner&utm_medium=Login%20Button&utm_campaign=TR-Dashboard&utm_content=Login%20Button&port=${PORT}&state=${RANDOM_STRING}&machineId=abc123&cypressVersion=${pkg.version}&platform=linux`
describe('lib/gui/auth', function () {
beforeEach(() => {
@@ -66,6 +67,13 @@ describe('lib/gui/auth', function () {
expect(random.id).to.be.calledOnce
})
})
it('uses utm code to form a trackable URL', function () {
return auth._buildFullLoginUrl(BASE_URL, this.server, 'Login Button')
.then((url) => {
expect(url).to.eq(FULL_LOGIN_URL_UTM)
})
})
})
context('._launchNativeAuth', function () {